Add sources for API 35

Downloaded from https://dl.google.com/android/repository/source-35_r01.zip
using SdkManager in Studio

Test: None
Change-Id: I83f78aa820b66edfdc9f8594d17bc7b6cacccec1
diff --git a/android-35/android/telephony/AccessNetworkConstants.java b/android-35/android/telephony/AccessNetworkConstants.java
new file mode 100644
index 0000000..35721f1
--- /dev/null
+++ b/android-35/android/telephony/AccessNetworkConstants.java
@@ -0,0 +1,978 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.hardware.radio.AccessNetwork;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
+
+/**
+ * Contains access network related constants.
+ */
+public final class AccessNetworkConstants {
+
+    private static final String TAG = AccessNetworkConstants.class.getSimpleName();
+
+    /**
+     * Wireless transportation type
+     *
+     * @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"TRANSPORT_TYPE_"},
+            value = {
+                    TRANSPORT_TYPE_INVALID,
+                    TRANSPORT_TYPE_WWAN,
+                    TRANSPORT_TYPE_WLAN})
+    public @interface TransportType {}
+
+    /**
+     * Invalid transport type
+     * @hide
+     */
+    @SystemApi
+    public static final int TRANSPORT_TYPE_INVALID = -1;
+
+    /**
+     * Transport type for Wireless Wide Area Networks (i.e. Cellular)
+     */
+    public static final int TRANSPORT_TYPE_WWAN = 1;
+
+    /**
+     * Transport type for Wireless Local Area Networks (i.e. Wifi)
+     */
+    public static final int TRANSPORT_TYPE_WLAN = 2;
+
+    /** @hide */
+    public static String transportTypeToString(@TransportType int transportType) {
+        switch (transportType) {
+            case TRANSPORT_TYPE_WWAN: return "WWAN";
+            case TRANSPORT_TYPE_WLAN: return "WLAN";
+            case TRANSPORT_TYPE_INVALID: return "INVALID";
+            default: return Integer.toString(transportType);
+        }
+    }
+
+    /**
+     * Access network type
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"RADIO_ACCESS_NETWORK_TYPE_"},
+            value = {
+                    AccessNetworkType.UNKNOWN,
+                    AccessNetworkType.GERAN,
+                    AccessNetworkType.UTRAN,
+                    AccessNetworkType.EUTRAN,
+                    AccessNetworkType.CDMA2000,
+                    AccessNetworkType.IWLAN,
+                    AccessNetworkType.NGRAN})
+    public @interface RadioAccessNetworkType {}
+
+    public static final class AccessNetworkType {
+        public static final int UNKNOWN = AccessNetwork.UNKNOWN;
+        public static final int GERAN = AccessNetwork.GERAN;
+        public static final int UTRAN = AccessNetwork.UTRAN;
+        public static final int EUTRAN = AccessNetwork.EUTRAN;
+        public static final int CDMA2000 = AccessNetwork.CDMA2000;
+        public static final int IWLAN = AccessNetwork.IWLAN;
+        public static final int NGRAN = AccessNetwork.NGRAN;
+
+        /** @hide */
+        private AccessNetworkType() {}
+
+        /** @hide */
+        public static String toString(int type) {
+            switch (type) {
+                case UNKNOWN: return "UNKNOWN";
+                case GERAN: return "GERAN";
+                case UTRAN: return "UTRAN";
+                case EUTRAN: return "EUTRAN";
+                case CDMA2000: return "CDMA2000";
+                case IWLAN: return "IWLAN";
+                case NGRAN: return "NGRAN";
+                default: return Integer.toString(type);
+            }
+        }
+
+        /** @hide */
+        public static @RadioAccessNetworkType int fromString(@NonNull String str) {
+            switch (str.toUpperCase(Locale.ROOT)) {
+                case "UNKNOWN": return UNKNOWN;
+                case "GERAN": return GERAN;
+                case "UTRAN": return UTRAN;
+                case "EUTRAN": return EUTRAN;
+                case "CDMA2000": return CDMA2000;
+                case "IWLAN": return IWLAN;
+                case "NGRAN": return NGRAN;
+                default:
+                    throw new IllegalArgumentException("Invalid access network type " + str);
+            }
+        }
+    }
+
+    /**
+     * Frequency bands for GERAN.
+     * http://www.etsi.org/deliver/etsi_ts/145000_145099/145005/14.00.00_60/ts_145005v140000p.pdf
+     */
+    public static final class GeranBand {
+        public static final int BAND_T380 = android.hardware.radio.network.GeranBands.BAND_T380;
+        public static final int BAND_T410 = android.hardware.radio.network.GeranBands.BAND_T410;
+        public static final int BAND_450 = android.hardware.radio.network.GeranBands.BAND_450;
+        public static final int BAND_480 = android.hardware.radio.network.GeranBands.BAND_480;
+        public static final int BAND_710 = android.hardware.radio.network.GeranBands.BAND_710;
+        public static final int BAND_750 = android.hardware.radio.network.GeranBands.BAND_750;
+        public static final int BAND_T810 = android.hardware.radio.network.GeranBands.BAND_T810;
+        public static final int BAND_850 = android.hardware.radio.network.GeranBands.BAND_850;
+        public static final int BAND_P900 = android.hardware.radio.network.GeranBands.BAND_P900;
+        public static final int BAND_E900 = android.hardware.radio.network.GeranBands.BAND_E900;
+        public static final int BAND_R900 = android.hardware.radio.network.GeranBands.BAND_R900;
+        public static final int BAND_DCS1800 =
+                android.hardware.radio.network.GeranBands.BAND_DCS1800;
+        public static final int BAND_PCS1900 =
+                android.hardware.radio.network.GeranBands.BAND_PCS1900;
+        public static final int BAND_ER900 = android.hardware.radio.network.GeranBands.BAND_ER900;
+
+        /**
+         * GeranBand
+         *
+         * @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"BAND_"},
+                value = {BAND_T380,
+                        BAND_T410,
+                        BAND_450,
+                        BAND_480,
+                        BAND_710,
+                        BAND_750,
+                        BAND_T810,
+                        BAND_850,
+                        BAND_P900,
+                        BAND_E900,
+                        BAND_R900,
+                        BAND_DCS1800,
+                        BAND_PCS1900,
+                        BAND_ER900})
+
+        public @interface GeranBands {}
+
+        /** @hide */
+        private GeranBand() {}
+    }
+
+    /**
+     * 3GPP TS 45.005 Table 2-1 Dynamically mapped ARFCN.
+     * 3GPP TS 45.005 Table 2-2 Fixed designation of ARFCN.
+     * @hide
+     */
+    enum GeranBandArfcnFrequency {
+
+        // Dynamically mapped ARFCN
+//        GERAN_ARFCN_FREQUENCY_BAND_T380(GeranBand.BAND_T380, 380.2, 0),
+//        GERAN_ARFCN_FREQUENCY_BAND_T410(GeranBand.BAND_T410, 410.2, 0),
+//        GERAN_ARFCN_FREQUENCY_BAND_710(GeranBand.BAND_710, 698, 0),
+//        GERAN_ARFCN_FREQUENCY_BAND_750(GeranBand.BAND_750, 747, 438, 30),
+//        GERAN_ARFCN_FREQUENCY_BAND_T810(GeranBand.BAND_T810, 806, 350),
+        // Fixed designation of ARFCN
+        GERAN_ARFCN_FREQUENCY_BAND_450(GeranBand.BAND_450, 450600, 259, 259, 293, 10),
+        GERAN_ARFCN_FREQUENCY_BAND_480(GeranBand.BAND_480, 479000, 306, 306, 340, 10),
+        GERAN_ARFCN_FREQUENCY_BAND_850(GeranBand.BAND_850, 824200, 128, 128, 251, 45),
+        GERAN_ARFCN_FREQUENCY_BAND_DCS1800(GeranBand.BAND_DCS1800, 1710200, 512, 512, 885, 95),
+        GERAN_ARFCN_FREQUENCY_BAND_PCS1900(GeranBand.BAND_PCS1900, 1850200, 512, 512, 810, 80),
+        GERAN_ARFCN_FREQUENCY_BAND_E900_1(GeranBand.BAND_E900, 890000, 0, 0, 124, 45),
+        GERAN_ARFCN_FREQUENCY_BAND_E900_2(GeranBand.BAND_E900, 890000, 1024, 975, 1023, 45),
+        GERAN_ARFCN_FREQUENCY_BAND_R900_1(GeranBand.BAND_R900, 890000, 0, 0, 124, 45),
+        GERAN_ARFCN_FREQUENCY_BAND_R900_2(GeranBand.BAND_R900, 890000, 1024, 955, 1023, 45),
+        GERAN_ARFCN_FREQUENCY_BAND_P900(GeranBand.BAND_P900, 890000, 0, 1, 124, 45),
+        GERAN_ARFCN_FREQUENCY_BAND_ER900_1(GeranBand.BAND_ER900, 890000, 0, 0, 124, 45),
+        GERAN_ARFCN_FREQUENCY_BAND_ER900_2(GeranBand.BAND_ER900, 890000, 1024, 940, 1023, 1024);
+
+        GeranBandArfcnFrequency(int band, int uplinkFrequencyFirstKhz, int arfcnOffset,
+                                int arfcnRangeFirst, int arfcnRangeLast, int downlinkOffset) {
+            this.band = band;
+            this.uplinkFrequencyFirst = uplinkFrequencyFirstKhz;
+            this.arfcnOffset = arfcnOffset;
+            this.arfcnRangeFirst = arfcnRangeFirst;
+            this.arfcnRangeLast = arfcnRangeLast;
+            this.downlinkOffset = downlinkOffset;
+        }
+
+        int band;
+        int uplinkFrequencyFirst;
+        int arfcnOffset;
+        int arfcnRangeFirst;
+        int arfcnRangeLast;
+        int downlinkOffset;
+    }
+
+    /**
+     * Frequency bands for UTRAN.
+     * http://www.etsi.org/deliver/etsi_ts/125100_125199/125104/13.03.00_60/ts_125104v130p.pdf
+     */
+    public static final class UtranBand {
+        public static final int BAND_1 = android.hardware.radio.network.UtranBands.BAND_1;
+        public static final int BAND_2 = android.hardware.radio.network.UtranBands.BAND_2;
+        public static final int BAND_3 = android.hardware.radio.network.UtranBands.BAND_3;
+        public static final int BAND_4 = android.hardware.radio.network.UtranBands.BAND_4;
+        public static final int BAND_5 = android.hardware.radio.network.UtranBands.BAND_5;
+        public static final int BAND_6 = android.hardware.radio.network.UtranBands.BAND_6;
+        public static final int BAND_7 = android.hardware.radio.network.UtranBands.BAND_7;
+        public static final int BAND_8 = android.hardware.radio.network.UtranBands.BAND_8;
+        public static final int BAND_9 = android.hardware.radio.network.UtranBands.BAND_9;
+        public static final int BAND_10 = android.hardware.radio.network.UtranBands.BAND_10;
+        public static final int BAND_11 = android.hardware.radio.network.UtranBands.BAND_11;
+        public static final int BAND_12 = android.hardware.radio.network.UtranBands.BAND_12;
+        public static final int BAND_13 = android.hardware.radio.network.UtranBands.BAND_13;
+        public static final int BAND_14 = android.hardware.radio.network.UtranBands.BAND_14;
+        // band 15, 16, 17, 18 are reserved
+        public static final int BAND_19 = android.hardware.radio.network.UtranBands.BAND_19;
+        public static final int BAND_20 = android.hardware.radio.network.UtranBands.BAND_20;
+        public static final int BAND_21 = android.hardware.radio.network.UtranBands.BAND_21;
+        public static final int BAND_22 = android.hardware.radio.network.UtranBands.BAND_22;
+        // band 23, 24 are reserved
+        public static final int BAND_25 = android.hardware.radio.network.UtranBands.BAND_25;
+        public static final int BAND_26 = android.hardware.radio.network.UtranBands.BAND_26;
+
+        // Frequency bands for TD-SCDMA. Defined in 3GPP TS 25.102, Table 5.2.
+
+        /**
+         * Band A
+         * 1900 - 1920 MHz: Uplink and downlink transmission
+         * 2010 - 2025 MHz: Uplink and downlink transmission
+         */
+        public static final int BAND_A = android.hardware.radio.network.UtranBands.BAND_A;
+
+        /**
+         * Band B
+         * 1850 - 1910 MHz: Uplink and downlink transmission
+         * 1930 - 1990 MHz: Uplink and downlink transmission
+         */
+        public static final int BAND_B = android.hardware.radio.network.UtranBands.BAND_B;
+
+        /**
+         * Band C
+         * 1910 - 1930 MHz: Uplink and downlink transmission
+         */
+        public static final int BAND_C = android.hardware.radio.network.UtranBands.BAND_C;
+
+        /**
+         * Band D
+         * 2570 - 2620 MHz: Uplink and downlink transmission
+         */
+        public static final int BAND_D = android.hardware.radio.network.UtranBands.BAND_D;
+
+        /**
+         * Band E
+         * 2300—2400 MHz: Uplink and downlink transmission
+         */
+        public static final int BAND_E = android.hardware.radio.network.UtranBands.BAND_E;
+
+        /**
+         * Band F
+         * 1880 - 1920 MHz: Uplink and downlink transmission
+         */
+        public static final int BAND_F = android.hardware.radio.network.UtranBands.BAND_F;
+
+        /**
+         * UtranBand
+         *
+         * @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"BAND_"},
+                value = {BAND_1,
+                        BAND_2,
+                        BAND_3,
+                        BAND_4,
+                        BAND_5,
+                        BAND_6,
+                        BAND_7,
+                        BAND_8,
+                        BAND_9,
+                        BAND_10,
+                        BAND_11,
+                        BAND_12,
+                        BAND_13,
+                        BAND_14,
+                        BAND_19,
+                        BAND_20,
+                        BAND_21,
+                        BAND_22,
+                        BAND_25,
+                        BAND_26,
+                        BAND_A,
+                        BAND_B,
+                        BAND_C,
+                        BAND_D,
+                        BAND_E,
+                        BAND_F})
+
+        public @interface UtranBands {}
+
+        /** @hide */
+        private UtranBand() {}
+    }
+
+    /**
+     * 3GPP TS 25.101, Table 5.1 UARFCN definition (general)
+     * 3GPP TS 25.102, Table 5.2 UTRA Absolute Radio Frequency Channel Number 1.28 Mcps TDD Option.
+     *
+     * @hide
+     */
+    enum UtranBandArfcnFrequency {
+
+        UTRAN_ARFCN_FREQUENCY_BAND_1(UtranBand.BAND_1, 0, 10562, 10838, 0, 9612, 9888),
+        UTRAN_ARFCN_FREQUENCY_BAND_2(UtranBand.BAND_2, 0, 9662, 9938, 0, 9262, 9538),
+        UTRAN_ARFCN_FREQUENCY_BAND_3(UtranBand.BAND_3, 1575000, 1162, 1513, 1525000, 937, 1288),
+        UTRAN_ARFCN_FREQUENCY_BAND_4(UtranBand.BAND_4, 1805000, 1537, 1738, 1450000, 1312, 1513),
+        UTRAN_ARFCN_FREQUENCY_BAND_5(UtranBand.BAND_5, 0, 4357, 4458, 0, 4132, 4233),
+        UTRAN_ARFCN_FREQUENCY_BAND_6(UtranBand.BAND_6, 0, 4387, 4413, 0, 4162, 4188),
+        UTRAN_ARFCN_FREQUENCY_BAND_7(UtranBand.BAND_7, 2175000, 2237, 2563, 2100000, 2012, 2338),
+        UTRAN_ARFCN_FREQUENCY_BAND_8(UtranBand.BAND_8, 340000, 2937, 3088, 340000, 2712, 2863),
+        UTRAN_ARFCN_FREQUENCY_BAND_9(UtranBand.BAND_9, 0, 9327, 9837, 0, 8762, 8912),
+        UTRAN_ARFCN_FREQUENCY_BAND_10(UtranBand.BAND_10, 1490000, 3112, 3388, 1135000, 2887, 3163),
+        UTRAN_ARFCN_FREQUENCY_BAND_11(UtranBand.BAND_11, 736000, 3712, 3787, 733000, 3487, 3562),
+        UTRAN_ARFCN_FREQUENCY_BAND_12(UtranBand.BAND_12, -37000, 3842, 3903, -22000, 3617, 3678),
+        UTRAN_ARFCN_FREQUENCY_BAND_13(UtranBand.BAND_13, -55000, 4017, 4043, 21000, 3792, 3818),
+        UTRAN_ARFCN_FREQUENCY_BAND_14(UtranBand.BAND_14, -63000, 4117, 4143, 12000, 3892, 3918),
+        UTRAN_ARFCN_FREQUENCY_BAND_19(UtranBand.BAND_19, 735000, 712, 763, 770000, 312, 363),
+        UTRAN_ARFCN_FREQUENCY_BAND_20(UtranBand.BAND_20, -109000, 4512, 4638, -23000, 4287, 4413),
+        UTRAN_ARFCN_FREQUENCY_BAND_21(UtranBand.BAND_21, 1326000, 862, 912, 1358000, 462, 512),
+        UTRAN_ARFCN_FREQUENCY_BAND_22(UtranBand.BAND_22, 2580000, 4662, 5038, 2525000, 4437, 4813),
+        UTRAN_ARFCN_FREQUENCY_BAND_25(UtranBand.BAND_25, 910000, 5112, 5413, 875000, 4887, 5188),
+        UTRAN_ARFCN_FREQUENCY_BAND_A(UtranBand.BAND_A, 0, 10054, 10121, 0, 9504, 9596),
+        UTRAN_ARFCN_FREQUENCY_BAND_B(UtranBand.BAND_B, 0, 9654, 9946, 0, 9254, 9546),
+        UTRAN_ARFCN_FREQUENCY_BAND_C(UtranBand.BAND_C, 0, 0, 0, 0, 9554, 9646),
+        UTRAN_ARFCN_FREQUENCY_BAND_D(UtranBand.BAND_D, 0, 0, 0, 0, 12854, 13096),
+        UTRAN_ARFCN_FREQUENCY_BAND_E(UtranBand.BAND_E, 0, 0, 0, 0, 11504, 11996),
+        UTRAN_ARFCN_FREQUENCY_BAND_F(UtranBand.BAND_F, 0, 0, 0, 0, 9404, 9596);
+
+        UtranBandArfcnFrequency(int band, int downlinkOffsetKhz, int downlinkRangeFirst,
+                                int downlinkRangeLast, int uplinkOffsetKhz, int uplinkRangeFirst,
+                                int uplinkRangeLast) {
+            this.band = band;
+            this.downlinkOffset = downlinkOffsetKhz;
+            this.downlinkRangeFirst = downlinkRangeFirst;
+            this.downlinkRangeLast = downlinkRangeLast;
+            this.uplinkOffset = uplinkOffsetKhz;
+            this.uplinkRangeFirst = uplinkRangeFirst;
+            this.uplinkRangeLast = uplinkRangeLast;
+        }
+
+        int band;
+        int downlinkOffset;
+        int downlinkRangeFirst;
+        int downlinkRangeLast;
+        int uplinkOffset;
+        int uplinkRangeFirst;
+        int uplinkRangeLast;
+    }
+
+    /**
+     * Frequency bands for EUTRAN.
+     * 3GPP TS 36.101, Version 16.4.0, Table 5.5: Operating bands
+     * https://www.etsi.org/deliver/etsi_ts/136100_136199/136101/15.09.00_60/ts_136101v150900p.pdf
+     */
+    public static final class EutranBand {
+        public static final int BAND_1 = android.hardware.radio.network.EutranBands.BAND_1;
+        public static final int BAND_2 = android.hardware.radio.network.EutranBands.BAND_2;
+        public static final int BAND_3 = android.hardware.radio.network.EutranBands.BAND_3;
+        public static final int BAND_4 = android.hardware.radio.network.EutranBands.BAND_4;
+        public static final int BAND_5 = android.hardware.radio.network.EutranBands.BAND_5;
+        public static final int BAND_6 = android.hardware.radio.network.EutranBands.BAND_6;
+        public static final int BAND_7 = android.hardware.radio.network.EutranBands.BAND_7;
+        public static final int BAND_8 = android.hardware.radio.network.EutranBands.BAND_8;
+        public static final int BAND_9 = android.hardware.radio.network.EutranBands.BAND_9;
+        public static final int BAND_10 = android.hardware.radio.network.EutranBands.BAND_10;
+        public static final int BAND_11 = android.hardware.radio.network.EutranBands.BAND_11;
+        public static final int BAND_12 = android.hardware.radio.network.EutranBands.BAND_12;
+        public static final int BAND_13 = android.hardware.radio.network.EutranBands.BAND_13;
+        public static final int BAND_14 = android.hardware.radio.network.EutranBands.BAND_14;
+        public static final int BAND_17 = android.hardware.radio.network.EutranBands.BAND_17;
+        public static final int BAND_18 = android.hardware.radio.network.EutranBands.BAND_18;
+        public static final int BAND_19 = android.hardware.radio.network.EutranBands.BAND_19;
+        public static final int BAND_20 = android.hardware.radio.network.EutranBands.BAND_20;
+        public static final int BAND_21 = android.hardware.radio.network.EutranBands.BAND_21;
+        public static final int BAND_22 = android.hardware.radio.network.EutranBands.BAND_22;
+        public static final int BAND_23 = android.hardware.radio.network.EutranBands.BAND_23;
+        public static final int BAND_24 = android.hardware.radio.network.EutranBands.BAND_24;
+        public static final int BAND_25 = android.hardware.radio.network.EutranBands.BAND_25;
+        public static final int BAND_26 = android.hardware.radio.network.EutranBands.BAND_26;
+        public static final int BAND_27 = android.hardware.radio.network.EutranBands.BAND_27;
+        public static final int BAND_28 = android.hardware.radio.network.EutranBands.BAND_28;
+        public static final int BAND_30 = android.hardware.radio.network.EutranBands.BAND_30;
+        public static final int BAND_31 = android.hardware.radio.network.EutranBands.BAND_31;
+        public static final int BAND_33 = android.hardware.radio.network.EutranBands.BAND_33;
+        public static final int BAND_34 = android.hardware.radio.network.EutranBands.BAND_34;
+        public static final int BAND_35 = android.hardware.radio.network.EutranBands.BAND_35;
+        public static final int BAND_36 = android.hardware.radio.network.EutranBands.BAND_36;
+        public static final int BAND_37 = android.hardware.radio.network.EutranBands.BAND_37;
+        public static final int BAND_38 = android.hardware.radio.network.EutranBands.BAND_38;
+        public static final int BAND_39 = android.hardware.radio.network.EutranBands.BAND_39;
+        public static final int BAND_40 = android.hardware.radio.network.EutranBands.BAND_40;
+        public static final int BAND_41 = android.hardware.radio.network.EutranBands.BAND_41;
+        public static final int BAND_42 = android.hardware.radio.network.EutranBands.BAND_42;
+        public static final int BAND_43 = android.hardware.radio.network.EutranBands.BAND_43;
+        public static final int BAND_44 = android.hardware.radio.network.EutranBands.BAND_44;
+        public static final int BAND_45 = android.hardware.radio.network.EutranBands.BAND_45;
+        public static final int BAND_46 = android.hardware.radio.network.EutranBands.BAND_46;
+        public static final int BAND_47 = android.hardware.radio.network.EutranBands.BAND_47;
+        public static final int BAND_48 = android.hardware.radio.network.EutranBands.BAND_48;
+        public static final int BAND_49 = android.hardware.radio.network.EutranBands.BAND_49;
+        public static final int BAND_50 = android.hardware.radio.network.EutranBands.BAND_50;
+        public static final int BAND_51 = android.hardware.radio.network.EutranBands.BAND_51;
+        public static final int BAND_52 = android.hardware.radio.network.EutranBands.BAND_52;
+        public static final int BAND_53 = android.hardware.radio.network.EutranBands.BAND_53;
+        public static final int BAND_65 = android.hardware.radio.network.EutranBands.BAND_65;
+        public static final int BAND_66 = android.hardware.radio.network.EutranBands.BAND_66;
+        public static final int BAND_68 = android.hardware.radio.network.EutranBands.BAND_68;
+        public static final int BAND_70 = android.hardware.radio.network.EutranBands.BAND_70;
+        public static final int BAND_71 = android.hardware.radio.network.EutranBands.BAND_71;
+        public static final int BAND_72 = android.hardware.radio.network.EutranBands.BAND_72;
+        public static final int BAND_73 = android.hardware.radio.network.EutranBands.BAND_73;
+        public static final int BAND_74 = android.hardware.radio.network.EutranBands.BAND_74;
+        public static final int BAND_85 = android.hardware.radio.network.EutranBands.BAND_85;
+        public static final int BAND_87 = android.hardware.radio.network.EutranBands.BAND_87;
+        public static final int BAND_88 = android.hardware.radio.network.EutranBands.BAND_88;
+
+        /**
+         * EutranBands
+         *
+         * @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"BAND_"},
+                value = {BAND_1,
+                        BAND_2,
+                        BAND_3,
+                        BAND_4,
+                        BAND_5,
+                        BAND_6,
+                        BAND_7,
+                        BAND_8,
+                        BAND_9,
+                        BAND_10,
+                        BAND_11,
+                        BAND_12,
+                        BAND_13,
+                        BAND_14,
+                        BAND_17,
+                        BAND_18,
+                        BAND_19,
+                        BAND_20,
+                        BAND_21,
+                        BAND_22,
+                        BAND_23,
+                        BAND_24,
+                        BAND_25,
+                        BAND_26,
+                        BAND_27,
+                        BAND_28,
+                        BAND_30,
+                        BAND_31,
+                        BAND_33,
+                        BAND_34,
+                        BAND_35,
+                        BAND_36,
+                        BAND_37,
+                        BAND_38,
+                        BAND_39,
+                        BAND_40,
+                        BAND_41,
+                        BAND_42,
+                        BAND_43,
+                        BAND_44,
+                        BAND_45,
+                        BAND_46,
+                        BAND_47,
+                        BAND_48,
+                        BAND_49,
+                        BAND_50,
+                        BAND_51,
+                        BAND_52,
+                        BAND_53,
+                        BAND_65,
+                        BAND_66,
+                        BAND_68,
+                        BAND_70,
+                        BAND_71,
+                        BAND_72,
+                        BAND_73,
+                        BAND_74,
+                        BAND_85,
+                        BAND_87,
+                        BAND_88})
+
+        public @interface EutranBands {}
+
+        /** @hide */
+        private EutranBand() {};
+    }
+
+    /**
+     * 3GPP TS 36.101 Table 5.7.3-1 E-UTRA channel numbers.
+     *
+     * @hide
+     */
+    enum EutranBandArfcnFrequency {
+
+        EUTRAN_ARFCN_FREQUENCY_BAND_1(
+                EutranBand.BAND_1, 2110000, 0, 599, 1920000, 18800, 18599),
+        EUTRAN_ARFCN_FREQUENCY_BAND_2(
+                EutranBand.BAND_2, 1930000, 600, 1199, 1850000, 18600, 19199),
+        EUTRAN_ARFCN_FREQUENCY_BAND_3(
+                EutranBand.BAND_3, 1805000, 1200, 1949, 1710000, 19200, 19949),
+        EUTRAN_ARFCN_FREQUENCY_BAND_4(
+                EutranBand.BAND_4, 2110000, 1950, 2399, 1710000, 19950, 20399),
+        EUTRAN_ARFCN_FREQUENCY_BAND_5(
+                EutranBand.BAND_5, 869000, 2400, 2649, 824000, 20400, 20649),
+        EUTRAN_ARFCN_FREQUENCY_BAND_6(
+                EutranBand.BAND_6, 875000, 2650, 2749, 830000, 20650, 20749),
+        EUTRAN_ARFCN_FREQUENCY_BAND_7(
+                EutranBand.BAND_7, 2620000, 2750, 3449, 2500000, 20750, 21449),
+        EUTRAN_ARFCN_FREQUENCY_BAND_8(
+                EutranBand.BAND_8, 925000, 3450, 3799, 880000, 21450, 21799),
+        EUTRAN_ARFCN_FREQUENCY_BAND_9(
+                EutranBand.BAND_9, 1844900, 3800, 4149, 1749900, 21800, 22149),
+        EUTRAN_ARFCN_FREQUENCY_BAND_10(
+                EutranBand.BAND_10, 2110000, 4150, 4749, 1710000, 22150, 22749),
+        EUTRAN_ARFCN_FREQUENCY_BAND_11(
+                EutranBand.BAND_11, 1475900, 4750, 4949, 1427900, 22750, 22949),
+        EUTRAN_ARFCN_FREQUENCY_BAND_12(
+                EutranBand.BAND_12, 729000, 5010, 5179, 699000, 23010, 23179),
+        EUTRAN_ARFCN_FREQUENCY_BAND_13(
+                EutranBand.BAND_13, 746000, 5180, 5279, 777000, 23180, 23279),
+        EUTRAN_ARFCN_FREQUENCY_BAND_14(
+                EutranBand.BAND_14, 758000, 5280, 5379, 788000, 23230, 23379),
+        EUTRAN_ARFCN_FREQUENCY_BAND_17(
+                EutranBand.BAND_17, 734000, 5730, 5849, 704000, 23730, 23849),
+        EUTRAN_ARFCN_FREQUENCY_BAND_18(
+                EutranBand.BAND_18, 860000, 5850, 5999, 815000, 23850, 23999),
+        EUTRAN_ARFCN_FREQUENCY_BAND_19(
+                EutranBand.BAND_19, 875000, 6000, 6149, 830000, 24000, 24149),
+        EUTRAN_ARFCN_FREQUENCY_BAND_20(
+                EutranBand.BAND_20, 791000, 6150, 6449, 832000, 24150, 24449),
+        EUTRAN_ARFCN_FREQUENCY_BAND_21(
+                EutranBand.BAND_21, 1495900, 6450, 6599, 1447900, 24450, 24599),
+        EUTRAN_ARFCN_FREQUENCY_BAND_22(
+                EutranBand.BAND_22, 3510000, 6600, 7399, 3410000, 24600, 25399),
+        EUTRAN_ARFCN_FREQUENCY_BAND_23(
+                EutranBand.BAND_23, 2180000, 7500, 7699, 2000000, 25500, 25699),
+        EUTRAN_ARFCN_FREQUENCY_BAND_24(
+                EutranBand.BAND_24, 1525000, 7700, 8039, 1626500, 25700, 26039),
+        EUTRAN_ARFCN_FREQUENCY_BAND_25(
+                EutranBand.BAND_25, 1930000, 8040, 8689, 1850000, 26040, 26689),
+        EUTRAN_ARFCN_FREQUENCY_BAND_26(
+                EutranBand.BAND_26, 859000, 8690, 9039, 814000, 26690, 27039),
+        EUTRAN_ARFCN_FREQUENCY_BAND_27(
+                EutranBand.BAND_27, 852000, 9040, 9209, 807000, 27040, 27209),
+        EUTRAN_ARFCN_FREQUENCY_BAND_28(
+                EutranBand.BAND_28, 758000, 9210, 9659, 703000, 27210, 27659),
+        EUTRAN_ARFCN_FREQUENCY_BAND_30(
+                EutranBand.BAND_30, 2350000, 9770, 9869, 2305000, 27660, 27759),
+        EUTRAN_ARFCN_FREQUENCY_BAND_31(
+                EutranBand.BAND_31, 462500, 9870, 9919, 452500, 27760, 27809),
+        EUTRAN_ARFCN_FREQUENCY_BAND_33(
+                EutranBand.BAND_33, 1900000, 36000, 36199, 1900000, 36000, 36199),
+        EUTRAN_ARFCN_FREQUENCY_BAND_34(
+                EutranBand.BAND_34, 2010000, 36200, 36349, 2010000, 36200, 36349),
+        EUTRAN_ARFCN_FREQUENCY_BAND_35(
+                EutranBand.BAND_35, 1850000, 36350, 36949, 1850000, 36350, 36949),
+        EUTRAN_ARFCN_FREQUENCY_BAND_36(
+                EutranBand.BAND_36, 1930000, 36950, 37549, 1930000, 36950, 37549),
+        EUTRAN_ARFCN_FREQUENCY_BAND_37(
+                EutranBand.BAND_37, 1910000, 37550, 37749, 1910000, 37550, 37749),
+        EUTRAN_ARFCN_FREQUENCY_BAND_38(
+                EutranBand.BAND_38, 2570000, 37750, 38249, 2570000, 37750, 38249),
+        EUTRAN_ARFCN_FREQUENCY_BAND_39(
+                EutranBand.BAND_39, 1880000, 38250, 38649, 1880000, 38250, 38649),
+        EUTRAN_ARFCN_FREQUENCY_BAND_40(
+                EutranBand.BAND_40, 2300000, 38650, 39649, 2300000, 38650, 39649),
+        EUTRAN_ARFCN_FREQUENCY_BAND_41(
+                EutranBand.BAND_41, 2496000, 39650, 41589, 2496000, 39650, 41589),
+        EUTRAN_ARFCN_FREQUENCY_BAND_42(
+                EutranBand.BAND_42, 3400000, 41590, 43589, 3400000, 41590, 43589),
+        EUTRAN_ARFCN_FREQUENCY_BAND_43(
+                EutranBand.BAND_43, 3600000, 43590, 45589, 3600000, 43590, 45589),
+        EUTRAN_ARFCN_FREQUENCY_BAND_44(
+                EutranBand.BAND_44, 703000, 45590, 46589, 703000, 45590, 46589),
+        EUTRAN_ARFCN_FREQUENCY_BAND_45(
+                EutranBand.BAND_45, 1447000, 46590, 46789, 1447000, 46590, 46789),
+        EUTRAN_ARFCN_FREQUENCY_BAND_46(
+                EutranBand.BAND_46, 5150000, 46790, 54539, 5150000, 46790, 54539),
+        EUTRAN_ARFCN_FREQUENCY_BAND_47(
+                EutranBand.BAND_47, 5855000, 54540, 55239, 5855000, 54540, 55239),
+        EUTRAN_ARFCN_FREQUENCY_BAND_48(
+                EutranBand.BAND_48, 3550000, 55240, 56739, 3550000, 55240, 56739),
+        EUTRAN_ARFCN_FREQUENCY_BAND_49(
+                EutranBand.BAND_49, 3550000, 56740, 58239, 3550000, 56740, 58239),
+        EUTRAN_ARFCN_FREQUENCY_BAND_50(
+                EutranBand.BAND_50, 1432000, 58240, 59089, 1432000, 58240, 59089),
+        EUTRAN_ARFCN_FREQUENCY_BAND_51(
+                EutranBand.BAND_51, 1427000, 59090, 59139, 1427000, 59090, 59139),
+        EUTRAN_ARFCN_FREQUENCY_BAND_52(
+                EutranBand.BAND_52, 3300000, 59140, 60139, 3300000, 59140, 60139),
+        EUTRAN_ARFCN_FREQUENCY_BAND_53(
+                EutranBand.BAND_53, 2483500, 60140, 60254, 2483500, 60140, 60254),
+        EUTRAN_ARFCN_FREQUENCY_BAND_65(
+                EutranBand.BAND_65, 2110000, 65536, 66435, 1920000, 131072, 131971),
+        EUTRAN_ARFCN_FREQUENCY_BAND_66(
+                EutranBand.BAND_66, 2110000, 66436, 67335, 1710000, 131972, 132671),
+        EUTRAN_ARFCN_FREQUENCY_BAND_68(
+                EutranBand.BAND_68, 753000, 67536, 67835, 698000, 132672, 132971),
+        EUTRAN_ARFCN_FREQUENCY_BAND_70(
+                EutranBand.BAND_70, 1995000, 68336, 68585, 1695000, 132972, 133121),
+        EUTRAN_ARFCN_FREQUENCY_BAND_71(
+                EutranBand.BAND_71, 617000, 68586, 68935, 663000, 133122, 133471),
+        EUTRAN_ARFCN_FREQUENCY_BAND_72(
+                EutranBand.BAND_72, 461000, 68936, 68985, 451000, 133472, 133521),
+        EUTRAN_ARFCN_FREQUENCY_BAND_73(
+                EutranBand.BAND_73, 460000, 68986, 69035, 450000, 133522, 133571),
+        EUTRAN_ARFCN_FREQUENCY_BAND_74(
+                EutranBand.BAND_74, 1475000, 69036, 69465, 1427000, 133572, 134001),
+        EUTRAN_ARFCN_FREQUENCY_BAND_85(
+                EutranBand.BAND_85, 728000, 70366, 70545, 698000, 134002, 134181),
+        EUTRAN_ARFCN_FREQUENCY_BAND_87(
+                EutranBand.BAND_87, 420000, 70546, 70595, 410000, 134182, 134231),
+        EUTRAN_ARFCN_FREQUENCY_BAND_88(
+                EutranBand.BAND_88, 422000, 70596, 70645, 412000, 134231, 134280);
+
+        EutranBandArfcnFrequency(int band, int downlinkLowKhz, int downlinkOffset,
+                                 int downlinkRange, int uplinkLowKhz, int uplinkOffset,
+                                 int uplinkRange) {
+            this.band = band;
+            this.downlinkLowKhz = downlinkLowKhz;
+            this.downlinkOffset = downlinkOffset;
+            this.downlinkRange = downlinkRange;
+            this.uplinkLowKhz = uplinkLowKhz;
+            this.uplinkOffset = uplinkOffset;
+            this.uplinkRange = uplinkRange;
+        }
+
+        int band;
+        int downlinkLowKhz;
+        int downlinkOffset;
+        int downlinkRange;
+        int uplinkLowKhz;
+        int uplinkOffset;
+        int uplinkRange;
+    }
+
+    /**
+     * Frequency bands for CDMA2000.
+     * http://www.3gpp2.org/Public_html/Specs/C.S0057-E_v1.0_Bandclass_Specification.pdf
+     * @hide
+     *
+     * TODO(yinxu): Check with the nexus team about the definition of CDMA bands.
+     */
+    public static final class CdmaBands {
+        public static final int BAND_0 = 1;
+        public static final int BAND_1 = 2;
+        public static final int BAND_2 = 3;
+        public static final int BAND_3 = 4;
+        public static final int BAND_4 = 5;
+        public static final int BAND_5 = 6;
+        public static final int BAND_6 = 7;
+        public static final int BAND_7 = 8;
+        public static final int BAND_8 = 9;
+        public static final int BAND_9 = 10;
+        public static final int BAND_10 = 11;
+        public static final int BAND_11 = 12;
+        public static final int BAND_12 = 13;
+        public static final int BAND_13 = 14;
+        public static final int BAND_14 = 15;
+        public static final int BAND_15 = 16;
+        public static final int BAND_16 = 17;
+        public static final int BAND_17 = 18;
+        public static final int BAND_18 = 19;
+        public static final int BAND_19 = 20;
+        public static final int BAND_20 = 21;
+        public static final int BAND_21 = 22;
+
+        /** @hide */
+        private CdmaBands() {}
+    }
+
+    /**
+     * Frequency bands for NGRAN
+     * https://www.etsi.org/deliver/etsi_ts/138100_138199/13810101/15.08.02_60/ts_13810101v150802p.pdf
+     * https://www.etsi.org/deliver/etsi_ts/138100_138199/13810102/15.08.00_60/ts_13810102v150800p.pdf
+     */
+    public static final class NgranBands {
+        /** 3GPP TS 38.101-1, Version 16.5.0, Table 5.2-1: FR1 bands */
+        public static final int BAND_1 = android.hardware.radio.network.NgranBands.BAND_1;
+        public static final int BAND_2 = android.hardware.radio.network.NgranBands.BAND_2;
+        public static final int BAND_3 = android.hardware.radio.network.NgranBands.BAND_3;
+        public static final int BAND_5 = android.hardware.radio.network.NgranBands.BAND_5;
+        public static final int BAND_7 = android.hardware.radio.network.NgranBands.BAND_7;
+        public static final int BAND_8 = android.hardware.radio.network.NgranBands.BAND_8;
+        public static final int BAND_12 = android.hardware.radio.network.NgranBands.BAND_12;
+        public static final int BAND_14 = android.hardware.radio.network.NgranBands.BAND_14;
+        public static final int BAND_18 = android.hardware.radio.network.NgranBands.BAND_18;
+        public static final int BAND_20 = android.hardware.radio.network.NgranBands.BAND_20;
+        public static final int BAND_25 = android.hardware.radio.network.NgranBands.BAND_25;
+        public static final int BAND_26 = android.hardware.radio.network.NgranBands.BAND_26;
+        public static final int BAND_28 = android.hardware.radio.network.NgranBands.BAND_28;
+        public static final int BAND_29 = android.hardware.radio.network.NgranBands.BAND_29;
+        public static final int BAND_30 = android.hardware.radio.network.NgranBands.BAND_30;
+        public static final int BAND_34 = android.hardware.radio.network.NgranBands.BAND_34;
+        public static final int BAND_38 = android.hardware.radio.network.NgranBands.BAND_38;
+        public static final int BAND_39 = android.hardware.radio.network.NgranBands.BAND_39;
+        public static final int BAND_40 = android.hardware.radio.network.NgranBands.BAND_40;
+        public static final int BAND_41 = android.hardware.radio.network.NgranBands.BAND_41;
+        public static final int BAND_46 = android.hardware.radio.network.NgranBands.BAND_46;
+        public static final int BAND_48 = android.hardware.radio.network.NgranBands.BAND_48;
+        public static final int BAND_50 = android.hardware.radio.network.NgranBands.BAND_50;
+        public static final int BAND_51 = android.hardware.radio.network.NgranBands.BAND_51;
+        public static final int BAND_53 = android.hardware.radio.network.NgranBands.BAND_53;
+        public static final int BAND_65 = android.hardware.radio.network.NgranBands.BAND_65;
+        public static final int BAND_66 = android.hardware.radio.network.NgranBands.BAND_66;
+        public static final int BAND_70 = android.hardware.radio.network.NgranBands.BAND_70;
+        public static final int BAND_71 = android.hardware.radio.network.NgranBands.BAND_71;
+        public static final int BAND_74 = android.hardware.radio.network.NgranBands.BAND_74;
+        public static final int BAND_75 = android.hardware.radio.network.NgranBands.BAND_75;
+        public static final int BAND_76 = android.hardware.radio.network.NgranBands.BAND_76;
+        public static final int BAND_77 = android.hardware.radio.network.NgranBands.BAND_77;
+        public static final int BAND_78 = android.hardware.radio.network.NgranBands.BAND_78;
+        public static final int BAND_79 = android.hardware.radio.network.NgranBands.BAND_79;
+        public static final int BAND_80 = android.hardware.radio.network.NgranBands.BAND_80;
+        public static final int BAND_81 = android.hardware.radio.network.NgranBands.BAND_81;
+        public static final int BAND_82 = android.hardware.radio.network.NgranBands.BAND_82;
+        public static final int BAND_83 = android.hardware.radio.network.NgranBands.BAND_83;
+        public static final int BAND_84 = android.hardware.radio.network.NgranBands.BAND_84;
+        public static final int BAND_86 = android.hardware.radio.network.NgranBands.BAND_86;
+        public static final int BAND_89 = android.hardware.radio.network.NgranBands.BAND_89;
+        public static final int BAND_90 = android.hardware.radio.network.NgranBands.BAND_90;
+        public static final int BAND_91 = android.hardware.radio.network.NgranBands.BAND_91;
+        public static final int BAND_92 = android.hardware.radio.network.NgranBands.BAND_92;
+        public static final int BAND_93 = android.hardware.radio.network.NgranBands.BAND_93;
+        public static final int BAND_94 = android.hardware.radio.network.NgranBands.BAND_94;
+        public static final int BAND_95 = android.hardware.radio.network.NgranBands.BAND_95;
+        public static final int BAND_96 = android.hardware.radio.network.NgranBands.BAND_96;
+
+        /** 3GPP TS 38.101-2, Version 16.2.0, Table 5.2-1: FR2 bands */
+        public static final int BAND_257 = android.hardware.radio.network.NgranBands.BAND_257;
+        public static final int BAND_258 = android.hardware.radio.network.NgranBands.BAND_258;
+        public static final int BAND_260 = android.hardware.radio.network.NgranBands.BAND_260;
+        public static final int BAND_261 = android.hardware.radio.network.NgranBands.BAND_261;
+
+        /**
+         * NR Bands
+         *
+         * @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"BAND_"},
+                value = {BAND_1,
+                        BAND_2,
+                        BAND_3,
+                        BAND_5,
+                        BAND_7,
+                        BAND_8,
+                        BAND_12,
+                        BAND_14,
+                        BAND_18,
+                        BAND_20,
+                        BAND_25,
+                        BAND_26,
+                        BAND_28,
+                        BAND_29,
+                        BAND_30,
+                        BAND_34,
+                        BAND_38,
+                        BAND_39,
+                        BAND_40,
+                        BAND_41,
+                        BAND_46,
+                        BAND_48,
+                        BAND_50,
+                        BAND_51,
+                        BAND_53,
+                        BAND_65,
+                        BAND_66,
+                        BAND_70,
+                        BAND_71,
+                        BAND_74,
+                        BAND_75,
+                        BAND_76,
+                        BAND_77,
+                        BAND_78,
+                        BAND_79,
+                        BAND_80,
+                        BAND_81,
+                        BAND_82,
+                        BAND_83,
+                        BAND_84,
+                        BAND_86,
+                        BAND_89,
+                        BAND_90,
+                        BAND_91,
+                        BAND_92,
+                        BAND_93,
+                        BAND_94,
+                        BAND_95,
+                        BAND_96,
+                        BAND_257,
+                        BAND_258,
+                        BAND_260,
+                        BAND_261})
+        public @interface NgranBand {}
+
+        /**
+         * Unknown NR frequency.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0;
+
+        /**
+         * NR frequency group 1 defined in 3GPP TS 38.101-1 table 5.2-1
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final int FREQUENCY_RANGE_GROUP_1 = 1;
+
+        /**
+         * NR frequency group 2 defined in 3GPP TS 38.101-2 table 5.2-1
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final int FREQUENCY_RANGE_GROUP_2 = 2;
+
+        /**
+         * Radio frequency range group
+         *
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"FREQUENCY_RANGE_GROUP_"},
+                value = {
+                        FREQUENCY_RANGE_GROUP_UNKNOWN,
+                        FREQUENCY_RANGE_GROUP_1,
+                        FREQUENCY_RANGE_GROUP_2
+                })
+        public @interface FrequencyRangeGroup {}
+
+        /**
+         * Get frequency range group
+         *
+         * @param band NR band
+         * @return The frequency range group
+         *
+         * @hide
+         */
+        @SystemApi
+        public static @FrequencyRangeGroup int getFrequencyRangeGroup(@NgranBand int band) {
+            switch (band) {
+                case BAND_1:
+                case BAND_2:
+                case BAND_3:
+                case BAND_5:
+                case BAND_7:
+                case BAND_8:
+                case BAND_12:
+                case BAND_14:
+                case BAND_18:
+                case BAND_20:
+                case BAND_25:
+                case BAND_26:
+                case BAND_28:
+                case BAND_29:
+                case BAND_30:
+                case BAND_34:
+                case BAND_38:
+                case BAND_39:
+                case BAND_40:
+                case BAND_41:
+                case BAND_46:
+                case BAND_48:
+                case BAND_50:
+                case BAND_51:
+                case BAND_53:
+                case BAND_65:
+                case BAND_66:
+                case BAND_70:
+                case BAND_71:
+                case BAND_74:
+                case BAND_75:
+                case BAND_76:
+                case BAND_77:
+                case BAND_78:
+                case BAND_79:
+                case BAND_80:
+                case BAND_81:
+                case BAND_82:
+                case BAND_83:
+                case BAND_84:
+                case BAND_86:
+                case BAND_89:
+                case BAND_90:
+                case BAND_91:
+                case BAND_92:
+                case BAND_93:
+                case BAND_94:
+                case BAND_95:
+                case BAND_96:
+                    return FREQUENCY_RANGE_GROUP_1;
+                case BAND_257:
+                case BAND_258:
+                case BAND_260:
+                case BAND_261:
+                    return FREQUENCY_RANGE_GROUP_2;
+                default:
+                    return FREQUENCY_RANGE_GROUP_UNKNOWN;
+            }
+        };
+
+        /** @hide */
+        private NgranBands() {}
+    }
+
+    /**
+     * 3GPP TS 38.104 Table 5.4.2.1-1 NR-ARFCN parameters for the global frequency raster.
+     *
+     * @hide
+     */
+    enum NgranArfcnFrequency {
+
+        NGRAN_ARFCN_FREQUENCY_RANGE_1(5, 0, 0, 0, 599999),
+        NGRAN_ARFCN_FREQUENCY_RANGE_2(15, 3000000, 600000, 600000, 2016666),
+        NGRAN_ARFCN_FREQUENCY_RANGE_3(60, 24250080, 2016667, 2016667, 3279165);
+
+        NgranArfcnFrequency(int globalKhz, int rangeOffset, int arfcnOffset,
+                            int rangeFirst, int rangeLast) {
+            this.globalKhz = globalKhz;
+            this.rangeOffset = rangeOffset;
+            this.arfcnOffset = arfcnOffset;
+            this.rangeFirst = rangeFirst;
+            this.rangeLast = rangeLast;
+        }
+
+        int globalKhz;
+        int rangeOffset;
+        int arfcnOffset;
+        int rangeFirst;
+        int rangeLast;
+    }
+
+    /** @hide */
+    private AccessNetworkConstants() {};
+}
diff --git a/android-35/android/telephony/AccessNetworkUtils.java b/android-35/android/telephony/AccessNetworkUtils.java
new file mode 100644
index 0000000..c2a9864
--- /dev/null
+++ b/android-35/android/telephony/AccessNetworkUtils.java
@@ -0,0 +1,869 @@
+package android.telephony;
+
+import static android.telephony.ServiceState.DUPLEX_MODE_FDD;
+import static android.telephony.ServiceState.DUPLEX_MODE_TDD;
+import static android.telephony.ServiceState.DUPLEX_MODE_UNKNOWN;
+
+import android.telephony.AccessNetworkConstants.EutranBand;
+import android.telephony.AccessNetworkConstants.EutranBandArfcnFrequency;
+import android.telephony.AccessNetworkConstants.GeranBand;
+import android.telephony.AccessNetworkConstants.GeranBandArfcnFrequency;
+import android.telephony.AccessNetworkConstants.NgranArfcnFrequency;
+import android.telephony.AccessNetworkConstants.NgranBands;
+import android.telephony.AccessNetworkConstants.UtranBand;
+import android.telephony.AccessNetworkConstants.UtranBandArfcnFrequency;
+import android.telephony.ServiceState.DuplexMode;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Utilities to map between radio constants.
+ *
+ * @hide
+ */
+public class AccessNetworkUtils {
+
+    // do not instantiate
+    private AccessNetworkUtils() {}
+
+    public static final int INVALID_BAND = -1;
+    public static final int INVALID_FREQUENCY = -1;
+
+    /** ISO country code of Japan. */
+    private static final String JAPAN_ISO_COUNTRY_CODE = "jp";
+    private static final String TAG = "AccessNetworkUtils";
+
+    private static final int FREQUENCY_KHZ = 1000;
+    private static final int FREQUENCY_RANGE_LOW_KHZ = 1000000;
+    private static final int FREQUENCY_RANGE_MID_KHZ = 3000000;
+    private static final int FREQUENCY_RANGE_HIGH_KHZ = 6000000;
+
+    private static final Set<Integer> UARFCN_NOT_GENERAL_BAND;
+    static {
+        UARFCN_NOT_GENERAL_BAND = new HashSet<Integer>();
+        UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_A);
+        UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_B);
+        UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_C);
+        UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_D);
+        UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_E);
+        UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_F);
+    }
+
+    /**
+     * Gets the duplex mode for the given EUTRAN operating band.
+     *
+     * <p>See 3GPP 36.101 sec 5.5-1 for calculation
+     *
+     * @param band The EUTRAN band number
+     * @return The duplex mode of the given EUTRAN band
+     */
+    @DuplexMode
+    public static int getDuplexModeForEutranBand(int band) {
+        if (band == INVALID_BAND) {
+            return DUPLEX_MODE_UNKNOWN;
+        }
+
+        if (band > EutranBand.BAND_88) {
+            return DUPLEX_MODE_UNKNOWN;
+        } else if (band >= EutranBand.BAND_65) {
+            return DUPLEX_MODE_FDD;
+        } else if (band >= EutranBand.BAND_33) {
+            return DUPLEX_MODE_TDD;
+        } else if (band >= EutranBand.BAND_1) {
+            return DUPLEX_MODE_FDD;
+        }
+
+        return DUPLEX_MODE_UNKNOWN;
+    }
+
+    /**
+     * Gets the EUTRAN Operating band for a given downlink EARFCN.
+     *
+     * <p>See 3GPP TS 36.101 clause 5.7.3-1 for calculation.
+     *
+     * @param earfcn The downlink EARFCN
+     * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
+     */
+    public static int getOperatingBandForEarfcn(int earfcn) {
+        if (earfcn > 70645) {
+            return INVALID_BAND;
+        } else if (earfcn >= 70596) {
+            return EutranBand.BAND_88;
+        } else if (earfcn >= 70546) {
+            return EutranBand.BAND_87;
+        } else if (earfcn >= 70366) {
+            return EutranBand.BAND_85;
+        } else if (earfcn > 69465) {
+            return INVALID_BAND;
+        } else if (earfcn >= 69036) {
+            return EutranBand.BAND_74;
+        } else if (earfcn >= 68986) {
+            return EutranBand.BAND_73;
+        } else if (earfcn >= 68936) {
+            return EutranBand.BAND_72;
+        } else if (earfcn >= 68586) {
+            return EutranBand.BAND_71;
+        } else if (earfcn >= 68336) {
+            return EutranBand.BAND_70;
+        } else if (earfcn > 67835) {
+            return INVALID_BAND;
+        } else if (earfcn >= 67536) {
+            return EutranBand.BAND_68;
+        } else if (earfcn >= 67366) {
+            return INVALID_BAND; // band 67 only for CarrierAgg
+        } else if (earfcn >= 66436) {
+            return EutranBand.BAND_66;
+        } else if (earfcn >= 65536) {
+            return EutranBand.BAND_65;
+        } else if (earfcn > 60254) {
+            return INVALID_BAND;
+        } else if (earfcn >= 60140) {
+            return EutranBand.BAND_53;
+        } else if (earfcn >= 59140) {
+            return EutranBand.BAND_52;
+        } else if (earfcn >= 59090) {
+            return EutranBand.BAND_51;
+        } else if (earfcn >= 58240) {
+            return EutranBand.BAND_50;
+        } else if (earfcn >= 56740) {
+            return EutranBand.BAND_49;
+        } else if (earfcn >= 55240) {
+            return EutranBand.BAND_48;
+        } else if (earfcn >= 54540) {
+            return EutranBand.BAND_47;
+        } else if (earfcn >= 46790) {
+            return EutranBand.BAND_46;
+        } else if (earfcn >= 46590) {
+            return EutranBand.BAND_45;
+        } else if (earfcn >= 45590) {
+            return EutranBand.BAND_44;
+        } else if (earfcn >= 43590) {
+            return EutranBand.BAND_43;
+        } else if (earfcn >= 41590) {
+            return EutranBand.BAND_42;
+        } else if (earfcn >= 39650) {
+            return EutranBand.BAND_41;
+        } else if (earfcn >= 38650) {
+            return EutranBand.BAND_40;
+        } else if (earfcn >= 38250) {
+            return EutranBand.BAND_39;
+        } else if (earfcn >= 37750) {
+            return EutranBand.BAND_38;
+        } else if (earfcn >= 37550) {
+            return EutranBand.BAND_37;
+        } else if (earfcn >= 36950) {
+            return EutranBand.BAND_36;
+        } else if (earfcn >= 36350) {
+            return EutranBand.BAND_35;
+        } else if (earfcn >= 36200) {
+            return EutranBand.BAND_34;
+        } else if (earfcn >= 36000) {
+            return EutranBand.BAND_33;
+        } else if (earfcn > 10359) {
+            return INVALID_BAND;
+        } else if (earfcn >= 9920) {
+            return INVALID_BAND; // band 32 only for CarrierAgg
+        } else if (earfcn >= 9870) {
+            return EutranBand.BAND_31;
+        } else if (earfcn >= 9770) {
+            return EutranBand.BAND_30;
+        } else if (earfcn >= 9660) {
+            return INVALID_BAND; // band 29 only for CarrierAgg
+        } else if (earfcn >= 9210) {
+            return EutranBand.BAND_28;
+        } else if (earfcn >= 9040) {
+            return EutranBand.BAND_27;
+        } else if (earfcn >= 8690) {
+            return EutranBand.BAND_26;
+        } else if (earfcn >= 8040) {
+            return EutranBand.BAND_25;
+        } else if (earfcn >= 7700) {
+            return EutranBand.BAND_24;
+        } else if (earfcn >= 7500) {
+            return EutranBand.BAND_23;
+        } else if (earfcn >= 6600) {
+            return EutranBand.BAND_22;
+        } else if (earfcn >= 6450) {
+            return EutranBand.BAND_21;
+        } else if (earfcn >= 6150) {
+            return EutranBand.BAND_20;
+        } else if (earfcn >= 6000) {
+            return EutranBand.BAND_19;
+        } else if (earfcn >= 5850) {
+            return EutranBand.BAND_18;
+        } else if (earfcn >= 5730) {
+            return EutranBand.BAND_17;
+        } else if (earfcn > 5379) {
+            return INVALID_BAND;
+        } else if (earfcn >= 5280) {
+            return EutranBand.BAND_14;
+        } else if (earfcn >= 5180) {
+            return EutranBand.BAND_13;
+        } else if (earfcn >= 5010) {
+            return EutranBand.BAND_12;
+        } else if (earfcn >= 4750) {
+            return EutranBand.BAND_11;
+        } else if (earfcn >= 4150) {
+            return EutranBand.BAND_10;
+        } else if (earfcn >= 3800) {
+            return EutranBand.BAND_9;
+        } else if (earfcn >= 3450) {
+            return EutranBand.BAND_8;
+        } else if (earfcn >= 2750) {
+            return EutranBand.BAND_7;
+        } else if (earfcn >= 2650) {
+            return EutranBand.BAND_6;
+        } else if (earfcn >= 2400) {
+            return EutranBand.BAND_5;
+        } else if (earfcn >= 1950) {
+            return EutranBand.BAND_4;
+        } else if (earfcn >= 1200) {
+            return EutranBand.BAND_3;
+        } else if (earfcn >= 600) {
+            return EutranBand.BAND_2;
+        } else if (earfcn >= 0) {
+            return EutranBand.BAND_1;
+        }
+
+        return INVALID_BAND;
+    }
+
+    /**
+     * Gets the NR Operating band for a given downlink NRARFCN.
+     *
+     * <p>See 3GPP TS 38.104 Table 5.2-1 NR operating bands in FR1 and
+     * Table 5.2-2 NR operating bands in FR2
+     *
+     * @param nrarfcn The downlink NRARFCN
+     * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
+     */
+    public static int getOperatingBandForNrarfcn(int nrarfcn) {
+        if (nrarfcn >= 422000 && nrarfcn <= 434000) {
+            return NgranBands.BAND_1;
+        } else if (nrarfcn >= 386000 && nrarfcn <= 398000) {
+            return NgranBands.BAND_2;
+        } else if (nrarfcn >= 361000 && nrarfcn <= 376000) {
+            return NgranBands.BAND_3;
+        } else if (nrarfcn >= 173800 && nrarfcn <= 178800) {
+            return NgranBands.BAND_5;
+        } else if (nrarfcn >= 524000 && nrarfcn <= 538000) {
+            return NgranBands.BAND_7;
+        } else if (nrarfcn >= 185000 && nrarfcn <= 192000) {
+            return NgranBands.BAND_8;
+        } else if (nrarfcn >= 145800 && nrarfcn <= 149200) {
+            return NgranBands.BAND_12;
+        } else if (nrarfcn >= 151600 && nrarfcn <= 153600) {
+            return NgranBands.BAND_14;
+        } else if (nrarfcn >= 172000 && nrarfcn <= 175000) {
+            return NgranBands.BAND_18;
+        } else if (nrarfcn >= 158200 && nrarfcn <= 164200) {
+            return NgranBands.BAND_20;
+        } else if (nrarfcn >= 386000 && nrarfcn <= 399000) {
+            return NgranBands.BAND_25;
+        } else if (nrarfcn >= 171800 && nrarfcn <= 178800) {
+            return NgranBands.BAND_26;
+        } else if (nrarfcn >= 151600 && nrarfcn <= 160600) {
+            return NgranBands.BAND_28;
+        } else if (nrarfcn >= 143400 && nrarfcn <= 145600) {
+            return NgranBands.BAND_29;
+        } else if (nrarfcn >= 470000 && nrarfcn <= 472000) {
+            return NgranBands.BAND_30;
+        } else if (nrarfcn >= 402000 && nrarfcn <= 405000) {
+            return NgranBands.BAND_34;
+        } else if (nrarfcn >= 514000 && nrarfcn <= 524000) {
+            return NgranBands.BAND_38;
+        } else if (nrarfcn >= 376000 && nrarfcn <= 384000) {
+            return NgranBands.BAND_39;
+        } else if (nrarfcn >= 460000 && nrarfcn <= 480000) {
+            return NgranBands.BAND_40;
+        } else if (nrarfcn >= 499200 && nrarfcn <= 537999) {
+            return NgranBands.BAND_41;
+        } else if (nrarfcn >= 743334 && nrarfcn <= 795000) {
+            return NgranBands.BAND_46;
+        } else if (nrarfcn >= 636667 && nrarfcn <= 646666) {
+            return NgranBands.BAND_48;
+        } else if (nrarfcn >= 286400 && nrarfcn <= 303400) {
+            return NgranBands.BAND_50;
+        } else if (nrarfcn >= 285400 && nrarfcn <= 286400) {
+            return NgranBands.BAND_51;
+        } else if (nrarfcn >= 496700 && nrarfcn <= 499000) {
+            return NgranBands.BAND_53;
+        } else if (nrarfcn >= 422000 && nrarfcn <= 440000) {
+            return NgranBands.BAND_65; // BAND_66 has the same channels
+        } else if (nrarfcn >= 399000 && nrarfcn <= 404000) {
+            return NgranBands.BAND_70;
+        } else if (nrarfcn >= 123400 && nrarfcn <= 130400) {
+            return NgranBands.BAND_71;
+        } else if (nrarfcn >= 295000 && nrarfcn <= 303600) {
+            return NgranBands.BAND_74;
+        } else if (nrarfcn >= 286400 && nrarfcn <= 303400) {
+            return NgranBands.BAND_75;
+        } else if (nrarfcn >= 285400 && nrarfcn <= 286400) {
+            return NgranBands.BAND_76;
+        } else if (nrarfcn >= 620000 && nrarfcn <= 680000) {
+            return NgranBands.BAND_77;
+        } else if (nrarfcn >= 620000 && nrarfcn <= 653333) {
+            return NgranBands.BAND_78;
+        } else if (nrarfcn >= 693334 && nrarfcn <= 733333) {
+            return NgranBands.BAND_79;
+        } else if (nrarfcn >= 499200 && nrarfcn <= 538000) {
+            return NgranBands.BAND_90;
+        } else if (nrarfcn >= 285400 && nrarfcn <= 286400) {
+            return NgranBands.BAND_91;
+        } else if (nrarfcn >= 286400 && nrarfcn <= 303400) {
+            return NgranBands.BAND_92;
+        } else if (nrarfcn >= 285400 && nrarfcn <= 286400) {
+            return NgranBands.BAND_93;
+        } else if (nrarfcn >= 286400 && nrarfcn <= 303400) {
+            return NgranBands.BAND_94;
+        } else if (nrarfcn >= 795000 && nrarfcn <= 875000) {
+            return NgranBands.BAND_96;
+        } else if (nrarfcn >= 2054166 && nrarfcn <= 2104165) {
+            return NgranBands.BAND_257;
+        } else if (nrarfcn >= 2016667 && nrarfcn <= 2070832) {
+            return NgranBands.BAND_258;
+        } else if (nrarfcn >= 2229166 && nrarfcn <= 2279165) {
+            return NgranBands.BAND_260;
+        } else if (nrarfcn >= 2070833 && nrarfcn <= 2084999) {
+            return NgranBands.BAND_261;
+        }
+        return INVALID_BAND;
+    }
+
+    /**
+     * Gets the GERAN Operating band for a given ARFCN.
+     *
+     * <p>See 3GPP TS 45.005 clause 2 for calculation.
+     *
+     * @param arfcn The ARFCN
+     * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
+     */
+    public static int getOperatingBandForArfcn(int arfcn) {
+        if (arfcn >= 0 && arfcn <= 124) {
+            return GeranBand.BAND_E900;
+        } else if (arfcn >= 128 && arfcn <= 251) {
+            return GeranBand.BAND_850;
+        } else if (arfcn >= 259 && arfcn <= 293) {
+            return GeranBand.BAND_450;
+        } else if (arfcn >= 306 && arfcn <= 340) {
+            return GeranBand.BAND_480;
+        } else if (arfcn >= 438 && arfcn <= 511) {
+            return GeranBand.BAND_750;
+        } else if (arfcn >= 512 && arfcn <= 885) {
+            // ARFCN between 512 and 810 are also part of BAND_PCS1900.
+            // Returning BAND_DCS1800 in both cases.
+            return GeranBand.BAND_DCS1800;
+        } else if (arfcn >= 940 && arfcn <= 974) {
+            return GeranBand.BAND_ER900;
+        } else if (arfcn >= 975 && arfcn <= 1023) {
+            return GeranBand.BAND_E900;
+        }
+        return INVALID_BAND;
+    }
+
+    /**
+     * Gets the UTRAN Operating band for a given downlink UARFCN.
+     *
+     * <p>See 3GPP TS 25.101 clause 5.4.4 for calculation.
+     *
+     * @param uarfcn The downlink UARFCN
+     * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
+     */
+    public static int getOperatingBandForUarfcn(int uarfcn) {
+        // List of additional bands defined in TS 25.101.
+        int[] addlBand2 = {412, 437, 462, 487, 512, 537, 562, 587, 612, 637, 662, 687};
+        int[] addlBand4 = {1887, 1912, 1937, 1962, 1987, 2012, 2037, 2062, 2087};
+        int[] addlBand5 = {1007, 1012, 1032, 1037, 1062, 1087};
+        int[] addlBand6 = {1037, 1062};
+        int[] addlBand7 =
+                {2587, 2612, 2637, 2662, 2687, 2712, 2737, 2762, 2787, 2812, 2837, 2862,
+                2887, 2912};
+        int[] addlBand10 =
+                {3412, 3437, 3462, 3487, 3512, 3537, 3562, 3587, 3612, 3637, 3662, 3687};
+        int[] addlBand12 = {3932, 3957, 3962, 3987, 3992};
+        int[] addlBand13 = {4067, 4092};
+        int[] addlBand14 = {4167, 4192};
+        int[] addlBand19 = {787, 812, 837};
+        int[] addlBand25 =
+                {6292, 6317, 6342, 6367, 6392, 6417, 6442, 6467, 6492, 6517, 6542, 6567, 6592};
+        int[] addlBand26 = {5937, 5962, 5987, 5992, 6012, 6017, 6037, 6042, 6062, 6067, 6087};
+
+        if (uarfcn >= 10562 && uarfcn <= 10838) {
+            return UtranBand.BAND_1;
+        } else if ((uarfcn >= 9662 && uarfcn <= 9938)
+                || Arrays.binarySearch(addlBand2, uarfcn) >= 0) {
+            return UtranBand.BAND_2;
+        } else if (uarfcn >= 1162 && uarfcn <= 1513) {
+            return UtranBand.BAND_3;
+        } else if ((uarfcn >= 1537 && uarfcn <= 1738)
+                || Arrays.binarySearch(addlBand4, uarfcn) >= 0) {
+            return UtranBand.BAND_4;
+        } else if (uarfcn >= 4387 && uarfcn <= 4413) {
+            // Band 6 is a subset of band 5. Only Japan uses band 6 and Japan does not have band 5.
+            String country = TelephonyManager.getDefault().getNetworkCountryIso();
+            if (JAPAN_ISO_COUNTRY_CODE.compareToIgnoreCase(country) == 0) {
+                return UtranBand.BAND_6;
+            } else {
+                return UtranBand.BAND_5;
+            }
+        } else if ((uarfcn >= 4357 && uarfcn <= 4458)
+                || Arrays.binarySearch(addlBand5, uarfcn) >= 0) {
+            return UtranBand.BAND_5;
+        } else if (Arrays.binarySearch(addlBand6, uarfcn) >= 0) {
+            return UtranBand.BAND_6;
+        } else if ((uarfcn >= 2237 && uarfcn <= 2563)
+                || Arrays.binarySearch(addlBand7, uarfcn) >= 0) {
+            return UtranBand.BAND_7;
+        } else if (uarfcn >= 2937 && uarfcn <= 3088) {
+            return UtranBand.BAND_8;
+        } else if (uarfcn >= 9237 && uarfcn <= 9387) {
+            return UtranBand.BAND_9;
+        } else if ((uarfcn >= 3112 && uarfcn <= 3388)
+                || Arrays.binarySearch(addlBand10, uarfcn) >= 0) {
+            return UtranBand.BAND_10;
+        } else if (uarfcn >= 3712 && uarfcn <= 3787) {
+            return UtranBand.BAND_11;
+        } else if ((uarfcn >= 3842 && uarfcn <= 3903)
+                || Arrays.binarySearch(addlBand12, uarfcn) >= 0) {
+            return UtranBand.BAND_12;
+        } else if ((uarfcn >= 4017 && uarfcn <= 4043)
+                || Arrays.binarySearch(addlBand13, uarfcn) >= 0) {
+            return UtranBand.BAND_13;
+        } else if ((uarfcn >= 4117 && uarfcn <= 4143)
+                || Arrays.binarySearch(addlBand14, uarfcn) >= 0) {
+            return UtranBand.BAND_14;
+        } else if ((uarfcn >= 712 && uarfcn <= 763)
+                || Arrays.binarySearch(addlBand19, uarfcn) >= 0) {
+            return UtranBand.BAND_19;
+        } else if (uarfcn >= 4512 && uarfcn <= 4638) {
+            return UtranBand.BAND_20;
+        } else if (uarfcn >= 862 && uarfcn <= 912) {
+            return UtranBand.BAND_21;
+        } else if (uarfcn >= 4662 && uarfcn <= 5038) {
+            return UtranBand.BAND_22;
+        } else if ((uarfcn >= 5112 && uarfcn <= 5413)
+                || Arrays.binarySearch(addlBand25, uarfcn) >= 0) {
+            return UtranBand.BAND_25;
+        } else if ((uarfcn >= 5762 && uarfcn <= 5913)
+                || Arrays.binarySearch(addlBand26, uarfcn) >= 0) {
+            return UtranBand.BAND_26;
+        }
+        return INVALID_BAND;
+    }
+
+    /**
+     * Get geran bands from {@link PhysicalChannelConfig#getBand()}
+     */
+    public static int getFrequencyRangeGroupFromGeranBand(@GeranBand.GeranBands int band) {
+        switch (band) {
+            case GeranBand.BAND_T380:
+            case GeranBand.BAND_T410:
+            case GeranBand.BAND_450:
+            case GeranBand.BAND_480:
+            case GeranBand.BAND_710:
+            case GeranBand.BAND_750:
+            case GeranBand.BAND_T810:
+            case GeranBand.BAND_850:
+            case GeranBand.BAND_P900:
+            case GeranBand.BAND_E900:
+            case GeranBand.BAND_R900:
+            case GeranBand.BAND_ER900:
+                return ServiceState.FREQUENCY_RANGE_LOW;
+            case GeranBand.BAND_DCS1800:
+            case GeranBand.BAND_PCS1900:
+                return ServiceState.FREQUENCY_RANGE_MID;
+            default:
+                return ServiceState.FREQUENCY_RANGE_UNKNOWN;
+        }
+    }
+
+    /**
+     * Get utran bands from {@link PhysicalChannelConfig#getBand()}
+     */
+    public static int getFrequencyRangeGroupFromUtranBand(@UtranBand.UtranBands int band) {
+        switch (band) {
+            case UtranBand.BAND_5:
+            case UtranBand.BAND_6:
+            case UtranBand.BAND_8:
+            case UtranBand.BAND_12:
+            case UtranBand.BAND_13:
+            case UtranBand.BAND_14:
+            case UtranBand.BAND_19:
+            case UtranBand.BAND_20:
+            case UtranBand.BAND_26:
+                return ServiceState.FREQUENCY_RANGE_LOW;
+            case UtranBand.BAND_1:
+            case UtranBand.BAND_2:
+            case UtranBand.BAND_3:
+            case UtranBand.BAND_4:
+            case UtranBand.BAND_7:
+            case UtranBand.BAND_9:
+            case UtranBand.BAND_10:
+            case UtranBand.BAND_11:
+            case UtranBand.BAND_21:
+            case UtranBand.BAND_25:
+            case UtranBand.BAND_A:
+            case UtranBand.BAND_B:
+            case UtranBand.BAND_C:
+            case UtranBand.BAND_D:
+            case UtranBand.BAND_E:
+            case UtranBand.BAND_F:
+                return ServiceState.FREQUENCY_RANGE_MID;
+            case UtranBand.BAND_22:
+                return ServiceState.FREQUENCY_RANGE_HIGH;
+            default:
+                return ServiceState.FREQUENCY_RANGE_UNKNOWN;
+        }
+    }
+
+    /**
+     * Get eutran bands from {@link PhysicalChannelConfig#getBand()}
+     * 3GPP TS 36.101 Table 5.5 EUTRA operating bands
+     */
+    public static int getFrequencyRangeGroupFromEutranBand(@EutranBand.EutranBands int band) {
+        switch (band) {
+            case EutranBand.BAND_5:
+            case EutranBand.BAND_6:
+            case EutranBand.BAND_8:
+            case EutranBand.BAND_12:
+            case EutranBand.BAND_13:
+            case EutranBand.BAND_14:
+            case EutranBand.BAND_17:
+            case EutranBand.BAND_18:
+            case EutranBand.BAND_19:
+            case EutranBand.BAND_20:
+            case EutranBand.BAND_26:
+            case EutranBand.BAND_27:
+            case EutranBand.BAND_28:
+            case EutranBand.BAND_31:
+            case EutranBand.BAND_44:
+            case EutranBand.BAND_50:
+            case EutranBand.BAND_51:
+            case EutranBand.BAND_68:
+            case EutranBand.BAND_71:
+            case EutranBand.BAND_72:
+            case EutranBand.BAND_73:
+            case EutranBand.BAND_85:
+            case EutranBand.BAND_87:
+            case EutranBand.BAND_88:
+                return ServiceState.FREQUENCY_RANGE_LOW;
+            case EutranBand.BAND_1:
+            case EutranBand.BAND_2:
+            case EutranBand.BAND_3:
+            case EutranBand.BAND_4:
+            case EutranBand.BAND_7:
+            case EutranBand.BAND_9:
+            case EutranBand.BAND_10:
+            case EutranBand.BAND_11:
+            case EutranBand.BAND_21:
+            case EutranBand.BAND_23:
+            case EutranBand.BAND_24:
+            case EutranBand.BAND_25:
+            case EutranBand.BAND_30:
+            case EutranBand.BAND_33:
+            case EutranBand.BAND_34:
+            case EutranBand.BAND_35:
+            case EutranBand.BAND_36:
+            case EutranBand.BAND_37:
+            case EutranBand.BAND_38:
+            case EutranBand.BAND_39:
+            case EutranBand.BAND_40:
+            case EutranBand.BAND_41:
+            case EutranBand.BAND_45:
+            case EutranBand.BAND_53:
+            case EutranBand.BAND_65:
+            case EutranBand.BAND_66:
+            case EutranBand.BAND_70:
+            case EutranBand.BAND_74:
+                return ServiceState.FREQUENCY_RANGE_MID;
+            case EutranBand.BAND_22:
+            case EutranBand.BAND_42:
+            case EutranBand.BAND_43:
+            case EutranBand.BAND_46:
+            case EutranBand.BAND_47:
+            case EutranBand.BAND_48:
+            case EutranBand.BAND_49:
+            case EutranBand.BAND_52:
+                return ServiceState.FREQUENCY_RANGE_HIGH;
+            default:
+                return ServiceState.FREQUENCY_RANGE_UNKNOWN;
+        }
+    }
+
+    /**
+     * Get ngran band from {@link PhysicalChannelConfig#getBand()}
+     * 3GPP TS 38.104 Table 5.2-1 NR operating bands in FR1
+     * 3GPP TS 38.104 Table 5.2-2 NR operating bands in FR2
+     */
+    public static int getFrequencyRangeGroupFromNrBand(@NgranBands.NgranBand int band) {
+        switch (band) {
+            case NgranBands.BAND_5:
+            case NgranBands.BAND_8:
+            case NgranBands.BAND_12:
+            case NgranBands.BAND_14:
+            case NgranBands.BAND_18:
+            case NgranBands.BAND_20:
+            case NgranBands.BAND_26:
+            case NgranBands.BAND_28:
+            case NgranBands.BAND_29:
+            case NgranBands.BAND_71:
+            case NgranBands.BAND_81:
+            case NgranBands.BAND_82:
+            case NgranBands.BAND_83:
+            case NgranBands.BAND_89:
+                return ServiceState.FREQUENCY_RANGE_LOW;
+            case NgranBands.BAND_1:
+            case NgranBands.BAND_2:
+            case NgranBands.BAND_3:
+            case NgranBands.BAND_7:
+            case NgranBands.BAND_25:
+            case NgranBands.BAND_30:
+            case NgranBands.BAND_34:
+            case NgranBands.BAND_38:
+            case NgranBands.BAND_39:
+            case NgranBands.BAND_40:
+            case NgranBands.BAND_41:
+            case NgranBands.BAND_50:
+            case NgranBands.BAND_51:
+            case NgranBands.BAND_53:
+            case NgranBands.BAND_65:
+            case NgranBands.BAND_66:
+            case NgranBands.BAND_70:
+            case NgranBands.BAND_74:
+            case NgranBands.BAND_75:
+            case NgranBands.BAND_76:
+            case NgranBands.BAND_80:
+            case NgranBands.BAND_84:
+            case NgranBands.BAND_86:
+            case NgranBands.BAND_90:
+            case NgranBands.BAND_91:
+            case NgranBands.BAND_92:
+            case NgranBands.BAND_93:
+            case NgranBands.BAND_94:
+            case NgranBands.BAND_95:
+                return ServiceState.FREQUENCY_RANGE_MID;
+            case NgranBands.BAND_46:
+            case NgranBands.BAND_48:
+            case NgranBands.BAND_77:
+            case NgranBands.BAND_78:
+            case NgranBands.BAND_79:
+                return ServiceState.FREQUENCY_RANGE_HIGH;
+            case NgranBands.BAND_96:
+            case NgranBands.BAND_257:
+            case NgranBands.BAND_258:
+            case NgranBands.BAND_260:
+            case NgranBands.BAND_261:
+                return ServiceState.FREQUENCY_RANGE_MMWAVE;
+            default:
+                return ServiceState.FREQUENCY_RANGE_UNKNOWN;
+        }
+    }
+
+    /**
+     * 3GPP TS 38.104 Table 5.4.2.1-1 NR-ARFCN parameters for the global frequency raster.
+     * Formula of NR-ARFCN convert to actual frequency:
+     * Actual frequency(kHz) = (RANGE_OFFSET + GLOBAL_KHZ * (ARFCN - ARFCN_OFFSET))
+     */
+    public static int getFrequencyFromNrArfcn(int nrArfcn) {
+
+        if (nrArfcn == PhysicalChannelConfig.CHANNEL_NUMBER_UNKNOWN) {
+            return PhysicalChannelConfig.FREQUENCY_UNKNOWN;
+        }
+
+        int globalKhz = 0;
+        int rangeOffset = 0;
+        int arfcnOffset = 0;
+        for (NgranArfcnFrequency nrArfcnFrequency : AccessNetworkConstants.
+                NgranArfcnFrequency.values()) {
+            if (nrArfcn >= nrArfcnFrequency.rangeFirst
+                    && nrArfcn <= nrArfcnFrequency.rangeLast) {
+                globalKhz = nrArfcnFrequency.globalKhz;
+                rangeOffset = nrArfcnFrequency.rangeOffset;
+                arfcnOffset = nrArfcnFrequency.arfcnOffset;
+                break;
+            }
+        }
+        return rangeOffset + globalKhz * (nrArfcn - arfcnOffset);
+    }
+
+    /**
+     * Get actual frequency from E-UTRA ARFCN.
+     */
+    public static int getFrequencyFromEarfcn(int band, int earfcn, boolean isUplink) {
+
+        int low = 0;
+        int offset = 0;
+        for (EutranBandArfcnFrequency earfcnFrequency : EutranBandArfcnFrequency.values()) {
+            if (band == earfcnFrequency.band) {
+                if (isInEarfcnRange(earfcn, earfcnFrequency, isUplink)) {
+                    low = isUplink ? earfcnFrequency.uplinkLowKhz : earfcnFrequency.downlinkLowKhz;
+                    offset = isUplink ? earfcnFrequency.uplinkOffset
+                            : earfcnFrequency.downlinkOffset;
+                    break;
+                } else {
+                    Rlog.w(TAG,"Band and the range of EARFCN are not consistent: band = " + band
+                            + " ,earfcn = " + earfcn + " ,isUplink = " + isUplink);
+                    return INVALID_FREQUENCY;
+                }
+            }
+        }
+        return convertEarfcnToFrequency(low, earfcn, offset);
+    }
+
+    /**
+     * 3GPP TS 36.101 Table 5.7.3-1 E-UTRA channel numbers.
+     * Formula of E-UTRA ARFCN convert to actual frequency:
+     * Actual frequency(kHz) = (DOWNLINK_LOW + 0.1 * (ARFCN - DOWNLINK_OFFSET)) * FREQUENCY_KHZ
+     * Actual frequency(kHz) = (UPLINK_LOW + 0.1 * (ARFCN - UPLINK_OFFSET)) * FREQUENCY_KHZ
+     */
+    private static int convertEarfcnToFrequency(int low, int earfcn, int offset) {
+        return low + 100 * (earfcn - offset);
+    }
+
+    private static boolean isInEarfcnRange(int earfcn, EutranBandArfcnFrequency earfcnFrequency,
+            boolean isUplink) {
+        if (isUplink) {
+            return earfcn >= earfcnFrequency.uplinkOffset && earfcn <= earfcnFrequency.uplinkRange;
+        } else {
+            return earfcn >= earfcnFrequency.downlinkOffset
+                    && earfcn <= earfcnFrequency.downlinkRange;
+        }
+    }
+
+    /**
+     * Get actual frequency from UTRA ARFCN.
+     */
+    public static int getFrequencyFromUarfcn(int band, int uarfcn, boolean isUplink) {
+
+        if (uarfcn == PhysicalChannelConfig.CHANNEL_NUMBER_UNKNOWN) {
+            return PhysicalChannelConfig.FREQUENCY_UNKNOWN;
+        }
+
+        int offsetKhz = 0;
+        for (UtranBandArfcnFrequency uarfcnFrequency : AccessNetworkConstants.
+                UtranBandArfcnFrequency.values()) {
+            if (band == uarfcnFrequency.band) {
+                if (isInUarfcnRange(uarfcn, uarfcnFrequency, isUplink)) {
+                    offsetKhz = isUplink ? uarfcnFrequency.uplinkOffset
+                            : uarfcnFrequency.downlinkOffset;
+                    break;
+                } else {
+                    Rlog.w(TAG,"Band and the range of UARFCN are not consistent: band = " + band
+                            + " ,uarfcn = " + uarfcn + " ,isUplink = " + isUplink);
+                    return INVALID_FREQUENCY;
+                }
+            }
+        }
+
+        if (!UARFCN_NOT_GENERAL_BAND.contains(band)) {
+            return convertUarfcnToFrequency(offsetKhz, uarfcn);
+        } else {
+            return convertUarfcnTddToFrequency(band, uarfcn);
+        }
+    }
+
+    /**
+     * 3GPP TS 25.101, Table 5.1 UARFCN definition (general).
+     * Formula of UTRA ARFCN convert to actual frequency:
+     * For general bands:
+     * Downlink actual frequency(kHz) = (DOWNLINK_OFFSET + 0.2 * ARFCN) * FREQUENCY_KHZ
+     * Uplink actual frequency(kHz) = (UPLINK_OFFSET + 0.2 * ARFCN) * FREQUENCY_KHZ
+     */
+    private static int convertUarfcnToFrequency(int offsetKhz, int uarfcn) {
+        return offsetKhz + (200 * uarfcn);
+    }
+
+    /**
+     * 3GPP TS 25.102, Table 5.2 UTRA Absolute Radio Frequency Channel Number 1.28 Mcps TDD Option.
+     * For FDD bands A, B, C, E, F:
+     * Actual frequency(kHz) =  5 * ARFCN * FREQUENCY_KHZ
+     * For TDD bands D:
+     * Actual frequency(kHz) =  (5 * (ARFCN - 2150.1MHz)) * FREQUENCY_KHZ
+     */
+    private static int convertUarfcnTddToFrequency(int band, int uarfcn) {
+        if (band != UtranBand.BAND_D) {
+            return 5 * uarfcn * FREQUENCY_KHZ;
+        } else {
+            return 5 * ((FREQUENCY_KHZ * uarfcn) - 2150100);
+        }
+    }
+
+    private static boolean isInUarfcnRange(int uarfcn, UtranBandArfcnFrequency uarfcnFrequency,
+                                           boolean isUplink) {
+        if (isUplink) {
+            return uarfcn >= uarfcnFrequency.uplinkRangeFirst
+                    && uarfcn <= uarfcnFrequency.uplinkRangeLast;
+        } else {
+            if (uarfcnFrequency.downlinkRangeFirst != 0 && uarfcnFrequency.downlinkRangeLast != 0) {
+                return uarfcn >= uarfcnFrequency.downlinkRangeFirst
+                        && uarfcn <= uarfcnFrequency.downlinkRangeLast;
+            } else {
+                // BAND_C, BAND_D, BAND_E and BAND_F do not have the downlink range.
+                return true;
+            }
+        }
+    }
+
+    /**
+     * Get actual frequency from GERAN ARFCN.
+     */
+    public static int getFrequencyFromArfcn(int band, int arfcn, boolean isUplink) {
+
+        if (arfcn == PhysicalChannelConfig.CHANNEL_NUMBER_UNKNOWN) {
+            return PhysicalChannelConfig.FREQUENCY_UNKNOWN;
+        }
+
+        int uplinkFrequencyFirst = 0;
+        int arfcnOffset = 0;
+        int downlinkOffset = 0;
+        int frequency = 0;
+        for (GeranBandArfcnFrequency arfcnFrequency : AccessNetworkConstants.
+                GeranBandArfcnFrequency.values()) {
+            if (band == arfcnFrequency.band) {
+                if (arfcn >= arfcnFrequency.arfcnRangeFirst
+                        && arfcn <= arfcnFrequency.arfcnRangeLast) {
+                    uplinkFrequencyFirst = arfcnFrequency.uplinkFrequencyFirst;
+                    downlinkOffset = arfcnFrequency.downlinkOffset;
+                    arfcnOffset = arfcnFrequency.arfcnOffset;
+                    frequency = convertArfcnToFrequency(arfcn, uplinkFrequencyFirst,
+                            arfcnOffset);
+                    break;
+                } else {
+                    Rlog.w(TAG,"Band and the range of ARFCN are not consistent: band = " + band
+                            + " ,arfcn = " + arfcn + " ,isUplink = " + isUplink);
+                    return INVALID_FREQUENCY;
+                }
+            }
+        }
+
+        return isUplink ? frequency : frequency + downlinkOffset;
+    }
+
+    /**
+     * 3GPP TS 45.005 Table 2-1 Dynamically mapped ARFCN
+     * Formula of Geran ARFCN convert to actual frequency:
+     * Uplink actual frequency(kHz) =
+     *      (UPLINK_FREQUENCY_FIRST + 0.2 * (ARFCN - ARFCN_RANGE_FIRST)) * FREQUENCY_KHZ
+     * Downlink actual frequency(kHz) = Uplink actual frequency + 10
+     */
+    private static int convertArfcnToFrequency(int arfcn, int uplinkFrequencyFirstKhz,
+            int arfcnOffset) {
+        return uplinkFrequencyFirstKhz + 200 * (arfcn - arfcnOffset);
+    }
+
+    public static int getFrequencyRangeFromArfcn(int frequency) {
+        if (frequency < FREQUENCY_RANGE_LOW_KHZ) {
+            return ServiceState.FREQUENCY_RANGE_LOW;
+        } else if (frequency < FREQUENCY_RANGE_MID_KHZ
+                && frequency >= FREQUENCY_RANGE_LOW_KHZ) {
+            return ServiceState.FREQUENCY_RANGE_MID;
+        } else if (frequency < FREQUENCY_RANGE_HIGH_KHZ
+                && frequency >= FREQUENCY_RANGE_MID_KHZ) {
+            return ServiceState.FREQUENCY_RANGE_HIGH;
+        } else {
+            return ServiceState.FREQUENCY_RANGE_MMWAVE;
+        }
+    }
+}
diff --git a/android-35/android/telephony/ActivityStatsTechSpecificInfo.java b/android-35/android/telephony/ActivityStatsTechSpecificInfo.java
new file mode 100644
index 0000000..bed9c45
--- /dev/null
+++ b/android-35/android/telephony/ActivityStatsTechSpecificInfo.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ServiceState.FrequencyRange;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Technology specific activity stats info. List of the activity stats for each RATs (2G, 3G, 4G and
+ * 5G) and frequency ranges (HIGH for sub6 and MMWAVE) in case of 5G. In case implementation doesn't
+ * have RAT specific activity stats then send only one activity stats info with RAT unknown.
+ *
+ * @hide
+ */
[email protected]
+public final class ActivityStatsTechSpecificInfo implements Parcelable {
+    private static final int TX_POWER_LEVELS = 5;
+
+    private int mRat;
+    private int mFrequencyRange;
+    private int[] mTxTimeMs;
+    private int mRxTimeMs;
+
+    /** @hide */
+    public ActivityStatsTechSpecificInfo(
+            int rat, @FrequencyRange int frequencyRange, @NonNull int[] txTimeMs, int rxTimeMs) {
+        Objects.requireNonNull(txTimeMs);
+        if (txTimeMs.length != TX_POWER_LEVELS) {
+            throw new IllegalArgumentException("txTimeMs must have length == TX_POWER_LEVELS");
+        }
+        mRat = rat;
+        mFrequencyRange = frequencyRange;
+        mTxTimeMs = txTimeMs;
+        mRxTimeMs = rxTimeMs;
+    }
+
+    /**
+     * Returns the radio access technology for this activity stats info.
+     *
+     * The returned value is define in {@link AccessNetworkConstants.AccessNetworkType};
+     * @hide
+     */
+    public int getRat() {
+        return mRat;
+    }
+
+    /**
+     * Returns the rough frequency range for this activity stats info.
+     *
+     * The returned value is define in {@link ServiceState.FrequencyRange};
+     * @hide
+     */
+    public @FrequencyRange int getFrequencyRange() {
+        return mFrequencyRange;
+    }
+
+    /**
+     * Gets the amount of time the modem spent transmitting at a certain power level.
+     *
+     * @return The amount of time, in milliseconds, that the modem spent transmitting at the given
+     *     power level.
+     */
+    public @DurationMillisLong long getTransmitTimeMillis(int powerLevel) {
+        return mTxTimeMs[powerLevel];
+    }
+
+    /**
+     * @return The raw array of transmit power durations
+     * @hide
+     */
+    @NonNull
+    public int[] getTransmitTimeMillis() {
+        return mTxTimeMs;
+    }
+
+    /**
+     * Gets the amount of time (in milliseconds) when the modem is awake and receiving data.
+     *
+     * @return Time in milliseconds.
+     * @hide
+     */
+    public @DurationMillisLong long getReceiveTimeMillis() {
+        return mRxTimeMs;
+    }
+    /** @hide */
+    public void setRat(int rat) {
+        mRat = rat;
+    }
+
+    /** @hide */
+    public void setFrequencyRange(@FrequencyRange int frequencyRange) {
+        mFrequencyRange = frequencyRange;
+    }
+
+    /** @hide */
+    public void setReceiveTimeMillis(int receiveTimeMillis) {
+        mRxTimeMs = receiveTimeMillis;
+    }
+
+    /**
+     * Provided for convenience, since the API surface needs to return longs but internal
+     * representations are ints.
+     *
+     * @hide
+     */
+    public void setReceiveTimeMillis(long receiveTimeMillis) {
+        mRxTimeMs = (int) receiveTimeMillis;
+    }
+
+    /** @hide */
+    public void setTransmitTimeMillis(int[] txTimeMs) {
+        mTxTimeMs = Arrays.copyOf(txTimeMs, TX_POWER_LEVELS);
+    }
+
+    /** @hide */
+    public boolean isTxPowerValid() {
+        return Arrays.stream(mTxTimeMs).allMatch((i) -> i >= 0);
+    }
+
+    /** @hide */
+    public boolean isRxPowerValid() {
+        return getReceiveTimeMillis() >= 0;
+    }
+
+    /** @hide */
+    public boolean isTxPowerEmpty() {
+        boolean isTxPowerEmpty =
+                mTxTimeMs == null
+                        || mTxTimeMs.length == 0
+                        || Arrays.stream(mTxTimeMs).allMatch((i) -> i == 0);
+        return isTxPowerEmpty;
+    }
+
+    /** @hide */
+    public boolean isRxPowerEmpty() {
+        return getReceiveTimeMillis() == 0;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mRat, mFrequencyRange, mRxTimeMs);
+        result = 31 * result + Arrays.hashCode(mTxTimeMs);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ActivityStatsTechSpecificInfo)) return false;
+        ActivityStatsTechSpecificInfo that = (ActivityStatsTechSpecificInfo) o;
+        return mRat == that.mRat
+                && mFrequencyRange == that.mFrequencyRange
+                && Arrays.equals(mTxTimeMs, that.mTxTimeMs)
+                && mRxTimeMs == that.mRxTimeMs;
+    }
+
+    private static String ratToString(int type) {
+        switch (type) {
+            case AccessNetworkConstants.AccessNetworkType.UNKNOWN:
+                return "UNKNOWN";
+            case AccessNetworkConstants.AccessNetworkType.GERAN:
+                return "GERAN";
+            case AccessNetworkConstants.AccessNetworkType.UTRAN:
+                return "UTRAN";
+            case AccessNetworkConstants.AccessNetworkType.EUTRAN:
+                return "EUTRAN";
+            case AccessNetworkConstants.AccessNetworkType.CDMA2000:
+                return "CDMA2000";
+            case AccessNetworkConstants.AccessNetworkType.IWLAN:
+                return "IWLAN";
+            case AccessNetworkConstants.AccessNetworkType.NGRAN:
+                return "NGRAN";
+            default:
+                return Integer.toString(type);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+                .append("{mRat=")
+                .append(ratToString(mRat))
+                .append(",mFrequencyRange=")
+                .append(ServiceState.frequencyRangeToString(mFrequencyRange))
+                .append(",mTxTimeMs[]=")
+                .append(Arrays.toString(mTxTimeMs))
+                .append(",mRxTimeMs=")
+                .append(mRxTimeMs)
+                .append("}")
+                .toString();
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<
+                    ActivityStatsTechSpecificInfo>
+            CREATOR =
+                    new Parcelable.Creator<ActivityStatsTechSpecificInfo>() {
+                public ActivityStatsTechSpecificInfo createFromParcel(@NonNull Parcel in) {
+                    int rat = in.readInt();
+                    int frequencyRange = in.readInt();
+                    int[] txTimeMs = new int[TX_POWER_LEVELS];
+                    in.readIntArray(txTimeMs);
+                    int rxTimeMs = in.readInt();
+                    return new ActivityStatsTechSpecificInfo(
+                            rat, frequencyRange, txTimeMs, rxTimeMs);
+                }
+
+                public ActivityStatsTechSpecificInfo[] newArray(int size) {
+                    return new ActivityStatsTechSpecificInfo[size];
+                }
+    };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mRat);
+        dest.writeInt(mFrequencyRange);
+        dest.writeIntArray(mTxTimeMs);
+        dest.writeInt(mRxTimeMs);
+    }
+}
diff --git a/android-35/android/telephony/Annotation.java b/android-35/android/telephony/Annotation.java
new file mode 100644
index 0000000..2435243
--- /dev/null
+++ b/android-35/android/telephony/Annotation.java
@@ -0,0 +1,772 @@
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
+import android.telecom.Connection;
+import android.telephony.data.ApnSetting;
+import android.telephony.ims.ImsCallProfile;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Telephony Annotations.
+ * Telephony sdk is a mainline module and others cannot reference hidden @IntDef. Moving some
+ * telephony annotations to a separate class to allow others statically link to it.
+ *
+ * @hide
+ */
+public class Annotation {
+    @IntDef(prefix = {"DATA_"}, value = {
+            TelephonyManager.DATA_ACTIVITY_NONE,
+            TelephonyManager.DATA_ACTIVITY_IN,
+            TelephonyManager.DATA_ACTIVITY_OUT,
+            TelephonyManager.DATA_ACTIVITY_INOUT,
+            TelephonyManager.DATA_ACTIVITY_DORMANT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DataActivityType {
+    }
+
+    @IntDef(prefix = {"DATA_"}, value = {
+            TelephonyManager.DATA_UNKNOWN,
+            TelephonyManager.DATA_DISCONNECTED,
+            TelephonyManager.DATA_CONNECTING,
+            TelephonyManager.DATA_CONNECTED,
+            TelephonyManager.DATA_SUSPENDED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DataState {
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"RADIO_POWER_"},
+            value = {
+                    TelephonyManager.RADIO_POWER_OFF,
+                    TelephonyManager.RADIO_POWER_ON,
+                    TelephonyManager.RADIO_POWER_UNAVAILABLE,
+            })
+    public @interface RadioPowerState {
+    }
+
+    @IntDef({
+            TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN,
+            TelephonyManager.SIM_ACTIVATION_STATE_ACTIVATING,
+            TelephonyManager.SIM_ACTIVATION_STATE_ACTIVATED,
+            TelephonyManager.SIM_ACTIVATION_STATE_DEACTIVATED,
+            TelephonyManager.SIM_ACTIVATION_STATE_RESTRICTED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SimActivationState {
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"SRVCC_STATE_"},
+            value = {
+                    TelephonyManager.SRVCC_STATE_HANDOVER_NONE,
+                    TelephonyManager.SRVCC_STATE_HANDOVER_STARTED,
+                    TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED,
+                    TelephonyManager.SRVCC_STATE_HANDOVER_FAILED,
+                    TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED})
+    public @interface SrvccState {
+    }
+
+    @IntDef(prefix = {"CALL_STATE_"}, value = {
+            TelephonyManager.CALL_STATE_IDLE,
+            TelephonyManager.CALL_STATE_RINGING,
+            TelephonyManager.CALL_STATE_OFFHOOK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallState {
+    }
+
+    @IntDef({
+            TelephonyManager.NETWORK_TYPE_UNKNOWN,
+            TelephonyManager.NETWORK_TYPE_GPRS,
+            TelephonyManager.NETWORK_TYPE_EDGE,
+            TelephonyManager.NETWORK_TYPE_UMTS,
+            TelephonyManager.NETWORK_TYPE_CDMA,
+            TelephonyManager.NETWORK_TYPE_EVDO_0,
+            TelephonyManager.NETWORK_TYPE_EVDO_A,
+            TelephonyManager.NETWORK_TYPE_1xRTT,
+            TelephonyManager.NETWORK_TYPE_HSDPA,
+            TelephonyManager.NETWORK_TYPE_HSUPA,
+            TelephonyManager.NETWORK_TYPE_HSPA,
+            TelephonyManager.NETWORK_TYPE_IDEN,
+            TelephonyManager.NETWORK_TYPE_EVDO_B,
+            TelephonyManager.NETWORK_TYPE_LTE,
+            TelephonyManager.NETWORK_TYPE_EHRPD,
+            TelephonyManager.NETWORK_TYPE_HSPAP,
+            TelephonyManager.NETWORK_TYPE_GSM,
+            TelephonyManager.NETWORK_TYPE_TD_SCDMA,
+            TelephonyManager.NETWORK_TYPE_IWLAN,
+
+            //TODO: In order for @SystemApi methods to use this class, there cannot be any
+            // public hidden members.  This network type is marked as hidden because it is not a
+            // true network type and we are looking to remove it completely from the available list
+            // of network types.
+            //TelephonyManager.NETWORK_TYPE_LTE_CA,
+
+            TelephonyManager.NETWORK_TYPE_NR,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NetworkType {
+    }
+
+    // TODO(b/180542000): remove and replace references with @ApnSetting.ApnType
+    @IntDef(flag = true, prefix = {"TYPE_"}, value = {
+            ApnSetting.TYPE_DEFAULT,
+            ApnSetting.TYPE_MMS,
+            ApnSetting.TYPE_SUPL,
+            ApnSetting.TYPE_DUN,
+            ApnSetting.TYPE_HIPRI,
+            ApnSetting.TYPE_FOTA,
+            ApnSetting.TYPE_IMS,
+            ApnSetting.TYPE_CBS,
+            ApnSetting.TYPE_IA,
+            ApnSetting.TYPE_EMERGENCY,
+            ApnSetting.TYPE_MCX,
+            ApnSetting.TYPE_XCAP,
+            ApnSetting.TYPE_BIP,
+            ApnSetting.TYPE_VSIM,
+            ApnSetting.TYPE_ENTERPRISE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApnType {
+    }
+
+    @IntDef(value = {
+            DataFailCause.NONE,
+            DataFailCause.OPERATOR_BARRED,
+            DataFailCause.NAS_SIGNALLING,
+            DataFailCause.LLC_SNDCP,
+            DataFailCause.INSUFFICIENT_RESOURCES,
+            DataFailCause.MISSING_UNKNOWN_APN,
+            DataFailCause.UNKNOWN_PDP_ADDRESS_TYPE,
+            DataFailCause.USER_AUTHENTICATION,
+            DataFailCause.ACTIVATION_REJECT_GGSN,
+            DataFailCause.ACTIVATION_REJECT_UNSPECIFIED,
+            DataFailCause.SERVICE_OPTION_NOT_SUPPORTED,
+            DataFailCause.SERVICE_OPTION_NOT_SUBSCRIBED,
+            DataFailCause.SERVICE_OPTION_OUT_OF_ORDER,
+            DataFailCause.NSAPI_IN_USE,
+            DataFailCause.REGULAR_DEACTIVATION,
+            DataFailCause.QOS_NOT_ACCEPTED,
+            DataFailCause.NETWORK_FAILURE,
+            DataFailCause.UMTS_REACTIVATION_REQ,
+            DataFailCause.FEATURE_NOT_SUPP,
+            DataFailCause.TFT_SEMANTIC_ERROR,
+            DataFailCause.TFT_SYTAX_ERROR,
+            DataFailCause.UNKNOWN_PDP_CONTEXT,
+            DataFailCause.FILTER_SEMANTIC_ERROR,
+            DataFailCause.FILTER_SYTAX_ERROR,
+            DataFailCause.PDP_WITHOUT_ACTIVE_TFT,
+            DataFailCause.ACTIVATION_REJECTED_BCM_VIOLATION,
+            DataFailCause.ONLY_IPV4_ALLOWED,
+            DataFailCause.ONLY_IPV6_ALLOWED,
+            DataFailCause.ONLY_SINGLE_BEARER_ALLOWED,
+            DataFailCause.ESM_INFO_NOT_RECEIVED,
+            DataFailCause.PDN_CONN_DOES_NOT_EXIST,
+            DataFailCause.MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
+            DataFailCause.COLLISION_WITH_NETWORK_INITIATED_REQUEST,
+            DataFailCause.ONLY_IPV4V6_ALLOWED,
+            DataFailCause.ONLY_NON_IP_ALLOWED,
+            DataFailCause.UNSUPPORTED_QCI_VALUE,
+            DataFailCause.BEARER_HANDLING_NOT_SUPPORTED,
+            DataFailCause.ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED,
+            DataFailCause.UNSUPPORTED_APN_IN_CURRENT_PLMN,
+            DataFailCause.INVALID_TRANSACTION_ID,
+            DataFailCause.MESSAGE_INCORRECT_SEMANTIC,
+            DataFailCause.INVALID_MANDATORY_INFO,
+            DataFailCause.MESSAGE_TYPE_UNSUPPORTED,
+            DataFailCause.MSG_TYPE_NONCOMPATIBLE_STATE,
+            DataFailCause.UNKNOWN_INFO_ELEMENT,
+            DataFailCause.CONDITIONAL_IE_ERROR,
+            DataFailCause.MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE,
+            DataFailCause.PROTOCOL_ERRORS,
+            DataFailCause.APN_TYPE_CONFLICT,
+            DataFailCause.INVALID_PCSCF_ADDR,
+            DataFailCause.INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN,
+            DataFailCause.EMM_ACCESS_BARRED,
+            DataFailCause.EMERGENCY_IFACE_ONLY,
+            DataFailCause.IFACE_MISMATCH,
+            DataFailCause.COMPANION_IFACE_IN_USE,
+            DataFailCause.IP_ADDRESS_MISMATCH,
+            DataFailCause.IFACE_AND_POL_FAMILY_MISMATCH,
+            DataFailCause.EMM_ACCESS_BARRED_INFINITE_RETRY,
+            DataFailCause.AUTH_FAILURE_ON_EMERGENCY_CALL,
+            DataFailCause.INVALID_DNS_ADDR,
+            DataFailCause.INVALID_PCSCF_OR_DNS_ADDRESS,
+            DataFailCause.CALL_PREEMPT_BY_EMERGENCY_APN,
+            DataFailCause.UE_INITIATED_DETACH_OR_DISCONNECT,
+            DataFailCause.MIP_FA_REASON_UNSPECIFIED,
+            DataFailCause.MIP_FA_ADMIN_PROHIBITED,
+            DataFailCause.MIP_FA_INSUFFICIENT_RESOURCES,
+            DataFailCause.MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE,
+            DataFailCause.MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE,
+            DataFailCause.MIP_FA_REQUESTED_LIFETIME_TOO_LONG,
+            DataFailCause.MIP_FA_MALFORMED_REQUEST,
+            DataFailCause.MIP_FA_MALFORMED_REPLY,
+            DataFailCause.MIP_FA_ENCAPSULATION_UNAVAILABLE,
+            DataFailCause.MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE,
+            DataFailCause.MIP_FA_REVERSE_TUNNEL_UNAVAILABLE,
+            DataFailCause.MIP_FA_REVERSE_TUNNEL_IS_MANDATORY,
+            DataFailCause.MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED,
+            DataFailCause.MIP_FA_MISSING_NAI,
+            DataFailCause.MIP_FA_MISSING_HOME_AGENT,
+            DataFailCause.MIP_FA_MISSING_HOME_ADDRESS,
+            DataFailCause.MIP_FA_UNKNOWN_CHALLENGE,
+            DataFailCause.MIP_FA_MISSING_CHALLENGE,
+            DataFailCause.MIP_FA_STALE_CHALLENGE,
+            DataFailCause.MIP_HA_REASON_UNSPECIFIED,
+            DataFailCause.MIP_HA_ADMIN_PROHIBITED,
+            DataFailCause.MIP_HA_INSUFFICIENT_RESOURCES,
+            DataFailCause.MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE,
+            DataFailCause.MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE,
+            DataFailCause.MIP_HA_REGISTRATION_ID_MISMATCH,
+            DataFailCause.MIP_HA_MALFORMED_REQUEST,
+            DataFailCause.MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS,
+            DataFailCause.MIP_HA_REVERSE_TUNNEL_UNAVAILABLE,
+            DataFailCause.MIP_HA_REVERSE_TUNNEL_IS_MANDATORY,
+            DataFailCause.MIP_HA_ENCAPSULATION_UNAVAILABLE,
+            DataFailCause.CLOSE_IN_PROGRESS,
+            DataFailCause.NETWORK_INITIATED_TERMINATION,
+            DataFailCause.MODEM_APP_PREEMPTED,
+            DataFailCause.PDN_IPV4_CALL_DISALLOWED,
+            DataFailCause.PDN_IPV4_CALL_THROTTLED,
+            DataFailCause.PDN_IPV6_CALL_DISALLOWED,
+            DataFailCause.PDN_IPV6_CALL_THROTTLED,
+            DataFailCause.MODEM_RESTART,
+            DataFailCause.PDP_PPP_NOT_SUPPORTED,
+            DataFailCause.UNPREFERRED_RAT,
+            DataFailCause.PHYSICAL_LINK_CLOSE_IN_PROGRESS,
+            DataFailCause.APN_PENDING_HANDOVER,
+            DataFailCause.PROFILE_BEARER_INCOMPATIBLE,
+            DataFailCause.SIM_CARD_CHANGED,
+            DataFailCause.LOW_POWER_MODE_OR_POWERING_DOWN,
+            DataFailCause.APN_DISABLED,
+            DataFailCause.MAX_PPP_INACTIVITY_TIMER_EXPIRED,
+            DataFailCause.IPV6_ADDRESS_TRANSFER_FAILED,
+            DataFailCause.TRAT_SWAP_FAILED,
+            DataFailCause.EHRPD_TO_HRPD_FALLBACK,
+            DataFailCause.MIP_CONFIG_FAILURE,
+            DataFailCause.PDN_INACTIVITY_TIMER_EXPIRED,
+            DataFailCause.MAX_IPV4_CONNECTIONS,
+            DataFailCause.MAX_IPV6_CONNECTIONS,
+            DataFailCause.APN_MISMATCH,
+            DataFailCause.IP_VERSION_MISMATCH,
+            DataFailCause.DUN_CALL_DISALLOWED,
+            DataFailCause.INTERNAL_EPC_NONEPC_TRANSITION,
+            DataFailCause.INTERFACE_IN_USE,
+            DataFailCause.APN_DISALLOWED_ON_ROAMING,
+            DataFailCause.APN_PARAMETERS_CHANGED,
+            DataFailCause.NULL_APN_DISALLOWED,
+            DataFailCause.THERMAL_MITIGATION,
+            DataFailCause.DATA_SETTINGS_DISABLED,
+            DataFailCause.DATA_ROAMING_SETTINGS_DISABLED,
+            DataFailCause.DDS_SWITCHED,
+            DataFailCause.FORBIDDEN_APN_NAME,
+            DataFailCause.DDS_SWITCH_IN_PROGRESS,
+            DataFailCause.CALL_DISALLOWED_IN_ROAMING,
+            DataFailCause.NON_IP_NOT_SUPPORTED,
+            DataFailCause.PDN_NON_IP_CALL_THROTTLED,
+            DataFailCause.PDN_NON_IP_CALL_DISALLOWED,
+            DataFailCause.CDMA_LOCK,
+            DataFailCause.CDMA_INTERCEPT,
+            DataFailCause.CDMA_REORDER,
+            DataFailCause.CDMA_RELEASE_DUE_TO_SO_REJECTION,
+            DataFailCause.CDMA_INCOMING_CALL,
+            DataFailCause.CDMA_ALERT_STOP,
+            DataFailCause.CHANNEL_ACQUISITION_FAILURE,
+            DataFailCause.MAX_ACCESS_PROBE,
+            DataFailCause.CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION,
+            DataFailCause.NO_RESPONSE_FROM_BASE_STATION,
+            DataFailCause.REJECTED_BY_BASE_STATION,
+            DataFailCause.CONCURRENT_SERVICES_INCOMPATIBLE,
+            DataFailCause.NO_CDMA_SERVICE,
+            DataFailCause.RUIM_NOT_PRESENT,
+            DataFailCause.CDMA_RETRY_ORDER,
+            DataFailCause.ACCESS_BLOCK,
+            DataFailCause.ACCESS_BLOCK_ALL,
+            DataFailCause.IS707B_MAX_ACCESS_PROBES,
+            DataFailCause.THERMAL_EMERGENCY,
+            DataFailCause.CONCURRENT_SERVICES_NOT_ALLOWED,
+            DataFailCause.INCOMING_CALL_REJECTED,
+            DataFailCause.NO_SERVICE_ON_GATEWAY,
+            DataFailCause.NO_GPRS_CONTEXT,
+            DataFailCause.ILLEGAL_MS,
+            DataFailCause.ILLEGAL_ME,
+            DataFailCause.GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED,
+            DataFailCause.GPRS_SERVICES_NOT_ALLOWED,
+            DataFailCause.MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK,
+            DataFailCause.IMPLICITLY_DETACHED,
+            DataFailCause.PLMN_NOT_ALLOWED,
+            DataFailCause.LOCATION_AREA_NOT_ALLOWED,
+            DataFailCause.GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN,
+            DataFailCause.PDP_DUPLICATE,
+            DataFailCause.UE_RAT_CHANGE,
+            DataFailCause.CONGESTION,
+            DataFailCause.NO_PDP_CONTEXT_ACTIVATED,
+            DataFailCause.ACCESS_CLASS_DSAC_REJECTION,
+            DataFailCause.PDP_ACTIVATE_MAX_RETRY_FAILED,
+            DataFailCause.RADIO_ACCESS_BEARER_FAILURE,
+            DataFailCause.ESM_UNKNOWN_EPS_BEARER_CONTEXT,
+            DataFailCause.DRB_RELEASED_BY_RRC,
+            DataFailCause.CONNECTION_RELEASED,
+            DataFailCause.EMM_DETACHED,
+            DataFailCause.EMM_ATTACH_FAILED,
+            DataFailCause.EMM_ATTACH_STARTED,
+            DataFailCause.LTE_NAS_SERVICE_REQUEST_FAILED,
+            DataFailCause.DUPLICATE_BEARER_ID,
+            DataFailCause.ESM_COLLISION_SCENARIOS,
+            DataFailCause.ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK,
+            DataFailCause.ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER,
+            DataFailCause.ESM_BAD_OTA_MESSAGE,
+            DataFailCause.ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL,
+            DataFailCause.ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT,
+            DataFailCause.DS_EXPLICIT_DEACTIVATION,
+            DataFailCause.ESM_LOCAL_CAUSE_NONE,
+            DataFailCause.LTE_THROTTLING_NOT_REQUIRED,
+            DataFailCause.ACCESS_CONTROL_LIST_CHECK_FAILURE,
+            DataFailCause.SERVICE_NOT_ALLOWED_ON_PLMN,
+            DataFailCause.EMM_T3417_EXPIRED,
+            DataFailCause.EMM_T3417_EXT_EXPIRED,
+            DataFailCause.RRC_UPLINK_DATA_TRANSMISSION_FAILURE,
+            DataFailCause.RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER,
+            DataFailCause.RRC_UPLINK_CONNECTION_RELEASE,
+            DataFailCause.RRC_UPLINK_RADIO_LINK_FAILURE,
+            DataFailCause.RRC_UPLINK_ERROR_REQUEST_FROM_NAS,
+            DataFailCause.RRC_CONNECTION_ACCESS_STRATUM_FAILURE,
+            DataFailCause.RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS,
+            DataFailCause.RRC_CONNECTION_ACCESS_BARRED,
+            DataFailCause.RRC_CONNECTION_CELL_RESELECTION,
+            DataFailCause.RRC_CONNECTION_CONFIG_FAILURE,
+            DataFailCause.RRC_CONNECTION_TIMER_EXPIRED,
+            DataFailCause.RRC_CONNECTION_LINK_FAILURE,
+            DataFailCause.RRC_CONNECTION_CELL_NOT_CAMPED,
+            DataFailCause.RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE,
+            DataFailCause.RRC_CONNECTION_REJECT_BY_NETWORK,
+            DataFailCause.RRC_CONNECTION_NORMAL_RELEASE,
+            DataFailCause.RRC_CONNECTION_RADIO_LINK_FAILURE,
+            DataFailCause.RRC_CONNECTION_REESTABLISHMENT_FAILURE,
+            DataFailCause.RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER,
+            DataFailCause.RRC_CONNECTION_ABORT_REQUEST,
+            DataFailCause.RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR,
+            DataFailCause.NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH,
+            DataFailCause.NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH,
+            DataFailCause.ESM_PROCEDURE_TIME_OUT,
+            DataFailCause.INVALID_CONNECTION_ID,
+            DataFailCause.MAXIMIUM_NSAPIS_EXCEEDED,
+            DataFailCause.INVALID_PRIMARY_NSAPI,
+            DataFailCause.CANNOT_ENCODE_OTA_MESSAGE,
+            DataFailCause.RADIO_ACCESS_BEARER_SETUP_FAILURE,
+            DataFailCause.PDP_ESTABLISH_TIMEOUT_EXPIRED,
+            DataFailCause.PDP_MODIFY_TIMEOUT_EXPIRED,
+            DataFailCause.PDP_INACTIVE_TIMEOUT_EXPIRED,
+            DataFailCause.PDP_LOWERLAYER_ERROR,
+            DataFailCause.PDP_MODIFY_COLLISION,
+            DataFailCause.MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED,
+            DataFailCause.NAS_REQUEST_REJECTED_BY_NETWORK,
+            DataFailCause.RRC_CONNECTION_INVALID_REQUEST,
+            DataFailCause.RRC_CONNECTION_TRACKING_AREA_ID_CHANGED,
+            DataFailCause.RRC_CONNECTION_RF_UNAVAILABLE,
+            DataFailCause.RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE,
+            DataFailCause.RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE,
+            DataFailCause.RRC_CONNECTION_ABORTED_AFTER_HANDOVER,
+            DataFailCause.RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE,
+            DataFailCause.RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE,
+            DataFailCause.IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER,
+            DataFailCause.IMEI_NOT_ACCEPTED,
+            DataFailCause.EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED,
+            DataFailCause.EPS_SERVICES_NOT_ALLOWED_IN_PLMN,
+            DataFailCause.MSC_TEMPORARILY_NOT_REACHABLE,
+            DataFailCause.CS_DOMAIN_NOT_AVAILABLE,
+            DataFailCause.ESM_FAILURE,
+            DataFailCause.MAC_FAILURE,
+            DataFailCause.SYNCHRONIZATION_FAILURE,
+            DataFailCause.UE_SECURITY_CAPABILITIES_MISMATCH,
+            DataFailCause.SECURITY_MODE_REJECTED,
+            DataFailCause.UNACCEPTABLE_NON_EPS_AUTHENTICATION,
+            DataFailCause.CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED,
+            DataFailCause.NO_EPS_BEARER_CONTEXT_ACTIVATED,
+            DataFailCause.INVALID_EMM_STATE,
+            DataFailCause.NAS_LAYER_FAILURE,
+            DataFailCause.MULTIPLE_PDP_CALL_NOT_ALLOWED,
+            DataFailCause.EMBMS_NOT_ENABLED,
+            DataFailCause.IRAT_HANDOVER_FAILED,
+            DataFailCause.EMBMS_REGULAR_DEACTIVATION,
+            DataFailCause.TEST_LOOPBACK_REGULAR_DEACTIVATION,
+            DataFailCause.LOWER_LAYER_REGISTRATION_FAILURE,
+            DataFailCause.DATA_PLAN_EXPIRED,
+            DataFailCause.UMTS_HANDOVER_TO_IWLAN,
+            DataFailCause.EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY,
+            DataFailCause.EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE,
+            DataFailCause.EVDO_HDR_CHANGED,
+            DataFailCause.EVDO_HDR_EXITED,
+            DataFailCause.EVDO_HDR_NO_SESSION,
+            DataFailCause.EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL,
+            DataFailCause.EVDO_HDR_CONNECTION_SETUP_TIMEOUT,
+            DataFailCause.FAILED_TO_ACQUIRE_COLOCATED_HDR,
+            DataFailCause.OTASP_COMMIT_IN_PROGRESS,
+            DataFailCause.NO_HYBRID_HDR_SERVICE,
+            DataFailCause.HDR_NO_LOCK_GRANTED,
+            DataFailCause.DBM_OR_SMS_IN_PROGRESS,
+            DataFailCause.HDR_FADE,
+            DataFailCause.HDR_ACCESS_FAILURE,
+            DataFailCause.UNSUPPORTED_1X_PREV,
+            DataFailCause.LOCAL_END,
+            DataFailCause.NO_SERVICE,
+            DataFailCause.FADE,
+            DataFailCause.NORMAL_RELEASE,
+            DataFailCause.ACCESS_ATTEMPT_ALREADY_IN_PROGRESS,
+            DataFailCause.REDIRECTION_OR_HANDOFF_IN_PROGRESS,
+            DataFailCause.EMERGENCY_MODE,
+            DataFailCause.PHONE_IN_USE,
+            DataFailCause.INVALID_MODE,
+            DataFailCause.INVALID_SIM_STATE,
+            DataFailCause.NO_COLLOCATED_HDR,
+            DataFailCause.UE_IS_ENTERING_POWERSAVE_MODE,
+            DataFailCause.DUAL_SWITCH,
+            DataFailCause.PPP_TIMEOUT,
+            DataFailCause.PPP_AUTH_FAILURE,
+            DataFailCause.PPP_OPTION_MISMATCH,
+            DataFailCause.PPP_PAP_FAILURE,
+            DataFailCause.PPP_CHAP_FAILURE,
+            DataFailCause.PPP_CLOSE_IN_PROGRESS,
+            DataFailCause.LIMITED_TO_IPV4,
+            DataFailCause.LIMITED_TO_IPV6,
+            DataFailCause.VSNCP_TIMEOUT,
+            DataFailCause.VSNCP_GEN_ERROR,
+            DataFailCause.VSNCP_APN_UNAUTHORIZED,
+            DataFailCause.VSNCP_PDN_LIMIT_EXCEEDED,
+            DataFailCause.VSNCP_NO_PDN_GATEWAY_ADDRESS,
+            DataFailCause.VSNCP_PDN_GATEWAY_UNREACHABLE,
+            DataFailCause.VSNCP_PDN_GATEWAY_REJECT,
+            DataFailCause.VSNCP_INSUFFICIENT_PARAMETERS,
+            DataFailCause.VSNCP_RESOURCE_UNAVAILABLE,
+            DataFailCause.VSNCP_ADMINISTRATIVELY_PROHIBITED,
+            DataFailCause.VSNCP_PDN_ID_IN_USE,
+            DataFailCause.VSNCP_SUBSCRIBER_LIMITATION,
+            DataFailCause.VSNCP_PDN_EXISTS_FOR_THIS_APN,
+            DataFailCause.VSNCP_RECONNECT_NOT_ALLOWED,
+            DataFailCause.IPV6_PREFIX_UNAVAILABLE,
+            DataFailCause.HANDOFF_PREFERENCE_CHANGED,
+            DataFailCause.SLICE_REJECTED,
+            DataFailCause.MATCH_ALL_RULE_NOT_ALLOWED,
+            DataFailCause.ALL_MATCHING_RULES_FAILED,
+            DataFailCause.OEM_DCFAILCAUSE_1,
+            DataFailCause.OEM_DCFAILCAUSE_2,
+            DataFailCause.OEM_DCFAILCAUSE_3,
+            DataFailCause.OEM_DCFAILCAUSE_4,
+            DataFailCause.OEM_DCFAILCAUSE_5,
+            DataFailCause.OEM_DCFAILCAUSE_6,
+            DataFailCause.OEM_DCFAILCAUSE_7,
+            DataFailCause.OEM_DCFAILCAUSE_8,
+            DataFailCause.OEM_DCFAILCAUSE_9,
+            DataFailCause.OEM_DCFAILCAUSE_10,
+            DataFailCause.OEM_DCFAILCAUSE_11,
+            DataFailCause.OEM_DCFAILCAUSE_12,
+            DataFailCause.OEM_DCFAILCAUSE_13,
+            DataFailCause.OEM_DCFAILCAUSE_14,
+            DataFailCause.OEM_DCFAILCAUSE_15,
+            DataFailCause.REGISTRATION_FAIL,
+            DataFailCause.GPRS_REGISTRATION_FAIL,
+            DataFailCause.SIGNAL_LOST,
+            DataFailCause.PREF_RADIO_TECH_CHANGED,
+            DataFailCause.RADIO_POWER_OFF,
+            DataFailCause.TETHERED_CALL_ACTIVE,
+            DataFailCause.ERROR_UNSPECIFIED,
+            DataFailCause.UNKNOWN,
+            DataFailCause.RADIO_NOT_AVAILABLE,
+            DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
+            DataFailCause.LOST_CONNECTION,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DataFailureCause {
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"PRECISE_CALL_STATE_"},
+            value = {
+            PreciseCallState.PRECISE_CALL_STATE_NOT_VALID,
+            PreciseCallState.PRECISE_CALL_STATE_IDLE,
+            PreciseCallState.PRECISE_CALL_STATE_ACTIVE,
+            PreciseCallState.PRECISE_CALL_STATE_HOLDING,
+            PreciseCallState.PRECISE_CALL_STATE_DIALING,
+            PreciseCallState.PRECISE_CALL_STATE_ALERTING,
+            PreciseCallState.PRECISE_CALL_STATE_INCOMING,
+            PreciseCallState.PRECISE_CALL_STATE_WAITING,
+            PreciseCallState.PRECISE_CALL_STATE_DISCONNECTED,
+            PreciseCallState.PRECISE_CALL_STATE_DISCONNECTING})
+    public @interface PreciseCallStates {}
+
+    @IntDef(value = {
+            DisconnectCause.NOT_VALID,
+            DisconnectCause.NOT_DISCONNECTED,
+            DisconnectCause.INCOMING_MISSED,
+            DisconnectCause.NORMAL,
+            DisconnectCause.LOCAL,
+            DisconnectCause.BUSY,
+            DisconnectCause.CONGESTION,
+            DisconnectCause.MMI,
+            DisconnectCause.INVALID_NUMBER,
+            DisconnectCause.NUMBER_UNREACHABLE,
+            DisconnectCause.SERVER_UNREACHABLE,
+            DisconnectCause.INVALID_CREDENTIALS,
+            DisconnectCause.OUT_OF_NETWORK,
+            DisconnectCause.SERVER_ERROR,
+            DisconnectCause.TIMED_OUT,
+            DisconnectCause.LOST_SIGNAL,
+            DisconnectCause.LIMIT_EXCEEDED,
+            DisconnectCause.INCOMING_REJECTED,
+            DisconnectCause.POWER_OFF,
+            DisconnectCause.OUT_OF_SERVICE,
+            DisconnectCause.ICC_ERROR,
+            DisconnectCause.CALL_BARRED,
+            DisconnectCause.FDN_BLOCKED,
+            DisconnectCause.CS_RESTRICTED,
+            DisconnectCause.CS_RESTRICTED_NORMAL,
+            DisconnectCause.CS_RESTRICTED_EMERGENCY,
+            DisconnectCause.UNOBTAINABLE_NUMBER,
+            DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE,
+            DisconnectCause.CDMA_DROP,
+            DisconnectCause.CDMA_INTERCEPT,
+            DisconnectCause.CDMA_REORDER,
+            DisconnectCause.CDMA_SO_REJECT,
+            DisconnectCause.CDMA_RETRY_ORDER,
+            DisconnectCause.CDMA_ACCESS_FAILURE,
+            DisconnectCause.CDMA_PREEMPTED,
+            DisconnectCause.CDMA_NOT_EMERGENCY,
+            DisconnectCause.CDMA_ACCESS_BLOCKED,
+            DisconnectCause.ERROR_UNSPECIFIED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DisconnectCauses {
+    }
+
+    @IntDef(value = {
+            PreciseDisconnectCause.NOT_VALID,
+            PreciseDisconnectCause.NO_DISCONNECT_CAUSE_AVAILABLE,
+            PreciseDisconnectCause.UNOBTAINABLE_NUMBER,
+            PreciseDisconnectCause.NORMAL,
+            PreciseDisconnectCause.BUSY,
+            PreciseDisconnectCause.NUMBER_CHANGED,
+            PreciseDisconnectCause.STATUS_ENQUIRY,
+            PreciseDisconnectCause.NORMAL_UNSPECIFIED,
+            PreciseDisconnectCause.NO_CIRCUIT_AVAIL,
+            PreciseDisconnectCause.TEMPORARY_FAILURE,
+            PreciseDisconnectCause.SWITCHING_CONGESTION,
+            PreciseDisconnectCause.CHANNEL_NOT_AVAIL,
+            PreciseDisconnectCause.QOS_NOT_AVAIL,
+            PreciseDisconnectCause.BEARER_NOT_AVAIL,
+            PreciseDisconnectCause.ACM_LIMIT_EXCEEDED,
+            PreciseDisconnectCause.CALL_BARRED,
+            PreciseDisconnectCause.FDN_BLOCKED,
+            PreciseDisconnectCause.IMSI_UNKNOWN_IN_VLR,
+            PreciseDisconnectCause.IMEI_NOT_ACCEPTED,
+            PreciseDisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE,
+            PreciseDisconnectCause.CDMA_DROP,
+            PreciseDisconnectCause.CDMA_INTERCEPT,
+            PreciseDisconnectCause.CDMA_REORDER,
+            PreciseDisconnectCause.CDMA_SO_REJECT,
+            PreciseDisconnectCause.CDMA_RETRY_ORDER,
+            PreciseDisconnectCause.CDMA_ACCESS_FAILURE,
+            PreciseDisconnectCause.CDMA_PREEMPTED,
+            PreciseDisconnectCause.CDMA_NOT_EMERGENCY,
+            PreciseDisconnectCause.CDMA_ACCESS_BLOCKED,
+            PreciseDisconnectCause.ERROR_UNSPECIFIED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PreciseDisconnectCauses {
+    }
+
+    /**
+     * Carrier Privilege Status.
+     */
+    @IntDef(prefix = { "CARRIER_PRIVILEGE_STATUS_" }, value = {
+        TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS,
+        TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS,
+        TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED,
+        TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CarrierPrivilegeStatus {
+    }
+
+    @IntDef({
+            Connection.AUDIO_CODEC_NONE,
+            Connection.AUDIO_CODEC_AMR,
+            Connection.AUDIO_CODEC_AMR_WB,
+            Connection.AUDIO_CODEC_QCELP13K,
+            Connection.AUDIO_CODEC_EVRC,
+            Connection.AUDIO_CODEC_EVRC_B,
+            Connection.AUDIO_CODEC_EVRC_WB,
+            Connection.AUDIO_CODEC_EVRC_NW,
+            Connection.AUDIO_CODEC_GSM_EFR,
+            Connection.AUDIO_CODEC_GSM_FR,
+            Connection.AUDIO_CODEC_G711U,
+            Connection.AUDIO_CODEC_G723,
+            Connection.AUDIO_CODEC_G711A,
+            Connection.AUDIO_CODEC_G722,
+            Connection.AUDIO_CODEC_G711AB,
+            Connection.AUDIO_CODEC_G729,
+            Connection.AUDIO_CODEC_EVS_NB,
+            Connection.AUDIO_CODEC_EVS_WB,
+            Connection.AUDIO_CODEC_EVS_SWB,
+            Connection.AUDIO_CODEC_EVS_FB
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsAudioCodec {
+    }
+
+    /**
+     * UICC SIM Application Types
+     */
+    @IntDef(prefix = { "APPTYPE_" }, value = {
+            TelephonyManager.APPTYPE_SIM,
+            TelephonyManager.APPTYPE_USIM,
+            TelephonyManager.APPTYPE_RUIM,
+            TelephonyManager.APPTYPE_CSIM,
+            TelephonyManager.APPTYPE_ISIM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UiccAppType{}
+
+    /**
+     * UICC SIM Application Types including UNKNOWN
+     */
+    @IntDef(prefix = { "APPTYPE_" }, value = {
+            TelephonyManager.APPTYPE_UNKNOWN,
+            TelephonyManager.APPTYPE_SIM,
+            TelephonyManager.APPTYPE_USIM,
+            TelephonyManager.APPTYPE_RUIM,
+            TelephonyManager.APPTYPE_CSIM,
+            TelephonyManager.APPTYPE_ISIM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UiccAppTypeExt{}
+
+    /**
+     * Override network type
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "OVERRIDE_NETWORK_TYPE_", value = {
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO,
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED})
+    public @interface OverrideNetworkType {}
+
+    /**
+     *  Result of a thermal mitigation request.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "THERMAL_MITIGATION_RESULT_" }, value = {
+        TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS,
+        TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_ERROR,
+        TelephonyManager.THERMAL_MITIGATION_RESULT_INVALID_STATE,
+        TelephonyManager.THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR})
+    public @interface ThermalMitigationResult {}
+
+    /**
+     * Per Android API guideline 8.15, annotation can't be public APIs. So duplicate
+     * android.net.NetworkCapabilities.NetCapability here. Must update here when new capabilities
+     * are added in {@link NetworkCapabilities}.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "NET_CAPABILITY_" }, value = {
+            NetworkCapabilities.NET_CAPABILITY_MMS,
+            NetworkCapabilities.NET_CAPABILITY_SUPL,
+            NetworkCapabilities.NET_CAPABILITY_DUN,
+            NetworkCapabilities.NET_CAPABILITY_FOTA,
+            NetworkCapabilities.NET_CAPABILITY_IMS,
+            NetworkCapabilities.NET_CAPABILITY_CBS,
+            NetworkCapabilities.NET_CAPABILITY_WIFI_P2P,
+            NetworkCapabilities.NET_CAPABILITY_IA,
+            NetworkCapabilities.NET_CAPABILITY_RCS,
+            NetworkCapabilities.NET_CAPABILITY_XCAP,
+            NetworkCapabilities.NET_CAPABILITY_EIMS,
+            NetworkCapabilities.NET_CAPABILITY_NOT_METERED,
+            NetworkCapabilities.NET_CAPABILITY_INTERNET,
+            NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED,
+            NetworkCapabilities.NET_CAPABILITY_TRUSTED,
+            NetworkCapabilities.NET_CAPABILITY_NOT_VPN,
+            NetworkCapabilities.NET_CAPABILITY_VALIDATED,
+            NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL,
+            NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING,
+            NetworkCapabilities.NET_CAPABILITY_FOREGROUND,
+            NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED,
+            NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED,
+            NetworkCapabilities.NET_CAPABILITY_OEM_PAID,
+            NetworkCapabilities.NET_CAPABILITY_MCX,
+            NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+            NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED,
+            NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE,
+            NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL,
+            NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED,
+            NetworkCapabilities.NET_CAPABILITY_ENTERPRISE,
+            NetworkCapabilities.NET_CAPABILITY_VSIM,
+            NetworkCapabilities.NET_CAPABILITY_BIP,
+            NetworkCapabilities.NET_CAPABILITY_HEAD_UNIT,
+            NetworkCapabilities.NET_CAPABILITY_MMTEL,
+            NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY,
+            NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH
+    })
+    public @interface NetCapability { }
+
+    /**
+     * Per Android API guideline 8.15, annotation can't be public APIs. So duplicate
+     * android.net.NetworkAgent.ValidationStatus here. Must update here when new validation status
+     * are added in {@link NetworkAgent}.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "VALIDATION_STATUS_" }, value = {
+            NetworkAgent.VALIDATION_STATUS_VALID,
+            NetworkAgent.VALIDATION_STATUS_NOT_VALID
+    })
+    public @interface ValidationStatus {}
+
+    /**
+     * IMS call Service types
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "SERVICE_TYPE_" }, value = {
+            ImsCallProfile.SERVICE_TYPE_NONE,
+            ImsCallProfile.SERVICE_TYPE_NORMAL,
+            ImsCallProfile.SERVICE_TYPE_EMERGENCY,
+    })
+    public @interface ImsCallServiceType {}
+
+    /**
+     * IMS call types
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "CALL_TYPE_" }, value = {
+            ImsCallProfile.CALL_TYPE_NONE,
+            ImsCallProfile.CALL_TYPE_VOICE_N_VIDEO,
+            ImsCallProfile.CALL_TYPE_VOICE,
+            ImsCallProfile.CALL_TYPE_VIDEO_N_VOICE,
+            ImsCallProfile.CALL_TYPE_VT,
+            ImsCallProfile.CALL_TYPE_VT_TX,
+            ImsCallProfile.CALL_TYPE_VT_RX,
+            ImsCallProfile.CALL_TYPE_VT_NODIR,
+            ImsCallProfile.CALL_TYPE_VS,
+            ImsCallProfile.CALL_TYPE_VS_TX,
+            ImsCallProfile.CALL_TYPE_VS_RX,
+    })
+    public @interface ImsCallType {}
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "NET_CAPABILITY_ENTERPRISE_SUB_LEVEL" }, value = {
+            NetworkCapabilities.NET_ENTERPRISE_ID_1,
+            NetworkCapabilities.NET_ENTERPRISE_ID_2,
+            NetworkCapabilities.NET_ENTERPRISE_ID_3,
+            NetworkCapabilities.NET_ENTERPRISE_ID_4,
+            NetworkCapabilities.NET_ENTERPRISE_ID_5
+    })
+
+    public @interface EnterpriseId {}
+}
diff --git a/android-35/android/telephony/AnomalyReporter.java b/android-35/android/telephony/AnomalyReporter.java
new file mode 100644
index 0000000..575ec27
--- /dev/null
+++ b/android-35/android/telephony/AnomalyReporter.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
+
+import static com.android.internal.telephony.TelephonyStatsLog.TELEPHONY_ANOMALY_DETECTED;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.ParcelUuid;
+import android.provider.DeviceConfig;
+
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.telephony.Rlog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A Simple Surface for Telephony to notify a loosely-coupled debugger of particular issues.
+ *
+ * AnomalyReporter allows an optional external logging component to receive events detected by
+ * the framework and take action. This log surface is designed to provide maximium flexibility
+ * to the receiver of these events. Envisioned use cases of this include notifying a vendor
+ * component of: an event that necessitates (timely) log collection on non-AOSP components;
+ * notifying a vendor component of a rare event that should prompt further action such as a
+ * bug report or user intervention for debug purposes.
+ *
+ * <p>This surface is not intended to enable a diagnostic monitor, nor is it intended to support
+ * streaming logs.
+ *
+ * @hide
+ */
+public final class AnomalyReporter {
+    private static final String TAG = "AnomalyReporter";
+
+    private static final String KEY_IS_TELEPHONY_ANOMALY_REPORT_ENABLED =
+            "is_telephony_anomaly_report_enabled";
+
+    private static Context sContext = null;
+
+    private static Map<UUID, Integer> sEvents = new ConcurrentHashMap<>();
+
+    /*
+     * Because this is only supporting system packages, once we find a package, it will be the
+     * same package until the next system upgrade. Thus, to save time in processing debug events
+     * we can cache this info and skip the resolution process after it's done the first time.
+     */
+    private static String sDebugPackageName = null;
+
+    private AnomalyReporter() {};
+
+    /**
+     * If enabled, build and send an intent to a Debug Service for logging.
+     *
+     * This method sends the {@link TelephonyManager#ACTION_ANOMALY_REPORTED} broadcast, which is
+     * system protected. Invoking this method unless you are the system will result in an error.
+     * Carrier Id will be set as UNKNOWN_CARRIER_ID.
+     *
+     * @param eventId a fixed event ID that will be sent for each instance of the same event. This
+     *        ID should be generated randomly.
+     * @param description an optional description, that if included will be used as the subject for
+     *        identification and discussion of this event. This description should ideally be
+     *        static and must not contain any sensitive information (especially PII).
+     */
+    public static void reportAnomaly(@NonNull UUID eventId, String description) {
+        reportAnomaly(eventId, description, UNKNOWN_CARRIER_ID);
+    }
+
+    /**
+     * If enabled, build and send an intent to a Debug Service for logging.
+     *
+     * This method sends the {@link TelephonyManager#ACTION_ANOMALY_REPORTED} broadcast, which is
+     * system protected. Invoking this method unless you are the system will result in an error.
+     *
+     * @param eventId a fixed event ID that will be sent for each instance of the same event. This
+     *        ID should be generated randomly.
+     * @param description an optional description, that if included will be used as the subject for
+     *        identification and discussion of this event. This description should ideally be
+     *        static and must not contain any sensitive information (especially PII).
+     * @param carrierId the carrier of the id associated with this event.
+     */
+    public static void reportAnomaly(@NonNull UUID eventId, String description, int carrierId) {
+        Rlog.i(TAG, "reportAnomaly: Received anomaly event report with eventId= " + eventId
+                + " and description= " + description);
+        if (sContext == null) {
+            Rlog.w(TAG, "AnomalyReporter not yet initialized, dropping event=" + eventId);
+            return;
+        }
+
+        //always write atoms to statsd
+        TelephonyStatsLog.write(
+                TELEPHONY_ANOMALY_DETECTED,
+                carrierId,
+                eventId.getLeastSignificantBits(),
+                eventId.getMostSignificantBits());
+
+        // Don't report via Intent if the server-side flag isn't loaded, as it implies other anomaly
+        // report related config hasn't loaded.
+        try {
+            boolean isAnomalyReportEnabledFromServer = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_TELEPHONY, KEY_IS_TELEPHONY_ANOMALY_REPORT_ENABLED,
+                    false);
+            if (!isAnomalyReportEnabledFromServer) return;
+        } catch (Exception e) {
+            Rlog.w(TAG, "Unable to read device config, dropping event=" + eventId);
+            return;
+        }
+
+        // If this event has already occurred, skip sending intents for it; regardless log its
+        // invocation here.
+        Integer count = sEvents.containsKey(eventId) ? sEvents.get(eventId) + 1 : 1;
+        sEvents.put(eventId, count);
+        if (count > 1) return;
+
+        // Even if we are initialized, that doesn't mean that a package name has been found.
+        // This is normal in many cases, such as when no debug package is installed on the system,
+        // so drop these events silently.
+        if (sDebugPackageName == null) return;
+
+        Intent dbgIntent = new Intent(TelephonyManager.ACTION_ANOMALY_REPORTED);
+        dbgIntent.putExtra(TelephonyManager.EXTRA_ANOMALY_ID, new ParcelUuid(eventId));
+        if (description != null) {
+            dbgIntent.putExtra(TelephonyManager.EXTRA_ANOMALY_DESCRIPTION, description);
+        }
+        dbgIntent.setPackage(sDebugPackageName);
+        sContext.sendBroadcast(dbgIntent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+    }
+
+    /**
+     * Initialize the AnomalyReporter with the current context.
+     *
+     * This method must be invoked before any calls to reportAnomaly() will succeed. This method
+     * should only be invoked at most once.
+     *
+     * @param context a Context object used to initialize this singleton AnomalyReporter in
+     *        the current process.
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public static void initialize(@NonNull Context context) {
+        if (context == null) {
+            throw new IllegalArgumentException("AnomalyReporter needs a non-null context.");
+        }
+
+        // Ensure that this context has sufficient permissions to send debug events.
+        context.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE,
+                "This app does not have privileges to send debug events");
+
+        sContext = context;
+
+        // Check to see if there is a valid debug package; if there are multiple, that's a config
+        // error, so just take the first one.
+        PackageManager pm = sContext.getPackageManager();
+        if (pm == null) return;
+        List<ResolveInfo> packages = pm.queryBroadcastReceivers(
+                new Intent(TelephonyManager.ACTION_ANOMALY_REPORTED),
+                PackageManager.MATCH_SYSTEM_ONLY
+                        | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+        if (packages == null || packages.isEmpty()) return;
+        if (packages.size() > 1) {
+            Rlog.e(TAG, "Multiple Anomaly Receivers installed.");
+        }
+
+        for (ResolveInfo r : packages) {
+            if (r.activityInfo == null) {
+                Rlog.w(TAG, "Found package without activity");
+                continue;
+            } else if (pm.checkPermission(
+                            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+                            r.activityInfo.packageName)
+                      != PackageManager.PERMISSION_GRANTED) {
+                Rlog.w(TAG, "Found package without proper permissions"
+                                    + r.activityInfo.packageName);
+                continue;
+            }
+            Rlog.d(TAG, "Found a valid package " + r.activityInfo.packageName);
+            sDebugPackageName = r.activityInfo.packageName;
+            break;
+        }
+        // Initialization may only be performed once.
+    }
+
+    /** Dump the contents of the AnomalyReporter */
+    public static void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+        if (sContext == null) return;
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        sContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "Requires DUMP");
+        pw.println("Initialized=" + (sContext != null ? "Yes" : "No"));
+        pw.println("Debug Package=" + sDebugPackageName);
+        pw.println("Anomaly Counts:");
+        pw.increaseIndent();
+        for (UUID event : sEvents.keySet()) {
+            pw.println(event + ": " + sEvents.get(event));
+        }
+        pw.decreaseIndent();
+        pw.flush();
+    }
+}
diff --git a/android-35/android/telephony/AvailableNetworkInfo.java b/android-35/android/telephony/AvailableNetworkInfo.java
new file mode 100644
index 0000000..6d673fb
--- /dev/null
+++ b/android-35/android/telephony/AvailableNetworkInfo.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.RadioAccessSpecifier;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Defines available network information which includes corresponding subscription id,
+ * network plmns and corresponding priority to be used for network selection by Opportunistic
+ * Network Service when passed through {@link TelephonyManager#updateAvailableNetworks}
+ */
+public final class AvailableNetworkInfo implements Parcelable {
+    /*
+     * Defines number of priority level high.
+     */
+    public static final int PRIORITY_HIGH = 1;
+
+    /*
+     * Defines number of priority level medium.
+     */
+    public static final int PRIORITY_MED = 2;
+
+    /*
+     * Defines number of priority level low.
+     */
+    public static final int PRIORITY_LOW = 3;
+
+    /** @hide */
+    @IntDef({
+        PRIORITY_HIGH,
+        PRIORITY_MED,
+        PRIORITY_LOW,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AvailableNetworkInfoPriority {}
+    /**
+     * subscription Id of the available network. This value must be one of the entry retrieved from
+     * {@link SubscriptionManager#getOpportunisticSubscriptions}
+     */
+    private int mSubId;
+
+    /**
+     * Priority for the subscription id.
+     * Priorities are in the range of {@link AvailableNetworkInfo#PRIORITY_LOW} to
+     * {@link AvailableNetworkInfo#PRIORITY_HIGH}
+     * Among all networks available after network scan, subId with highest priority is chosen
+     * for network selection. If there are more than one subId with highest priority then the
+     * network with highest RSRP is chosen.
+     */
+    private @AvailableNetworkInfoPriority int mPriority;
+
+    /**
+     * Describes the List of PLMN ids (MCC-MNC) associated with mSubId.
+     * Opportunistic Network Service will scan and verify specified PLMNs are available.
+     * If this entry is left empty, then the Opportunistic Network Service will not scan the network
+     * to validate the network availability.
+     */
+    private ArrayList<String> mMccMncs;
+
+    /**
+     * Returns the frequency bands associated with the {@link #getMccMncs() MCC/MNCs}.
+     * Opportunistic network service will use these bands to scan.
+     *
+     * When no specific bands are specified (empty array or null) CBRS band
+     * {@link AccessNetworkConstants.EutranBand.BAND_48} will be used for network scan.
+     *
+     * See {@link AccessNetworkConstants} for details.
+     *
+     * @deprecated use {@link #mRadioAccessSpecifiers} instead
+     */
+    @Deprecated
+    private ArrayList<Integer> mBands;
+
+    /**
+     * Returns a list of {@link RadioAccessSpecifier} associated with the available network.
+     * Opportunistic network service will use this to determine which bands to scan for.
+     *
+     * If this entry is left empty, {@link RadioAcccessSpecifier}s with {@link AccessNetworkType}s
+     * of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link
+     * AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed
+     * by Opportunistic network service for a network scan.
+     */
+    private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers;
+
+    /**
+     * Return subscription Id of the available network.
+     * This value must be one of the entry retrieved from
+     * {@link SubscriptionManager#getOpportunisticSubscriptions}
+     * @return subscription id
+     */
+    public int getSubId() {
+        return mSubId;
+    }
+
+    /**
+     * Return priority for the subscription id.
+     * Priorities are in the range of {@link AvailableNetworkInfo#PRIORITY_LOW} to
+     * {@link AvailableNetworkInfo#PRIORITY_HIGH}
+     * Among all networks available after network scan, subId with highest priority is chosen
+     * for network selection. If there are more than one subId with highest priority then the
+     * network with highest RSRP is chosen.
+     * @return priority level
+     */
+    @AvailableNetworkInfoPriority
+    public int getPriority() {
+        return mPriority;
+    }
+
+    /**
+     * Return List of PLMN ids (MCC-MNC) associated with the sub ID.
+     * Opportunistic Network Service will scan and verify specified PLMNs are available.
+     * If this entry is left empty, then the Opportunistic Network Service will not scan the network
+     * to validate the network availability.
+     * @return list of PLMN ids
+     */
+    public @NonNull List<String> getMccMncs() {
+        return (List<String>) mMccMncs.clone();
+    }
+
+    /**
+     * Returns the frequency bands that need to be scanned by opportunistic network service
+     *
+     * The returned value is defined in either of {@link AccessNetworkConstants.GeranBand},
+     * {@link AccessNetworkConstants.UtranBand} and {@link AccessNetworkConstants.EutranBand}
+     * See {@link AccessNetworkConstants.AccessNetworkType} for details regarding different network
+     * types. When no specific bands are specified (empty array or null) CBRS band
+     * {@link AccessNetworkConstants.EutranBand#BAND_48} will be used for network scan.
+     */
+    public @NonNull List<Integer> getBands() {
+        return (List<Integer>) mBands.clone();
+    }
+
+    /**
+     * Returns a list of {@link RadioAccessSpecifier} associated with the available network.
+     * Opportunistic network service will use this to determine which bands to scan for.
+     *
+     * @return the access network type associated with the available network.
+     */
+    public @NonNull List<RadioAccessSpecifier> getRadioAccessSpecifiers() {
+        return (List<RadioAccessSpecifier>) mRadioAccessSpecifiers.clone();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSubId);
+        dest.writeInt(mPriority);
+        dest.writeStringList(mMccMncs);
+        dest.writeList(mBands);
+        dest.writeList(mRadioAccessSpecifiers);
+    }
+
+    private AvailableNetworkInfo(Parcel in) {
+        mSubId = in.readInt();
+        mPriority = in.readInt();
+        mMccMncs = new ArrayList<>();
+        in.readStringList(mMccMncs);
+        mBands = new ArrayList<>();
+        in.readList(mBands, Integer.class.getClassLoader(), java.lang.Integer.class);
+        mRadioAccessSpecifiers = new ArrayList<>();
+        in.readList(mRadioAccessSpecifiers, RadioAccessSpecifier.class.getClassLoader(), android.telephony.RadioAccessSpecifier.class);
+    }
+
+    public AvailableNetworkInfo(int subId, int priority, @NonNull List<String> mccMncs,
+            @NonNull List<Integer> bands) {
+        this(subId, priority, mccMncs, bands,
+                new ArrayList<RadioAccessSpecifier>());
+    }
+
+    /** @hide */
+    private AvailableNetworkInfo(int subId, @AvailableNetworkInfoPriority int priority,
+            @NonNull List<String> mccMncs, @NonNull List<Integer> bands,
+            @NonNull List<RadioAccessSpecifier> radioAccessSpecifiers) {
+        mSubId = subId;
+        mPriority = priority;
+        mMccMncs = new ArrayList<String>(mccMncs);
+        mBands = new ArrayList<Integer>(bands);
+        mRadioAccessSpecifiers = new ArrayList<RadioAccessSpecifier>(radioAccessSpecifiers);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        AvailableNetworkInfo ani;
+
+        try {
+            ani = (AvailableNetworkInfo) o;
+        } catch (ClassCastException ex) {
+            return false;
+        }
+
+        if (o == null) {
+            return false;
+        }
+
+        return (mSubId == ani.mSubId
+            && mPriority == ani.mPriority
+            && (((mMccMncs != null)
+            && mMccMncs.equals(ani.mMccMncs)))
+            && mBands.equals(ani.mBands))
+            && mRadioAccessSpecifiers.equals(ani.getRadioAccessSpecifiers());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSubId, mPriority, mMccMncs, mBands, mRadioAccessSpecifiers);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<AvailableNetworkInfo> CREATOR =
+            new Creator<AvailableNetworkInfo>() {
+                @Override
+                public AvailableNetworkInfo createFromParcel(Parcel in) {
+                    return new AvailableNetworkInfo(in);
+                }
+
+                @Override
+                public AvailableNetworkInfo[] newArray(int size) {
+                    return new AvailableNetworkInfo[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return ("AvailableNetworkInfo:"
+            + " mSubId: " + mSubId
+            + " mPriority: " + mPriority
+            + " mMccMncs: " + Arrays.toString(mMccMncs.toArray())
+            + " mBands: " + Arrays.toString(mBands.toArray())
+            + " mRadioAccessSpecifiers: " + Arrays.toString(mRadioAccessSpecifiers.toArray()));
+    }
+
+    /**
+     * Provides a convenient way to set the fields of a {@link AvailableNetworkInfo} when
+     * creating a new instance.
+     *
+     * <p>The example below shows how you might create a new {@code AvailableNetworkInfo}:
+     *
+     * <pre><code>
+     *
+     * AvailableNetworkInfo aNI = new AvailableNetworkInfo.Builder(subId)
+     *     .setPriority(AvailableNetworkInfo.PRIORITY_MED)
+     *     .setRadioAccessSpecifiers(radioAccessSpecifiers)
+     *     .setMccMncs(mccMncs)
+     *     .build();
+     * </code></pre>
+     */
+    public static final class Builder {
+        private int mSubId = Integer.MIN_VALUE;
+        private @AvailableNetworkInfoPriority int mPriority = AvailableNetworkInfo.PRIORITY_LOW;
+        private ArrayList<String> mMccMncs = new ArrayList<>();
+        private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers = new ArrayList<>();
+
+        /**
+         *
+         */
+        /**
+         * Creates an AvailableNetworkInfo Builder with specified subscription id.
+         *
+         * @param subId of the availableNetwork.
+         */
+        public Builder(int subId) {
+            mSubId = subId;
+        }
+
+        /**
+         * Sets the priority for the subscription id.
+         *
+         * @param priority of the subscription id. See {@link AvailableNetworkInfo#getPriority} for
+         * more details
+         * @return the original Builder object.
+         */
+        public @NonNull Builder setPriority(@AvailableNetworkInfoPriority int priority) {
+            if (priority > AvailableNetworkInfo.PRIORITY_LOW
+                    || priority < AvailableNetworkInfo.PRIORITY_HIGH) {
+                throw new IllegalArgumentException("A valid priority must be set");
+            }
+            mPriority = priority;
+            return this;
+        }
+
+        /**
+         * Sets the list of mccmncs associated with the subscription id.
+         *
+         * @param mccMncs nonull list of mccmncs. An empty List is still accepted. Please read
+         * documentation in {@link AvailableNetworkInfo} to see consequences of an empty List.
+         * @return the original Builder object.
+         */
+        public @NonNull Builder setMccMncs(@NonNull List<String> mccMncs) {
+            Objects.requireNonNull(mccMncs, "A non-null List of mccmncs must be set. An empty "
+                    + "List is still accepted. Please read documentation in "
+                    + "AvailableNetworkInfo to see consequences of an empty List.");
+            mMccMncs = new ArrayList<>(mccMncs);
+            return this;
+        }
+
+        /**
+         * Sets the list of mccmncs associated with the subscription id.
+         *
+         * @param radioAccessSpecifiers nonull list of radioAccessSpecifiers. An empty List is still
+         * accepted. Please read documentation in {@link AvailableNetworkInfo} to see
+         * consequences of an empty List.
+         * @return the original Builder object.
+         */
+        public @NonNull Builder setRadioAccessSpecifiers(
+                @NonNull List<RadioAccessSpecifier> radioAccessSpecifiers) {
+            Objects.requireNonNull(radioAccessSpecifiers, "A non-null List of "
+                    + "RadioAccessSpecifiers must be set. An empty List is still accepted. Please "
+                    + "read documentation in AvailableNetworkInfo to see consequences of an "
+                    + "empty List.");
+            mRadioAccessSpecifiers = new ArrayList<>(radioAccessSpecifiers);
+            return this;
+        }
+
+        /**
+         * @return an AvailableNetworkInfo object with all the fields previously set by the Builder.
+         */
+        public @NonNull AvailableNetworkInfo build() {
+            if (mSubId == Integer.MIN_VALUE) {
+                throw new IllegalArgumentException("A valid subId must be set");
+            }
+
+            return new AvailableNetworkInfo(mSubId, mPriority, mMccMncs, new ArrayList<>(),
+                    mRadioAccessSpecifiers);
+        }
+    }
+}
diff --git a/android-35/android/telephony/BarringInfo.java b/android-35/android/telephony/BarringInfo.java
new file mode 100644
index 0000000..e20e4d2
--- /dev/null
+++ b/android-35/android/telephony/BarringInfo.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright 2019 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Provides the barring configuration for a particular service type.
+ *
+ * Provides indication about the barring of a particular service for use. Certain barring types
+ * are only valid for certain technology families. Any service that does not have a barring
+ * configuration is unbarred by default.
+ */
+public final class BarringInfo implements Parcelable {
+
+    /**
+     * Barring Service Type
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "BARRING_SERVICE_TYPE_", value = {
+            BARRING_SERVICE_TYPE_CS_SERVICE,
+            BARRING_SERVICE_TYPE_PS_SERVICE,
+            BARRING_SERVICE_TYPE_CS_VOICE,
+            BARRING_SERVICE_TYPE_MO_SIGNALLING,
+            BARRING_SERVICE_TYPE_MO_DATA,
+            BARRING_SERVICE_TYPE_CS_FALLBACK,
+            BARRING_SERVICE_TYPE_MMTEL_VOICE,
+            BARRING_SERVICE_TYPE_MMTEL_VIDEO,
+            BARRING_SERVICE_TYPE_EMERGENCY,
+            BARRING_SERVICE_TYPE_SMS})
+    public @interface BarringServiceType {}
+
+    /* Applicable to UTRAN */
+    /** Barring indicator for circuit-switched service; applicable to UTRAN */
+    public static final int BARRING_SERVICE_TYPE_CS_SERVICE =
+            android.hardware.radio.network.BarringInfo.SERVICE_TYPE_CS_SERVICE;
+    /** Barring indicator for packet-switched service; applicable to UTRAN */
+    public static final int BARRING_SERVICE_TYPE_PS_SERVICE =
+            android.hardware.radio.network.BarringInfo.SERVICE_TYPE_PS_SERVICE;
+    /** Barring indicator for circuit-switched voice service; applicable to UTRAN */
+    public static final int BARRING_SERVICE_TYPE_CS_VOICE =
+            android.hardware.radio.network.BarringInfo.SERVICE_TYPE_CS_VOICE;
+
+    /* Applicable to EUTRAN, NGRAN */
+    /** Barring indicator for mobile-originated signalling; applicable to EUTRAN and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_MO_SIGNALLING =
+            android.hardware.radio.network.BarringInfo.SERVICE_TYPE_MO_SIGNALLING;
+    /** Barring indicator for mobile-originated data traffic; applicable to EUTRAN and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_MO_DATA =
+            android.hardware.radio.network.BarringInfo.SERVICE_TYPE_MO_DATA;
+    /** Barring indicator for circuit-switched fallback for voice; applicable to EUTRAN and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_CS_FALLBACK =
+            android.hardware.radio.network.BarringInfo.SERVICE_TYPE_CS_FALLBACK;
+    /** Barring indicator for MMTEL (IMS) voice; applicable to EUTRAN and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_MMTEL_VOICE =
+            android.hardware.radio.network.BarringInfo.SERVICE_TYPE_MMTEL_VOICE;
+    /** Barring indicator for MMTEL (IMS) video; applicable to EUTRAN and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_MMTEL_VIDEO =
+            android.hardware.radio.network.BarringInfo.SERVICE_TYPE_MMTEL_VIDEO;
+
+    /* Applicable to UTRAN, EUTRAN, NGRAN */
+    /** Barring indicator for emergency services; applicable to UTRAN, EUTRAN, and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_EMERGENCY =
+            android.hardware.radio.network.BarringInfo.SERVICE_TYPE_EMERGENCY;
+    /** Barring indicator for SMS sending; applicable to UTRAN, EUTRAN, and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_SMS =
+            android.hardware.radio.network.BarringInfo.SERVICE_TYPE_SMS;
+
+    //TODO: add barring constants for Operator-Specific barring codes
+
+    /** Describe the current barring configuration of a cell */
+    public static final class BarringServiceInfo implements Parcelable {
+        /**
+         * Barring Type
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = "BARRING_TYPE_", value =
+                    {BARRING_TYPE_NONE,
+                    BARRING_TYPE_UNCONDITIONAL,
+                    BARRING_TYPE_CONDITIONAL,
+                    BARRING_TYPE_UNKNOWN})
+        public @interface BarringType {}
+
+        /** Barring is inactive */
+        public static final int BARRING_TYPE_NONE =
+                android.hardware.radio.network.BarringInfo.BARRING_TYPE_NONE;
+        /** The service is barred */
+        public static final int BARRING_TYPE_UNCONDITIONAL =
+                android.hardware.radio.network.BarringInfo.BARRING_TYPE_UNCONDITIONAL;
+        /** The service may be barred based on additional factors */
+        public static final int BARRING_TYPE_CONDITIONAL =
+                android.hardware.radio.network.BarringInfo.BARRING_TYPE_CONDITIONAL;
+
+        /** If a modem does not report barring info, then the barring type will be UNKNOWN */
+        public static final int BARRING_TYPE_UNKNOWN = -1;
+
+        private final @BarringType int mBarringType;
+
+        private final boolean mIsConditionallyBarred;
+        private final int mConditionalBarringFactor;
+        private final int mConditionalBarringTimeSeconds;
+
+        /** @hide */
+        public BarringServiceInfo(@BarringType int type) {
+            this(type, false, 0, 0);
+        }
+
+        /** @hide */
+        @TestApi
+        public BarringServiceInfo(@BarringType int barringType, boolean isConditionallyBarred,
+                int conditionalBarringFactor, int conditionalBarringTimeSeconds) {
+            mBarringType = barringType;
+            mIsConditionallyBarred = isConditionallyBarred;
+            mConditionalBarringFactor = conditionalBarringFactor;
+            mConditionalBarringTimeSeconds = conditionalBarringTimeSeconds;
+        }
+
+        public @BarringType int getBarringType() {
+            return mBarringType;
+        }
+
+        /**
+         * @return true if the conditional barring parameters have resulted in the service being
+         *         barred; false if the service has either not been evaluated for conditional
+         *         barring or has been evaluated and isn't barred.
+         */
+        public boolean isConditionallyBarred() {
+            return mIsConditionallyBarred;
+        }
+
+        /**
+         * @return the conditional barring factor as a percentage 0-100, which is the probability of
+         *         a random device being barred for the service type.
+         */
+        public int getConditionalBarringFactor() {
+            return mConditionalBarringFactor;
+        }
+
+        /**
+         * @return the conditional barring time seconds, which is the interval between successive
+         *         evaluations for conditional barring based on the barring factor.
+         */
+        @SuppressLint("MethodNameUnits")
+        public int getConditionalBarringTimeSeconds() {
+            return mConditionalBarringTimeSeconds;
+        }
+
+        /**
+         * Return whether a service is currently barred based on the BarringInfo
+         *
+         * @return true if the service is currently being barred, otherwise false
+         */
+        public boolean isBarred() {
+            return mBarringType == BarringServiceInfo.BARRING_TYPE_UNCONDITIONAL
+                    || (mBarringType == BarringServiceInfo.BARRING_TYPE_CONDITIONAL
+                            && mIsConditionallyBarred);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mBarringType, mIsConditionallyBarred,
+                    mConditionalBarringFactor, mConditionalBarringTimeSeconds);
+        }
+
+        @Override
+        public boolean equals(Object rhs) {
+            if (!(rhs instanceof BarringServiceInfo)) return false;
+
+            BarringServiceInfo other = (BarringServiceInfo) rhs;
+            return mBarringType == other.mBarringType
+                    && mIsConditionallyBarred == other.mIsConditionallyBarred
+                    && mConditionalBarringFactor == other.mConditionalBarringFactor
+                    && mConditionalBarringTimeSeconds == other.mConditionalBarringTimeSeconds;
+        }
+
+        private static String barringTypeToString(@BarringType int barringType) {
+            return switch (barringType) {
+                case BARRING_TYPE_NONE -> "NONE";
+                case BARRING_TYPE_CONDITIONAL -> "CONDITIONAL";
+                case BARRING_TYPE_UNCONDITIONAL -> "UNCONDITIONAL";
+                case BARRING_TYPE_UNKNOWN -> "UNKNOWN";
+                default -> "UNKNOWN(" + barringType + ")";
+            };
+        }
+
+        @Override
+        public String toString() {
+            return "BarringServiceInfo {mBarringType=" + barringTypeToString(mBarringType)
+                    + ", mIsConditionallyBarred=" + mIsConditionallyBarred
+                    + ", mConditionalBarringFactor=" + mConditionalBarringFactor
+                    + ", mConditionalBarringTimeSeconds=" + mConditionalBarringTimeSeconds + "}";
+        }
+
+        /** @hide */
+        public BarringServiceInfo(Parcel p) {
+            mBarringType = p.readInt();
+            mIsConditionallyBarred = p.readBoolean();
+            mConditionalBarringFactor = p.readInt();
+            mConditionalBarringTimeSeconds = p.readInt();
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mBarringType);
+            dest.writeBoolean(mIsConditionallyBarred);
+            dest.writeInt(mConditionalBarringFactor);
+            dest.writeInt(mConditionalBarringTimeSeconds);
+        }
+
+        /* @inheritDoc */
+        public static final @NonNull Parcelable.Creator<BarringServiceInfo> CREATOR =
+                new Parcelable.Creator<BarringServiceInfo>() {
+                    @Override
+                    public BarringServiceInfo createFromParcel(Parcel source) {
+                        return new BarringServiceInfo(source);
+                    }
+
+                    @Override
+                    public BarringServiceInfo[] newArray(int size) {
+                        return new BarringServiceInfo[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+    }
+
+    private static final BarringServiceInfo BARRING_SERVICE_INFO_UNKNOWN =
+            new BarringServiceInfo(BarringServiceInfo.BARRING_TYPE_UNKNOWN);
+
+    private static final BarringServiceInfo BARRING_SERVICE_INFO_UNBARRED =
+            new BarringServiceInfo(BarringServiceInfo.BARRING_TYPE_NONE);
+
+    private CellIdentity mCellIdentity;
+
+    // A SparseArray potentially mapping each BarringService type to a BarringServiceInfo config
+    // that describes the current barring status of that particular service.
+    private SparseArray<BarringServiceInfo> mBarringServiceInfos;
+
+    /** @hide */
+    @SystemApi
+    public BarringInfo() {
+        mBarringServiceInfos = new SparseArray<>();
+    }
+
+    /**
+     * Constructor for new BarringInfo instances.
+     *
+     * @hide
+     */
+    @TestApi
+    public BarringInfo(@Nullable CellIdentity barringCellId,
+            @NonNull SparseArray<BarringServiceInfo> barringServiceInfos) {
+        mCellIdentity = barringCellId;
+        mBarringServiceInfos = barringServiceInfos;
+    }
+
+    /**
+     * Get the BarringServiceInfo for a specified service.
+     *
+     * @return a BarringServiceInfo struct describing the current barring status for a service
+     */
+    public @NonNull BarringServiceInfo getBarringServiceInfo(@BarringServiceType int service) {
+        BarringServiceInfo bsi = mBarringServiceInfos.get(service);
+        // If barring is reported but not for a particular service, then we report the barring
+        // type as UNKNOWN; if the modem reports barring info but doesn't report for a particular
+        // service then we can safely assume that the service isn't barred (for instance because
+        // that particular service isn't applicable to the current RAN).
+        return (bsi != null) ? bsi : mBarringServiceInfos.size() > 0
+                ? BARRING_SERVICE_INFO_UNBARRED : BARRING_SERVICE_INFO_UNKNOWN;
+    }
+
+    /** @hide */
+    @SystemApi
+    public @NonNull BarringInfo createLocationInfoSanitizedCopy() {
+        // The only thing that would need sanitizing is the CellIdentity
+        if (mCellIdentity == null) return this;
+
+        return new BarringInfo(mCellIdentity.sanitizeLocationInfo(), mBarringServiceInfos);
+    }
+
+    /** @hide */
+    public BarringInfo(Parcel p) {
+        mCellIdentity = p.readParcelable(CellIdentity.class.getClassLoader(), android.telephony.CellIdentity.class);
+        mBarringServiceInfos = p.readSparseArray(BarringServiceInfo.class.getClassLoader(), android.telephony.BarringInfo.BarringServiceInfo.class);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeParcelable(mCellIdentity, flags);
+        dest.writeSparseArray(mBarringServiceInfos);
+    }
+
+    public static final @NonNull Parcelable.Creator<BarringInfo> CREATOR =
+            new Parcelable.Creator<BarringInfo>() {
+                @Override
+                public BarringInfo createFromParcel(Parcel source) {
+                    return new BarringInfo(source);
+                }
+
+                @Override
+                public BarringInfo[] newArray(int size) {
+                    return new BarringInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = mCellIdentity != null ? mCellIdentity.hashCode() : 7;
+        for (int i = 0; i < mBarringServiceInfos.size(); i++) {
+            hash = hash + 15 * mBarringServiceInfos.keyAt(i);
+            hash = hash + 31 * mBarringServiceInfos.valueAt(i).hashCode();
+        }
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object rhs) {
+        if (!(rhs instanceof BarringInfo)) return false;
+
+        BarringInfo bi = (BarringInfo) rhs;
+
+        if (hashCode() != bi.hashCode()) return false;
+
+        if (mBarringServiceInfos.size() != bi.mBarringServiceInfos.size()) return false;
+
+        for (int i = 0; i < mBarringServiceInfos.size(); i++) {
+            if (mBarringServiceInfos.keyAt(i) != bi.mBarringServiceInfos.keyAt(i)) return false;
+            if (!Objects.equals(mBarringServiceInfos.valueAt(i),
+                        bi.mBarringServiceInfos.valueAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "BarringInfo {mCellIdentity=" + mCellIdentity
+               + ", mBarringServiceInfos=" + mBarringServiceInfos + "}";
+    }
+}
diff --git a/android-35/android/telephony/BinderCacheManager.java b/android-35/android/telephony/BinderCacheManager.java
new file mode 100644
index 0000000..0d3e2fe
--- /dev/null
+++ b/android-35/android/telephony/BinderCacheManager.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Keeps track of the connection to a Binder node, refreshes the cache if the node dies, and lets
+ * interested parties register listeners on the node to be notified when the node has died via the
+ * registered {@link Runnable}.
+ * @param <T> The IInterface representing the Binder type that this manager will be managing the
+ *           cache of.
+ * @hide
+ */
+public class BinderCacheManager<T extends IInterface> {
+
+    /**
+     * Factory class for creating new IInterfaces in the case that {@link #getBinder()} is
+     * called and there is no active binder available.
+     * @param <T> The IInterface that should be cached and returned to the caller when
+     * {@link #getBinder()} is called until the Binder node dies.
+     */
+    public interface BinderInterfaceFactory<T> {
+        /**
+         * @return A new instance of the Binder node, which will be cached until it dies.
+         */
+        T create();
+    }
+
+    /**
+     * Tracks the cached Binder node as well as the listeners that were associated with that
+     * Binder node during its lifetime. If the Binder node dies, the listeners will be called and
+     * then this tracker will be unlinked and cleaned up.
+     */
+    private class BinderDeathTracker implements IBinder.DeathRecipient {
+
+        private final T mConnection;
+        private final HashMap<Object, Runnable> mListeners = new HashMap<>();
+
+        /**
+         * Create a tracker to cache the Binder node and add the ability to listen for the cached
+         * interface's death.
+         */
+        BinderDeathTracker(@NonNull T connection) {
+            mConnection = connection;
+            try {
+                mConnection.asBinder().linkToDeath(this, 0 /*flags*/);
+            } catch (RemoteException e) {
+                // isAlive will return false.
+            }
+        }
+
+        public boolean addListener(Object key, Runnable r) {
+            synchronized (mListeners) {
+                if (!isAlive()) return false;
+                mListeners.put(key, r);
+                return true;
+            }
+        }
+
+        public void removeListener(Object runnableKey) {
+            synchronized (mListeners) {
+                mListeners.remove(runnableKey);
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            ArrayList<Runnable> listeners;
+            synchronized (mListeners) {
+                listeners = new ArrayList<>(mListeners.values());
+                mListeners.clear();
+                try {
+                    mConnection.asBinder().unlinkToDeath(this, 0 /*flags*/);
+                } catch (NoSuchElementException e) {
+                    // No need to worry about this, this means the death recipient was never linked.
+                }
+            }
+            listeners.forEach(Runnable::run);
+        }
+
+        /**
+         * @return The cached Binder.
+         */
+        public T getConnection() {
+            return mConnection;
+        }
+
+        /**
+         * @return true if the cached Binder is alive at the time of calling, false otherwise.
+         */
+        public boolean isAlive() {
+            return mConnection.asBinder().isBinderAlive();
+        }
+    }
+
+    private final BinderInterfaceFactory<T> mBinderInterfaceFactory;
+    private final AtomicReference<BinderDeathTracker> mCachedConnection;
+
+    /**
+     * Create a new instance, which manages a cached IInterface and creates new ones using the
+     * provided factory when the cached IInterface dies.
+     * @param factory The factory used to create new Instances of the cached IInterface when it
+     *                dies.
+     */
+    public BinderCacheManager(BinderInterfaceFactory<T> factory) {
+        mBinderInterfaceFactory = factory;
+        mCachedConnection = new AtomicReference<>();
+    }
+
+    /**
+     * Get the binder node connection and add a Runnable to be run if this Binder dies. Once this
+     * Runnable is run, the Runnable itself is discarded and must be added again.
+     * <p>
+     * Note: There should be no assumptions here as to which Thread this Runnable is called on. If
+     * the Runnable should be called on a specific thread, it should be up to the caller to handle
+     * that in the runnable implementation.
+     * @param runnableKey The Key associated with this runnable so that it can be removed later
+     *                    using {@link #removeRunnable(Object)} if needed.
+     * @param deadRunnable The runnable that will be run if the cached Binder node dies.
+     * @return T if the runnable was added or {@code null} if the connection is not alive right now
+     * and the associated runnable was never added.
+     */
+    public T listenOnBinder(Object runnableKey, Runnable deadRunnable) {
+        if (runnableKey == null || deadRunnable == null) return null;
+        BinderDeathTracker tracker = getTracker();
+        if (tracker == null) return null;
+
+        boolean addSucceeded = tracker.addListener(runnableKey, deadRunnable);
+        return addSucceeded ? tracker.getConnection() : null;
+    }
+
+    /**
+     * @return The cached Binder node. May return null if the requested Binder node is not currently
+     * available.
+     */
+    public T getBinder() {
+        BinderDeathTracker tracker = getTracker();
+        return (tracker != null) ? tracker.getConnection() : null;
+    }
+
+    /**
+     * Removes a previously registered runnable associated with the returned  cached Binder node
+     * using the key it was registered with in {@link #listenOnBinder} if the runnable still exists.
+     * @param runnableKey The key that was used to register the Runnable earlier.
+     * @return The cached Binder node that the runnable used to registered to or null if the cached
+     * Binder node is not alive anymore.
+     */
+    public T removeRunnable(Object runnableKey) {
+        if (runnableKey == null) return null;
+        BinderDeathTracker tracker = getTracker();
+        if (tracker == null) return null;
+        tracker.removeListener(runnableKey);
+        return tracker.getConnection();
+    }
+
+    /**
+     * @return The BinderDeathTracker container, which contains the cached IInterface instance or
+     * null if it is not available right now.
+     */
+    private BinderDeathTracker getTracker() {
+        return mCachedConnection.updateAndGet((oldVal) -> {
+            BinderDeathTracker tracker = oldVal;
+            // Update cache if no longer alive. BinderDied will eventually be called on the tracker,
+            // which will call listeners & clean up.
+            if (tracker == null || !tracker.isAlive()) {
+                T binder = mBinderInterfaceFactory.create();
+                tracker = (binder != null) ? new BinderDeathTracker(binder) : null;
+
+            }
+            return (tracker != null && tracker.isAlive()) ? tracker : null;
+        });
+    }
+
+}
diff --git a/android-35/android/telephony/CallAttributes.java b/android-35/android/telephony/CallAttributes.java
new file mode 100644
index 0000000..1dc64a9
--- /dev/null
+++ b/android-35/android/telephony/CallAttributes.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
+
+import java.util.Objects;
+
+/**
+ * Contains information about a call's attributes as passed up from the HAL. If there are multiple
+ * ongoing calls, the CallAttributes will pertain to the call in the foreground.
+ * @hide
+ * @deprecated use {@link CallState} for call information for each call.
+ */
+@SystemApi
+@Deprecated
+public final class CallAttributes implements Parcelable {
+    private PreciseCallState mPreciseCallState;
+    @NetworkType
+    private int mNetworkType; // TelephonyManager.NETWORK_TYPE_* ints
+    private CallQuality mCallQuality;
+
+
+    public CallAttributes(@NonNull PreciseCallState state, @NetworkType int networkType,
+            @NonNull CallQuality callQuality) {
+        this.mPreciseCallState = state;
+        this.mNetworkType = networkType;
+        this.mCallQuality = callQuality;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "mPreciseCallState=" + mPreciseCallState + " mNetworkType=" + mNetworkType
+                + " mCallQuality=" + mCallQuality;
+    }
+
+    private CallAttributes(Parcel in) {
+        this.mPreciseCallState = in.readParcelable(PreciseCallState.class.getClassLoader(), android.telephony.PreciseCallState.class);
+        this.mNetworkType = in.readInt();
+        this.mCallQuality = in.readParcelable(CallQuality.class.getClassLoader(), android.telephony.CallQuality.class);
+    }
+
+    // getters
+    /**
+     * Returns the {@link PreciseCallState} of the call.
+     */
+    @NonNull
+    public PreciseCallState getPreciseCallState() {
+        return mPreciseCallState;
+    }
+
+    /**
+     * Returns the {@link TelephonyManager#NetworkType} of the call.
+     *
+     * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
+     * @see TelephonyManager#NETWORK_TYPE_GPRS
+     * @see TelephonyManager#NETWORK_TYPE_EDGE
+     * @see TelephonyManager#NETWORK_TYPE_UMTS
+     * @see TelephonyManager#NETWORK_TYPE_CDMA
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_0
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_A
+     * @see TelephonyManager#NETWORK_TYPE_1xRTT
+     * @see TelephonyManager#NETWORK_TYPE_HSDPA
+     * @see TelephonyManager#NETWORK_TYPE_HSUPA
+     * @see TelephonyManager#NETWORK_TYPE_HSPA
+     * @see TelephonyManager#NETWORK_TYPE_IDEN
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_B
+     * @see TelephonyManager#NETWORK_TYPE_LTE
+     * @see TelephonyManager#NETWORK_TYPE_EHRPD
+     * @see TelephonyManager#NETWORK_TYPE_HSPAP
+     * @see TelephonyManager#NETWORK_TYPE_GSM
+     * @see TelephonyManager#NETWORK_TYPE_TD_SCDMA
+     * @see TelephonyManager#NETWORK_TYPE_IWLAN
+     * @see TelephonyManager#NETWORK_TYPE_LTE_CA
+     * @see TelephonyManager#NETWORK_TYPE_NR
+     */
+    @NetworkType
+    public int getNetworkType() {
+        return mNetworkType;
+    }
+
+    /**
+     * Returns the {#link CallQuality} of the call.
+     */
+    @NonNull
+    public CallQuality getCallQuality() {
+        return mCallQuality;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPreciseCallState, mNetworkType, mCallQuality);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o == null || !(o instanceof CallAttributes) || hashCode() != o.hashCode()) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        CallAttributes s = (CallAttributes) o;
+
+        return (Objects.equals(mPreciseCallState, s.mPreciseCallState)
+                && mNetworkType == s.mNetworkType
+                && Objects.equals(mCallQuality, s.mCallQuality));
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mPreciseCallState, flags);
+        dest.writeInt(mNetworkType);
+        dest.writeParcelable(mCallQuality, flags);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<CallAttributes> CREATOR = new Parcelable.Creator() {
+        public CallAttributes createFromParcel(Parcel in) {
+            return new CallAttributes(in);
+        }
+
+        public CallAttributes[] newArray(int size) {
+            return new CallAttributes[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/CallForwardingInfo.java b/android-35/android/telephony/CallForwardingInfo.java
new file mode 100644
index 0000000..aeac36e
--- /dev/null
+++ b/android-35/android/telephony/CallForwardingInfo.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Defines the call forwarding information.
+ * @hide
+ */
+@SystemApi
+public final class CallForwardingInfo implements Parcelable {
+    private static final String TAG = "CallForwardingInfo";
+
+    /**
+     * Indicates that call forwarding reason is "unconditional".
+     * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
+     *            and conditions +CCFC
+     */
+    public static final int REASON_UNCONDITIONAL = 0;
+
+    /**
+     * Indicates the call forwarding status is "busy".
+     * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
+     *            and conditions +CCFC
+     */
+    public static final int REASON_BUSY = 1;
+
+    /**
+     * Indicates the call forwarding reason is "no reply".
+     * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
+     *            and conditions +CCFC
+     */
+    public static final int REASON_NO_REPLY = 2;
+
+    /**
+     * Indicates the call forwarding reason is "not reachable".
+     * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
+     *            and conditions +CCFC
+     */
+    public static final int REASON_NOT_REACHABLE = 3;
+
+    /**
+     * Indicates the call forwarding reason is "all", for setting all call forwarding reasons
+     * simultaneously (unconditional, busy, no reply, and not reachable).
+     * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
+     *            and conditions +CCFC
+     */
+    public static final int REASON_ALL = 4;
+
+    /**
+     * Indicates the call forwarding reason is "all_conditional", for setting all conditional call
+     * forwarding reasons simultaneously (busy, no reply, and not reachable).
+     * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
+     *            and conditions +CCFC
+     */
+    public static final int REASON_ALL_CONDITIONAL = 5;
+
+    /**
+     * Call forwarding reason types
+     * @hide
+     */
+    @IntDef(prefix = { "REASON_" }, value = {
+        REASON_UNCONDITIONAL,
+        REASON_BUSY,
+        REASON_NO_REPLY,
+        REASON_NOT_REACHABLE,
+        REASON_ALL,
+        REASON_ALL_CONDITIONAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallForwardingReason {
+    }
+
+    /**
+     * Whether call forwarding is enabled for this reason.
+     */
+    private boolean mEnabled;
+
+    /**
+     * The call forwarding reason indicates the condition under which calls will be forwarded.
+     * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
+     *            and conditions +CCFC
+     */
+    private int mReason;
+
+    /**
+     * The phone number to which calls will be forwarded.
+     * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
+     *            and conditions +CCFC
+     */
+    private String mNumber;
+
+    /**
+     * Gets the timeout (in seconds) before the forwarding is attempted.
+     */
+    private int mTimeSeconds;
+
+    /**
+     * Construct a CallForwardingInfo.
+     *
+     * @param enabled Whether to enable call forwarding for the reason specified
+     *                in {@link #getReason()}.
+     * @param reason the call forwarding reason
+     * @param number the phone number to which calls will be forwarded
+     * @param timeSeconds the timeout (in seconds) before the forwarding is attempted
+     */
+    public CallForwardingInfo(boolean enabled, @CallForwardingReason int reason,
+            @Nullable String number, int timeSeconds) {
+        mEnabled = enabled;
+        mReason = reason;
+        mNumber = number;
+        mTimeSeconds = timeSeconds;
+    }
+
+    /**
+     * Whether call forwarding is enabled for the reason from {@link #getReason()}.
+     *
+     * @return {@code true} if enabled, {@code false} otherwise.
+     */
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
+    /**
+     * Returns the call forwarding reason. The call forwarding reason indicates the condition
+     * under which calls will be forwarded. For example, {@link #REASON_NO_REPLY} indicates
+     * that calls will be forwarded when the user fails to answer the call.
+     *
+     * @return the call forwarding reason.
+     */
+    public @CallForwardingReason int getReason() {
+        return mReason;
+    }
+
+    /**
+     * Returns the phone number to which calls will be forwarded.
+     *
+     * @return the number calls will be forwarded to, or {@code null} if call forwarding
+     *         is disabled.
+     */
+    @Nullable
+    public String getNumber() {
+        return mNumber;
+    }
+
+    /**
+     * Gets the timeout (in seconds) before forwarding is attempted. For example,
+     * if {@link #REASON_NO_REPLY} is the call forwarding reason, the device will wait this
+     * duration of time before forwarding the call to the number returned by {@link #getNumber()}.
+     *
+     * Reference: 3GPP TS 27.007 version 10.3.0 Release 10
+     *            7.11 Call forwarding number and conditions +CCFC
+     *
+     * @return the timeout (in seconds) before the forwarding is attempted.
+     */
+    @SuppressLint("MethodNameUnits")
+    public int getTimeoutSeconds() {
+        return mTimeSeconds;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mNumber);
+        out.writeBoolean(mEnabled);
+        out.writeInt(mReason);
+        out.writeInt(mTimeSeconds);
+    }
+
+    private CallForwardingInfo(Parcel in) {
+        mNumber = in.readString();
+        mEnabled = in.readBoolean();
+        mReason = in.readInt();
+        mTimeSeconds = in.readInt();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (!(o instanceof CallForwardingInfo)) {
+            return false;
+        }
+
+        CallForwardingInfo other = (CallForwardingInfo) o;
+        return mEnabled == other.mEnabled
+                && mNumber == other.mNumber
+                && mReason == other.mReason
+                && mTimeSeconds == other.mTimeSeconds;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mEnabled, mNumber, mReason, mTimeSeconds);
+    }
+
+    public static final @NonNull Parcelable.Creator<CallForwardingInfo> CREATOR =
+            new Parcelable.Creator<CallForwardingInfo>() {
+                @Override
+                public CallForwardingInfo createFromParcel(Parcel in) {
+                    return new CallForwardingInfo(in);
+                }
+
+                @Override
+                public CallForwardingInfo[] newArray(int size) {
+                    return new CallForwardingInfo[size];
+                }
+            };
+
+    /**
+     * @hide
+     */
+    @Override
+    public String toString() {
+        return "[CallForwardingInfo: enabled=" + mEnabled
+                + ", reason= " + mReason
+                + ", timeSec= " + mTimeSeconds + " seconds"
+                + ", number=" + Rlog.pii(TAG, mNumber) + "]";
+    }
+}
diff --git a/android-35/android/telephony/CallQuality.java b/android-35/android/telephony/CallQuality.java
new file mode 100644
index 0000000..9b12ae5
--- /dev/null
+++ b/android-35/android/telephony/CallQuality.java
@@ -0,0 +1,853 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Parcelable object to handle call quality.
+ * <p>
+ * Currently this supports IMS calls.
+ * <p>
+ * It provides the call quality level, duration, and additional information related to RTP packets,
+ * jitter and delay.
+ * <p>
+ * If there are multiple active calls, the CallQuality will pertain to the call in the foreground.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CallQuality implements Parcelable {
+
+    // Constants representing the call quality level (see #CallQuality);
+    public static final int CALL_QUALITY_EXCELLENT = 0;
+    public static final int CALL_QUALITY_GOOD = 1;
+    public static final int CALL_QUALITY_FAIR = 2;
+    public static final int CALL_QUALITY_POOR = 3;
+    public static final int CALL_QUALITY_BAD = 4;
+    public static final int CALL_QUALITY_NOT_AVAILABLE = 5;
+
+    /**
+     * Call quality
+     * @hide
+     */
+    @IntDef(prefix = { "CALL_QUALITY_" }, value = {
+            CALL_QUALITY_EXCELLENT,
+            CALL_QUALITY_GOOD,
+            CALL_QUALITY_FAIR,
+            CALL_QUALITY_POOR,
+            CALL_QUALITY_BAD,
+            CALL_QUALITY_NOT_AVAILABLE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallQualityLevel {}
+
+    @CallQualityLevel
+    private int mDownlinkCallQualityLevel;
+    @CallQualityLevel
+    private int mUplinkCallQualityLevel;
+    private int mCallDuration;
+    private int mNumRtpPacketsTransmitted;
+    private int mNumRtpPacketsReceived;
+    private int mNumRtpPacketsTransmittedLost;
+    private int mNumRtpPacketsNotReceived;
+    private int mAverageRelativeJitter;
+    private int mMaxRelativeJitter;
+    private int mAverageRoundTripTime;
+    private int mCodecType;
+    private boolean mRtpInactivityDetected;
+    private boolean mRxSilenceDetected;
+    private boolean mTxSilenceDetected;
+    private int mNumVoiceFrames;
+    private int mNumNoDataFrames;
+    private int mNumDroppedRtpPackets;
+    private long mMinPlayoutDelayMillis;
+    private long mMaxPlayoutDelayMillis;
+    private int mNumRtpSidPacketsReceived;
+    private int mNumRtpDuplicatePackets;
+
+    /** @hide **/
+    public CallQuality(Parcel in) {
+        mDownlinkCallQualityLevel = in.readInt();
+        mUplinkCallQualityLevel = in.readInt();
+        mCallDuration = in.readInt();
+        mNumRtpPacketsTransmitted = in.readInt();
+        mNumRtpPacketsReceived = in.readInt();
+        mNumRtpPacketsTransmittedLost = in.readInt();
+        mNumRtpPacketsNotReceived = in.readInt();
+        mAverageRelativeJitter = in.readInt();
+        mMaxRelativeJitter = in.readInt();
+        mAverageRoundTripTime = in.readInt();
+        mCodecType = in.readInt();
+        mRtpInactivityDetected = in.readBoolean();
+        mRxSilenceDetected = in.readBoolean();
+        mTxSilenceDetected = in.readBoolean();
+        mNumVoiceFrames = in.readInt();
+        mNumNoDataFrames = in.readInt();
+        mNumDroppedRtpPackets = in.readInt();
+        mMinPlayoutDelayMillis = in.readLong();
+        mMaxPlayoutDelayMillis = in.readLong();
+        mNumRtpSidPacketsReceived = in.readInt();
+        mNumRtpDuplicatePackets = in.readInt();
+    }
+
+    /** @hide **/
+    public CallQuality() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param callQualityLevel the call quality level (see #CallQualityLevel)
+     * @param callDuration the call duration in milliseconds
+     * @param numRtpPacketsTransmitted RTP packets sent to network
+     * @param numRtpPacketsReceived RTP packets received from network
+     * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
+     * transmitted
+     * @param numRtpPacketsNotReceived RTP packets which were lost in network and never received
+     * @param averageRelativeJitter average relative jitter in milliseconds
+     * @param maxRelativeJitter maximum relative jitter in milliseconds
+     * @param averageRoundTripTime average round trip delay in milliseconds
+     * @param codecType the codec type
+     */
+    public CallQuality(
+            @CallQualityLevel int downlinkCallQualityLevel,
+            @CallQualityLevel int uplinkCallQualityLevel,
+            int callDuration,
+            int numRtpPacketsTransmitted,
+            int numRtpPacketsReceived,
+            int numRtpPacketsTransmittedLost,
+            int numRtpPacketsNotReceived,
+            int averageRelativeJitter,
+            int maxRelativeJitter,
+            int averageRoundTripTime,
+            int codecType) {
+        this(downlinkCallQualityLevel, uplinkCallQualityLevel, callDuration,
+            numRtpPacketsTransmitted, numRtpPacketsReceived, numRtpPacketsTransmittedLost,
+            numRtpPacketsNotReceived, averageRelativeJitter, maxRelativeJitter,
+            averageRoundTripTime, codecType, false, false, false);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param callQualityLevel the call quality level (see #CallQualityLevel)
+     * @param callDuration the call duration in milliseconds
+     * @param numRtpPacketsTransmitted RTP packets sent to network
+     * @param numRtpPacketsReceived RTP packets received from network
+     * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
+     * transmitted
+     * @param numRtpPacketsNotReceived RTP packets which were lost in network and never received
+     * @param averageRelativeJitter average relative jitter in milliseconds
+     * @param maxRelativeJitter maximum relative jitter in milliseconds
+     * @param averageRoundTripTime average round trip delay in milliseconds
+     * @param codecType the codec type
+     * @param rtpInactivityDetected True if no incoming RTP is received for a continuous duration of
+     * 4 seconds
+     * @param rxSilenceDetected True if only silence RTP packets are received for 20 seconds
+     * immediately after call is connected
+     * @param txSilenceDetected True if only silence RTP packets are sent for 20 seconds immediately
+     * after call is connected
+     */
+    public CallQuality(
+            @CallQualityLevel int downlinkCallQualityLevel,
+            @CallQualityLevel int uplinkCallQualityLevel,
+            int callDuration,
+            int numRtpPacketsTransmitted,
+            int numRtpPacketsReceived,
+            int numRtpPacketsTransmittedLost,
+            int numRtpPacketsNotReceived,
+            int averageRelativeJitter,
+            int maxRelativeJitter,
+            int averageRoundTripTime,
+            int codecType,
+            boolean rtpInactivityDetected,
+            boolean rxSilenceDetected,
+            boolean txSilenceDetected) {
+        this.mDownlinkCallQualityLevel = downlinkCallQualityLevel;
+        this.mUplinkCallQualityLevel = uplinkCallQualityLevel;
+        this.mCallDuration = callDuration;
+        this.mNumRtpPacketsTransmitted = numRtpPacketsTransmitted;
+        this.mNumRtpPacketsReceived = numRtpPacketsReceived;
+        this.mNumRtpPacketsTransmittedLost = numRtpPacketsTransmittedLost;
+        this.mNumRtpPacketsNotReceived = numRtpPacketsNotReceived;
+        this.mAverageRelativeJitter = averageRelativeJitter;
+        this.mMaxRelativeJitter = maxRelativeJitter;
+        this.mAverageRoundTripTime = averageRoundTripTime;
+        this.mCodecType = codecType;
+        this.mRtpInactivityDetected = rtpInactivityDetected;
+        this.mRxSilenceDetected = rxSilenceDetected;
+        this.mTxSilenceDetected = txSilenceDetected;
+    }
+
+    // getters
+    /**
+     * Returns the downlink CallQualityLevel for a given ongoing call.
+     */
+    @CallQualityLevel
+    public int getDownlinkCallQualityLevel() {
+        return mDownlinkCallQualityLevel;
+    }
+
+    /**
+     * Returns the uplink CallQualityLevel for a given ongoing call.
+     */
+    @CallQualityLevel
+    public int getUplinkCallQualityLevel() {
+        return mUplinkCallQualityLevel;
+    }
+
+    /**
+     * Returns the duration of the call, in milliseconds.
+     */
+    public int getCallDuration() {
+        return mCallDuration;
+    }
+
+    /**
+     * Returns the total number of RTP packets transmitted by this device for a given ongoing call.
+     */
+    public int getNumRtpPacketsTransmitted() {
+        return mNumRtpPacketsTransmitted;
+    }
+
+    /**
+     * Returns the total number of RTP packets received by this device for a given ongoing call.
+     */
+    public int getNumRtpPacketsReceived() {
+        return mNumRtpPacketsReceived;
+    }
+
+    /**
+     * Returns the number of RTP packets which were sent by this device but were lost in the
+     * network before reaching the other party.
+     */
+    public int getNumRtpPacketsTransmittedLost() {
+        return mNumRtpPacketsTransmittedLost;
+    }
+
+    /**
+     * Returns the number of RTP packets which were sent by the other party but were lost in the
+     * network before reaching this device.
+     */
+    public int getNumRtpPacketsNotReceived() {
+        return mNumRtpPacketsNotReceived;
+    }
+
+    /**
+     * Returns the average relative jitter in milliseconds. Jitter represents the amount of variance
+     * in interarrival time of packets, for example, if two packets are sent 2 milliseconds apart
+     * but received 3 milliseconds apart, the relative jitter between those packets is 1
+     * millisecond.
+     *
+     * <p>See RFC 3550 for more information on jitter calculations.
+     */
+    public int getAverageRelativeJitter() {
+        return mAverageRelativeJitter;
+    }
+
+    /**
+     * Returns the maximum relative jitter for a given ongoing call. Jitter represents the amount of
+     * variance in interarrival time of packets, for example, if two packets are sent 2 milliseconds
+     * apart but received 3 milliseconds apart, the relative jitter between those packets is 1
+     * millisecond.
+     *
+     * <p>See RFC 3550 for more information on jitter calculations.
+     */
+    public int getMaxRelativeJitter() {
+        return mMaxRelativeJitter;
+    }
+
+    /**
+     * Returns the average round trip time in milliseconds.
+     */
+    public int getAverageRoundTripTime() {
+        return mAverageRoundTripTime;
+    }
+
+    /**
+     * Returns true if no rtp packets are received continuously for the last 4 seconds
+     */
+    public boolean isRtpInactivityDetected() {
+        return mRtpInactivityDetected;
+    }
+
+    /**
+     * Returns true if only silence rtp packets are received for a duration of 20 seconds starting
+     * at call setup
+     */
+    public boolean isIncomingSilenceDetectedAtCallSetup() {
+        return mRxSilenceDetected;
+    }
+
+    /**
+      * Returns true if only silence rtp packets are sent for a duration of 20 seconds starting at
+      * call setup
+      */
+    public boolean isOutgoingSilenceDetectedAtCallSetup() {
+        return mTxSilenceDetected;
+    }
+
+    /**
+     * Returns the number of Voice frames sent by jitter buffer to audio
+     */
+    public int getNumVoiceFrames() {
+        return mNumVoiceFrames;
+    }
+
+    /**
+     * Returns the number of no-data frames sent by jitter buffer to audio
+     */
+    public int getNumNoDataFrames() {
+        return mNumNoDataFrames;
+    }
+
+    /**
+     * Returns the number of RTP voice packets dropped by jitter buffer
+     */
+    public int getNumDroppedRtpPackets() {
+        return mNumDroppedRtpPackets;
+    }
+
+    /**
+     * Returns the minimum playout delay in the reporting interval
+     * in milliseconds.
+     */
+    public long getMinPlayoutDelayMillis() {
+        return mMinPlayoutDelayMillis;
+    }
+
+    /**
+     * Returns the maximum playout delay in the reporting interval
+     * in milliseconds.
+     */
+    public long getMaxPlayoutDelayMillis() {
+        return mMaxPlayoutDelayMillis;
+    }
+
+    /**
+     * Returns the total number of RTP SID (Silence Insertion Descriptor) packets
+     * received by this device for an ongoing call
+     */
+    public int getNumRtpSidPacketsReceived() {
+        return mNumRtpSidPacketsReceived;
+    }
+
+    /**
+     * Returns the total number of RTP duplicate packets received by this device
+     * for an ongoing call
+     */
+    public int getNumRtpDuplicatePackets() {
+        return mNumRtpDuplicatePackets;
+    }
+
+    /**
+     * Returns the codec type. This value corresponds to the AUDIO_QUALITY_* constants in
+     * {@link ImsStreamMediaProfile}.
+     *
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_NONE
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_AMR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_AMR_WB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_QCELP13K
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_B
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_WB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVRC_NW
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_EFR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_FR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_GSM_HR
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711U
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G723
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711A
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G722
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G711AB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_G729
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_NB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_WB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_SWB
+     * @see ImsStreamMediaProfile#AUDIO_QUALITY_EVS_FB
+     */
+    public int getCodecType() {
+        return mCodecType;
+    }
+
+    // Parcelable things
+    @NonNull
+    @Override
+    public String toString() {
+        return "CallQuality: {downlinkCallQualityLevel=" + mDownlinkCallQualityLevel
+                + " uplinkCallQualityLevel=" + mUplinkCallQualityLevel
+                + " callDuration=" + mCallDuration
+                + " numRtpPacketsTransmitted=" + mNumRtpPacketsTransmitted
+                + " numRtpPacketsReceived=" + mNumRtpPacketsReceived
+                + " numRtpPacketsTransmittedLost=" + mNumRtpPacketsTransmittedLost
+                + " numRtpPacketsNotReceived=" + mNumRtpPacketsNotReceived
+                + " averageRelativeJitter=" + mAverageRelativeJitter
+                + " maxRelativeJitter=" + mMaxRelativeJitter
+                + " averageRoundTripTime=" + mAverageRoundTripTime
+                + " codecType=" + mCodecType
+                + " rtpInactivityDetected=" + mRtpInactivityDetected
+                + " txSilenceDetected=" + mTxSilenceDetected
+                + " rxSilenceDetected=" + mRxSilenceDetected
+                + " numVoiceFrames=" + mNumVoiceFrames
+                + " numNoDataFrames=" + mNumNoDataFrames
+                + " numDroppedRtpPackets=" + mNumDroppedRtpPackets
+                + " minPlayoutDelayMillis=" + mMinPlayoutDelayMillis
+                + " maxPlayoutDelayMillis=" + mMaxPlayoutDelayMillis
+                + " numRtpSidPacketsReceived=" + mNumRtpSidPacketsReceived
+                + " numRtpDuplicatePackets=" + mNumRtpDuplicatePackets
+                + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mDownlinkCallQualityLevel,
+                mUplinkCallQualityLevel,
+                mCallDuration,
+                mNumRtpPacketsTransmitted,
+                mNumRtpPacketsReceived,
+                mNumRtpPacketsTransmittedLost,
+                mNumRtpPacketsNotReceived,
+                mAverageRelativeJitter,
+                mMaxRelativeJitter,
+                mAverageRoundTripTime,
+                mCodecType,
+                mRtpInactivityDetected,
+                mRxSilenceDetected,
+                mTxSilenceDetected,
+                mNumVoiceFrames,
+                mNumNoDataFrames,
+                mNumDroppedRtpPackets,
+                mMinPlayoutDelayMillis,
+                mMaxPlayoutDelayMillis,
+                mNumRtpSidPacketsReceived,
+                mNumRtpDuplicatePackets);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o == null || !(o instanceof CallQuality) || hashCode() != o.hashCode()) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        CallQuality s = (CallQuality) o;
+
+        return (mDownlinkCallQualityLevel == s.mDownlinkCallQualityLevel
+                && mUplinkCallQualityLevel == s.mUplinkCallQualityLevel
+                && mCallDuration == s.mCallDuration
+                && mNumRtpPacketsTransmitted == s.mNumRtpPacketsTransmitted
+                && mNumRtpPacketsReceived == s.mNumRtpPacketsReceived
+                && mNumRtpPacketsTransmittedLost == s.mNumRtpPacketsTransmittedLost
+                && mNumRtpPacketsNotReceived == s.mNumRtpPacketsNotReceived
+                && mAverageRelativeJitter == s.mAverageRelativeJitter
+                && mMaxRelativeJitter == s.mMaxRelativeJitter
+                && mAverageRoundTripTime == s.mAverageRoundTripTime
+                && mCodecType == s.mCodecType
+                && mRtpInactivityDetected == s.mRtpInactivityDetected
+                && mRxSilenceDetected == s.mRxSilenceDetected
+                && mTxSilenceDetected == s.mTxSilenceDetected
+                && mNumVoiceFrames == s.mNumVoiceFrames
+                && mNumNoDataFrames == s.mNumNoDataFrames
+                && mNumDroppedRtpPackets == s.mNumDroppedRtpPackets
+                && mMinPlayoutDelayMillis == s.mMinPlayoutDelayMillis
+                && mMaxPlayoutDelayMillis == s.mMaxPlayoutDelayMillis
+                && mNumRtpSidPacketsReceived == s.mNumRtpSidPacketsReceived
+                && mNumRtpDuplicatePackets == s.mNumRtpDuplicatePackets);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mDownlinkCallQualityLevel);
+        dest.writeInt(mUplinkCallQualityLevel);
+        dest.writeInt(mCallDuration);
+        dest.writeInt(mNumRtpPacketsTransmitted);
+        dest.writeInt(mNumRtpPacketsReceived);
+        dest.writeInt(mNumRtpPacketsTransmittedLost);
+        dest.writeInt(mNumRtpPacketsNotReceived);
+        dest.writeInt(mAverageRelativeJitter);
+        dest.writeInt(mMaxRelativeJitter);
+        dest.writeInt(mAverageRoundTripTime);
+        dest.writeInt(mCodecType);
+        dest.writeBoolean(mRtpInactivityDetected);
+        dest.writeBoolean(mRxSilenceDetected);
+        dest.writeBoolean(mTxSilenceDetected);
+        dest.writeInt(mNumVoiceFrames);
+        dest.writeInt(mNumNoDataFrames);
+        dest.writeInt(mNumDroppedRtpPackets);
+        dest.writeLong(mMinPlayoutDelayMillis);
+        dest.writeLong(mMaxPlayoutDelayMillis);
+        dest.writeInt(mNumRtpSidPacketsReceived);
+        dest.writeInt(mNumRtpDuplicatePackets);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<CallQuality> CREATOR = new Parcelable.Creator() {
+        public CallQuality createFromParcel(Parcel in) {
+            return new CallQuality(in);
+        }
+
+        public CallQuality[] newArray(int size) {
+            return new CallQuality[size];
+        }
+    };
+
+    /**
+     * Provides a convenient way to set the fields of a {@link CallQuality} when creating a new
+     * instance.
+     *
+     * <p>The example below shows how you might create a new {@code CallQuality}:
+     *
+     * <pre><code>
+     *
+     * CallQuality callQuality = new CallQuality.Builder()
+     *     .setNumRtpPacketsTransmitted(150)
+     *     .setNumRtpPacketsReceived(200)
+     *     .build();
+     * </code></pre>
+     */
+    public static final class Builder {
+
+        private int mDownlinkCallQualityLevel;
+        private int mUplinkCallQualityLevel;
+        private int mCallDuration;
+        private int mNumRtpPacketsTransmitted;
+        private int mNumRtpPacketsReceived;
+        private int mNumRtpPacketsTransmittedLost;
+        private int mNumRtpPacketsNotReceived;
+        private int mAverageRelativeJitter;
+        private int mMaxRelativeJitter;
+        private int mAverageRoundTripTime;
+        private int mCodecType;
+        private boolean mRtpInactivityDetected;
+        private boolean mRxSilenceDetected;
+        private boolean mTxSilenceDetected;
+        private int mNumVoiceFrames;
+        private int mNumNoDataFrames;
+        private int mNumDroppedRtpPackets;
+        private long mMinPlayoutDelayMillis;
+        private long mMaxPlayoutDelayMillis;
+        private int mNumRtpSidPacketsReceived;
+        private int mNumRtpDuplicatePackets;
+
+        /**
+         * Set the downlink call quality level for ongoing call.
+         *
+         * @param downlinkCallQualityLevel the Downlink call quality level
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setDownlinkCallQualityLevel(
+                @CallQualityLevel int downlinkCallQualityLevel) {
+            mDownlinkCallQualityLevel = downlinkCallQualityLevel;
+            return this;
+        }
+
+        /**
+         * Set the uplink call quality level for ongoing call.
+         *
+         * @param uplinkCallQualityLevel the Uplink call quality level
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setUplinkCallQualityLevel(
+                @CallQualityLevel int uplinkCallQualityLevel) {
+            mUplinkCallQualityLevel = uplinkCallQualityLevel;
+            return this;
+        }
+
+        /**
+         * Set the call duration in milliseconds.
+         *
+         * @param callDuration the call duration in milliseconds
+         * @return The same instance of the builder.
+         */
+        // Newer builder includes guidelines compliant units; existing method does not.
+        @NonNull
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        public Builder setCallDurationMillis(int callDurationMillis) {
+            mCallDuration = callDurationMillis;
+            return this;
+        }
+
+        /**
+         * Set the number of RTP packets sent for ongoing call.
+         *
+         * @param numRtpPacketsTransmitted RTP packets sent to network
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumRtpPacketsTransmitted(int numRtpPacketsTransmitted) {
+            mNumRtpPacketsTransmitted = numRtpPacketsTransmitted;
+            return this;
+        }
+
+        /**
+         * Set the number of RTP packets received for ongoing call.
+         *
+         * @param numRtpPacketsReceived RTP packets received from network
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumRtpPacketsReceived(int numRtpPacketsReceived) {
+            mNumRtpPacketsReceived = numRtpPacketsReceived;
+            return this;
+        }
+
+        /**
+         * Set the number of RTP packets which were lost in network and never
+         * transmitted.
+         *
+         * @param numRtpPacketsTransmittedLost RTP packets which were lost in network and never
+         * transmitted
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumRtpPacketsTransmittedLost(int numRtpPacketsTransmittedLost) {
+            mNumRtpPacketsTransmittedLost = numRtpPacketsTransmittedLost;
+            return this;
+        }
+
+        /**
+         * Set the number of RTP packets which were lost in network and never received.
+         *
+         * @param numRtpPacketsNotReceived RTP packets which were lost in network and
+         * never received
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumRtpPacketsNotReceived(int numRtpPacketsNotReceived) {
+            mNumRtpPacketsNotReceived = numRtpPacketsNotReceived;
+            return this;
+        }
+
+        /**
+         * Set the average relative jitter in milliseconds.
+         *
+         * @param averageRelativeJitter average relative jitter in milliseconds
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setAverageRelativeJitter(int averageRelativeJitter) {
+            mAverageRelativeJitter = averageRelativeJitter;
+            return this;
+        }
+
+        /**
+         * Set the maximum relative jitter in milliseconds.
+         *
+         * @param maxRelativeJitter maximum relative jitter in milliseconds
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setMaxRelativeJitter(int maxRelativeJitter) {
+            mMaxRelativeJitter = maxRelativeJitter;
+            return this;
+        }
+
+        /**
+         * Set the average round trip delay in milliseconds.
+         *
+         * @param averageRoundTripTime average round trip delay in milliseconds
+         * @return The same instance of the builder.
+         */
+        // Newer builder includes guidelines compliant units; existing method does not.
+        @NonNull
+        @SuppressWarnings("MissingGetterMatchingBuilder")
+        public Builder setAverageRoundTripTimeMillis(int averageRoundTripTimeMillis) {
+            mAverageRoundTripTime = averageRoundTripTimeMillis;
+            return this;
+        }
+
+        /**
+         * Set the codec type used in the ongoing call.
+         *
+         * @param codecType the codec type.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setCodecType(int codecType) {
+            mCodecType = codecType;
+            return this;
+        }
+
+        /**
+         * Set to be True if no incoming RTP is received for a continuous
+         * duration of 4 seconds.
+         *
+         * @param rtpInactivityDetected True if no incoming RTP is received for
+         * a continuous duration of 4 seconds
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setRtpInactivityDetected(boolean rtpInactivityDetected) {
+            mRtpInactivityDetected = rtpInactivityDetected;
+            return this;
+        }
+
+        /**
+         * Set to be True if only silence RTP packets are received for 20 seconds
+         * immediately after call is connected.
+         *
+         * @param rxSilenceDetected True if only silence RTP packets are received for 20 seconds
+         * immediately after call is connected
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setIncomingSilenceDetectedAtCallSetup(boolean rxSilenceDetected) {
+            mRxSilenceDetected = rxSilenceDetected;
+            return this;
+        }
+
+        /**
+         * Set to be True if only silence RTP packets are sent for 20 seconds immediately
+         * after call is connected.
+         *
+         * @param txSilenceDetected True if only silence RTP packets are sent for
+         * 20 seconds immediately after call is connected.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setOutgoingSilenceDetectedAtCallSetup(boolean txSilenceDetected) {
+            mTxSilenceDetected = txSilenceDetected;
+            return this;
+        }
+
+        /**
+         * Set the number of voice frames sent by jitter buffer to audio.
+         *
+         * @param numVoiceFrames Voice frames sent by jitter buffer to audio.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumVoiceFrames(int numVoiceFrames) {
+            mNumVoiceFrames = numVoiceFrames;
+            return this;
+        }
+
+        /**
+         * Set the number of no-data frames sent by jitter buffer to audio.
+         *
+         * @param numNoDataFrames no-data frames sent by jitter buffer to audio
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumNoDataFrames(int numNoDataFrames) {
+            mNumNoDataFrames = numNoDataFrames;
+            return this;
+        }
+
+        /**
+         * Set the number of RTP Voice packets dropped by jitter buffer.
+         *
+         * @param numDroppedRtpPackets number of RTP Voice packets dropped by jitter buffer
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumDroppedRtpPackets(int numDroppedRtpPackets) {
+            mNumDroppedRtpPackets = numDroppedRtpPackets;
+            return this;
+        }
+
+        /**
+         * Set the minimum playout delay in the reporting interval in milliseconds.
+         *
+         * @param minPlayoutDelayMillis minimum playout delay in the reporting interval
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setMinPlayoutDelayMillis(long minPlayoutDelayMillis) {
+            mMinPlayoutDelayMillis = minPlayoutDelayMillis;
+            return this;
+        }
+
+        /**
+         * Set the maximum Playout delay in the reporting interval in milliseconds.
+         *
+         * @param maxPlayoutDelayMillis maximum Playout delay in the reporting interval
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setMaxPlayoutDelayMillis(long maxPlayoutDelayMillis) {
+            mMaxPlayoutDelayMillis = maxPlayoutDelayMillis;
+            return this;
+        }
+
+        /**
+         * Set the total number of RTP SID (Silence Insertion Descriptor)
+         * packets received by this device for an ongoing call.
+         *
+         * @param numRtpSidPacketsReceived the total number of RTP SID packets received
+         * by this device for an ongoing call.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumRtpSidPacketsReceived(int numRtpSidPacketsReceived) {
+            mNumRtpSidPacketsReceived = numRtpSidPacketsReceived;
+            return this;
+        }
+
+        /**
+         * Set the total number of RTP duplicate packets received by this device
+         * for an ongoing call.
+         *
+         * @param numRtpDuplicatePackets the total number of RTP duplicate packets
+         * received by this device for an ongoing call
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setNumRtpDuplicatePackets(int numRtpDuplicatePackets) {
+            mNumRtpDuplicatePackets = numRtpDuplicatePackets;
+            return this;
+        }
+
+        /**
+         * Build the CallQuality.
+         *
+         * @return the CallQuality object.
+         */
+        public @NonNull CallQuality build() {
+
+            CallQuality callQuality = new CallQuality();
+            callQuality.mDownlinkCallQualityLevel = mDownlinkCallQualityLevel;
+            callQuality.mUplinkCallQualityLevel = mUplinkCallQualityLevel;
+            callQuality.mCallDuration = mCallDuration;
+            callQuality.mNumRtpPacketsTransmitted = mNumRtpPacketsTransmitted;
+            callQuality.mNumRtpPacketsReceived = mNumRtpPacketsReceived;
+            callQuality.mNumRtpPacketsTransmittedLost = mNumRtpPacketsTransmittedLost;
+            callQuality.mNumRtpPacketsNotReceived = mNumRtpPacketsNotReceived;
+            callQuality.mAverageRelativeJitter = mAverageRelativeJitter;
+            callQuality.mMaxRelativeJitter = mMaxRelativeJitter;
+            callQuality.mAverageRoundTripTime = mAverageRoundTripTime;
+            callQuality.mCodecType = mCodecType;
+            callQuality.mRtpInactivityDetected = mRtpInactivityDetected;
+            callQuality.mTxSilenceDetected = mTxSilenceDetected;
+            callQuality.mRxSilenceDetected = mRxSilenceDetected;
+            callQuality.mNumVoiceFrames = mNumVoiceFrames;
+            callQuality.mNumNoDataFrames = mNumNoDataFrames;
+            callQuality.mNumDroppedRtpPackets = mNumDroppedRtpPackets;
+            callQuality.mMinPlayoutDelayMillis = mMinPlayoutDelayMillis;
+            callQuality.mMaxPlayoutDelayMillis = mMaxPlayoutDelayMillis;
+            callQuality.mNumRtpSidPacketsReceived = mNumRtpSidPacketsReceived;
+            callQuality.mNumRtpDuplicatePackets = mNumRtpDuplicatePackets;
+
+            return callQuality;
+        }
+    }
+}
diff --git a/android-35/android/telephony/CallState.java b/android-35/android/telephony/CallState.java
new file mode 100644
index 0000000..836cb53
--- /dev/null
+++ b/android-35/android/telephony/CallState.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.ImsCallServiceType;
+import android.telephony.Annotation.ImsCallType;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.Annotation.PreciseCallStates;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+
+import java.util.Objects;
+
+/**
+ * Contains information about various states for a call.
+ * @hide
+ */
+@SystemApi
+public final class CallState implements Parcelable {
+
+    /**
+     * Call classifications are just used for backward compatibility of deprecated API {@link
+     * TelephonyCallback#CallAttributesListener#onCallAttributesChanged}, Since these will be
+     * removed when the deprecated API is removed, they should not be opened.
+     */
+    /**
+     * Call classification is not valid. It should not be opened.
+     * @hide
+     */
+    public static final int CALL_CLASSIFICATION_UNKNOWN = -1;
+
+    /**
+     * Call classification indicating foreground call
+     * @hide
+     */
+    public static final int CALL_CLASSIFICATION_RINGING = 0;
+
+    /**
+     * Call classification indicating background call
+     * @hide
+     */
+    public static final int CALL_CLASSIFICATION_FOREGROUND = 1;
+
+    /**
+     * Call classification indicating ringing call
+     * @hide
+     */
+    public static final int CALL_CLASSIFICATION_BACKGROUND = 2;
+
+    /**
+     * Call classification Max value.
+     * @hide
+     */
+    public static final int CALL_CLASSIFICATION_MAX = CALL_CLASSIFICATION_BACKGROUND + 1;
+
+    @PreciseCallStates
+    private final int mPreciseCallState;
+
+    @NetworkType
+    private final int mNetworkType; // TelephonyManager.NETWORK_TYPE_* ints
+    private final CallQuality mCallQuality;
+
+    private final int mCallClassification;
+    /**
+     * IMS call session ID. {@link ImsCallSession#getCallId()}
+     */
+    @Nullable
+    private String mImsCallId;
+
+    /**
+     * IMS call service type of this call
+     */
+    @ImsCallServiceType
+    private int mImsCallServiceType;
+
+    /**
+     * IMS call type of this call.
+     */
+    @ImsCallType
+    private int mImsCallType;
+
+    /**
+     * Constructor of CallAttributes
+     *
+     * @param callState call state defined in {@link PreciseCallState}
+     * @param networkType network type for this call attributes
+     * @param callQuality call quality for this call attributes, only CallState in
+     *                    {@link PreciseCallState#PRECISE_CALL_STATE_ACTIVE} will have valid call
+     *                    quality.
+     * @param callClassification call classification
+     * @param imsCallId IMS call session ID for this call attributes
+     * @param imsCallServiceType IMS call service type for this call attributes
+     * @param imsCallType IMS call type for this call attributes
+     */
+    private CallState(@PreciseCallStates int callState, @NetworkType int networkType,
+            @NonNull CallQuality callQuality, int callClassification, @Nullable String imsCallId,
+            @ImsCallServiceType int imsCallServiceType, @ImsCallType int imsCallType) {
+        this.mPreciseCallState = callState;
+        this.mNetworkType = networkType;
+        this.mCallQuality = callQuality;
+        this.mCallClassification = callClassification;
+        this.mImsCallId = imsCallId;
+        this.mImsCallServiceType = imsCallServiceType;
+        this.mImsCallType = imsCallType;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "mPreciseCallState=" + mPreciseCallState + " mNetworkType=" + mNetworkType
+                + " mCallQuality=" + mCallQuality + " mCallClassification" + mCallClassification
+                + " mImsCallId=" + mImsCallId + " mImsCallServiceType=" + mImsCallServiceType
+                + " mImsCallType=" + mImsCallType;
+    }
+
+    private CallState(Parcel in) {
+        this.mPreciseCallState = in.readInt();
+        this.mNetworkType = in.readInt();
+        this.mCallQuality = in.readParcelable(
+                CallQuality.class.getClassLoader(), CallQuality.class);
+        this.mCallClassification = in.readInt();
+        this.mImsCallId = in.readString();
+        this.mImsCallServiceType = in.readInt();
+        this.mImsCallType = in.readInt();
+    }
+
+    // getters
+    /**
+     * Returns the precise call state of the call.
+     */
+    @PreciseCallStates
+    public int getCallState() {
+        return mPreciseCallState;
+    }
+
+    /**
+     * Returns the {@link TelephonyManager#NetworkType} of the call.
+     *
+     * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
+     * @see TelephonyManager#NETWORK_TYPE_GPRS
+     * @see TelephonyManager#NETWORK_TYPE_EDGE
+     * @see TelephonyManager#NETWORK_TYPE_UMTS
+     * @see TelephonyManager#NETWORK_TYPE_CDMA
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_0
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_A
+     * @see TelephonyManager#NETWORK_TYPE_1xRTT
+     * @see TelephonyManager#NETWORK_TYPE_HSDPA
+     * @see TelephonyManager#NETWORK_TYPE_HSUPA
+     * @see TelephonyManager#NETWORK_TYPE_HSPA
+     * @see TelephonyManager#NETWORK_TYPE_IDEN
+     * @see TelephonyManager#NETWORK_TYPE_EVDO_B
+     * @see TelephonyManager#NETWORK_TYPE_LTE
+     * @see TelephonyManager#NETWORK_TYPE_EHRPD
+     * @see TelephonyManager#NETWORK_TYPE_HSPAP
+     * @see TelephonyManager#NETWORK_TYPE_GSM
+     * @see TelephonyManager#NETWORK_TYPE_TD_SCDMA
+     * @see TelephonyManager#NETWORK_TYPE_IWLAN
+     * @see TelephonyManager#NETWORK_TYPE_LTE_CA
+     * @see TelephonyManager#NETWORK_TYPE_NR
+     */
+    @NetworkType
+    public int getNetworkType() {
+        return mNetworkType;
+    }
+
+    /**
+     * Returns the {#link CallQuality} of the call.
+     * @return call quality for this call attributes, only CallState in {@link
+     *         PreciseCallState#PRECISE_CALL_STATE_ACTIVE} will have valid call quality. It will be
+     *         null for the call which is not in {@link PreciseCallState#PRECISE_CALL_STATE_ACTIVE}.
+     */
+    @Nullable
+    public CallQuality getCallQuality() {
+        return mCallQuality;
+    }
+
+    /**
+     * Returns the call classification.
+     * @hide
+     */
+    public int getCallClassification() {
+        return mCallClassification;
+    }
+
+    /**
+     * Returns the IMS call session ID.
+     */
+    @Nullable
+    public String getImsCallSessionId() {
+        return mImsCallId;
+    }
+
+    /**
+     * Returns the IMS call service type.
+     */
+    @ImsCallServiceType
+    public int getImsCallServiceType() {
+        return mImsCallServiceType;
+    }
+
+    /**
+     * Returns the IMS call type.
+     */
+    @ImsCallType
+    public int getImsCallType() {
+        return mImsCallType;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPreciseCallState, mNetworkType, mCallQuality, mCallClassification,
+                mImsCallId, mImsCallServiceType, mImsCallType);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o == null || !(o instanceof CallState) || hashCode() != o.hashCode()) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        CallState s = (CallState) o;
+
+        return (mPreciseCallState == s.mPreciseCallState
+                && mNetworkType == s.mNetworkType
+                && Objects.equals(mCallQuality, s.mCallQuality)
+                && mCallClassification == s.mCallClassification
+                && Objects.equals(mImsCallId, s.mImsCallId)
+                && mImsCallType == s.mImsCallType
+                && mImsCallServiceType == s.mImsCallServiceType);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(@Nullable Parcel dest, int flags) {
+        dest.writeInt(mPreciseCallState);
+        dest.writeInt(mNetworkType);
+        dest.writeParcelable(mCallQuality, flags);
+        dest.writeInt(mCallClassification);
+        dest.writeString(mImsCallId);
+        dest.writeInt(mImsCallServiceType);
+        dest.writeInt(mImsCallType);
+    }
+
+    public static final @NonNull Creator<CallState> CREATOR = new Creator() {
+        public CallState createFromParcel(Parcel in) {
+            return new CallState(in);
+        }
+
+        public CallState[] newArray(int size) {
+            return new CallState[size];
+        }
+    };
+
+    /**
+     * Builder of {@link CallState}
+     *
+     * <p>The example below shows how you might create a new {@code CallState}. A precise call state
+     * {@link PreciseCallStates} is mandatory to build a CallState.
+     *
+     * <pre><code>
+     *
+     * CallState = new CallState.Builder({@link PreciseCallStates})
+     *     .setNetworkType({@link TelephonyManager#NETWORK_TYPE_LTE})
+     *     .setCallQuality({@link CallQuality})
+     *     .setImsCallSessionId({@link String})
+     *     .setImsCallServiceType({@link ImsCallProfile#SERVICE_TYPE_NORMAL})
+     *     .setImsCallType({@link ImsCallProfile#CALL_TYPE_VOICE})
+     *     .build();
+     * </code></pre>
+     */
+    public static final class Builder {
+        private @PreciseCallStates int mPreciseCallState;
+        private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        private CallQuality mCallQuality = null;
+        private int mCallClassification = CALL_CLASSIFICATION_UNKNOWN;
+        private String mImsCallId;
+        private @ImsCallServiceType int mImsCallServiceType = ImsCallProfile.SERVICE_TYPE_NONE;
+        private @ImsCallType int mImsCallType = ImsCallProfile.CALL_TYPE_NONE;
+
+
+        /**
+         * Default constructor for the Builder.
+         */
+        public Builder(@PreciseCallStates int preciseCallState) {
+            mPreciseCallState = preciseCallState;
+        }
+
+        /**
+         * Set network type of this call.
+         *
+         * @param networkType the transport type.
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public CallState.Builder setNetworkType(@NetworkType int networkType) {
+            this.mNetworkType = networkType;
+            return this;
+        }
+
+        /**
+         * Set the call quality {@link CallQuality} of this call.
+         *
+         * @param callQuality call quality of active call.
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public CallState.Builder setCallQuality(@Nullable CallQuality callQuality) {
+            this.mCallQuality = callQuality;
+            return this;
+        }
+
+        /**
+         * Set call classification for this call.
+         *
+         * @param classification call classification type defined in this class.
+         * @return The same instance of the builder.
+         * @hide
+         */
+        @NonNull
+        public CallState.Builder setCallClassification(int classification) {
+            this.mCallClassification = classification;
+            return this;
+        }
+
+        /**
+         * Set IMS call session ID of this call.
+         *
+         * @param imsCallId  IMS call session ID.
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public CallState.Builder setImsCallSessionId(@Nullable String imsCallId) {
+            this.mImsCallId = imsCallId;
+            return this;
+        }
+
+        /**
+         * Set IMS call service type of this call.
+         *
+         * @param serviceType IMS call service type defined in {@link ImsCallProfile}.
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public CallState.Builder setImsCallServiceType(@ImsCallServiceType int serviceType) {
+            this.mImsCallServiceType = serviceType;
+            return this;
+        }
+
+        /**
+         * Set IMS call type of this call.
+         *
+         * @param callType IMS call type defined in {@link ImsCallProfile}.
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public CallState.Builder setImsCallType(@ImsCallType int callType) {
+            this.mImsCallType = callType;
+            return this;
+        }
+
+        /**
+         * Build the {@link CallState}
+         *
+         * @return the {@link CallState} object
+         */
+        @NonNull
+        public CallState build() {
+            return new CallState(
+                    mPreciseCallState,
+                    mNetworkType,
+                    mCallQuality,
+                    mCallClassification,
+                    mImsCallId,
+                    mImsCallServiceType,
+                    mImsCallType);
+        }
+    }
+}
diff --git a/android-35/android/telephony/CarrierConfigManager.java b/android-35/android/telephony/CarrierConfigManager.java
new file mode 100644
index 0000000..bc8f65e
--- /dev/null
+++ b/android-35/android/telephony/CarrierConfigManager.java
@@ -0,0 +1,11790 @@
+/*
+ * Copyright (C) 2015 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;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressAutoDoc;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.SaProposal;
+import android.os.Build;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.service.carrier.CarrierService;
+import android.telecom.TelecomManager;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.data.ApnSetting;
+import android.telephony.gba.TlsParams;
+import android.telephony.gba.UaSecurityProtocolIdentifier;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
+import android.telephony.ims.ImsSsData;
+import android.telephony.ims.MediaQualityStatus;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.RcsFeature;
+
+import com.android.internal.telephony.ICarrierConfigLoader;
+import com.android.internal.telephony.flags.Flags;
+import com.android.telephony.Rlog;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Provides access to telephony configuration values that are carrier-specific.
+ */
+@SystemService(Context.CARRIER_CONFIG_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+public class CarrierConfigManager {
+    private static final String TAG = "CarrierConfigManager";
+
+    /**
+     * Extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate the slot index that the
+     * broadcast is for.
+     */
+    public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
+
+    /**
+     * {@link #ACTION_CARRIER_CONFIG_CHANGED} is broadcast once on device bootup and then again when
+     * the device is unlocked. Direct-Boot-aware applications may use the first broadcast as an
+     * early signal that the carrier config has been loaded, but other applications will only
+     * receive the second broadcast, when the device is unlocked.
+     *
+     * This extra is included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate whether this is
+     * a rebroadcast on unlock.
+     */
+    public static final String EXTRA_REBROADCAST_ON_UNLOCK =
+            "android.telephony.extra.REBROADCAST_ON_UNLOCK";
+
+    /**
+     * Optional extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate the
+     * subscription index that the broadcast is for, if a valid one is available.
+     */
+    public static final String EXTRA_SUBSCRIPTION_INDEX =
+            SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX;
+
+    /**
+     * Service class flag if no specific service class is specified.
+     * Reference: 3GPP TS 27.007 Section 7.4 Facility lock +CLCK
+     */
+    public static final int SERVICE_CLASS_NONE = ImsSsData.SERVICE_CLASS_NONE;
+
+    /**
+     * Service class flag for voice telephony.
+     * Reference: 3GPP TS 27.007 Section 7.4 Facility lock +CLCK
+     */
+    public static final int SERVICE_CLASS_VOICE = ImsSsData.SERVICE_CLASS_VOICE;
+
+    /**
+     * Only send USSD over IMS while CS is out of service, otherwise send USSD over CS.
+     * {@link #KEY_CARRIER_USSD_METHOD_INT}
+     */
+    public static final int USSD_OVER_CS_PREFERRED = 0;
+
+    /**
+     * Send USSD over IMS or CS while IMS is out of service or silent redial over CS if needed.
+     * {@link #KEY_CARRIER_USSD_METHOD_INT}
+     */
+    public static final int USSD_OVER_IMS_PREFERRED = 1;
+
+    /**
+     * Only send USSD over CS.
+     * {@link #KEY_CARRIER_USSD_METHOD_INT}
+     */
+    public static final int USSD_OVER_CS_ONLY = 2;
+
+    /**
+     * Only send USSD over IMS and disallow silent redial over CS.
+     * {@link #KEY_CARRIER_USSD_METHOD_INT}
+     */
+    public static final int USSD_OVER_IMS_ONLY = 3;
+
+    /**
+     * Indicates CARRIER_NR_AVAILABILITY_NSA determine that the carrier enable the non-standalone
+     * (NSA) mode of 5G NR.
+     */
+    public static final int CARRIER_NR_AVAILABILITY_NSA = 1;
+
+    /**
+     * Indicates CARRIER_NR_AVAILABILITY_SA determine that the carrier enable the standalone (SA)
+     * mode of 5G NR.
+     */
+    public static final int CARRIER_NR_AVAILABILITY_SA = 2;
+
+    private final Context mContext;
+
+    /**
+     * @hide
+     */
+    public CarrierConfigManager(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * This intent is broadcast by the system when carrier config changes. An int is specified in
+     * {@link #EXTRA_SLOT_INDEX} to indicate the slot index that this is for. An optional int extra
+     * {@link #EXTRA_SUBSCRIPTION_INDEX} is included to indicate the subscription index if a valid
+     * one is available for the slot index. An optional int extra
+     * {@link TelephonyManager#EXTRA_CARRIER_ID} is included to indicate the carrier id for the
+     * changed carrier configuration. An optional int extra
+     * {@link TelephonyManager#EXTRA_SPECIFIC_CARRIER_ID} is included to indicate the precise
+     * carrier id for the changed carrier configuration.
+     * @see TelephonyManager#getSimCarrierId()
+     * @see TelephonyManager#getSimSpecificCarrierId()
+     */
+    public static final String ACTION_CARRIER_CONFIG_CHANGED =
+            "android.telephony.action.CARRIER_CONFIG_CHANGED";
+
+    // Below are the keys used in carrier config bundles. To add a new variable, define the key and
+    // give it a default value in sDefaults. If you need to ship a per-network override in the
+    // system image, that can be added in packages/apps/CarrierConfig.
+
+    /**
+     * Specifies a value that identifies the version of the carrier configuration that is
+     * currently in use. This string is displayed on the UI.
+     * The format of the string is not specified.
+     */
+    public static final String KEY_CARRIER_CONFIG_VERSION_STRING =
+            "carrier_config_version_string";
+
+    /**
+     * This flag specifies whether VoLTE availability is based on provisioning. By default this is
+     * false.
+     * Used for UCE to determine if EAB provisioning checks should be based on provisioning.
+     * @deprecated Use {@link Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL} instead.
+     */
+    @Deprecated
+    public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL =
+            "carrier_volte_provisioned_bool";
+
+    /**
+     * Boolean indicating the Supplementary Services(SS) is disable when airplane mode on in the
+     * Call Settings menu.
+     * {@code true}: SS is disable when airplane mode on.
+     * {@code false}: SS is enable when airplane mode on.
+     * The default value for this key is {@code false}
+     */
+    public static final String KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL =
+            "disable_supplementary_services_in_airplane_mode_bool";
+
+    /**
+     * Boolean indicating if the "Call forwarding" item is visible in the Call Settings menu.
+     * true means visible. false means gone.
+     * @hide
+     */
+    public static final String KEY_CALL_FORWARDING_VISIBILITY_BOOL =
+            "call_forwarding_visibility_bool";
+
+    /**
+     * Boolean indicating if carrier supports call forwarding option "When unreachable".
+     *
+     * {@code true}: Call forwarding option "When unreachable" is supported.
+     * {@code false}: Call forwarding option "When unreachable" is not supported. Option will be
+     * removed in the UI.
+     *
+     * By default this value is true.
+     * @hide
+     */
+    public static final String KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL =
+            "call_forwarding_when_unreachable_supported_bool";
+
+    /**
+     * Boolean indicating if carrier supports call forwarding option "When unanswered".
+     *
+     * {@code true}: Call forwarding option "When unanswered" is supported.
+     * {@code false}: Call forwarding option "When unanswered" is not supported. Option will be
+     * removed in the UI.
+     *
+     * By default this value is true.
+     * @hide
+     */
+    public static final String KEY_CALL_FORWARDING_WHEN_UNANSWERED_SUPPORTED_BOOL =
+            "call_forwarding_when_unanswered_supported_bool";
+
+    /**
+     * Boolean indicating if carrier supports call forwarding option "When busy".
+     *
+     * {@code true}: Call forwarding option "When busy" is supported.
+     * {@code false}: Call forwarding option "When busy" is not supported. Option will be
+     * removed in the UI.
+     *
+     * By default this value is true.
+     * @hide
+     */
+    public static final String KEY_CALL_FORWARDING_WHEN_BUSY_SUPPORTED_BOOL =
+            "call_forwarding_when_busy_supported_bool";
+
+    /**
+     * Boolean indicating if the "Caller ID" item is visible in the Additional Settings menu.
+     * true means visible. false means gone.
+     *
+     * The default value is true.
+     */
+    @FlaggedApi(Flags.FLAG_SHOW_CALL_ID_AND_CALL_WAITING_IN_ADDITIONAL_SETTINGS_MENU)
+    public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL =
+            "additional_settings_caller_id_visibility_bool";
+
+    /**
+     * Boolean indicating if the "Call Waiting" item is visible in the Additional Settings menu.
+     * true means visible. false means gone.
+     *
+     * The default value is true.
+     */
+    @FlaggedApi(Flags.FLAG_SHOW_CALL_ID_AND_CALL_WAITING_IN_ADDITIONAL_SETTINGS_MENU)
+    public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL =
+            "additional_settings_call_waiting_visibility_bool";
+
+    /**
+     * Boolean indicating if the "Call barring" item is visible in the Call Settings menu.
+     * If true, the "Call Barring" menu will be visible. If false, the menu will be gone.
+     *
+     * Disabled by default.
+     */
+    public static final String KEY_CALL_BARRING_VISIBILITY_BOOL =
+            "call_barring_visibility_bool";
+
+    /**
+     * Flag indicating whether or not changing the call barring password via the "Call Barring"
+     * settings menu is supported. If true, the option will be visible in the "Call
+     * Barring" settings menu. If false, the option will not be visible.
+     *
+     * Enabled by default.
+     */
+    public static final String KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL =
+            "call_barring_supports_password_change_bool";
+
+    /**
+     * Flag indicating whether or not deactivating all call barring features via the "Call Barring"
+     * settings menu is supported. If true, the option will be visible in the "Call
+     * Barring" settings menu. If false, the option will not be visible.
+     *
+     * Enabled by default.
+     */
+    public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL =
+            "call_barring_supports_deactivate_all_bool";
+
+    /**
+     * Specifies the service class for call barring service. Default value is
+     * {@link #SERVICE_CLASS_VOICE}.
+     * The value set as below:
+     * <ul>
+     * <li>0: {@link #SERVICE_CLASS_NONE}</li>
+     * <li>1: {@link #SERVICE_CLASS_VOICE}</li>
+     * </ul>
+     */
+    public static final String KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT =
+            "call_barring_default_service_class_int";
+
+    /**
+     * This carrier supports dialing USSD codes to enable/disable supplementary services such as
+     * call forwarding and call waiting over CDMA.
+     * <p>
+     * The supplementary service menu will still need to be set as visible, see
+     * {@link #KEY_CALL_FORWARDING_VISIBILITY_BOOL} and
+     * {@link #KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL}.
+     * <p>
+     * If this is set as false and the supplementary service menu is visible, the associated setting
+     * will be enabled and disabled based on the availability of supplementary services over UT. See
+     * {@link #KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL}.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_SS_OVER_CDMA_BOOL = "support_ss_over_cdma_bool";
+
+    /**
+     * Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
+     * events from the Sim.
+     * If true, this will prevent the IccNetworkDepersonalizationPanel from being shown, and
+     * effectively disable the "Sim network lock" feature.
+     */
+    public static final String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL =
+            "ignore_sim_network_locked_events_bool";
+
+    /**
+     * When checking if a given number is the voicemail number, if this flag is true
+     * then in addition to comparing the given number to the voicemail number, we also compare it
+     * to the mdn. If this flag is false, the given number is only compared to the voicemail number.
+     * By default this value is false.
+     */
+    public static final String KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL =
+            "mdn_is_additional_voicemail_number_bool";
+
+    /**
+     * Flag indicating whether the Phone app should provide a "Dismiss" button on the SIM network
+     * unlock screen. The default value is true. If set to false, there will be *no way* to dismiss
+     * the SIM network unlock screen if you don't enter the correct unlock code. (One important
+     * consequence: there will be no way to make an Emergency Call if your SIM is network-locked and
+     * you don't know the PIN.)
+     */
+    public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL =
+            "sim_network_unlock_allow_dismiss_bool";
+
+    /**
+     * Flag indicating whether or not sending emergency SMS messages over IMS
+     * is supported when in LTE/limited LTE (Emergency only) service mode..
+     */
+    public static final String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL =
+            "support_emergency_sms_over_ims_bool";
+
+    /** Flag indicating if the phone is a world phone */
+    public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
+
+    /**
+     * Flag to require or skip entitlement checks.
+     * If true, entitlement checks will be executed if device has been configured for it,
+     * If false, entitlement checks will be skipped.
+     */
+    public static final String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL =
+            "require_entitlement_checks_bool";
+
+    /**
+     * Flag indicating if the carrier supports tethering of mobile data.
+     */
+    public static final String KEY_CARRIER_SUPPORTS_TETHERING_BOOL =
+            "carrier_supports_tethering_bool";
+
+    /**
+     * Flag indicating whether radio is to be restarted on error PDP_FAIL_REGULAR_DEACTIVATION
+     * This is false by default.
+     *
+     * @deprecated Use {@link #KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY} instead
+     */
+    @Deprecated
+    public static final String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL =
+            "restart_radio_on_pdp_fail_regular_deactivation_bool";
+
+    /**
+     * A list of failure cause codes that will trigger a modem restart when telephony receiving
+     * one of those during data setup. The cause codes are defined in 3GPP TS 24.008 Annex I and
+     * TS 24.301 Annex B.
+     */
+    public static final String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY =
+            "radio_restart_failure_causes_int_array";
+
+    /**
+     * If true, enable vibration (haptic feedback) for key presses in the EmergencyDialer activity.
+     * The pattern is set on a per-platform basis using config_virtualKeyVibePattern. To be
+     * consistent with the regular Dialer, this value should agree with the corresponding values
+     * from config.xml under apps/Contacts.
+     */
+    public static final String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL =
+            "enable_dialer_key_vibration_bool";
+
+    /** Flag indicating if dtmf tone type is enabled */
+    public static final String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
+
+    /** Flag indicating if auto retry is enabled */
+    public static final String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
+
+    /**
+     * Determine whether we want to play local DTMF tones in a call, or just let the radio/BP handle
+     * playing of the tones.
+     */
+    public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
+
+    /**
+     * Determines if the carrier requires that a tone be played to the remote party when an app is
+     * recording audio during a call (e.g. using a call recording app).
+     * <p>
+     * Note: This requires the Telephony config_supports_telephony_audio_device overlay to be true
+     * in order to work.
+     * @hide
+     */
+    public static final String KEY_PLAY_CALL_RECORDING_TONE_BOOL = "play_call_recording_tone_bool";
+
+    /**
+     * Determines if the carrier requires converting the destination number before sending out an
+     * SMS. Certain networks and numbering plans require different formats.
+     */
+    public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL =
+            "sms_requires_destination_number_conversion_bool";
+
+    /**
+     * If true, show an onscreen "Dial" button in the dialer. In practice this is used on all
+     * platforms, even the ones with hard SEND/END keys, but for maximum flexibility it's controlled
+     * by a flag here (which can be overridden on a per-product basis.)
+     */
+    public static final String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL =
+            "show_onscreen_dial_button_bool";
+
+    /** Determines if device implements a noise suppression device for in call audio. */
+    public static final String
+            KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL = "has_in_call_noise_suppression_bool";
+
+    /**
+     * Determines if the current device should allow emergency numbers to be logged in the Call Log.
+     * (Some carriers require that emergency calls *not* be logged, presumably to avoid the risk of
+     * accidental redialing from the call log UI. This is a good idea, so the default here is
+     * false.)
+     */
+    public static final String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL =
+            "allow_emergency_numbers_in_call_log_bool";
+
+    /**
+     * A string array containing numbers that shouldn't be included in the call log.
+     */
+    public static final String KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY =
+            "unloggable_numbers_string_array";
+
+    /** If true, removes the Voice Privacy option from Call Settings */
+    public static final String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
+
+    /** Control whether users can reach the carrier portions of Cellular Network Settings. */
+    public static final String
+            KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool";
+
+    /**
+     * Only allow auto selection in Advanced Network Settings when in home network.
+     * Manual selection is allowed when in roaming network.
+     */
+    public static final String KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL =
+            "only_auto_select_in_home_network";
+
+    /**
+     * Flag indicating whether to show single operator row in the choose network setting.
+     *
+     * The device configuration value {@code config_enableNewAutoSelectNetworkUI} ultimately
+     * controls whether this carrier configuration option is used.
+     * Where {@code config_enableNewAutoSelectNetworkUI} is false, the value of this
+     * carrier configuration is ignored.
+     *
+     * If {@code true}, default value, merge the duplicate networks which with the same plmn, keep
+     * the one that with the higher signal strength level.
+     * If {@code false}, show all operators without merging.
+     * @hide
+     */
+    public static final String KEY_SHOW_SINGLE_OPERATOR_ROW_IN_CHOOSE_NETWORK_SETTING_BOOL =
+            "show_single_operator_row_in_choose_network_setting_bool";
+
+    /**
+     * Flag indicating whether to display SPN as network name for home network in choose
+     * network setting.
+     *
+     * If {@code true}, display SPN as network name in choose network setting.
+     * If {@code false}, display PLMN in choose network setting.
+     * @hide
+     */
+    public static final String KEY_SHOW_SPN_FOR_HOME_IN_CHOOSE_NETWORK_SETTING_BOOL =
+            "show_spn_for_home_in_choose_network_setting_bool";
+
+    /**
+     * Control whether users receive a simplified network settings UI and improved network
+     * selection.
+     *
+     * @deprecated Never implemented. Has no behavior impact when override. DO NOT USE.
+     */
+    @Deprecated
+    public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL =
+            "simplified_network_settings_bool";
+
+    /** Control whether users can reach the SIM lock settings. */
+    public static final String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool";
+
+    /** Control whether users can edit APNs in Settings. */
+    public static final String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
+
+    /** Control whether users can choose a network operator. */
+    public static final String KEY_OPERATOR_SELECTION_EXPAND_BOOL =
+            "operator_selection_expand_bool";
+
+    /**
+     * Used in the Preferred Network Types menu to determine if the 2G option is displayed.
+     * Value defaults to false as of Android T to discourage the use of insecure 2G protocols.
+     */
+    public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
+
+    /**
+     * Used in the Preferred Network Types menu to determine if the 3G option is displayed.
+     */
+    @FlaggedApi(Flags.FLAG_HIDE_PREFER_3G_ITEM)
+    public static final String KEY_PREFER_3G_VISIBILITY_BOOL = "prefer_3g_visibility_bool";
+
+    /**
+     * Used in Cellular Network Settings for preferred network type to show 4G only mode.
+     * @hide
+     */
+    public static final String KEY_4G_ONLY_BOOL = "4g_only_bool";
+
+    /** Show cdma network mode choices 1x, 3G, global etc. */
+    public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
+
+    /** CDMA activation goes through HFA */
+    public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
+
+    /**
+     * CDMA activation goes through OTASP.
+     */
+    // TODO: This should be combined with config_use_hfa_for_provisioning and implemented as an enum
+    // (NONE, HFA, OTASP).
+    public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL =
+            "use_otasp_for_provisioning_bool";
+
+    /** Display carrier settings menu if true */
+    public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
+
+    /** Does not display additional call setting for IMS phone based on GSM Phone */
+    public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
+
+    /** Show APN Settings for some CDMA carriers */
+    public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
+
+    /** After a CDMA conference call is merged, the swap button should be displayed. */
+    public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
+
+    /**
+     * Determine whether user can edit voicemail number in Settings.
+     */
+    public static final String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL =
+            "editable_voicemail_number_setting_bool";
+
+    /**
+     * Since the default voicemail number is empty, if a SIM card does not have a voicemail number
+     * available the user cannot use voicemail. This flag allows the user to edit the voicemail
+     * number in such cases, and is false by default.
+     */
+    public static final String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL =
+            "editable_voicemail_number_bool";
+
+    /**
+     * Determine whether the voicemail number in Settings is hidden.
+     * @hide
+     */
+    public static final String KEY_HIDE_VOICEMAIL_NUMBER_SETTING_BOOL =
+            "hide_voicemail_number_setting_bool";
+
+    /**
+     * Determine whether the voicemail notification is persistent in the notification bar. If true,
+     * the voicemail notifications cannot be dismissed from the notification bar.
+     */
+    public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL =
+            "voicemail_notification_persistent_bool";
+
+    /** For IMS video over LTE calls, determines whether video pause signalling is supported. */
+    public static final String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL =
+            "support_pause_ims_video_calls_bool";
+
+    /**
+     * Disables dialing "*228" (OTASP provisioning) on CDMA carriers where it is not supported or is
+     * potentially harmful by locking the SIM to 3G.
+     */
+    public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL =
+            "disable_cdma_activation_code_bool";
+
+    /**
+     * List of network type constants which support only a single data connection at a time.
+     * Some carriers do not support multiple PDP on UMTS.
+     * @see TelephonyManager NETWORK_TYPE_*
+     * @see #KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY
+     */
+    public static final String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY =
+            "only_single_dc_allowed_int_array";
+
+    /**
+     * Only apply if {@link #KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY} specifies the network types that
+     * support a single data connection at a time. This key defines a list of network capabilities
+     * which, if requested, will exempt the request from single data connection checks.
+     * @see NetworkCapabilities NET_CAPABILITY_*
+     */
+    public static final String KEY_CAPABILITIES_EXEMPT_FROM_SINGLE_DC_CHECK_INT_ARRAY =
+            "capabilities_exempt_from_single_dc_check_int_array";
+
+    /**
+     * Override the platform's notion of a network operator being considered roaming.
+     * Value is string array of MCCMNCs to be considered roaming for 3GPP RATs.
+     */
+    public static final String KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY =
+            "gsm_roaming_networks_string_array";
+
+    /**
+     * Override the platform's notion of a network operator being considered not roaming.
+     * Value is string array of MCCMNCs to be considered not roaming for 3GPP RATs.
+     */
+    public static final String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY =
+            "gsm_nonroaming_networks_string_array";
+
+    /**
+     * The package name containing the ImsService that will be bound to the telephony framework to
+     * support both IMS MMTEL and RCS feature functionality instead of the device default
+     * ImsService for this subscription.
+     * @deprecated Use {@link #KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING} and
+     * {@link #KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING} instead to configure these values
+     * separately. If any of those values are not empty, they will override this value.
+     */
+    @Deprecated
+    public static final String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING =
+            "config_ims_package_override_string";
+
+    /**
+     * The package name containing the ImsService that will be bound to the telephony framework to
+     * support IMS MMTEL feature functionality instead of the device default ImsService for this
+     * subscription.
+     */
+    public static final String KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING =
+            "config_ims_mmtel_package_override_string";
+
+    /**
+     * The package name containing the ImsService that will be bound to the telephony framework to
+     * support IMS RCS feature functionality instead of the device default ImsService for this
+     * subscription.
+     */
+    public static final String KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING =
+            "config_ims_rcs_package_override_string";
+
+    /**
+     * Override the package that will manage {@link SubscriptionPlan}
+     * information instead of the {@link CarrierService} that defines this
+     * value.
+     *
+     * @see SubscriptionManager#getSubscriptionPlans(int)
+     * @see SubscriptionManager#setSubscriptionPlans(int, java.util.List)
+     */
+    public static final String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING =
+            "config_plans_package_override_string";
+
+    /**
+     * Override the platform's notion of a network operator being considered roaming.
+     * Value is string array of SIDs to be considered roaming for 3GPP2 RATs.
+     */
+    public static final String
+            KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
+
+    /**
+     * Override the platform's notion of a network operator being considered non roaming.
+     * Value is string array of SIDs to be considered not roaming for 3GPP2 RATs.
+     */
+    public static final String
+            KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
+
+    /**
+     * Override the platform's notion of a network operator being considered non roaming.
+     * If true all networks are considered as home network a.k.a. non-roaming. When false,
+     * the 2 pairs of CMDA and GSM roaming/non-roaming arrays are consulted.
+     *
+     * @see #KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+     * @see #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+     * @see #KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY
+     * @see #KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY
+     */
+    public static final String
+            KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
+
+    /**
+     * Flag specifying whether VoLTE should be available for carrier, independent of carrier
+     * provisioning. If false: hard disabled. If true: then depends on carrier provisioning,
+     * availability, etc.
+     */
+    public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
+
+    /**
+     * Flag specifying whether video telephony is available for carrier. If false: hard disabled.
+     * If true: then depends on carrier provisioning, availability, etc.
+     */
+    public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
+
+    /**
+     * Specify the method of selection for UE sending USSD requests. The default value is
+     * {@link #USSD_OVER_CS_PREFERRED}.
+     * <p> Available options:
+     * <ul>
+     *   <li>0: {@link #USSD_OVER_CS_PREFERRED} </li>
+     *   <li>1: {@link #USSD_OVER_IMS_PREFERRED} </li>
+     *   <li>2: {@link #USSD_OVER_CS_ONLY} </li>
+     *   <li>3: {@link #USSD_OVER_IMS_ONLY} </li>
+     * </ul>
+     */
+    public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
+
+    /**
+     * Flag specifying whether to show an alert dialog for 5G disable when the user disables VoLTE.
+     * By default this value is {@code false}.
+     *
+     * @hide
+     */
+    public static final String KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL =
+            "volte_5g_limited_alert_dialog_bool";
+
+    /**
+     * Flag specifying whether the carrier wants to notify the user when a VT call has been handed
+     * over from WIFI to LTE.
+     * <p>
+     * The handover notification is sent as a
+     * {@link TelephonyManager#EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE}
+     * {@link android.telecom.Connection} event, which an {@link android.telecom.InCallService}
+     * should use to trigger the display of a user-facing message.
+     * <p>
+     * The Connection event is sent to the InCallService only once, the first time it occurs.
+     * @hide
+     */
+    public static final String KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL =
+            "notify_handover_video_from_wifi_to_lte_bool";
+
+    /**
+     * Flag specifying whether the carrier supports merging a RTT call with a voice call,
+     * downgrading the call in the process.
+     * @hide
+     */
+    public static final String KEY_ALLOW_MERGING_RTT_CALLS_BOOL = "allow_merging_rtt_calls_bool";
+
+    /**
+     * Flag specifying whether the carrier wants to notify the user when a VT call has been handed
+     * over from LTE to WIFI.
+     * <p>
+     * The handover notification is sent as a
+     * {@link TelephonyManager#EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI}
+     * {@link android.telecom.Connection} event, which an {@link android.telecom.InCallService}
+     * should use to trigger the display of a user-facing message.
+     * @hide
+     */
+    public static final String KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL =
+            "notify_handover_video_from_lte_to_wifi_bool";
+
+    /**
+     * Flag specifying whether the carrier supports downgrading a video call (tx, rx or tx/rx)
+     * directly to an audio call.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL =
+            "support_downgrade_vt_to_audio_bool";
+
+    /**
+     * Where there is no preloaded voicemail number on a SIM card, specifies the carrier's default
+     * voicemail number.
+     * When empty string, no default voicemail number is specified.
+     */
+    public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
+
+    /**
+     * Where there is no preloaded voicemail number on a SIM card, specifies the carrier's default
+     * voicemail number for roaming network.
+     * When empty string, no default voicemail number is specified for roaming network.
+     * @hide
+     */
+    public static final String KEY_DEFAULT_VM_NUMBER_ROAMING_STRING =
+            "default_vm_number_roaming_string";
+
+    /**
+     * Where there is no preloaded voicemail number on a SIM card, specifies the carrier's default
+     * voicemail number while the device is both roaming and not registered for IMS.
+     * When empty string, no default voicemail number is specified for roaming network and
+     * unregistered state in IMS.
+     */
+    public static final String KEY_DEFAULT_VM_NUMBER_ROAMING_AND_IMS_UNREGISTERED_STRING =
+            "default_vm_number_roaming_and_ims_unregistered_string";
+
+    /**
+     * Flag that specifies to use the user's own phone number as the voicemail number when there is
+     * no pre-loaded voicemail number on the SIM card.
+     * <p>
+     * {@link #KEY_DEFAULT_VM_NUMBER_STRING} takes precedence over this flag.
+     * <p>
+     * If false, the system default (*86) will be used instead.
+     */
+    public static final String KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL =
+            "config_telephony_use_own_number_for_voicemail_bool";
+
+    /**
+     * When {@code true}, changes to the mobile data enabled switch will not cause the VT
+     * registration state to change. That is, turning on or off mobile data will not cause VT to be
+     * enabled or disabled.
+     * When {@code false}, disabling mobile data will cause VT to be de-registered.
+     */
+    public static final String KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS =
+            "ignore_data_enabled_changed_for_video_calls";
+
+    /**
+     * Flag indicating whether data used for a video call over LTE is metered or not.
+     * <p>
+     * When {@code true}, if the device hits the data limit or data is disabled during a ViLTE call,
+     * the call will be downgraded to audio-only (or paused if
+     * {@link #KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL} is {@code true}).
+     *
+     * @hide
+     */
+    public static final String KEY_VILTE_DATA_IS_METERED_BOOL = "vilte_data_is_metered_bool";
+
+    /**
+     * Flag specifying whether WFC over IMS should be available for carrier: independent of
+     * carrier provisioning. If false: hard disabled. If true: then depends on carrier
+     * provisioning, availability etc.
+     */
+    public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL =
+            "carrier_wfc_ims_available_bool";
+
+    /**
+     * Flag specifying whether Cross SIM over IMS should be available for carrier.
+     * When {@code false} the carrier does not support cross SIM calling.
+     * When {@code true} the carrier does support cross sim calling, where available
+     */
+    public static final String KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL =
+            "carrier_cross_sim_ims_available_bool";
+
+    /**
+     * Flag specifying whether cross sim calling on opportunistic data is supported for carrier.
+     * When {@code false} the carrier does not support cross sim calling on opportunistic data.
+     * When {@code true} the carrier does support cross sim calling on opportunistic data.
+     */
+    public static final String KEY_ENABLE_CROSS_SIM_CALLING_ON_OPPORTUNISTIC_DATA_BOOL =
+            "enable_cross_sim_calling_on_opportunistic_data_bool";
+
+    /**
+     * Specifies a map from dialstrings to replacements for roaming network service numbers which
+     * cannot be replaced on the carrier side.
+     * <p>
+     * Individual entries have the format:
+     * [dialstring to replace]:[replacement]
+     */
+    public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY =
+            "dial_string_replace_string_array";
+
+    /**
+     * Specifies a map from dialstrings to replacements for international roaming network service
+     * numbers which cannot be replaced on the carrier side.
+     * <p>
+     * Individual entries have the format:
+     * [dialstring to replace]:[replacement]
+     * @hide
+     */
+    public static final String KEY_INTERNATIONAL_ROAMING_DIAL_STRING_REPLACE_STRING_ARRAY =
+            "international_roaming_dial_string_replace_string_array";
+
+    /**
+     * Flag specifying whether WFC over IMS supports the "wifi only" option. If false, the wifi
+     * calling settings will not include an option for "wifi only". If true, the wifi calling
+     * settings will include an option for "wifi only"
+     * <p>
+     * By default, it is assumed that WFC supports "wifi only".
+     */
+    public static final String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL =
+            "carrier_wfc_supports_wifi_only_bool";
+
+    /**
+     * Default mode for WFC over IMS on home network:
+     * <ul>
+     *   <li>0: Wi-Fi only
+     *   <li>1: prefer mobile network
+     *   <li>2: prefer Wi-Fi
+     * </ul>
+     */
+    public static final String KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT =
+            "carrier_default_wfc_ims_mode_int";
+
+    /**
+     * Default mode for WFC over IMS on roaming network.
+     * See {@link #KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT} for meaning of values.
+     */
+    public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT =
+            "carrier_default_wfc_ims_roaming_mode_int";
+
+    /**
+     * Default WFC_IMS_enabled: true VoWiFi by default is on
+     *                          false VoWiFi by default is off
+     */
+    public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL =
+            "carrier_default_wfc_ims_enabled_bool";
+
+    /**
+     * Default WFC_IMS_roaming_enabled: true VoWiFi roaming by default is on
+     *                                  false VoWiFi roaming by default is off
+     * @hide
+     */
+    public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL =
+            "carrier_default_wfc_ims_roaming_enabled_bool";
+
+    /**
+     * Flag indicating whether failed calls due to no service should prompt the user to enable
+     * WIFI calling. When {@code true}, if the user attempts to establish a call when there is no
+     * service available, they are connected to WIFI, and WIFI calling is disabled, a different
+     * call failure message will be used to encourage the user to enable WIFI calling.
+     * @hide
+     */
+    public static final String KEY_CARRIER_PROMOTE_WFC_ON_CALL_FAIL_BOOL =
+            "carrier_promote_wfc_on_call_fail_bool";
+
+    /**
+     * Flag specifying whether provisioning is required for RCS.
+     */
+    public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL =
+            "carrier_rcs_provisioning_required_bool";
+
+    /**
+     * Flag specifying whether provisioning is required for VoLTE, Video Telephony, and WiFi
+     * Calling.
+
+     * Combines VoLTE, VT, VoWiFI calling provisioning into one parameter.
+     * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead for
+     * finer-grained control.
+     * changing carrier_volte_provisioning_required_bool requires changes to
+     * mmtel_requires_provisioning_bundle and vice versa
+     * {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE}
+     */
+    @Deprecated
+    public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL =
+            "carrier_volte_provisioning_required_bool";
+
+    /**
+     * Flag indicating whether or not the IMS MmTel UT capability requires carrier provisioning
+     * before it can be set as enabled.
+     *
+     * If true, the UT capability will be set to false for the newly loaded subscription
+     * and will require the carrier provisioning app to set the persistent provisioning result.
+     * If false, the platform will not wait for provisioning status updates for the UT capability
+     * and enable the UT over IMS capability for the subscription when the subscription is loaded.
+     *
+     * The default value for this key is {@code false}.
+     *
+     * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead for
+     * determining if UT requires provisioning.
+     */
+    @Deprecated
+    public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL =
+            "carrier_ut_provisioning_required_bool";
+
+    /**
+     * Flag indicating whether or not the carrier supports Supplementary Services over the UT
+     * interface for this subscription.
+     *
+     * If true, the device will use Supplementary Services over UT when provisioned (see
+     * {@link #KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL}). If false, this device will fallback to
+     * circuit switch for supplementary services and will disable this capability for IMS entirely.
+     *
+     * The default value for this key is {@code false}.
+     */
+    public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL =
+            "carrier_supports_ss_over_ut_bool";
+
+    /**
+     * Flag specifying if WFC provisioning depends on VoLTE provisioning.
+     *
+     * {@code false}: default value; honor actual WFC provisioning state.
+     * {@code true}: when VoLTE is not provisioned, treat WFC as not provisioned; when VoLTE is
+     *               provisioned, honor actual WFC provisioning state.
+     *
+     * As of now, Verizon is the only carrier enforcing this dependency in their
+     * WFC awareness and activation requirements.
+     */
+    public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL =
+            "carrier_volte_override_wfc_provisioning_bool";
+
+    /**
+     * Override the device's configuration for the cellular data service to use for this SIM card.
+     * @hide
+     */
+    public static final String KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING =
+            "carrier_data_service_wwan_package_override_string";
+
+    /**
+     * Override the device's configuration for the IWLAN data service to use for this SIM card.
+     * @hide
+     */
+    public static final String KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING =
+            "carrier_data_service_wlan_package_override_string";
+
+    /**
+     * Override the device's configuration for the cellular data service class to use
+     * for this SIM card.
+     * @hide
+     */
+    public static final String KEY_CARRIER_DATA_SERVICE_WWAN_CLASS_OVERRIDE_STRING =
+            "carrier_data_service_wwan_class_override_string";
+
+    /**
+     * Override the device's configuration for the IWLAN data service class to use
+     * for this SIM card.
+     * @hide
+     */
+    public static final String KEY_CARRIER_DATA_SERVICE_WLAN_CLASS_OVERRIDE_STRING =
+            "carrier_data_service_wlan_class_override_string";
+
+    /** Flag specifying whether VoLTE TTY is supported. */
+    public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL =
+            "carrier_volte_tty_supported_bool";
+
+    /** Flag specifying whether VoWIFI TTY is supported.
+     * @hide
+     */
+    public static final String KEY_CARRIER_VOWIFI_TTY_SUPPORTED_BOOL =
+            "carrier_vowifi_tty_supported_bool";
+
+    /**
+     * Flag specifying whether IMS service can be turned off. If false then the service will not be
+     * turned-off completely, but individual features can be disabled.
+     */
+    public static final String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL =
+            "carrier_allow_turnoff_ims_bool";
+
+    /**
+     * Flag specifying whether Generic Bootstrapping Architecture capable SIM is required for IMS.
+     */
+    public static final String KEY_CARRIER_IMS_GBA_REQUIRED_BOOL =
+            "carrier_ims_gba_required_bool";
+
+    /**
+     * Flag specifying whether IMS instant lettering is available for the carrier. {@code True} if
+     * instant lettering is available for the carrier, {@code false} otherwise.
+     */
+    public static final String KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL =
+            "carrier_instant_lettering_available_bool";
+
+    /**
+     * Flag specifying whether IMS should be the first phone attempted for E911 even if the
+     * phone is not in service.
+     */
+    public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL =
+            "carrier_use_ims_first_for_emergency_bool";
+
+    /**
+     * When {@code true}, this carrier will preferentially dial normal routed emergency calls over
+     * an in-service SIM if one is available.
+     * @hide
+     */
+    public static final String KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL =
+            "prefer_in_service_sim_for_normal_routed_emergency_calls_bool";
+
+    /**
+     * When {@code true}, the determination of whether to place a call as an emergency call will be
+     * based on the known {@link android.telephony.emergency.EmergencyNumber}s for the SIM on which
+     * the call is being placed. In a dual SIM scenario, if Sim A has the emergency numbers
+     * 123, 456 and Sim B has the emergency numbers 789, and the user places a call on SIM A to 789,
+     * it will not be treated as an emergency call in this case.
+     * When {@code false}, the determination is based on the emergency numbers from all device SIMs,
+     * regardless of which SIM the call is being placed on. If Sim A has the emergency numbers
+     * 123, 456 and Sim B has the emergency numbers 789, and the user places a call on SIM A to 789,
+     * the call will be dialed as an emergency number, but with an unspecified routing.
+     * @hide
+     */
+    public static final String KEY_USE_ONLY_DIALED_SIM_ECC_LIST_BOOL =
+            "use_only_dialed_sim_ecc_list_bool";
+
+    /**
+     * When IMS instant lettering is available for a carrier (see
+     * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines the list of characters
+     * which may not be contained in messages. Should be specified as a regular expression suitable
+     * for use with {@link String#matches(String)}.
+     */
+    public static final String KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING =
+            "carrier_instant_lettering_invalid_chars_string";
+
+    /**
+     * When IMS instant lettering is available for a carrier (see
+     * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines a list of characters which
+     * must be escaped with a backslash '\' character. Should be specified as a string containing
+     * the characters to be escaped. For example to escape quote and backslash the string would be
+     * a quote and a backslash.
+     */
+    public static final String KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING =
+            "carrier_instant_lettering_escaped_chars_string";
+
+    /**
+     * When IMS instant lettering is available for a carrier (see
+     * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines the character encoding
+     * which will be used when determining the length of messages. Used in the InCall UI to limit
+     * the number of characters the user may type. If empty-string, the instant lettering
+     * message size limit will be enforced on a 1:1 basis. That is, each character will count
+     * towards the messages size limit as a single byte. If a character encoding is specified, the
+     * message size limit will be based on the number of bytes in the message per the specified
+     * encoding.
+     */
+    public static final String KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING =
+            "carrier_instant_lettering_encoding_string";
+
+    /**
+     * When IMS instant lettering is available for a carrier (see
+     * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), the length limit for messages. Used
+     * in the InCall UI to ensure the user cannot enter more characters than allowed by the carrier.
+     * See also {@link #KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING} for more information on how
+     * the length of the message is calculated.
+     */
+    public static final String KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT =
+            "carrier_instant_lettering_length_limit_int";
+
+    /**
+     * If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
+     * this is the value that should be used instead. A configuration value of
+     * RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default
+     * assumption for phone type (GSM) should be used.
+     */
+    public static final String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
+
+    /**
+     * The default sim call manager to use when the default dialer doesn't implement one. A sim call
+     * manager can control and route outgoing and incoming phone calls, even if they're placed
+     * using another connection service (PSTN, for example).
+     */
+    public static final String KEY_DEFAULT_SIM_CALL_MANAGER_STRING =
+            "default_sim_call_manager_string";
+
+    /**
+     * The default flag specifying whether ETWS/CMAS test setting is forcibly disabled in
+     * Settings->More->Emergency broadcasts menu even though developer options is turned on.
+     * @deprecated Use {@code com.android.cellbroadcastreceiver.CellBroadcastReceiver} resource
+     * {@code show_test_settings} to control whether to show test alert settings or not.
+     */
+    @Deprecated
+    public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL =
+            "carrier_force_disable_etws_cmas_test_bool";
+
+    /**
+     * The default flag specifying whether "Allow alerts" option will be always shown in
+     * emergency alerts settings regardless developer options is turned on or not.
+     *
+     * @deprecated The allow alerts option is always shown now. No longer need a config for that.
+     */
+    @Deprecated
+    public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL =
+            "always_show_emergency_alert_onoff_bool";
+
+    /**
+     * Default mobile network MTU value, in bytes.
+     * @hide
+     */
+    public static final String KEY_DEFAULT_MTU_INT = "default_mtu_int";
+
+    /**
+     * Delay in milliseconds for retrying APN after disconnect
+     * @hide
+     */
+    public static final String KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG =
+            "carrier_data_call_apn_retry_after_disconnect_long";
+
+    /**
+     * Data call setup permanent failure causes by the carrier.
+     *
+     * @deprecated This API key was added in mistake and is not used anymore by the telephony data
+     * frameworks.
+     */
+    @Deprecated
+    public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS =
+            "carrier_data_call_permanent_failure_strings";
+
+    /**
+     * A string array indicating the default APN types that are metered by the carrier.
+     *
+     * The string in the array is the name of the APN type. For example, "default" for
+     * {@link ApnSetting#TYPE_DEFAULT}, "mms" for {@link ApnSetting#TYPE_MMS}, etc.
+     *
+     * The default value is {@code {"default", "mms", "dun", "supl"}}.
+     *
+     * @see ApnSetting#TYPE_DEFAULT
+     * @see ApnSetting#TYPE_MMS
+     * @see ApnSetting#TYPE_SUPL
+     * @see ApnSetting#TYPE_DUN
+     * @see ApnSetting#TYPE_HIPRI
+     * @see ApnSetting#TYPE_FOTA
+     * @see ApnSetting#TYPE_IMS
+     * @see ApnSetting#TYPE_CBS
+     * @see ApnSetting#TYPE_IA
+     * @see ApnSetting#TYPE_EMERGENCY
+     * @see ApnSetting#TYPE_MCX
+     * @see ApnSetting#TYPE_XCAP
+     * @see ApnSetting#TYPE_BIP
+     * @see ApnSetting#TYPE_VSIM
+     * @see ApnSetting#TYPE_ENTERPRISE
+     */
+    public static final String KEY_CARRIER_METERED_APN_TYPES_STRINGS =
+            "carrier_metered_apn_types_strings";
+
+    /**
+     * A string array indicating the default APN types that are roaming-metered by the carrier.
+     *
+     * The string in the array is the name of the APN type. For example, "default" for
+     * {@link ApnSetting#TYPE_DEFAULT}, "mms" for {@link ApnSetting#TYPE_MMS}, etc.
+     *
+     * The default value is {@code {"default", "mms", "dun", "supl"}}.
+     *
+     * @see ApnSetting#TYPE_DEFAULT
+     * @see ApnSetting#TYPE_MMS
+     * @see ApnSetting#TYPE_SUPL
+     * @see ApnSetting#TYPE_DUN
+     * @see ApnSetting#TYPE_HIPRI
+     * @see ApnSetting#TYPE_FOTA
+     * @see ApnSetting#TYPE_IMS
+     * @see ApnSetting#TYPE_CBS
+     * @see ApnSetting#TYPE_IA
+     * @see ApnSetting#TYPE_EMERGENCY
+     * @see ApnSetting#TYPE_MCX
+     * @see ApnSetting#TYPE_XCAP
+     * @see ApnSetting#TYPE_BIP
+     * @see ApnSetting#TYPE_VSIM
+     * @see ApnSetting#TYPE_ENTERPRISE
+     */
+    public static final String KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS =
+            "carrier_metered_roaming_apn_types_strings";
+
+    /**
+     * CDMA carrier ERI (Enhanced Roaming Indicator) file name
+     * @hide
+     */
+    public static final String KEY_CARRIER_ERI_FILE_NAME_STRING = "carrier_eri_file_name_string";
+
+    /* The following 3 fields are related to carrier visual voicemail. */
+
+    /**
+     * The carrier number mobile outgoing (MO) sms messages are sent to.
+     */
+    public static final String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
+
+    /**
+     * The port through which the mobile outgoing (MO) sms messages are sent through.
+     */
+    public static final String KEY_VVM_PORT_NUMBER_INT = "vvm_port_number_int";
+
+    /**
+     * The type of visual voicemail protocol the carrier adheres to. See {@link TelephonyManager}
+     * for possible values. For example {@link TelephonyManager#VVM_TYPE_OMTP}.
+     */
+    public static final String KEY_VVM_TYPE_STRING = "vvm_type_string";
+
+    /**
+     * Whether cellular data is required to access visual voicemail.
+     */
+    public static final String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL =
+            "vvm_cellular_data_required_bool";
+
+    /**
+     * The default OMTP visual voicemail client prefix to use. Defaulted to "//VVM"
+     */
+    public static final String KEY_VVM_CLIENT_PREFIX_STRING = "vvm_client_prefix_string";
+
+    /**
+     * Whether to use SSL to connect to the visual voicemail IMAP server. Defaulted to false.
+     */
+    public static final String KEY_VVM_SSL_ENABLED_BOOL = "vvm_ssl_enabled_bool";
+
+    /**
+     * A set of capabilities that should not be used even if it is reported by the visual voicemail
+     * IMAP CAPABILITY command.
+     */
+    public static final String KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY =
+            "vvm_disabled_capabilities_string_array";
+
+    /**
+     * Whether legacy mode should be used when the visual voicemail client is disabled.
+     *
+     * <p>Legacy mode is a mode that on the carrier side visual voicemail is still activated, but on
+     * the client side all network operations are disabled. SMSs are still monitored so a new
+     * message SYNC SMS will be translated to show a message waiting indicator, like traditional
+     * voicemails.
+     *
+     * <p>This is for carriers that does not support VVM deactivation so voicemail can continue to
+     * function without the data cost.
+     */
+    public static final String KEY_VVM_LEGACY_MODE_ENABLED_BOOL = "vvm_legacy_mode_enabled_bool";
+
+    /**
+     * Whether to prefetch audio data on new voicemail arrival, defaulted to true.
+     */
+    public static final String KEY_VVM_PREFETCH_BOOL = "vvm_prefetch_bool";
+
+    /**
+     * The package name of the carrier's visual voicemail app to ensure that dialer visual voicemail
+     * and carrier visual voicemail are not active at the same time.
+     *
+     * @deprecated use {@link #KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY}.
+     */
+    @Deprecated
+    public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING =
+            "carrier_vvm_package_name_string";
+
+    /**
+     * A list of the carrier's visual voicemail app package names to ensure that dialer visual
+     * voicemail and carrier visual voicemail are not active at the same time.
+     */
+    public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY =
+            "carrier_vvm_package_name_string_array";
+
+    /**
+     * Flag specifying whether ICCID is showed in SIM Status screen, default to false.
+     */
+    public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
+
+    /**
+     * Flag specifying whether the {@link android.telephony.SignalStrength} is shown in the SIM
+     * Status screen. The default value is true.
+     */
+    public static final String KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL =
+            "show_signal_strength_in_sim_status_bool";
+
+    /**
+     * Flag specifying if we should interpret all signal strength as one bar higher
+     * This is a replacement for the former resource config_inflateSignalStrength
+     * The default value is false.
+     * @hide
+     */
+    public static final String KEY_INFLATE_SIGNAL_STRENGTH_BOOL =
+            "inflate_signal_strength_bool";
+
+    /**
+     * Flag specifying whether an additional (client initiated) intent needs to be sent on System
+     * update
+     */
+    public static final String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
+
+    /**
+     * Intent to be sent for the additional action on System update
+     */
+    public static final String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING =
+            "ci_action_on_sys_update_intent_string";
+
+    /**
+     * Extra to be included in the intent sent for additional action on System update
+     */
+    public static final String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING =
+            "ci_action_on_sys_update_extra_string";
+
+    /**
+     * Value of extra included in intent sent for additional action on System update
+     */
+    public static final String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING =
+            "ci_action_on_sys_update_extra_val_string";
+
+    /**
+     * Specifies the amount of gap to be added in millis between postdial DTMF tones. When a
+     * non-zero value is specified, the UE shall wait for the specified amount of time before it
+     * sends out successive DTMF tones on the network.
+     */
+    public static final String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
+
+    /**
+     * Specifies the amount of gap to be added in millis between DTMF tones. When a non-zero value
+     * is specified, the UE shall wait for the specified amount of time before it sends out
+     * successive DTMF tones on the network.
+     */
+    public static final String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
+
+    /**
+     * Specifies the amount of gap to be added in millis between postdial DTMF tones. When a
+     * non-zero value is specified, the UE shall wait for the specified amount of time before it
+     * sends out successive DTMF tones on the network.
+     */
+    public static final String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
+
+    /**
+     * Some carriers will send call forwarding responses for voicemail in a format that is not 3gpp
+     * compliant, which causes issues during parsing. This causes the
+     * {@link com.android.internal.telephony.CallForwardInfo#number} to contain non-numerical
+     * characters instead of a number.
+     *
+     * If true, we will detect the non-numerical characters and replace them with "Voicemail".
+     * @hide
+     */
+    public static final String KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL =
+            "call_forwarding_map_non_number_to_voicemail_bool";
+
+    /**
+     * When {@code true}, the phone will always tell the IMS stack to keep RTT enabled and
+     * determine on a per-call basis (based on extras from the dialer app) whether a call should be
+     * an RTT call or not.
+     *
+     * When {@code false}, the old behavior is used, where the toggle in accessibility settings is
+     * used to set the IMS stack's RTT enabled state.
+     */
+    public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL =
+            "ignore_rtt_mode_setting_bool";
+
+    /**
+     * Determines whether adhoc conference calls are supported by a carrier. When {@code true},
+     * adhoc conference calling is supported, {@code false otherwise}.
+     */
+    public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL =
+            "support_adhoc_conference_calls_bool";
+
+    /**
+     * Determines whether conference participants can be added to existing call to form an adhoc
+     * conference call (in contrast to merging calls to form a conference). When {@code true},
+     * adding conference participants to existing call is supported, {@code false otherwise}.
+     */
+    public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL =
+            "support_add_conference_participants_bool";
+
+    /**
+     * Determines whether conference calls are supported by a carrier. When {@code true},
+     * conference calling is supported, {@code false otherwise}.
+     */
+    public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
+
+    /**
+     * Determines whether a maximum size limit for IMS conference calls is enforced on the device.
+     * When {@code true}, IMS conference calls will be limited to at most
+     * {@link #KEY_IMS_CONFERENCE_SIZE_LIMIT_INT} participants. When {@code false}, no attempt is
+     * made to limit the number of participants in a conference (the carrier will raise an error
+     * when an attempt is made to merge too many participants into a conference).
+     * <p>
+     * Note: The maximum size of a conference can ONLY be supported where
+     * {@link #KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL} is {@code true} since the platform
+     * needs conference event package data to accurately know the number of participants in the
+     * conference.
+     */
+    public static final String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL =
+            "is_ims_conference_size_enforced_bool";
+
+    /**
+     * Determines the maximum number of participants the carrier supports for a conference call.
+     * This number is exclusive of the current device. A conference between 3 devices, for example,
+     * would have a size limit of 2 participants.
+     * Enforced when {@link #KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL} is {@code true}.
+     */
+    public static final String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
+
+    /**
+     * Determines whether manage IMS conference calls is supported by a carrier. When {@code true},
+     * manage IMS conference call is supported, {@code false otherwise}.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL =
+            "support_manage_ims_conference_call_bool";
+
+    /**
+     * Determines whether the IMS conference merge process supports and returns its participants
+     * data. When {@code true}, on merge complete, conference call would have a list of its
+     * participants returned in XML format, {@code false otherwise}.
+     */
+    public static final String KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL =
+            "support_ims_conference_event_package_bool";
+
+    /**
+     * Determines whether processing of conference event package data received on a device other
+     * than the conference host is supported.
+     * <p>
+     * When a device A merges calls B and C into a conference it is considered the conference host
+     * and B and C are considered the conference peers.
+     * <p>
+     * When {@code true}, the conference peer will display the conference state if it receives
+     * conference event package data from the network. When {@code false}, the conference peer will
+     * ignore conference event package data received from the network.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL =
+            "support_ims_conference_event_package_on_peer_bool";
+
+    /**
+     * Indicates whether the carrier supports the use of RFC8285 compliant RTP header extensions for
+     * the purpose of device to device communication while in a call.
+     * <p>
+     * See also {@link #KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL}.
+     */
+    public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL =
+            "supports_device_to_device_communication_using_rtp_bool";
+
+    /**
+     * Indicates whether the carrier supports the negotiations of RFC8285 compliant RTP header
+     * extensions supported on a call during the Session Description Protocol (SDP). This option
+     * is only used when {@link #KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL} is
+     * {@code true}.
+     * <p>
+     * When {@code true}, the RTP header extensions the platform uses for device to device
+     * communication will be offered to the remote end during the SDP negotiation process.
+     * When {@code false}, the RTP header extensions will not be negotiated during the SDP
+     * negotiation process and the platform will send RTP header extensions without prior
+     * negotiation if {@link #KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL} is
+     * {@code true}.
+     */
+    public static final String KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL =
+            "supports_sdp_negotiation_of_d2d_rtp_header_extensions_bool";
+
+    /**
+     * Indicates whether the carrier supports the use of DTMF digits A-D for the purpose of device
+     * to device communication while in a call.
+     */
+    public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL =
+            "supports_device_to_device_communication_using_dtmf_bool";
+
+    /**
+     * Determines whether High Definition audio property is displayed in the dialer UI.
+     * If {@code false}, remove the HD audio property from the connection so that HD audio related
+     * UI is not displayed. If {@code true}, keep HD audio property as it is configured.
+     */
+    public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL =
+            "display_hd_audio_property_bool";
+
+    /**
+     * Determines whether IMS conference calls are supported by a carrier. When {@code true},
+     * IMS conference calling is supported, {@code false} otherwise.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL =
+            "support_ims_conference_call_bool";
+
+    /**
+     * Determines whether the device will locally disconnect an IMS conference when the participant
+     * count drops to zero. When {@code true}, it is assumed the carrier does NOT disconnect a
+     * conference when the participant count drops to zero and that the device must do this by
+     * disconnecting the conference locally. When {@code false}, it is assumed that the carrier
+     * is responsible for disconnecting the conference when there are no longer any participants
+     * present.
+     * <p>
+     * Note: both {@link #KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL} and
+     * {@link #KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL} must be true for this configuration to
+     * have any effect.
+     * <p>
+     * Defaults to {@code false}, meaning the carrier network is responsible for disconnecting an
+     * empty IMS conference.
+     * @hide
+     */
+    public static final String KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL =
+            "local_disconnect_empty_ims_conference_bool";
+
+    /**
+     * Determines whether video conference calls are supported by a carrier. When {@code true},
+     * video calls can be merged into conference calls, {@code false} otherwise.
+     * <p>
+     * Note: even if video conference calls are not supported, audio calls may be merged into a
+     * conference if {@link #KEY_SUPPORT_CONFERENCE_CALL_BOOL} is {@code true}.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL =
+            "support_video_conference_call_bool";
+
+    /**
+     * Determine whether user can toggle Enhanced 4G LTE Mode in Settings.
+     */
+    public static final String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
+
+    /**
+     * Determines whether the Enhanced 4G LTE toggle will be shown in the settings. When this
+     * option is {@code true}, the toggle will be hidden regardless of whether the device and
+     * carrier supports 4G LTE or not.
+     */
+    public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
+
+    /**
+     * Sets the default state for the "Enhanced 4G LTE" or "Advanced Calling" mode toggle set by the
+     * user. When this is {@code true}, this mode by default is on, otherwise if {@code false},
+     * this mode by default is off.
+     */
+    public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL =
+            "enhanced_4g_lte_on_by_default_bool";
+
+    /**
+     * Determine whether IMS apn can be shown.
+     */
+    public static final String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool";
+
+    /**
+     * Determine whether preferred network type can be shown.
+     */
+    public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL =
+            "hide_preferred_network_type_bool";
+
+    /**
+     * String array for package names that need to be enabled for this carrier.
+     * If user has explicitly disabled some packages in the list, won't re-enable.
+     * Other carrier specific apps which are not in this list may be disabled for current carrier,
+     * and only be re-enabled when this config for another carrier includes it.
+     *
+     * @hide
+     */
+    public static final String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
+
+    /**
+     * Determine whether user can switch Wi-Fi preferred or Cellular preferred
+     * in calling preference.
+     * Some operators support Wi-Fi Calling only, not VoLTE.
+     * They don't need "Cellular preferred" option.
+     * In this case, set uneditable attribute for preferred preference.
+     */
+    public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool";
+
+    /**
+     * Flag to indicate if Wi-Fi needs to be disabled in ECBM.
+     */
+    public static final String KEY_CONFIG_WIFI_DISABLE_IN_ECBM = "config_wifi_disable_in_ecbm";
+
+    /**
+     * List operator-specific error codes and indices of corresponding error strings in
+     * wfcOperatorErrorAlertMessages and wfcOperatorErrorNotificationMessages.
+     *
+     * Example: "REG09|0" specifies error code "REG09" and index "0". This index will be
+     * used to find alert and notification messages in wfcOperatorErrorAlertMessages and
+     * wfcOperatorErrorNotificationMessages.
+     *
+     * @hide
+     */
+    public static final String KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY =
+            "wfc_operator_error_codes_string_array";
+
+    /**
+     * Indexes of SPN format strings in wfcSpnFormats.
+     *
+     * <p>Available options are:
+     * <ul>
+     * <li>  0: %s</li>
+     * <li>  1: %s Wi-Fi Calling</li>
+     * <li>  2: WLAN Call</li>
+     * <li>  3: %s WLAN Call</li>
+     * <li>  4: %s Wi-Fi</li>
+     * <li>  5: WiFi Calling | %s</li>
+     * <li>  6: %s VoWifi</li>
+     * <li>  7: Wi-Fi Calling</li>
+     * <li>  8: Wi-Fi</li>
+     * <li>  9: WiFi Calling</li>
+     * <li> 10: VoWifi</li>
+     * <li> 11: %s WiFi Calling</li>
+     * <li> 12: WiFi Call</li>
+     * @hide
+     */
+    public static final String KEY_WFC_SPN_FORMAT_IDX_INT = "wfc_spn_format_idx_int";
+
+    /**
+     * Indexes of data SPN format strings in wfcSpnFormats.
+     *
+     * @see KEY_WFC_SPN_FORMAT_IDX_INT for available options.
+     * @hide
+     */
+    public static final String KEY_WFC_DATA_SPN_FORMAT_IDX_INT = "wfc_data_spn_format_idx_int";
+
+    /**
+     * Indexes of SPN format strings in wfcSpnFormats used during flight mode.
+     *
+     * Set to -1 to use the value from KEY_WFC_SPN_FORMAT_IDX_INT also in this case.
+     * @see KEY_WFC_SPN_FORMAT_IDX_INT for other available options.
+     * @hide
+     */
+    public static final String KEY_WFC_FLIGHT_MODE_SPN_FORMAT_IDX_INT =
+            "wfc_flight_mode_spn_format_idx_int";
+
+    /**
+     * Use root locale when reading wfcSpnFormats.
+     *
+     * If true, then the root locale will always be used when reading wfcSpnFormats. This means the
+     * non localized version of wfcSpnFormats will be used.
+     * @hide
+     */
+    public static final String KEY_WFC_SPN_USE_ROOT_LOCALE = "wfc_spn_use_root_locale";
+
+    /**
+     * The Component Name of the activity that can setup the emergency address for WiFi Calling
+     * as per carrier requirement.
+     */
+     public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING =
+            "wfc_emergency_address_carrier_app_string";
+
+    /**
+     * Unconditionally override the carrier name string using #KEY_CARRIER_NAME_STRING.
+     *
+     * If true, then the carrier name string will be #KEY_CARRIER_NAME_STRING, unconditionally.
+     *
+     * <p>If false, then the override will be performed conditionally and the
+     * #KEY_CARRIER_NAME_STRING will have the lowest-precedence; it will only be used in the event
+     * that the name string would otherwise be empty, allowing it to serve as a last-resort. If
+     * used, this value functions in place of the SPN on any/all ICC records for the corresponding
+     * subscription.
+     */
+    public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
+
+    /**
+     * String to identify carrier name in CarrierConfig app. This string overrides SPN if
+     * #KEY_CARRIER_NAME_OVERRIDE_BOOL is true; otherwise, it will be used if its value is provided
+     * and SPN is unavailable
+     */
+    public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
+
+    /**
+     * To override wifi calling's carrier name string using ef_pnn from sim card when SPN in empty.
+     *
+     * @hide
+     */
+    public static final String KEY_WFC_CARRIER_NAME_OVERRIDE_BY_PNN_BOOL =
+            "wfc_carrier_name_override_by_pnn_bool";
+
+    /**
+     * Specifies SPN format of displaying carrier name only.
+     *
+     */
+    public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_ONLY = 0;
+
+    /**
+     * Specifies SPN format of displaying carrier name along with "Cross-SIM calling".
+     */
+    public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING = 1;
+
+    /**
+     * Indexes of SPN format strings in crossSimSpnFormats.
+     *
+     * <p>Available options are:
+     * <ul>
+     * <li>  {@link #CROSS_SIM_SPN_FORMAT_CARRIER_NAME_ONLY}: %s</li>
+     * <li>  {@link #CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING}: %s Cross-SIM Calling</li>
+     * </ul>
+     * %s will be filled with carrier name
+     */
+    public static final String KEY_CROSS_SIM_SPN_FORMAT_INT = "cross_sim_spn_format_int";
+
+    /**
+     * Override the SPN Display Condition 2 integer bits (lsb). B2, B1 is the last two bits of the
+     * spn display condition coding.
+     *
+     * The default value -1 mean this field is not set.
+     *
+     * B1 = 0: display of registered PLMN name not required when registered PLMN is either HPLMN
+     * or a PLMN in the service provider PLMN list (see EF_SPDI).
+     * B1 = 1: display of registered PLMN name required when registered PLMN is either HPLMN or a
+     * PLMN in the service provider PLMN list(see EF_SPDI).
+     * B2 = 0: display of the service provider name is required when registered PLMN is neither
+     * HPLMN nor a PLMN in the service provider PLMN list(see EF_SPDI).
+     * B2 = 1: display of the service provider name is not required when registered PLMN is neither
+     * HPLMN nor a PLMN in the service provider PLMN list(see EF_SPDI).
+     *
+     * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.12 EF_SPN.
+     * @hide
+     */
+    public static final String KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT =
+            "spn_display_condition_override_int";
+
+    /**
+     * Override the SPDI - an array of PLMN(MCC + MNC) strings.
+     *
+     * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.66 EF_SPDI.
+     * @hide
+     */
+    public static final String KEY_SPDI_OVERRIDE_STRING_ARRAY = "spdi_override_string_array";
+
+    /**
+     * Override the EHPLMNs - an array of PLMN(MCC + MNC) strings.
+     *
+     * To allow provision for multiple HPLMN codes, PLMN codes that are present within this list
+     * shall replace the HPLMN code derived from the IMSI for PLMN selection purposes.
+     *
+     * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.84 EF_EHPLMN
+     * Reference: 3GPP TS 23.122 v15.6.0 Section 1.2 Equivalent HPLMN list
+     * @hide
+     */
+    public static final String KEY_EHPLMN_OVERRIDE_STRING_ARRAY = "ehplmn_override_string_array";
+
+    /**
+     * Override the PNN - a string array of comma-separated alpha long and short names:
+     * "alpha_long1,alpha_short1".
+     *
+     * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.58 EF_PNN.
+     * @hide
+     */
+    public static final String KEY_PNN_OVERRIDE_STRING_ARRAY = "pnn_override_string_array";
+
+    /**
+     * A string array of OPL records, each with comma-delimited data fields as follows:
+     * "plmn1,lactac_start,lactac_end,index".
+     *
+     * Reference: 3GPP TS 31.102 v15.2.0 Section 4.2.59 EF_OPL.
+     * @hide
+     */
+    public static final String KEY_OPL_OVERRIDE_STRING_ARRAY = "opl_override_opl_string_array";
+
+    /**
+     * Allow ERI rules to select a carrier name display string when using 3gpp2 access technologies.
+     * If this bit is not set, the carrier name display string will be selected from the carrier
+     * display name resolver which doesn't apply the ERI rules.
+     *
+     * @hide
+     */
+    public static final String KEY_ALLOW_ERI_BOOL = "allow_cdma_eri_bool";
+
+    /**
+     * If true, use the carrier display name(SPN and PLMN) from the carrier display name resolver.
+     *
+     * @hide
+     */
+    public static final String KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL =
+            "enable_carrier_display_name_resolver_bool";
+
+    /**
+     * String to override sim country iso.
+     * Sim country iso is based on sim MCC which is coarse and doesn't work with dual IMSI SIM where
+     * a SIM can have multiple MCC from different countries.
+     * Instead, each sim carrier should have a single country code, apply per carrier based iso
+     * code as an override. The overridden value can be read from
+     * {@link TelephonyManager#getSimCountryIso()} and {@link SubscriptionInfo#getCountryIso()}
+     */
+    public static final String KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING =
+            "sim_country_iso_override_string";
+
+    /**
+     * The Component Name of a carrier-provided CallScreeningService implementation. Telecom will
+     * bind to {@link android.telecom.CallScreeningService} for ALL incoming calls and provide
+     * the carrier
+     * CallScreeningService with the opportunity to allow or block calls.
+     * <p>
+     * The String includes the package name/the class name.
+     * Example:
+     * <item>com.android.carrier/com.android.carrier.callscreeningserviceimpl</item>
+     * <p>
+     * Using {@link ComponentName#flattenToString()} to convert a ComponentName object to String.
+     * Using {@link ComponentName#unflattenFromString(String)} to convert a String object to a
+     * ComponentName.
+     */
+    public static final String KEY_CARRIER_CALL_SCREENING_APP_STRING = "call_screening_app";
+
+    /**
+     * Override the registered PLMN name using #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING.
+     *
+     * If true, then the registered PLMN name (only for CDMA/CDMA-LTE and only when not roaming)
+     * will be #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING. If false, or if phone type is not
+     * CDMA/CDMA-LTE or if roaming, then #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING will be ignored.
+     * @hide
+     */
+    public static final String KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL =
+            "cdma_home_registered_plmn_name_override_bool";
+
+    /**
+     * String to identify registered PLMN name in CarrierConfig app. This string overrides
+     * registered PLMN name if #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL is true, phone type
+     * is CDMA/CDMA-LTE and device is not in roaming state; otherwise, it will be ignored.
+     * @hide
+     */
+    public static final String KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING =
+            "cdma_home_registered_plmn_name_string";
+
+    /**
+     * If this is true, the SIM card (through Customer Service Profile EF file) will be able to
+     * prevent manual operator selection. If false, this SIM setting will be ignored and manual
+     * operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more
+     * information
+     */
+    public static final String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
+
+    /**
+     * Allow user to add APNs
+     */
+    public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
+
+    /**
+     * APN types that user is not allowed to modify.
+     */
+    public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY =
+            "read_only_apn_types_string_array";
+
+    /**
+     * APN fields that user is not allowed to modify.
+     */
+    public static final String KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY =
+            "read_only_apn_fields_string_array";
+
+    /**
+     * Default value of APN types field if not specified by user when adding/modifying an APN.
+     */
+    public static final String KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY =
+            "apn_settings_default_apn_types_string_array";
+
+    /**
+     * Configs used for APN setup.
+     */
+    public static final class Apn {
+        /**
+         * Prefix of all Apn.KEY_* constants.
+         *
+         * @deprecated Since KEY_PREFIX is unnecessary to public, it will modify to private
+         * next android generation.
+         */
+        @Deprecated
+        public static final String KEY_PREFIX = "apn.";
+
+        /** IPv4 internet protocol */
+        public static final String PROTOCOL_IPV4 = "IP";
+        /** IPv6 internet protocol */
+        public static final String PROTOCOL_IPV6 = "IPV6";
+        /** IPv4 or IPv6 internet protocol */
+        public static final String PROTOCOL_IPV4V6 = "IPV4V6";
+
+        /**
+         * Default value of APN protocol field if not specified by user when adding/modifying
+         * an APN.
+         *
+         * Available options are: {@link #PROTOCOL_IPV4}, {@link #PROTOCOL_IPV6},
+         * {@link #PROTOCOL_IPV4V6}
+         */
+        public static final String KEY_SETTINGS_DEFAULT_PROTOCOL_STRING =
+                KEY_PREFIX + "settings_default_protocol_string";
+
+        /**
+         * Default value of APN roaming protocol field if not specified by user when
+         * adding/modifying an APN.
+         *
+         * Available options are: {@link #PROTOCOL_IPV4}, {@link #PROTOCOL_IPV6},
+         * {@link #PROTOCOL_IPV4V6}
+         */
+        public static final String KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING =
+                KEY_PREFIX + "settings_default_roaming_protocol_string";
+
+        private Apn() {}
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putString(KEY_SETTINGS_DEFAULT_PROTOCOL_STRING, "");
+            defaults.putString(KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING, "");
+            return defaults;
+        }
+    }
+
+    /**
+     * Boolean indicating if intent for emergency call state changes should be broadcast
+     * @hide
+     */
+    public static final String KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL =
+            "broadcast_emergency_call_state_changes_bool";
+
+    /**
+     * Indicates whether STK LAUNCH_BROWSER command is disabled.
+     * If {@code true}, then the browser will not be launched
+     * on UI for the LAUNCH_BROWSER STK command.
+     * @hide
+     */
+    public static final String KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL =
+            "stk_disable_launch_browser_bool";
+
+    /**
+     * Boolean indicating if the helper text for STK GET INKEY/INPUT commands with the digit only
+     * mode is displayed on the input screen.
+     * The helper text is displayed regardless of the input mode, if {@code false}.
+     * @hide
+     */
+    public static final String KEY_HIDE_DIGITS_HELPER_TEXT_ON_STK_INPUT_SCREEN_BOOL =
+            "hide_digits_helper_text_on_stk_input_screen_bool";
+
+    /**
+     * Boolean indicating if show data RAT icon on status bar even when data is disabled.
+     */
+    public static final String KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL =
+            "always_show_data_rat_icon_bool";
+
+    /**
+     * Boolean indicating if default data account should show LTE or 4G icon.
+     */
+    public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL =
+            "show_4g_for_lte_data_icon_bool";
+
+    /**
+     * Boolean indicating if default data account should show 4G LTE or 4G icon.
+     * @hide
+     */
+    public static final String KEY_SHOW_4GLTE_FOR_LTE_DATA_ICON_BOOL =
+            "show_4glte_for_lte_data_icon_bool";
+
+    /**
+     * Boolean indicating if default data account should show 4G icon when in 3G.
+     */
+    public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL =
+            "show_4g_for_3g_data_icon_bool";
+
+    /**
+     * Boolean indicating if LTE+ icon should be shown if available.
+     */
+    public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL = "hide_lte_plus_data_icon_bool";
+
+    /**
+     * Boolean indicting if the 5G slice icon should be shown if available.
+     * @hide
+     */
+    public static final String KEY_SHOW_5G_SLICE_ICON_BOOL = "show_5g_slice_icon_bool";
+
+    /**
+     * The combined channel bandwidth threshold (non-inclusive) in KHz required to display the
+     * LTE+ data icon. It is 20000 by default, meaning the LTE+ icon will be shown if the device is
+     * using carrier aggregation and the combined channel bandwidth is strictly greater than 20 MHz.
+     * @hide
+     */
+    public static final String KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT =
+            "lte_plus_threshold_bandwidth_khz_int";
+
+    /**
+     * The combined channel bandwidth threshold (inclusive) in KHz required to display the
+     * NR advanced (i.e. 5G+) data icon. It is 0 by default, meaning minimum bandwidth check is
+     * not enabled. Other factors like bands or frequency can also determine whether the NR
+     * advanced data icon is shown or not.
+     *
+     * @see #KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY
+     * @see #KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT
+     *
+     * @hide
+     */
+    public static final String KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT =
+            "nr_advanced_threshold_bandwidth_khz_int";
+
+    /**
+     * Indicating whether to include LTE cell bandwidths when determining whether the aggregated
+     * cell bandwidth meets the required threshold for NR advanced.
+     *
+     * @see TelephonyDisplayInfo#OVERRIDE_NETWORK_TYPE_NR_ADVANCED
+     */
+    public static final String KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL =
+            "include_lte_for_nr_advanced_threshold_bandwidth_bool";
+
+    /**
+     * Indicating whether to ratchet the aggregated cell bandwidths on receiving new values when
+     * the device is in RRC IDLE mode.
+     * The aggregated cell bandwidths are used for determining NR advanced state.
+     *
+     * If this is {@code true}, we will only update the aggregate cell bandwidths if the new
+     * aggregate is higher than the current aggregate and the anchor NR cell is the same.
+     * If this is {@code false}, we will always update the aggregate cell bandwidths when receiving
+     * new values.
+     */
+    public static final String KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL =
+            "ratchet_nr_advanced_bandwidth_if_rrc_idle_bool";
+
+    /**
+     * Boolean indicating if operator name should be shown in the status bar
+     * @hide
+     */
+    public static final String KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL =
+            "show_operator_name_in_statusbar_bool";
+
+    /**
+     * The string is used to filter redundant string from PLMN Network Name that's supplied by
+     * specific carrier.
+     *
+     * @hide
+     */
+    public static final String KEY_OPERATOR_NAME_FILTER_PATTERN_STRING =
+            "operator_name_filter_pattern_string";
+
+    /**
+     * The string is used to compare with operator name.
+     * If it matches the pattern then show specific data icon.
+     * @hide
+     */
+    public static final String KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING =
+            "show_carrier_data_icon_pattern_string";
+
+    /**
+     * Boolean to decide whether to show precise call failed cause to user
+     * @hide
+     */
+    public static final String KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL =
+            "show_precise_failed_cause_bool";
+
+    /**
+     * A list of carrier nr availability is used to determine whether the carrier enable the
+     * non-standalone (NSA) mode of 5G NR, standalone (SA) mode of 5G NR
+     *
+     * <p> The value of list is
+     * {@link #CARRIER_NR_AVAILABILITY_NSA}, or {@link #CARRIER_NR_AVAILABILITY_SA}.
+     *
+     * <p> For example, if both NSA and SA are used, the list value is {
+     * {@link #CARRIER_NR_AVAILABILITY_NSA},{@link #CARRIER_NR_AVAILABILITY_SA}}.
+     * If the carrier doesn't support 5G NR, the value is the empty array.
+     * If the key is invalid or not configured, the default value {
+     * {@link #CARRIER_NR_AVAILABILITY_NSA},{@link #CARRIER_NR_AVAILABILITY_SA}} will apply.
+     */
+    public static final String KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY =
+            "carrier_nr_availabilities_int_array";
+
+    /**
+     * Boolean to decide whether LTE is enabled.
+     */
+    public static final String KEY_LTE_ENABLED_BOOL = "lte_enabled_bool";
+
+    /**
+     * Boolean to decide whether TD-SCDMA is supported.
+     */
+    public static final String KEY_SUPPORT_TDSCDMA_BOOL = "support_tdscdma_bool";
+
+    /**
+     * A list of mcc/mnc that support TD-SCDMA for device when connect to the roaming network.
+     */
+    public static final String KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY =
+            "support_tdscdma_roaming_networks_string_array";
+
+    /**
+     * Boolean to decide whether world mode is enabled.
+     */
+    public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
+
+    /**
+     * Flatten {@link android.content.ComponentName} of the carrier's settings activity.
+     */
+    public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING =
+            "carrier_settings_activity_component_name_string";
+
+    // These variables are used by the MMS service and exposed through another API,
+    // SmsManager. The variable names and string values are copied from there.
+    public static final String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
+    public static final String KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL = "allowAttachAudio";
+    public static final String KEY_MMS_APPEND_TRANSACTION_ID_BOOL = "enabledTransID";
+    public static final String KEY_MMS_GROUP_MMS_ENABLED_BOOL = "enableGroupMms";
+    public static final String KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL =
+            "enableMMSDeliveryReports";
+    public static final String KEY_MMS_MMS_ENABLED_BOOL = "enabledMMS";
+    public static final String KEY_MMS_MMS_READ_REPORT_ENABLED_BOOL = "enableMMSReadReports";
+    public static final String KEY_MMS_MULTIPART_SMS_ENABLED_BOOL = "enableMultipartSMS";
+    public static final String KEY_MMS_NOTIFY_WAP_MMSC_ENABLED_BOOL = "enabledNotifyWapMMSC";
+    public static final String KEY_MMS_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES_BOOL =
+            "sendMultipartSmsAsSeparateMessages";
+    public static final String KEY_MMS_SHOW_CELL_BROADCAST_APP_LINKS_BOOL =
+            "config_cellBroadcastAppLinks";
+    public static final String KEY_MMS_SMS_DELIVERY_REPORT_ENABLED_BOOL =
+            "enableSMSDeliveryReports";
+    public static final String KEY_MMS_SUPPORT_HTTP_CHARSET_HEADER_BOOL =
+            "supportHttpCharsetHeader";
+    public static final String KEY_MMS_SUPPORT_MMS_CONTENT_DISPOSITION_BOOL =
+            "supportMmsContentDisposition";
+    public static final String KEY_MMS_ALIAS_MAX_CHARS_INT = "aliasMaxChars";
+    public static final String KEY_MMS_ALIAS_MIN_CHARS_INT = "aliasMinChars";
+    public static final String KEY_MMS_HTTP_SOCKET_TIMEOUT_INT = "httpSocketTimeout";
+    public static final String KEY_MMS_MAX_IMAGE_HEIGHT_INT = "maxImageHeight";
+    public static final String KEY_MMS_MAX_IMAGE_WIDTH_INT = "maxImageWidth";
+    public static final String KEY_MMS_MAX_MESSAGE_SIZE_INT = "maxMessageSize";
+    public static final String KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT = "maxMessageTextSize";
+    public static final String KEY_MMS_RECIPIENT_LIMIT_INT = "recipientLimit";
+    public static final String KEY_MMS_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD_INT =
+            "smsToMmsTextLengthThreshold";
+    public static final String KEY_MMS_SMS_TO_MMS_TEXT_THRESHOLD_INT = "smsToMmsTextThreshold";
+    public static final String KEY_MMS_SUBJECT_MAX_LENGTH_INT = "maxSubjectLength";
+    public static final String KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING = "emailGatewayNumber";
+    public static final String KEY_MMS_HTTP_PARAMS_STRING = "httpParams";
+    public static final String KEY_MMS_NAI_SUFFIX_STRING = "naiSuffix";
+    public static final String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
+    public static final String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
+    public static final String KEY_MMS_USER_AGENT_STRING = "userAgent";
+    /**
+     * If true, add "Connection: close" header to MMS HTTP requests so the connection
+     * is immediately closed (disabling keep-alive).
+     */
+    public static final String KEY_MMS_CLOSE_CONNECTION_BOOL = "mmsCloseConnection";
+    /**
+     * Waiting time in milliseconds used before releasing an MMS data call. Not tearing down an MMS
+     * data connection immediately helps to reduce the message delivering latency if messaging
+     * continues between all parties in the conversation since the same data connection can be
+     * reused for further messages.
+     *
+     * This timer will control how long the data call will be kept alive before being torn down.
+     */
+    public static final String KEY_MMS_NETWORK_RELEASE_TIMEOUT_MILLIS_INT =
+            "mms_network_release_timeout_millis_int";
+    /**
+     * Maximum size in bytes of the PDU to send or download when connected to a non-terrestrial
+     * network. MmsService will return a result code of MMS_ERROR_TOO_LARGE_FOR_TRANSPORT if
+     * the PDU exceeds this limit when connected to a non-terrestrial network.
+     * @hide
+     */
+    public static final String KEY_MMS_MAX_NTN_PAYLOAD_SIZE_BYTES_INT =
+            "mms_max_ntn_payload_size_bytes_int";
+
+    /**
+     * The flatten {@link android.content.ComponentName componentName} of the activity that can
+     * setup the device and activate with the network per carrier requirements.
+     *
+     * e.g., com.google.android.carrierPackageName/.CarrierActivityName
+     * @hide
+     */
+    @SystemApi
+    public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
+
+    /**
+     * Defines carrier-specific actions which act upon
+     * com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED, used for customization of the
+     * default carrier app.
+     * Format: "CARRIER_ACTION_IDX, ..."
+     * Where {@code CARRIER_ACTION_IDX} is an integer defined in
+     * com.android.carrierdefaultapp.CarrierActionUtils
+     * Example:
+     * com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_DISABLE_METERED_APNS
+     * disables metered APNs
+     */
+    @SuppressLint("IntentName")
+    public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY =
+            "carrier_default_actions_on_redirection_string_array";
+
+    /**
+     * Defines carrier-specific actions which act upon CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+     * and configured signal args:
+     * android.telephony.TelephonyManager#EXTRA_APN_TYPE,
+     * android.telephony.TelephonyManager#EXTRA_ERROR_CODE
+     * used for customization of the default carrier app
+     * Format:
+     * {
+     *     "APN_1, ERROR_CODE_1 : CARRIER_ACTION_IDX_1, CARRIER_ACTION_IDX_2...",
+     *     "APN_1, ERROR_CODE_2 : CARRIER_ACTION_IDX_1 "
+     * }
+     * Where {@code APN_1} is an integer defined in {@link android.telephony.data.ApnSetting}
+     * (e.g. {@link android.telephony.data.ApnSetting#TYPE_DEFAULT}
+     *
+     * {@code ERROR_CODE_1} is an integer defined in android.telephony.DataFailCause
+     * Example:
+     * android.telephony.DataFailCause#MISSING_UNKNOWN_APN
+     *
+     * {@code CARRIER_ACTION_IDX_1} is an integer defined in
+     * com.android.carrierdefaultapp.CarrierActionUtils
+     * Example:
+     * com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_DISABLE_METERED_APNS
+     * disables metered APNs
+     */
+    @SuppressLint("IntentName")
+    public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY =
+            "carrier_default_actions_on_dcfailure_string_array";
+
+    /**
+     * Defines carrier-specific actions which act upon CARRIER_SIGNAL_RESET,
+     * used for customization of the default carrier app.
+     * Format: "CARRIER_ACTION_IDX, ..."
+     * Where {@code CARRIER_ACTION_IDX} is an integer defined in
+     * com.android.carrierdefaultapp.CarrierActionUtils
+     * Example:
+     * com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS
+     * clears all notifications on reset
+     */
+    @SuppressLint("IntentName")
+    public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET =
+            "carrier_default_actions_on_reset_string_array";
+
+    /**
+     * Defines carrier-specific actions which act upon
+     * com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE,
+     * used for customization of the default carrier app.
+     * Format:
+     * {
+     *     "true : CARRIER_ACTION_IDX_1",
+     *     "false: CARRIER_ACTION_IDX_2"
+     * }
+     * Where {@code true} is a boolean indicates default network available/unavailable
+     * Where {@code CARRIER_ACTION_IDX} is an integer defined in
+     * com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils
+     * Example:
+     * com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER
+     * enables the app as the default URL handler
+     */
+    @SuppressLint("IntentName")
+    public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE =
+            "carrier_default_actions_on_default_network_available_string_array";
+
+    /**
+     * Defines a list of acceptable redirection url for default carrier app.
+     */
+    public static final String KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY =
+            "carrier_default_redirection_url_string_array";
+
+    /**
+     * Each config includes the componentName of the carrier app, followed by a list of interesting
+     * signals(declared in the manifest) which could wake up the app.
+     * @see com.android.internal.telephony.TelephonyIntents
+     * Example:
+     * <item>com.google.android.carrierAPK/.CarrierSignalReceiverA:
+     * com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED,
+     * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE
+     * </item>
+     * <item>com.google.android.carrierAPK/.CarrierSignalReceiverB:
+     * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE
+     * </item>
+     * @hide
+     */
+    public static final String KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY =
+            "carrier_app_wake_signal_config";
+
+    /**
+     * Each config includes the componentName of the carrier app, followed by a list of interesting
+     * signals for the app during run-time. The list of signals(intents) are targeting on run-time
+     * broadcast receivers only, aiming to avoid unnecessary wake-ups and should not be declared in
+     * the app's manifest.
+     * @see com.android.internal.telephony.TelephonyIntents
+     * Example:
+     * <item>com.google.android.carrierAPK/.CarrierSignalReceiverA:
+     * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+     * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE
+     * </item>
+     * <item>com.google.android.carrierAPK/.CarrierSignalReceiverB:
+     * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+     * </item>
+     * @hide
+     */
+    public static final String KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY =
+            "carrier_app_no_wake_signal_config";
+
+    /**
+     * Determines whether the carrier app needed to be involved when users try to finish setting up
+     * the SIM card to get network service.
+     */
+    public static final String KEY_CARRIER_APP_REQUIRED_DURING_SIM_SETUP_BOOL =
+            "carrier_app_required_during_setup_bool";
+
+    /**
+     * Default value for {@link Settings.Global#DATA_ROAMING}
+     * @hide
+     */
+    public static final String KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL =
+            "carrier_default_data_roaming_enabled_bool";
+
+    /**
+     * Determines whether the carrier supports making non-emergency phone calls while the phone is
+     * in emergency callback mode. Default value is {@code true}, meaning that non-emergency calls
+     * are allowed in emergency callback mode.
+     */
+    public static final String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL =
+            "allow_non_emergency_calls_in_ecm_bool";
+
+    /**
+     * Time that the telephony framework stays in "emergency SMS mode" after an emergency SMS is
+     * sent to the network. This is used by carriers to configure the time
+     * {@link TelephonyManager#isInEmergencySmsMode()} will be true after an emergency SMS is sent.
+     * This is used by GNSS to override user location permissions so that the carrier network can
+     * get the user's location for emergency services.
+     *
+     * The default is 0, which means that this feature is disabled. The maximum value for this timer
+     * is 300000 mS (5 minutes).
+     *
+     * @hide
+     */
+    public static final String KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT =
+            "emergency_sms_mode_timer_ms_int";
+
+    /**
+     * Flag indicating whether to allow carrier video calls to emergency numbers.
+     * When {@code true}, video calls to emergency numbers will be allowed. When {@code false},
+     * video calls to emergency numbers will be initiated as audio-only calls instead.
+     */
+    public static final String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL =
+            "allow_emergency_video_calls_bool";
+
+    /**
+     * Flag indicating whether or not an ongoing call will be held when an outgoing emergency call
+     * is placed. If true, ongoing calls will be put on hold when an emergency call is placed. If
+     * false, placing an emergency call will trigger the disconnect of all ongoing calls before
+     * the emergency call is placed.
+     */
+    public static final String KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL =
+            "allow_hold_call_during_emergency_bool";
+
+    /**
+     * Flag indicating whether or not the carrier supports the periodic exchange of phone numbers
+     * in the user's address book with the carrier's presence server in order to retrieve the RCS
+     * capabilities for each contact used in the RCS User Capability Exchange (UCE) procedure. See
+     * RCC.71, section 3 for more information.
+     * <p>
+     * The flag {@link Ims#KEY_ENABLE_PRESENCE_PUBLISH_BOOL} must also be enabled if this flag is
+     * enabled, as sending a periodic SIP PUBLISH with this device's RCS capabilities is a
+     * requirement for capability exchange to begin.
+     * <p>
+     * When presence is supported, the device should use the
+     * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the
+     * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit to indicate
+     * whether each contact supports video calling. The UI is made aware that presence is enabled
+     * via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE}
+     * and can choose to hide or show the video calling icon based on whether a contact supports
+     * video.
+     *
+     * @deprecated No longer used in framework code, however it may still be used by applications
+     * that have not updated their code. This config should still be set to {@code true} if
+     * {@link Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is set to {@code true} and
+     * {@link Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL} is set to {@code true}.
+     */
+    @Deprecated
+    public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
+
+    /**
+     * Flag indicating whether the carrier supports RCS SIP OPTIONS indication for
+     * User Capability Exchange (UCE).
+     */
+    public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool";
+
+    /**
+     * The duration in seconds that platform call and message blocking is disabled after the user
+     * contacts emergency services. Platform considers values for below cases:
+     *  1) 0 <= VALUE <= 604800(one week): the value will be used as the duration directly.
+     *  2) VALUE > 604800(one week): will use the default value as duration instead.
+     *  3) VALUE < 0: block will be disabled forever until user re-enable block manually,
+     *     the suggested value to disable forever is -1.
+     * See {@code android.provider.BlockedNumberContract#notifyEmergencyContact(Context)}
+     * See {@code android.provider.BlockedNumberContract#isBlocked(Context, String)}.
+     */
+    public static final String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT =
+            "duration_blocking_disabled_after_emergency_int";
+
+    /**
+     * Determines whether to enable enhanced call blocking feature on the device.
+     * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED
+     * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE
+     * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE
+     * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN
+     * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNAVAILABLE
+     *
+     * <p>
+     * 1. For Single SIM(SS) device, it can be customized in both carrier_config_mccmnc.xml
+     *    and vendor.xml.
+     * <p>
+     * 2. For Dual SIM(DS) device, it should be customized in vendor.xml, since call blocking
+     *    function is used regardless of SIM.
+     * <p>
+     * If {@code true} enable enhanced call blocking feature on the device, {@code false} otherwise.
+     */
+    public static final String KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL =
+            "support_enhanced_call_blocking_bool";
+
+    /**
+     * For carriers which require an empty flash to be sent before sending the normal 3-way calling
+     * flash, the duration in milliseconds of the empty flash to send. When {@code 0}, no empty
+     * flash is sent.
+     */
+    public static final String KEY_CDMA_3WAYCALL_FLASH_DELAY_INT = "cdma_3waycall_flash_delay_int";
+
+    /**
+     * The CDMA roaming mode (aka CDMA system select).
+     *
+     * <p>The value should be one of the CDMA_ROAMING_MODE_ constants in {@link TelephonyManager}.
+     * Values other than {@link TelephonyManager#CDMA_ROAMING_MODE_RADIO_DEFAULT} (which is the
+     * default) will take precedence over user selection.
+     *
+     * @see TelephonyManager#CDMA_ROAMING_MODE_RADIO_DEFAULT
+     * @see TelephonyManager#CDMA_ROAMING_MODE_HOME
+     * @see TelephonyManager#CDMA_ROAMING_MODE_AFFILIATED
+     * @see TelephonyManager#CDMA_ROAMING_MODE_ANY
+     */
+    public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
+
+    /**
+     * Determines whether 1X voice calls is supported for some CDMA carriers.
+     * Default value is true.
+     * @hide
+     */
+    @SystemApi
+    public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL =
+            "support_cdma_1x_voice_calls_bool";
+
+    /**
+     * Boolean indicating if support is provided for directly dialing FDN number from FDN list.
+     * If false, this feature is not supported.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL =
+            "support_direct_fdn_dialing_bool";
+
+    /**
+     * Int indicating the max number length for FDN
+     * @hide
+     */
+    public static final String KEY_FDN_NUMBER_LENGTH_LIMIT_INT = "fdn_number_length_limit_int";
+
+    /**
+     * Report IMEI as device id even if it's a CDMA/LTE phone.
+     *
+     * @hide
+     */
+    public static final String KEY_FORCE_IMEI_BOOL = "force_imei_bool";
+
+    /**
+     * The families of Radio Access Technologies that will get clustered and ratcheted,
+     * ie, we will report transitions up within the family, but not down until we change
+     * cells. This prevents flapping between base technologies and higher techs that are
+     * granted on demand within the cell.
+     * @hide
+     */
+    public static final String KEY_RATCHET_RAT_FAMILIES = "ratchet_rat_families";
+
+    /**
+     * Flag indicating whether some telephony logic will treat a call which was formerly a video
+     * call as if it is still a video call. When {@code true}:
+     * <p>
+     * Logic which will automatically drop a video call which takes place over WIFI when a
+     * voice call is answered (see {@link #KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL}.
+     * <p>
+     * Logic which determines whether the user can use TTY calling.
+     */
+    public static final String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL =
+            "treat_downgraded_video_calls_as_video_calls_bool";
+
+    /**
+     * When {@code true}, if the user is in an ongoing video call over WIFI and answers an incoming
+     * audio call, the video call will be disconnected before the audio call is answered. This is
+     * in contrast to the usual expected behavior where a foreground video call would be put into
+     * the background and held when an incoming audio call is answered.
+     */
+    public static final String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL =
+            "drop_video_call_when_answering_audio_call_bool";
+
+    /**
+     * Flag indicating whether the carrier supports merging wifi calls when VoWIFI is disabled.
+     * This can happen in the case of a carrier which allows offloading video calls to WIFI
+     * separately of whether voice over wifi is enabled. In such a scenario when two video calls
+     * are downgraded to voice, they remain over wifi. However, if VoWIFI is disabled, these calls
+     * cannot be merged.
+     */
+    public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL =
+            "allow_merge_wifi_calls_when_vowifi_off_bool";
+
+    /**
+     * Flag indicating whether the carrier supports the Hold command while in an IMS call.
+     * <p>
+     * The device configuration value {@code config_device_respects_hold_carrier_config} ultimately
+     * controls whether this carrier configuration option is used.
+     * Where {@code config_device_respects_hold_carrier_config} is false, the value of
+     * this carrier configuration is ignored.
+     * @hide
+     */
+    public static final String KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL = "allow_hold_in_ims_call";
+
+    /**
+     * Flag indicating whether the carrier supports call deflection for an incoming IMS call.
+     */
+    public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL =
+            "carrier_allow_deflect_ims_call_bool";
+
+    /**
+     * Flag indicating whether the carrier supports explicit call transfer for an IMS call.
+     * @hide
+     */
+    public static final String KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL =
+            "carrier_allow_transfer_ims_call_bool";
+
+    /**
+     * Flag indicating whether the carrier always wants to play an "on-hold" tone when a call has
+     * been remotely held.
+     * <p>
+     * When {@code true}, if the IMS stack indicates that the call session has been held, a signal
+     * will be sent from Telephony to play an audible "on-hold" tone played to the user.
+     * When {@code false}, a hold tone will only be played if the audio session becomes inactive.
+     * @hide
+     */
+    public static final String KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL =
+            "always_play_remote_hold_tone_bool";
+
+    /**
+     * When true, the Telephony stack will automatically turn off airplane mode and retry a wifi
+     * emergency call over the cell network if the initial attempt at dialing was met with a SIP 308
+     * error.
+     * @hide
+     */
+    public static final String KEY_AUTO_RETRY_FAILED_WIFI_EMERGENCY_CALL =
+            "auto_retry_failed_wifi_emergency_call";
+
+    /**
+     * When true, indicates that adding a call is disabled when there is an ongoing video call
+     * or when there is an ongoing call on wifi which was downgraded from video and VoWifi is
+     * turned off.
+     */
+    public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL =
+            "allow_add_call_during_video_call";
+
+    /**
+     * When {@code true}, indicates that video calls can be put on hold in order to swap to another
+     * call (e.g. a new outgoing call).
+     * When {@code false}, indicates that video calls will be disconnected when swapping to another
+     * call.
+     * <p>
+     * This is {@code true} by default.
+     */
+    public static final String KEY_ALLOW_HOLD_VIDEO_CALL_BOOL = "allow_hold_video_call_bool";
+
+    /**
+     * When true, indicates that the HD audio icon in the in-call screen should not be shown for
+     * VoWifi calls.
+     * @hide
+     */
+    public static final String KEY_WIFI_CALLS_CAN_BE_HD_AUDIO = "wifi_calls_can_be_hd_audio";
+
+    /**
+     * When true, indicates that the HD audio icon in the in-call screen should not be shown for
+     * video calls.
+     * @hide
+     */
+    public static final String KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO = "video_calls_can_be_hd_audio";
+
+    /**
+     * When true, indicates that the HD audio icon in the in-call screen should be shown for
+     * GSM/CDMA calls.
+     * @hide
+     */
+    public static final String KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO =
+            "gsm_cdma_calls_can_be_hd_audio";
+
+    /**
+     * Whether system apps are allowed to use fallback if carrier video call is not available.
+     * Defaults to {@code true}.
+     */
+    public static final String KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL =
+            "allow_video_calling_fallback_bool";
+
+    /**
+     * Defines operator-specific {@link ImsReasonInfo} mappings.
+     *
+     * Format: "ORIGINAL_CODE|MESSAGE|NEW_CODE"
+     * Where {@code ORIGINAL_CODE} corresponds to a {@link ImsReasonInfo#getCode()} code,
+     * {@code MESSAGE} corresponds to an expected {@link ImsReasonInfo#getExtraMessage()} string,
+     * and {@code NEW_CODE} is the new {@code ImsReasonInfo#CODE_*} which this combination of
+     * original code and message shall be remapped to.
+     *
+     * Note: If {@code *} is specified for the original code, any ImsReasonInfo with the matching
+     * {@code MESSAGE} will be remapped to {@code NEW_CODE}.
+     * If {@code *} is specified for the message, any ImsReasonInfo with the matching
+     * {@code ORIGINAL_CODE} will be remapped to {@code NEW_CODE}.
+     * The wildcard for {@code ORIGINAL_CODE} takes precedence to the wildcard for {@code MESSAGE}.
+     * A mapping with both wildcards has no effect.
+     *
+     * Example: "501|call completion elsewhere|1014"
+     * When the {@link ImsReasonInfo#getCode()} is {@link ImsReasonInfo#CODE_USER_TERMINATED} and
+     * the {@link ImsReasonInfo#getExtraMessage()} is {@code "call completion elsewhere"},
+     * {@link ImsReasonInfo#CODE_ANSWERED_ELSEWHERE} shall be used as the {@link ImsReasonInfo}
+     * code instead.
+     * @hide
+     */
+    public static final String KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY =
+            "ims_reasoninfo_mapping_string_array";
+
+    /**
+     * When {@code false}, use default title for Enhanced 4G LTE Mode settings.
+     * When {@code true}, use the variant.
+     * @hide
+     * @deprecated use {@link #KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT}.
+     */
+    @Deprecated
+    public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL =
+            "enhanced_4g_lte_title_variant_bool";
+
+    /**
+     * The index indicates the carrier specified title string of Enhanced 4G LTE Mode settings.
+     * Default value is 0, which indicates the default title string.
+     */
+    public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT =
+            "enhanced_4g_lte_title_variant_int";
+
+    /**
+     * Indicates whether the carrier wants to notify the user when handover of an LTE video call to
+     * WIFI fails.
+     * <p>
+     * When {@code true}, if a video call starts on LTE and the modem reports a failure to handover
+     * the call to WIFI or if no handover success is reported within 60 seconds of call initiation,
+     * the {@link android.telephony.TelephonyManager#EVENT_HANDOVER_TO_WIFI_FAILED} event is raised
+     * on the connection.
+     * @hide
+     */
+    public static final String KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL =
+            "notify_vt_handover_to_wifi_failure_bool";
+
+    /**
+     * A upper case list of CNAP names that are unhelpful to the user for distinguising calls and
+     * should be filtered out of the CNAP information. This includes CNAP names such as "WIRELESS
+     * CALLER" or "UNKNOWN NAME". By default, if there are no filtered names for this carrier, null
+     * is returned.
+     * @hide
+     */
+    public static final String KEY_FILTERED_CNAP_NAMES_STRING_ARRAY =
+            "filtered_cnap_names_string_array";
+
+    /**
+     * The RCS configuration server URL. This URL is used to initiate RCS provisioning.
+     */
+    public static final String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
+
+    /**
+     * Determine whether user can change Wi-Fi Calling preference in roaming.
+     * {@code false} - roaming preference cannot be changed by user independently. If
+     *                 {@link #KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL} is false,
+     *                 {@link #KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT} is used as the default
+     *                 value. If {@link #KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL} is
+     *                 true, roaming preference is the same as home preference and
+     *                 {@link #KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT} is used as the default value.
+     * {@code true}  - roaming preference can be changed by user independently if
+     *                 {@link #KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL} is false. If
+     *                 {@link #KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL} is true, this
+     *                 configuration is ignored and roaming preference cannot be changed.
+     */
+    public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL =
+            "editable_wfc_roaming_mode_bool";
+
+    /**
+     * Flag specifying whether to show blocking pay phone option in blocked numbers screen.
+     * Only show the option if payphone call presentation is present in the carrier's region.
+     */
+    public static final java.lang.String KEY_SHOW_BLOCKING_PAY_PHONE_OPTION_BOOL =
+            "show_blocking_pay_phone_option_bool";
+
+    /**
+     * Flag specifying whether the carrier will use the
+     * WFC home network mode in roaming network.
+     * {@code false} - roaming preference can be selected separately from the home preference.
+     * {@code true}  - roaming preference is the same as home preference and
+     *                 {@link #KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT} is used as the default value.
+     */
+    public static final String KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL =
+            "use_wfc_home_network_mode_in_roaming_network_bool";
+
+    /**
+     * Flag specifying whether the carrier is allowed to use metered network to download a
+     * certificate of Carrier-WiFi.
+     * {@code false} - default value.
+     *
+     * @hide
+     */
+    public static final String KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL =
+            "allow_metered_network_for_cert_download_bool";
+
+    /**
+     * Time delay (in ms) after which we show the notification to switch the preferred
+     * network.
+     * @hide
+     */
+    public static final String KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT =
+            "network_notification_delay_int";
+
+    /**
+     * Time delay (in ms) after which we show the notification for emergency calls,
+     * while the device is registered over WFC. Default value is -1, which indicates
+     * that this notification is not pertinent for a particular carrier. We've added a delay
+     * to prevent false positives.
+     */
+    public static final String KEY_EMERGENCY_NOTIFICATION_DELAY_INT =
+            "emergency_notification_delay_int";
+
+    /**
+     * When {@code true}, the carrier allows the user of the {@link
+     * TelephonyManager#sendUssdRequest(String, TelephonyManager.UssdResponseCallback, Handler)}
+     * API to perform USSD requests. {@code True} by default.
+     * @hide
+     */
+    public static final String KEY_ALLOW_USSD_REQUESTS_VIA_TELEPHONY_MANAGER_BOOL =
+            "allow_ussd_requests_via_telephony_manager_bool";
+
+    /**
+     * Indicates whether the carrier supports 3gpp call forwarding MMI codes while roaming. If
+     * false, the user will be notified that call forwarding is not available when the MMI code
+     * fails.
+     */
+    public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL =
+            "support_3gpp_call_forwarding_while_roaming_bool";
+
+    /**
+     * Boolean indicating whether to display voicemail number as default call forwarding number in
+     * call forwarding settings.
+     * If true, display vm number when cf number is null.
+     * If false, display the cf number from network.
+     * By default this value is false.
+     * @hide
+     */
+    public static final String KEY_DISPLAY_VOICEMAIL_NUMBER_AS_DEFAULT_CALL_FORWARDING_NUMBER_BOOL =
+            "display_voicemail_number_as_default_call_forwarding_number";
+
+    /**
+     * When {@code true}, the user will be notified when they attempt to place an international call
+     * when the call is placed using wifi calling.
+     * @hide
+     */
+    public static final String KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL =
+            "notify_international_call_on_wfc_bool";
+
+    /**
+     * Flag to hide Preset APN details. If true, user cannot enter ApnEditor view of Preset APN,
+     * and cannot view details of the APN. If false, user can enter ApnEditor view of Preset APN.
+     * Default value is false.
+     */
+    public static final String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool";
+
+    /**
+     * Flag specifying whether to show an alert dialog for video call charges.
+     * By default this value is {@code false}.
+     */
+    public static final String KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL =
+            "show_video_call_charges_alert_dialog_bool";
+
+    /**
+     * An array containing custom call forwarding number prefixes that will be blocked while the
+     * device is reporting that it is roaming. By default, there are no custom call
+     * forwarding prefixes and none of these numbers will be filtered. If one or more entries are
+     * present, the system will not complete the call and display an error message.
+     *
+     * To display a message to the user when call forwarding fails for 3gpp MMI codes while roaming,
+     * use the {@link #KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL} option instead.
+     */
+    public static final String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY =
+            "call_forwarding_blocks_while_roaming_string_array";
+
+    /**
+     * Call forwarding number prefixes defined by {@link
+     * #KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY} which will be allowed while the
+     * device is reporting that it is roaming and IMS is registered over LTE or Wi-Fi.
+     * By default this value is {@code true}.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_IMS_CALL_FORWARDING_WHILE_ROAMING_BOOL =
+            "support_ims_call_forwarding_while_roaming_bool";
+
+    /**
+     * The day of the month (1-31) on which the data cycle rolls over.
+     * <p>
+     * If the current month does not have this day, the cycle will roll over at
+     * the start of the next month.
+     * <p>
+     * This setting may be still overridden by explicit user choice. By default,
+     * {@link #DATA_CYCLE_USE_PLATFORM_DEFAULT} will be used.
+     */
+    public static final String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
+
+    /**
+     * When {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, {@link #KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG},
+     * or {@link #KEY_DATA_WARNING_THRESHOLD_BYTES_LONG} are set to this value, the platform default
+     * value will be used for that key.
+     */
+    public static final int DATA_CYCLE_USE_PLATFORM_DEFAULT = -1;
+
+    /**
+     * Flag indicating that a data cycle threshold should be disabled.
+     * <p>
+     * If {@link #KEY_DATA_WARNING_THRESHOLD_BYTES_LONG} is set to this value, the platform's
+     * default data warning, if one exists, will be disabled. A user selected data warning will not
+     * be overridden.
+     * <p>
+     * If {@link #KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG} is set to this value, the platform's
+     * default data limit, if one exists, will be disabled. A user selected data limit will not be
+     * overridden.
+     */
+    public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2;
+
+    /**
+     * Controls the data usage warning.
+     * <p>
+     * If the user uses more than this amount of data in their billing cycle, as defined by
+     * {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, the user will be alerted about the usage.
+     * If the value is set to {@link #DATA_CYCLE_THRESHOLD_DISABLED}, the data usage warning will
+     * be disabled.
+     * <p>
+     * This setting may be overridden by explicit user choice. By default,
+     * {@link #DATA_CYCLE_USE_PLATFORM_DEFAULT} will be used.
+     */
+    public static final String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG =
+            "data_warning_threshold_bytes_long";
+
+    /**
+     * Controls if the device should automatically notify the user as they reach
+     * their cellular data warning. When set to {@code false} the carrier is
+     * expected to have implemented their own notification mechanism. {@code true} by default.
+     */
+    public static final String KEY_DATA_WARNING_NOTIFICATION_BOOL =
+            "data_warning_notification_bool";
+
+    /**
+     * Controls if the device should automatically warn the user that sim voice & data function
+     * might be limited due to dual sim scenario. When set to {@code true} display the notification,
+     * {@code false} otherwise.
+     * @hide
+     */
+    public static final String KEY_LIMITED_SIM_FUNCTION_NOTIFICATION_FOR_DSDS_BOOL =
+            "limited_sim_function_notification_for_dsds_bool";
+
+    /**
+     * Controls the cellular data limit.
+     * <p>
+     * If the user uses more than this amount of data in their billing cycle, as defined by
+     * {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, cellular data will be turned off by the user's
+     * phone. If the value is set to {@link #DATA_CYCLE_THRESHOLD_DISABLED}, the data limit will be
+     * disabled.
+     * <p>
+     * This setting may be overridden by explicit user choice. By default,
+     * {@link #DATA_CYCLE_USE_PLATFORM_DEFAULT} will be used.
+     */
+    public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG =
+            "data_limit_threshold_bytes_long";
+
+    /**
+     * Controls if the device should automatically notify the user as they reach
+     * their cellular data limit. When set to {@code false} the carrier is
+     * expected to have implemented their own notification mechanism. {@code true} by default.
+     */
+    public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL = "data_limit_notification_bool";
+
+    /**
+     * Controls if the device should automatically notify the user when rapid
+     * cellular data usage is observed. When set to {@code false} the carrier is
+     * expected to have implemented their own notification mechanism. {@code true} by default.
+     */
+    public static final String KEY_DATA_RAPID_NOTIFICATION_BOOL = "data_rapid_notification_bool";
+
+    /**
+     * Offset to be reduced from rsrp threshold while calculating signal strength level.
+     * @hide
+     */
+    public static final String KEY_LTE_EARFCNS_RSRP_BOOST_INT = "lte_earfcns_rsrp_boost_int";
+
+    /**
+     * List of EARFCN (E-UTRA Absolute Radio Frequency Channel Number,
+     * Reference: 3GPP TS 36.104 5.4.3) inclusive ranges on which lte_earfcns_rsrp_boost_int
+     * will be applied. Format of the String array is expected to be {"earfcn1_start-earfcn1_end",
+     * "earfcn2_start-earfcn2_end" ... }
+     * @hide
+     */
+    public static final String KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY =
+            "boosted_lte_earfcns_string_array";
+
+    /**
+     * Offset to be reduced from rsrp threshold while calculating signal strength level.
+     * @hide
+     */
+    public static final String KEY_NRARFCNS_RSRP_BOOST_INT_ARRAY = "nrarfcns_rsrp_boost_int_array";
+
+    /**
+     * List of NR ARFCN (5G Absolute Radio Frequency Channel Number,
+     * Reference: 3GPP TS 36.108) inclusive ranges on which corresponding
+     * nrarfcns_rsrp_boost_int_array will be applied. The size of this array and
+     * nrarfcns_rsrp_boost_int_array must be the same.
+     * Format of the String array is expected to be {"nrarfcn1_start-nrarfcn1_end",
+     * "nrarfcn2_start-nrarfcn2_end" ... }
+     * @hide
+     */
+    public static final String KEY_BOOSTED_NRARFCNS_STRING_ARRAY = "boosted_nrarfcns_string_array";
+
+    /**
+     * Determine whether to use only RSRP for the number of LTE signal bars.
+     * @hide
+     *
+     * @deprecated use {@link #KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT}.
+     */
+    // FIXME: this key and related keys must not be exposed without a consistent philosophy for
+    // all RATs.
+    @Deprecated
+    public static final String KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL =
+            "use_only_rsrp_for_lte_signal_bar_bool";
+
+    /**
+     * Bit-field integer to determine whether to use Reference Signal Received Power (RSRP),
+     * Reference Signal Received Quality (RSRQ), or/and Reference Signal Signal to Noise Ratio
+     * (RSSNR) for the number of LTE signal bars and signal criteria reporting enabling.
+     *
+     * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and
+     * not be used for calculating signal level. If multiple measures are set bit, the parameter
+     * whose value is smallest is used to indicate the signal level.
+     * <UL>
+     *  <LI>RSRP = 1 << 0</LI>
+     *  <LI>RSRQ = 1 << 1</LI>
+     *  <LI>RSSNR = 1 << 2</LI>
+     * </UL>
+     * <p> The value of this key must be bitwise OR of {@link CellSignalStrengthLte#USE_RSRP},
+     * {@link CellSignalStrengthLte#USE_RSRQ}, {@link CellSignalStrengthLte#USE_RSSNR}.
+     *
+     * <p> For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
+     * If the key is invalid or not configured, a default value (RSRP = 1 << 0) will apply.
+     *
+     * @hide
+     */
+    public static final String KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT =
+            "parameters_used_for_lte_signal_bar_int";
+
+    /**
+     * List of 4 customized 5G SS reference signal received power (SSRSRP) thresholds.
+     * <p>
+     * Reference: 3GPP TS 38.215
+     * <p>
+     * 4 threshold integers must be within the boundaries [-140 dB, -44 dB], and the levels are:
+     * <UL>
+     *     <LI>"NONE: [-140, threshold1)"</LI>
+     *     <LI>"POOR: [threshold1, threshold2)"</LI>
+     *     <LI>"MODERATE: [threshold2, threshold3)"</LI>
+     *     <LI>"GOOD:  [threshold3, threshold4)"</LI>
+     *     <LI>"EXCELLENT:  [threshold4, -44]"</LI>
+     * </UL>
+     * <p>
+     * This key is considered invalid if the format is violated. If the key is invalid or
+     * not configured, a default value set will apply.
+     */
+    public static final String KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY =
+            "5g_nr_ssrsrp_thresholds_int_array";
+
+    /**
+     * List of 4 customized 5G SS reference signal received quality (SSRSRQ) thresholds.
+     * <p>
+     * Reference: 3GPP TS 38.215; 3GPP TS 38.133 section 10
+     * <p>
+     * 4 threshold integers must be within the boundaries [-43 dB, 20 dB], and the levels are:
+     * <UL>
+     *     <LI>"NONE: [-43, threshold1)"</LI>
+     *     <LI>"POOR: [threshold1, threshold2)"</LI>
+     *     <LI>"MODERATE: [threshold2, threshold3)"</LI>
+     *     <LI>"GOOD:  [threshold3, threshold4)"</LI>
+     *     <LI>"EXCELLENT:  [threshold4, 20]"</LI>
+     * </UL>
+     * <p>
+     * This key is considered invalid if the format is violated. If the key is invalid or
+     * not configured, a default value set will apply.
+     */
+    public static final String KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY =
+            "5g_nr_ssrsrq_thresholds_int_array";
+
+    /**
+     * List of 4 customized 5G SS signal-to-noise and interference ratio (SSSINR) thresholds.
+     * <p>
+     * Reference: 3GPP TS 38.215,
+     *            3GPP TS 38.133 10.1.16.1
+     * <p>
+     * 4 threshold integers must be within the boundaries [-23 dB, 40 dB], and the levels are:
+     * <UL>
+     *     <LI>"NONE: [-23, threshold1)"</LI>
+     *     <LI>"POOR: [threshold1, threshold2)"</LI>
+     *     <LI>"MODERATE: [threshold2, threshold3)"</LI>
+     *     <LI>"GOOD:  [threshold3, threshold4)"</LI>
+     *     <LI>"EXCELLENT:  [threshold4, 40]"</LI>
+     * </UL>
+     * <p>
+     * This key is considered invalid if the format is violated. If the key is invalid or
+     * not configured, a default value set will apply.
+     */
+    public static final String KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY =
+            "5g_nr_sssinr_thresholds_int_array";
+
+    /**
+     * An interval in dB for {@link SignalThresholdInfo#SIGNAL_MEASUREMENT_TYPE_SSRSRP} measurement
+     * type defining the required magnitude change between reports.
+     *
+     * <p>The default value is 2 and the minimum allowed value is 0. If no value or negative value
+     * is set, the default value 2 is used.
+     * @hide
+     */
+    public static final String KEY_NGRAN_SSRSRP_HYSTERESIS_DB_INT =
+            "ngran_ssrsrp_hysteresis_db_int";
+
+    /**
+     * An interval in dB for {@link SignalThresholdInfo#SIGNAL_MEASUREMENT_TYPE_SSRSRQ} measurement
+     * type defining the required magnitude change between reports.
+     *
+     * <p>The default value is 2 and the minimum allowed value is 0. If no value or negative value
+     * is set, the default value 2 is used.
+     * @hide
+     */
+    public static final String KEY_NGRAN_SSRSRQ_HYSTERESIS_DB_INT =
+            "ngran_ssrsrq_hysteresis_db_int";
+
+    /**
+     * An interval in dB for {@link SignalThresholdInfo#SIGNAL_MEASUREMENT_TYPE_SSSINR} measurement
+     * type defining the required magnitude change between reports.
+     *
+     * <p>The default value is 2 and the minimum allowed value is 0. If no value or negative value
+     * is set, the default value 2 is used.
+     * @hide
+     */
+    public static final String KEY_NGRAN_SSSINR_HYSTERESIS_DB_INT =
+            "ngran_sssinr_hysteresis_db_int";
+
+    /**
+     * Bit-field integer to determine whether to use SS reference signal received power (SSRSRP),
+     * SS reference signal received quality (SSRSRQ), or/and SS signal-to-noise and interference
+     * ratio (SSSINR) for the number of 5G NR signal bars and signal criteria reporting enabling.
+     *
+     * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and
+     * not be used for calculating signal level. If multiple measures are set bit, the parameter
+     * whose value is smallest is used to indicate the signal level.
+     * <UL>
+     *  <LI>SSRSRP = 1 << 0</LI>
+     *  <LI>SSRSRQ = 1 << 1</LI>
+     *  <LI>SSSINR = 1 << 2</LI>
+     * </UL>
+     *  The value of this key must be bitwise OR of {@link CellSignalStrengthNr#USE_SSRSRP},
+     *  {@link CellSignalStrengthNr#USE_SSRSRQ}, {@link CellSignalStrengthNr#USE_SSSINR}.
+     *
+     * <p> For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2).
+     * If the key is invalid or not configured, a default value (SSRSRP = 1 << 0) will apply.
+     *
+     * <p> Reference: 3GPP TS 38.215,
+     *                3GPP TS 38.133 10.1.16.1
+     *
+     * @hide
+     */
+    public static final String KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT =
+            "parameters_use_for_5g_nr_signal_bar_int";
+
+    /**
+     * There are two signal strengths, NR and LTE signal strength, during NR (non-standalone).
+     * Boolean indicating whether to use LTE signal strength as primary during NR (non-standalone).
+     * By default this value is true.
+     *
+     * @hide
+     */
+    public static final String KEY_SIGNAL_STRENGTH_NR_NSA_USE_LTE_AS_PRIMARY_BOOL =
+            "signal_strength_nr_nsa_use_lte_as_primary_bool";
+
+    /**
+     * String array of default bandwidth values per network type.
+     * The entries should be of form: "network_name:downlink,uplink", with values in Kbps.
+     * For NR (5G), the following network names should be used:
+     * - NR_NSA: NR NSA, sub-6 frequencies
+     * - NR_NSA_MMWAVE: NR NSA, mmwave frequencies
+     * - NR_SA: NR SA, sub-6 frequencies
+     * - NR_SA_MMWAVE: NR SA, mmwave frequencies
+     * @hide
+     */
+    public static final String KEY_BANDWIDTH_STRING_ARRAY = "bandwidth_string_array";
+
+    /**
+     * For NR (non-standalone), whether to use the LTE value instead of NR value as the default for
+     * uplink bandwidth. Downlink bandwidth will still use the NR value as the default.
+     * @hide
+     */
+    public static final String KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPLINK_BOOL =
+            "bandwidth_nr_nsa_use_lte_value_for_uplink_bool";
+
+    /**
+     * Key identifying if voice call barring notification is required to be shown to the user.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final String KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL =
+            "disable_voice_barring_notification_bool";
+
+    /**
+     * List of operators considered non-roaming which won't show roaming icon.
+     * <p>
+     * Can use mcc or mcc+mnc as item. For example, 302 or 21407.
+     * If operators, 21404 and 21407, make roaming agreements, users of 21404 should not see
+     * the roaming icon as using 21407 network.
+     * @hide
+     */
+    public static final String KEY_NON_ROAMING_OPERATOR_STRING_ARRAY =
+            "non_roaming_operator_string_array";
+
+    /**
+     * List of operators considered roaming with the roaming icon.
+     * <p>
+     * Can use mcc or mcc+mnc as item. For example, 302 or 21407.
+     * If operators, 21404 and 21407, make roaming agreements, users of 21404 should see
+     * the roaming icon as using 21407 network.
+     * <p>
+     * A match on this supersedes a match on {@link #KEY_NON_ROAMING_OPERATOR_STRING_ARRAY}.
+     * @hide
+     */
+    public static final String KEY_ROAMING_OPERATOR_STRING_ARRAY = "roaming_operator_string_array";
+
+    /**
+     * Config to show the roaming indicator (i.e. the "R" icon) from the status bar when roaming.
+     * The roaming indicator will be shown if this is {@code true} and will not be shown if this is
+     * {@code false}.
+     */
+    @FlaggedApi(Flags.FLAG_HIDE_ROAMING_ICON)
+    public static final String KEY_SHOW_ROAMING_INDICATOR_BOOL = "show_roaming_indicator_bool";
+
+    /**
+     * URL from which the proto containing the public key of the Carrier used for
+     * IMSI encryption will be downloaded.
+     * @hide
+     */
+    public static final String IMSI_KEY_DOWNLOAD_URL_STRING = "imsi_key_download_url_string";
+
+    /**
+     * String representation of a carrier's public key used for IMSI encryption for ePDG. If this
+     * is provided, the device will use it as a fallback when no key exists on device, but the key
+     * download will still initiate.
+     * Example string:
+     *         "-----BEGIN CERTIFICATE-----\nabcde12345abcde12345abcde12345abcde1234
+     * 5abcde12345abcde12345\nabcde12345abcde12345abcde12345abcde12345a\n-----END CERTIFICATE-----"
+     * @hide
+     */
+    public static final String IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING =
+            "imsi_carrier_public_key_epdg_string";
+
+    /**
+     * String representation of a carrier's public key used for IMSI encryption for WLAN. If this
+     * is provided, the device will use it as a fallback when no key exists on device, but the key
+     * download will still initiate.
+     * Example string:
+     *         "-----BEGIN CERTIFICATE-----\nabcde12345abcde12345abcde12345abcde1234
+     * 5abcde12345abcde12345\nabcde12345abcde12345abcde12345abcde12345a\n-----END CERTIFICATE-----"
+     * @hide
+     */
+    public static final String IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING =
+            "imsi_carrier_public_key_wlan_string";
+
+    /**
+     * Identifies if the key is available for WLAN or EPDG or both. The value is a bitmask.
+     * 0 indicates that neither EPDG or WLAN is enabled.
+     * 1 indicates that key type TelephonyManager#KEY_TYPE_EPDG is enabled.
+     * 2 indicates that key type TelephonyManager#KEY_TYPE_WLAN is enabled.
+     * 3 indicates that both are enabled.
+     */
+    public static final String IMSI_KEY_AVAILABILITY_INT = "imsi_key_availability_int";
+
+    /**
+     * Key identifying if the CDMA Caller ID presentation and suppression MMI codes
+     * should be converted to 3GPP CLIR codes when a multimode (CDMA+UMTS+LTE) device is roaming
+     * on a 3GPP network. Specifically *67<number> will be converted to #31#<number> and
+     * *82<number> will be converted to *31#<number> before dialing a call when this key is
+     * set TRUE and device is roaming on a 3GPP network.
+     * @hide
+     */
+    public static final String KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL =
+            "convert_cdma_caller_id_mmi_codes_while_roaming_on_3gpp_bool";
+
+    /**
+     * Flag specifying whether IMS registration state menu is shown in Status Info setting,
+     * default to false.
+     */
+    public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL =
+            "show_ims_registration_status_bool";
+
+    /**
+     * Flag indicating whether the carrier supports RTT over IMS.
+     */
+    public static final String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
+
+    /**
+     * Boolean flag indicating whether the carrier supports TTY.
+     * <p>
+     * Note that {@link #KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL} controls availability of TTY over
+     * VoLTE; if this carrier configuration is disabled, then
+     * {@link #KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL} is also implicitly disabled.
+     * <p>
+     * {@link TelecomManager#isTtySupported()} should be used to determine if a device supports TTY,
+     * and this carrier config key should be used to see if the current carrier supports it.
+     */
+    public static final String KEY_TTY_SUPPORTED_BOOL = "tty_supported_bool";
+
+    /**
+     * Indicates if the carrier supports auto-upgrading a call to RTT when receiving a call from a
+     * RTT-supported device.
+     */
+    public static final String KEY_RTT_AUTO_UPGRADE_BOOL = "rtt_auto_upgrade_bool";
+
+    /**
+     * Indicates if the carrier supports RTT during a video call.
+     */
+    public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
+
+    /**
+     * Indicates if the carrier supports upgrading a call that was previously an RTT call to VT.
+     */
+    public static final String KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL =
+            "vt_upgrade_supported_for_downgraded_rtt_call";
+
+    /**
+     * Indicates if the carrier supports upgrading a call that was previously a VT call to RTT.
+     */
+    public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL =
+            "rtt_upgrade_supported_for_downgraded_vt_call";
+
+    /**
+     * Indicates if the carrier supports upgrading a voice call to an RTT call during the call.
+     */
+    public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
+
+    /**
+     * Indicates if the carrier supports downgrading a RTT call to a voice call during the call.
+     */
+    public static final String KEY_RTT_DOWNGRADE_SUPPORTED_BOOL = "rtt_downgrade_supported_bool";
+
+    /**
+     * Indicates if the TTY HCO and VCO options should be hidden in the accessibility menu
+     * if the device is capable of RTT.
+     */
+    public static final String KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL = "hide_tty_hco_vco_with_rtt";
+
+    /**
+     * The flag to disable the popup dialog which warns the user of data charges.
+     */
+    public static final String KEY_DISABLE_CHARGE_INDICATION_BOOL =
+            "disable_charge_indication_bool";
+
+    /**
+     * Boolean indicating whether to skip the call forwarding (CF) fail-to-disable dialog.
+     * The logic used to determine whether we succeeded in disabling is carrier specific,
+     * so the dialog may not always be accurate.
+     * {@code false} - show CF fail-to-disable dialog.
+     * {@code true}  - skip showing CF fail-to-disable dialog.
+     *
+     * @hide
+     */
+    public static final String KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL =
+            "skip_cf_fail_to_disable_dialog_bool";
+
+    /**
+     * Flag specifying whether operator supports including no reply condition timer option on
+     * CFNRy (3GPP TS 24.082 3: Call Forwarding on No Reply) in the call forwarding settings UI.
+     * {@code true}  - include no reply condition timer option on CFNRy
+     * {@code false} - don't include no reply condition timer option on CFNRy
+     *
+     * @hide
+     */
+    public static final String KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL =
+            "support_no_reply_timer_for_cfnry_bool";
+
+    /**
+     * No reply time value to be sent to network for call forwarding on no reply
+     * (CFNRy 3GPP TS 24.082 version 17.0 section 3).
+     * Controls time in seconds for the no reply condition on in the call forwarding
+     * settings UI.
+     * This is available when {@link #KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL} is true.
+     *
+     * @hide
+     */
+    public static final String KEY_NO_REPLY_TIMER_FOR_CFNRY_SEC_INT =
+            "no_reply_timer_for_cfnry_sec_int";
+
+    /**
+     * List of the FAC (feature access codes) to dial as a normal call.
+     * @hide
+     */
+    public static final String KEY_FEATURE_ACCESS_CODES_STRING_ARRAY =
+            "feature_access_codes_string_array";
+
+    /**
+     * Determines if the carrier wants to identify high definition calls in the call log.
+     * @hide
+     */
+    public static final String KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL =
+            "identify_high_definition_calls_in_call_log_bool";
+
+    /**
+     * Flag specifying whether to use the {@link ServiceState} roaming status, which can be
+     * affected by other carrier configs (e.g.
+     * {@link #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY}), when setting the SPN display.
+     * <p>
+     * If {@code true}, the SPN display uses {@link ServiceState#getRoaming}.
+     * If {@code false} the SPN display checks if the current MCC/MNC is different from the
+     * SIM card's MCC/MNC.
+     *
+     * @see #KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+     * @see #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+     * @see #KEY_NON_ROAMING_OPERATOR_STRING_ARRAY
+     * @see #KEY_ROAMING_OPERATOR_STRING_ARRAY
+     * @see #KEY_FORCE_HOME_NETWORK_BOOL
+     *
+     * @hide
+     */
+    public static final String KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL =
+            "spn_display_rule_use_roaming_from_service_state_bool";
+
+    /**
+     * Determines whether any carrier has been identified and its specific config has been applied,
+     * default to false.
+     */
+    public static final String KEY_CARRIER_CONFIG_APPLIED_BOOL = "carrier_config_applied_bool";
+
+    /**
+     * Determines whether we should show a warning asking the user to check with their carrier
+     * on pricing when the user enabled data roaming,
+     * default to false.
+     */
+    public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL =
+            "check_pricing_with_carrier_data_roaming_bool";
+
+    /**
+     * Determines whether we should show a notification when the phone established a data
+     * connection in roaming network, to warn users about possible roaming charges.
+     *
+     * @see #KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_EXCLUDED_MCCS_STRING_ARRAY
+     * @see #KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_INCLUDED_MCC_MNCS_STRING_ARRAY
+     * @hide
+     */
+    public static final String KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL =
+            "show_data_connected_roaming_notification";
+
+    /**
+     * Determines what MCCs are exceptions for the value of
+     * {@link #KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL}.
+     * An empty list indicates that there are no exceptions.
+     *
+     * @see #KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL
+     * @see #KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_INCLUDED_MCC_MNCS_STRING_ARRAY
+     * @hide
+     */
+    public static final String
+            KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_EXCLUDED_MCCS_STRING_ARRAY =
+            "data_connected_roaming_notification_excluded_mccs_string_array";
+
+    /**
+     * Determines what MCC+MNCs are exceptions for the MCCs specified in
+     * {@link #KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_EXCLUDED_MCCS_STRING_ARRAY}, meaning the
+     * value for the MCC+MNC is {@link #KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL}.
+     * An empty list indicates that there are no MNC-specific exceptions.
+     *
+     * @see #KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL
+     * @see #KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_EXCLUDED_MCCS_STRING_ARRAY
+     * @hide
+     */
+    public static final String
+            KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_INCLUDED_MCC_MNCS_STRING_ARRAY =
+            "data_connected_roaming_notification_included_mcc_mncs_string_array";
+
+    /**
+     * A list of 4 LTE RSRP thresholds above which a signal level is considered POOR,
+     * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
+     *
+     * Note that the min and max thresholds are fixed at -140 and -44, as explained in
+     * TS 136.133 9.1.4 - RSRP Measurement Report Mapping.
+     * <p>
+     * See SignalStrength#MAX_LTE_RSRP and SignalStrength#MIN_LTE_RSRP. Any signal level outside
+     * these boundaries is considered invalid.
+     * @hide
+     */
+    public static final String KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY = "lte_rsrp_thresholds_int_array";
+
+    /**
+     * A list of 4 customized LTE Reference Signal Received Quality (RSRQ) thresholds.
+     *
+     * Reference: TS 136.133 v12.6.0 section 9.1.7 - RSRQ Measurement Report Mapping.
+     *
+     * 4 threshold integers must be within the boundaries [-34 dB, 3 dB], and the levels are:
+     *     "NONE: [-34, threshold1)"
+     *     "POOR: [threshold1, threshold2)"
+     *     "MODERATE: [threshold2, threshold3)"
+     *     "GOOD:  [threshold3, threshold4)"
+     *     "EXCELLENT:  [threshold4, 3]"
+     *
+     * This key is considered invalid if the format is violated. If the key is invalid or
+     * not configured, a default value set will apply.
+     */
+    public static final String KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY = "lte_rsrq_thresholds_int_array";
+
+    /**
+     * A list of 4 customized LTE Reference Signal Signal to Noise Ratio (RSSNR) thresholds.
+     *
+     * 4 threshold integers must be within the boundaries [-20 dB, 30 dB], and the levels are:
+     *     "NONE: [-20, threshold1)"
+     *     "POOR: [threshold1, threshold2)"
+     *     "MODERATE: [threshold2, threshold3)"
+     *     "GOOD:  [threshold3, threshold4)"
+     *     "EXCELLENT:  [threshold4, 30]"
+     *
+     * This key is considered invalid if the format is violated. If the key is invalid or
+     * not configured, a default value set will apply.
+     */
+    public static final String KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY =
+            "lte_rssnr_thresholds_int_array";
+
+    /**
+     * An interval in dB for {@link SignalThresholdInfo#SIGNAL_MEASUREMENT_TYPE_RSRP} measurement
+     * type defining the required magnitude change between reports.
+     *
+     * <p>The default value is 2 and the minimum allowed value is 0. If no value or negative value
+     * is set, the default value 2 is used.
+     * @hide
+     */
+    public static final String KEY_EUTRAN_RSRP_HYSTERESIS_DB_INT = "eutran_rsrp_hysteresis_db_int";
+
+    /**
+     * An interval in dB for {@link SignalThresholdInfo#SIGNAL_MEASUREMENT_TYPE_RSRQ} measurement
+     * type defining the required magnitude change between reports.
+     *
+     * <p>The default value is 2 and the minimum allowed value is 0. If no value or negative value
+     * is set, the default value 2 is used.
+     * @hide
+     */
+    public static final String KEY_EUTRAN_RSRQ_HYSTERESIS_DB_INT = "eutran_rsrq_hysteresis_db_int";
+
+    /**
+     * An interval in dB for {@link SignalThresholdInfo#SIGNAL_MEASUREMENT_TYPE_RSSNR} measurement
+     * type defining the required magnitude change between reports.
+     *
+     * <p>The default value is 2 and the minimum allowed value is 0. If no value or negative value
+     * is set, the default value 2 is used.
+     * @hide
+     */
+    public static final String KEY_EUTRAN_RSSNR_HYSTERESIS_DB_INT =
+            "eutran_rssnr_hysteresis_db_int";
+
+    /**
+     * Decides when clients try to bind to iwlan network service, which package name will
+     * the binding intent go to.
+     * @hide
+     */
+    public static final String KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING =
+            "carrier_network_service_wlan_package_override_string";
+
+    /**
+     * Decides when clients try to bind to iwlan network service, which class name will
+     * the binding intent go to.
+     * @hide
+     */
+    public static final String KEY_CARRIER_NETWORK_SERVICE_WLAN_CLASS_OVERRIDE_STRING =
+            "carrier_network_service_wlan_class_override_string";
+
+    /**
+     * Decides when clients try to bind to wwan (cellular) network service, which package name will
+     * the binding intent go to.
+     * @hide
+     */
+    public static final String KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING =
+            "carrier_network_service_wwan_package_override_string";
+
+    /**
+     * Decides when clients try to bind to wwan (cellular) network service, which class name will
+     * the binding intent go to.
+     * @hide
+     */
+    public static final String KEY_CARRIER_NETWORK_SERVICE_WWAN_CLASS_OVERRIDE_STRING =
+            "carrier_network_service_wwan_class_override_string";
+
+    /**
+     * The package name of qualified networks service that telephony binds to.
+     *
+     * @hide
+     */
+    public static final String KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING =
+            "carrier_qualified_networks_service_package_override_string";
+
+    /**
+     * The class name of qualified networks service that telephony binds to.
+     *
+     * @hide
+     */
+    public static final String KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING =
+            "carrier_qualified_networks_service_class_override_string";
+
+    /**
+     * A list of 4 WCDMA RSCP thresholds above which a signal level is considered POOR,
+     * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
+     *
+     * Note that the min and max thresholds are fixed at -120 and -24, as set in 3GPP TS 27.007
+     * section 8.69.
+     * <p>
+     * See CellSignalStrengthWcdma#WCDMA_RSCP_MAX and CellSignalStrengthWcdma#WCDMA_RSCP_MIN.
+     * Any signal level outside these boundaries is considered invalid.
+     * @hide
+     */
+    public static final String KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY =
+            "wcdma_rscp_thresholds_int_array";
+
+    /**
+     * A list of 4 WCDMA ECNO thresholds above which a signal level is considered POOR,
+     * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
+     *
+     * Note that the min and max thresholds are fixed at -24 and 1, as set in 3GPP TS 25.215
+     * section 5.1.5.
+     * Any signal level outside these boundaries is considered invalid.
+     * <p>
+     *
+     * The default value is {@code {-24, -14, -6, 1}}.
+     * @hide
+     */
+    public static final String KEY_WCDMA_ECNO_THRESHOLDS_INT_ARRAY =
+            "wcdma_ecno_thresholds_int_array";
+
+    /**
+     * An interval in dB for {@link SignalThresholdInfo#SIGNAL_MEASUREMENT_TYPE_RSCP} measurement
+     * type defining the required magnitude change between reports.
+     *
+     * <p>The default value is 2 and the minimum allowed value is 0. If no value or negative value
+     * is set, the default value 2 is used.
+     * @hide
+     */
+    public static final String KEY_UTRAN_RSCP_HYSTERESIS_DB_INT = "utran_rscp_hysteresis_db_int";
+
+    /**
+     * An interval in dB for {@link SignalThresholdInfo#SIGNAL_MEASUREMENT_TYPE_ECNO} measurement
+     * type defining the required magnitude change between reports.
+     *
+     * <p>The default value is 2 and the minimum allowed value is 0. If no value or negative value
+     * is set, the default value 2 is used.
+     * @hide
+     */
+    public static final String KEY_UTRAN_ECNO_HYSTERESIS_DB_INT = "utran_ecno_hysteresis_db_int";
+
+    /**
+     * The default measurement to use for signal strength reporting. If this is not specified, the
+     * RSSI is used.
+     * <p>
+     * e.g.) To use RSCP by default, set the value to "rscp". The signal strength level will
+     * then be determined by #KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY
+     * <p>
+     * Currently this supports the value "rscp","rssi" and "ecno".
+     * @hide
+     */
+    // FIXME: this key and related keys must not be exposed without a consistent philosophy for
+    // all RATs.
+    public static final String KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING =
+            "wcdma_default_signal_strength_measurement_string";
+
+    /**
+     * When a partial sms / mms message stay in raw table for too long without being completed,
+     * we expire them and delete them from the raw table. This carrier config defines the
+     * expiration time. The default value is milliseconds in 7 days.
+     * @hide
+     */
+    public static final String KEY_UNDELIVERED_SMS_MESSAGE_EXPIRATION_TIME =
+            "undelivered_sms_message_expiration_time";
+
+    /**
+     * Specifies a carrier-defined {@link android.telecom.CallRedirectionService} which Telecom
+     * will bind to for outgoing calls. An empty string indicates that no carrier-defined
+     * {@link android.telecom.CallRedirectionService} is specified.
+     */
+    public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING =
+            "call_redirection_service_component_name_string";
+
+    /**
+     * Support for the original string display of CDMA MO call.
+     * By default, it is disabled.
+     * @hide
+     */
+    public static final String KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL =
+            "config_show_orig_dial_string_for_cdma";
+
+    /**
+     * Flag specifying whether to show notification(call blocking disabled) when Enhanced Call
+     * Blocking(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL) is enabled and making emergency call.
+     * When true, notification is shown always.
+     * When false, notification is shown only when any setting of "Enhanced Blocked number" is
+     * enabled.
+     */
+    public static final String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL =
+            "show_call_blocking_disabled_notification_always_bool";
+
+    /**
+     * Some carriers only support SS over UT via INTERNET PDN.
+     * When mobile data is OFF or data roaming OFF during roaming,
+     * UI should block the call forwarding operation and notify the user
+     * that the function only works if data is available.
+     * @hide
+     */
+    public static final String KEY_CALL_FORWARDING_OVER_UT_WARNING_BOOL =
+            "call_forwarding_over_ut_warning_bool";
+
+    /**
+     * Some carriers only support SS over UT via INTERNET PDN.
+     * When mobile data is OFF or data roaming OFF during roaming,
+     * UI should block the call barring operation and notify the user
+     * that the function only works if data is available.
+     * @hide
+     */
+    public static final String KEY_CALL_BARRING_OVER_UT_WARNING_BOOL =
+            "call_barring_over_ut_warning_bool";
+
+    /**
+     * Some carriers only support SS over UT via INTERNET PDN.
+     * When mobile data is OFF or data roaming OFF during roaming,
+     * UI should block the caller id operation and notify the user
+     * that the function only works if data is available.
+     * @hide
+     */
+    public static final String KEY_CALLER_ID_OVER_UT_WARNING_BOOL =
+            "caller_id_over_ut_warning_bool";
+
+    /**
+     * Some carriers only support SS over UT via INTERNET PDN.
+     * When mobile data is OFF or data roaming OFF during roaming,
+     * UI should block the call waiting operation and notify the user
+     * that the function only works if data is available.
+     * @hide
+     */
+    public static final String KEY_CALL_WAITING_OVER_UT_WARNING_BOOL =
+            "call_waiting_over_ut_warning_bool";
+
+    /**
+     * Flag indicating whether to support "Network default" option in Caller ID settings for Calling
+     * Line Identification Restriction (CLIR).
+     */
+    public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL =
+            "support_clir_network_default_bool";
+
+    /**
+     * Determines whether the carrier want to support emergency dialer shortcut.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_EMERGENCY_DIALER_SHORTCUT_BOOL =
+            "support_emergency_dialer_shortcut_bool";
+
+    /**
+     * Call forwarding uses USSD command without SS command.
+     * When {@code true}, the call forwarding query/set by ussd command and UI only display Call
+     * Forwarding when unanswered.
+     * When {@code false}, don't use USSD to query/set call forwarding.
+     * @hide
+     */
+    public static final String KEY_USE_CALL_FORWARDING_USSD_BOOL = "use_call_forwarding_ussd_bool";
+
+    /**
+     * This flag specifies whether to support for the caller id set command by ussd.
+     * When {@code true}, device shall sync caller id ussd result to ss command.
+     * When {@code false}, caller id don't support ussd command.
+     * @hide
+     */
+    public static final String KEY_USE_CALLER_ID_USSD_BOOL = "use_caller_id_ussd_bool";
+
+    /**
+     * Call waiting uses USSD command without SS command.
+     * When {@code true}, the call waiting query/set by ussd command.
+     * When {@code false}, doesn't use USSD to query/set call waiting.
+     * @hide
+     */
+    public static final String KEY_USE_CALL_WAITING_USSD_BOOL = "use_call_waiting_ussd_bool";
+
+    /**
+     * Specifies the service class for call waiting service.
+     * Default value is
+     * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_VOICE}.
+     * <p>
+     * See 27.007 +CCFC or +CLCK.
+     * The value set as below:
+     * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_NONE}
+     * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_VOICE}
+     * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_DATA}
+     * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_FAX}
+     * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_SMS}
+     * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_DATA_SYNC}
+     * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_DATA_ASYNC}
+     * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_PACKET}
+     * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_PAD}
+     * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_MAX}
+     * @hide
+     */
+    public static final String KEY_CALL_WAITING_SERVICE_CLASS_INT =
+            "call_waiting_service_class_int";
+
+    /**
+     * This configuration allows the system UI to display different 5G icons for different 5G
+     * scenarios.
+     *
+     * There are six 5G scenarios for icon configuration:
+     * 1. connected_mmwave: device currently connected to 5G cell as the primary or secondary cell
+     *    and considered NR advanced.
+     * 2. connected: device currently connected to 5G cell as the primary or secondary cell but not
+     *    considered NR advanced.
+     * 3. connected_rrc_idle: device currently connected to 5G cell as the primary or secondary cell
+     *    and RRC currently in IDLE state.
+     * 4. not_restricted_rrc_idle: device camped on a network that has 5G capability and the use of
+     *    5G is not restricted and RRC currently in IDLE state.
+     * 5. not_restricted_rrc_con: device camped on a network that has 5G capability and the use of
+     *    5G is not restricted and RRC currently in CONNECTED state.
+     * 6. restricted: device camped on a network that has 5G capability but the use of 5G is
+     *    restricted.
+     *
+     * The configured string contains multiple key-value pairs separated by comma. For each pair,
+     * the key and value are separated by a colon. The key corresponds to a 5G status above and
+     * the value is the icon name. Use "None" as the icon name if no icon should be shown in a
+     * specific 5G scenario. If the scenario is "None", config can skip this key and value.
+     *
+     * Icon name options: "5G_Plus", "5G".
+     *
+     * Here is an example:
+     * UE wants to display 5G_Plus icon for scenario#1, and 5G icon for scenario#2; otherwise not
+     * define.
+     * The configuration is: "connected_mmwave:5G_Plus,connected:5G"
+     * @hide
+     */
+    public static final String KEY_5G_ICON_CONFIGURATION_STRING = "5g_icon_configuration_string";
+
+    /**
+     * This configuration allows the system UI to determine how long to continue to display 5G icons
+     * when the device switches between different 5G scenarios.
+     *
+     * There are eight 5G scenarios:
+     * 1. connected_mmwave: device currently connected to 5G cell as the primary or secondary cell
+     *    and considered NR advanced.
+     * 2. connected: device currently connected to 5G cell as the primary or secondary cell but not
+     *    considered NR advanced.
+     * 3. connected_rrc_idle: device currently connected to 5G cell as the primary or secondary cell
+     *    and RRC currently in IDLE state.
+     * 4. not_restricted_rrc_idle: device camped on a network that has 5G capability and the use of
+     *    5G is not restricted and RRC currently in IDLE state.
+     * 5. not_restricted_rrc_con: device camped on a network that has 5G capability and the use of
+     *    5G is not restricted and RRC currently in CONNECTED state.
+     * 6. restricted: device camped on a network that has 5G capability but the use of 5G is
+     *    restricted.
+     * 7. legacy: device is not camped on a network that has 5G capability
+     * 8. any: any of the above scenarios
+     *
+     * The configured string contains various timer rules separated by a semicolon.
+     * Each rule will have three items: prior 5G scenario, current 5G scenario, and grace period
+     * in seconds before changing the icon. When the 5G state changes from the prior to the current
+     * 5G scenario, the system UI will continue to show the icon for the prior 5G scenario (defined
+     * in {@link #KEY_5G_ICON_CONFIGURATION_STRING}) for the amount of time specified by the grace
+     * period. If the prior 5G scenario is reestablished, the timer will reset and start again if
+     * the UE changes 5G scenarios again. Defined states (5G scenarios #1-7) take precedence over
+     * 'any' (5G scenario #8), and unspecified transitions have a default grace period of 0.
+     * The order of rules in the configuration determines the priority (the first applicable timer
+     * rule will be used).
+     *
+     * Here is an example: "connected_mmwave,connected,30;connected_mmwave,any,10;connected,any,10"
+     * This configuration defines 3 timers:
+     * 1. When UE goes from 'connected_mmwave' to 'connected', system UI will continue to display
+     *    the 5G icon for 'connected_mmwave' for 30 seconds.
+     * 2. When UE goes from 'connected_mmwave' to any other state (except for connected, since
+     *    rule 1 would be used instead), system UI will continue to display the 5G icon for
+     *    'connected_mmwave' for 10 seconds.
+     * 3. When UE goes from 'connected' to any other state, system UI will continue to display the
+     *    5G icon for 'connected' for 10 seconds.
+     *
+     * @hide
+     */
+    public static final String KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING =
+            "5g_icon_display_grace_period_string";
+
+    /**
+     * This configuration extends {@link #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING} to allow the
+     * system UI to continue displaying 5G icons after the initial timer expires.
+     *
+     * There are eight 5G scenarios:
+     * 1. connected_mmwave: device currently connected to 5G cell as the primary or secondary cell
+     *    and considered NR advanced.
+     * 2. connected: device currently connected to 5G cell as the primary or secondary cell but not
+     *    considered NR advanced.
+     * 3. connected_rrc_idle: device currently connected to 5G cell as the primary or secondary cell
+     *    and RRC currently in IDLE state.
+     * 4. not_restricted_rrc_idle: device camped on a network that has 5G capability and the use of
+     *    5G is not restricted and RRC currently in IDLE state.
+     * 5. not_restricted_rrc_con: device camped on a network that has 5G capability and the use of
+     *    5G is not restricted and RRC currently in CONNECTED state.
+     * 6. restricted: device camped on a network that has 5G capability but the use of 5G is
+     *    restricted.
+     * 7. legacy: device is not camped on a network that has 5G capability
+     * 8. any: any of the above scenarios
+     *
+     * The configured string contains various timer rules separated by a semicolon.
+     * Each rule will have three items: primary 5G scenario, secondary 5G scenario, and
+     * grace period in seconds before changing the icon. When the timer for the primary 5G timer
+     * expires, the system UI will continue to show the icon for the primary 5G scenario (defined
+     * in {@link #KEY_5G_ICON_CONFIGURATION_STRING}) for the amount of time specified by the grace
+     * period. If the primary 5G scenario is reestablished, the timers will reset and the system UI
+     * will continue to display the icon for the primary 5G scenario without interruption. If the
+     * secondary 5G scenario is lost, the timer will reset and the icon will reflect the true state.
+     * Defined states (5G scenarios #1-7) take precedence over 'any' (5G scenario #8), and
+     * unspecified transitions have a default grace period of 0. The order of rules in the
+     * configuration determines the priority (the first applicable timer rule will be used).
+     *
+     * Here is an example: "connected,not_restricted_rrc_idle,30"
+     * This configuration defines a secondary timer that extends the primary 'connected' timer.
+     * When the primary 'connected' timer expires while the UE is in the 'not_restricted_rrc_idle'
+     * 5G state, system UI will continue to display the 5G icon for 'connected' for 30 seconds.
+     * If the 5G state returns to 'connected', the timer will be reset without change to the icon,
+     * and if the 5G state changes to neither 'connected' not 'not_restricted_rrc_idle', the icon
+     * will change to reflect the true state.
+     *
+     * The value can be overridden by {@link #KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT}
+     * @hide
+     */
+    public static final String KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING =
+            "5g_icon_display_secondary_grace_period_string";
+
+    /**
+     * The secondary grace periods in seconds to use if NR advanced icon was shown due to connecting
+     * to bands specified in {@link #KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY}.
+     *
+     * The default value is 0, meaning the original value in
+     * {@link #KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING} is used. Otherwise, it overrides
+     * the value in {@link #KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING}.
+     *
+     * @hide
+     */
+    public static final String KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT =
+            "nr_advanced_bands_secondary_timer_seconds_int";
+
+    /**
+     * Whether device resets all of NR timers when device camped on a network that haven't 5G
+     * capability and RRC currently in IDLE state.
+     *
+     * The default value is false;
+     *
+     * @hide
+     */
+    public static final String KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL =
+            "nr_timers_reset_if_non_endc_and_rrc_idle_bool";
+
+    /**
+     * Whether device resets all of NR timers when device is in a voice call and QOS is established.
+     * The default value is true;
+     *
+     * @see #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING
+     * @see #KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING
+     *
+     * @hide
+     */
+    public static final String KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL =
+            "nr_timers_reset_on_voice_qos_bool";
+
+    /**
+     * Whether device resets all of NR timers when the PLMN changes.
+     * The default value is false;
+     *
+     * @see #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING
+     * @see #KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING
+     *
+     * @hide
+     */
+    public static final String KEY_NR_TIMERS_RESET_ON_PLMN_CHANGE_BOOL =
+            "nr_timers_reset_on_plmn_change_bool";
+
+    /**
+     * A list of additional NR advanced band would map to
+     * {@link TelephonyDisplayInfo#OVERRIDE_NETWORK_TYPE_NR_ADVANCED} when the device is on that
+     * band.
+     *
+     * @hide
+     */
+    public static final String KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY =
+            "additional_nr_advanced_bands_int_array";
+
+    /**
+     * This configuration allows the framework to control the NR advanced capable by protocol
+     * configuration options(PCO).
+     *
+     * If this config is 0, then the nr advanced capable is enabled.
+     * If this config is not 0 and PCO container with this config's address is 1, then the nr
+     * advanced capable is enabled.
+     * If this config is not 0 and PCO container with this config's address is 0, then the nr
+     * advanced capable is disabled.
+     *
+     * @hide
+     */
+    public static final String KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT =
+            "nr_advanced_capable_pco_id_int";
+
+    /**
+     * Enabled NR advanced (i.e. 5G+) icon while roaming. The default value is {@code true}, meaming
+     * the same NR advanced logic used for home network will be used for roaming network as well.
+     * Set this to {@code false} will disable NR advanced icon while the device is roaming,
+     * regardless meeting NR advanced criteria or not.
+     *
+     * @hide
+     */
+    public static final String KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL =
+            "enable_nr_advanced_for_roaming_bool";
+
+    /**
+     * This configuration allows the framework to use user data communication to detect Idle state,
+     * and this is used on the 5G icon.
+     *
+     * There is a new way for RRC state detection at Android 12. If
+     * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}(
+     * {@link TelephonyManager#CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED}) returns true,
+     * then framework can use PHYSICAL_CHANNEL_CONFIG for RRC state detection. Based on this
+     * condition, some carriers want to use the legacy behavior that way is using user data
+     * communication to detect the Idle state. Therefore, this configuration allows the framework
+     * to use user data communication to detect Idle state.
+     *
+     * There are 3 situations reflects the carrier define Idle state.
+     * 1. using PHYSICAL_CHANNEL_CONFIG to detect RRC Idle
+     * 2. using all of data connections to detect RRC Idle.
+     * 3. using data communication(consider internet data connection only) to detect data Idle.
+     *
+     * How to setup for above 3 cases?
+     * For below part, we call the condition#1 is device support
+     * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}(
+     * {@link TelephonyManager#CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED}).
+     * The condition#2 is carrier enable the KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL.
+     *
+     * For case#1, the condition#1 is true and the condition#2 is false.
+     * For case#2, the condition#1 is false and the condition#2 is false.
+     * For case#3, the condition#2 is true.
+     * @hide
+     */
+    public static final String KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL =
+            "lte_endc_using_user_data_for_rrc_detection_bool";
+
+    /**
+     * Controls time in milliseconds until DcTracker reevaluates 5G connection state.
+     * @hide
+     */
+    public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_ms_long";
+
+    /**
+     * Which network types are unmetered. A string array that can contain network type names from
+     * {@link TelephonyManager#getNetworkTypeName(int)} in addition to the following NR keys:
+     * NR_NSA - NR NSA is unmetered for sub-6 frequencies
+     * NR_NSA_MMWAVE - NR NSA is unmetered for mmwave frequencies
+     * NR_SA - NR SA is unmetered for sub-6 frequencies
+     * NR_SA_MMWAVE - NR SA is unmetered for mmwave frequencies
+     *
+     * Note that this config only applies if an unmetered SubscriptionPlan is set via {@link
+     * SubscriptionManager#setSubscriptionPlans(int, List, long)} or an unmetered override is set
+     * via {@link SubscriptionManager#setSubscriptionOverrideUnmetered(int, boolean, int[], long)}
+     * or {@link SubscriptionManager#setSubscriptionOverrideUnmetered(int, boolean, long)}.
+     * If neither SubscriptionPlans nor an override are set, then no network types can be unmetered
+     * regardless of the value of this config.
+     * @hide
+     */
+    public static final String KEY_UNMETERED_NETWORK_TYPES_STRING_ARRAY =
+            "unmetered_network_types_string_array";
+
+    /**
+     * Which network types are unmetered when roaming. A string array that can contain network type
+     * names from {@link TelephonyManager#getNetworkTypeName(int)} in addition to the following
+     * NR keys:
+     * NR_NSA - NR NSA is unmetered when roaming for sub-6 frequencies
+     * NR_NSA_MMWAVE - NR NSA is unmetered when roaming for mmwave frequencies
+     * NR_SA - NR SA is unmetered when roaming for sub-6 frequencies
+     * NR_SA_MMWAVE - NR SA is unmetered when roaming for mmwave frequencies
+     *
+     * Note that this config only applies if an unmetered SubscriptionPlan is set via {@link
+     * SubscriptionManager#setSubscriptionPlans(int, List, long)} or an unmetered override is set
+     * via {@link SubscriptionManager#setSubscriptionOverrideUnmetered(int, boolean, int[], long)}
+     * or {@link SubscriptionManager#setSubscriptionOverrideUnmetered(int, boolean, long)}.
+     * If neither SubscriptionPlans nor an override are set, then no network types can be unmetered
+     * when roaming regardless of the value of this config.
+     * @hide
+     */
+    public static final String KEY_ROAMING_UNMETERED_NETWORK_TYPES_STRING_ARRAY =
+            "roaming_unmetered_network_types_string_array";
+
+    /**
+     * Support ASCII 7-BIT encoding for long SMS. This carrier config is used to enable
+     * this feature.
+     * @hide
+     */
+    public static final String KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL =
+            "ascii_7_bit_support_for_long_message_bool";
+
+    /**
+     * Controls whether to show wifi calling icon in statusbar when wifi calling is available.
+     * @hide
+     */
+    public static final String KEY_SHOW_WIFI_CALLING_ICON_IN_STATUS_BAR_BOOL =
+            "show_wifi_calling_icon_in_status_bar_bool";
+
+    /**
+     * Configuration to indicate that the carrier supports opportunistic data
+     * auto provisioning. Based on this flag, the device downloads and activates
+     * corresponding opportunistic profile.
+     */
+    public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL =
+            "carrier_supports_opp_data_auto_provisioning_bool";
+
+    /**
+     * SMDP+ server address for downloading opportunistic eSIM profile.
+     * FQDN (Fully Qualified Domain Name) of the SM-DP+ (e.g., smdp.gsma.com) restricted to the
+     * Alphanumeric mode character set defined in table 5 of ISO/IEC 18004 excluding '$'.
+     */
+    public static final String KEY_SMDP_SERVER_ADDRESS_STRING = "smdp_server_address_string";
+
+    /**
+     * This timer value is used in the eSIM Exponential Backoff download retry algorithm.
+     * Value should be in seconds.
+     * <OL>
+     *     <LI>When the first download failure occurs, retry download after BACKOFF_TIMER_VALUE
+     * seconds.</LI>
+     *
+     * <LI>If download fails again then, retry after either BACKOFF_TIMER_VALUE,
+     * 2xBACKOFF_TIMER_VALUE, or 3xBACKOFF_TIMER_VALUE seconds.</LI>
+     *
+     * <LI>In general after the cth failed attempt, retry after k * BACKOFF_TIMER_VALUE
+     * seconds, where k is a random integer between 1 and 2^c − 1. Max c value is
+     * {@link #KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT}</LI>
+     * </OL>
+     */
+    public static final String KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT =
+            "esim_download_retry_backoff_timer_sec_int";
+
+    /**
+     * If eSIM profile download fails then, the number of retry attempts by UE
+     * will be based on this configuration. If download still fails even after the
+     * MAX attempts configured by this item then the retry is postponed until next
+     * device bootup.
+     */
+    public static final String KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT =
+            "esim_max_download_retry_attempts_int";
+
+    /**
+     * List of opportunistic carrier-ids associated with CBRS Primary SIM. When CBRS pSIM is
+     * inserted, opportunistic eSIM is download and this configuration is used for grouping pSIM
+     * and opportunistic eSIM. Also when a new CBRS pSIM is inserted, old opportunistic eSIMs are
+     * deleted using the carrier-ids in this configuration.
+     *
+     * @hide
+     */
+    public static final String KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY =
+            "opportunistic_carrier_ids_int_array";
+
+    /**
+     * Boolean configuration to control auto provisioning eSIM download in
+     * OpportunisticNetworkService using only WiFi or both WiFi/Data.
+     * True will download esim only via WiFi.
+     * False will use both WiFi and Data connection.
+     *
+     * @hide
+     */
+    public static final String KEY_OPPORTUNISTIC_ESIM_DOWNLOAD_VIA_WIFI_ONLY_BOOL =
+            "opportunistic_esim_download_via_wifi_only_bool";
+
+    /**
+     * Controls RSRP threshold, in dBm, at which OpportunisticNetworkService will decide whether
+     * the opportunistic network is good enough for internet data.
+     *
+     * <p>The value of {@link CellSignalStrengthLte#getRsrp()} will be compared with this
+     * threshold.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT =
+            "opportunistic_network_entry_threshold_rsrp_int";
+
+    /**
+     * Controls RSSNR threshold, in dB, at which OpportunisticNetworkService will
+     * decide whether the opportunistic network is good enough for internet data.
+     *
+     * <p>The value of {@link CellSignalStrengthLte#getRssnr()} will be compared with this
+     * threshold.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT =
+            "opportunistic_network_entry_threshold_rssnr_int";
+
+    /**
+     * Controls RSRP threshold, in dBm, below which OpportunisticNetworkService will decide whether
+     * the opportunistic network available is not good enough for internet data.
+     *
+     * <p>The value of {@link CellSignalStrengthLte#getRsrp()} will be compared with this
+     * threshold.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT =
+            "opportunistic_network_exit_threshold_rsrp_int";
+
+    /**
+     * Controls RSSNR threshold, in dB, below which OpportunisticNetworkService will
+     * decide whether the opportunistic network available is not good enough for internet data.
+     *
+     * <p>The value of {@link CellSignalStrengthLte#getRssnr()} will be compared with this
+     * threshold.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT =
+            "opportunistic_network_exit_threshold_rssnr_int";
+
+    /**
+     * Controls bandwidth threshold in Kbps at which OpportunisticNetworkService will decide whether
+     * the opportunistic network is good enough for internet data.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_BANDWIDTH_INT =
+            "opportunistic_network_entry_threshold_bandwidth_int";
+
+    /**
+     * Controls hysteresis time in milli seconds for which OpportunisticNetworkService
+     * will wait before attaching to a network.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_OR_EXIT_HYSTERESIS_TIME_LONG =
+            "opportunistic_network_entry_or_exit_hysteresis_time_long";
+
+    /**
+     * Controls hysteresis time in milli seconds for which OpportunisticNetworkService
+     * will wait before switching data to an opportunistic network.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG =
+            "opportunistic_network_data_switch_hysteresis_time_long";
+
+    /**
+     * Controls hysteresis time in milli seconds for which OpportunisticNetworkService
+     * will wait before switching data from opportunistic network to primary network.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG =
+            "opportunistic_network_data_switch_exit_hysteresis_time_long";
+
+    /**
+     * Controls whether to do ping test before switching data to opportunistic network.
+     * This carrier config is used to disable this feature.
+     */
+    public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL =
+            "ping_test_before_data_switch_bool";
+
+    /**
+     * Controls whether to switch data to primary from opportunistic subscription
+     * if primary is out of service. This control only affects system or 1st party app
+     * initiated data switch, but will not override data switch initiated by privileged carrier apps
+     * This carrier config is used to disable this feature.
+     */
+    public static final String KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL =
+            "switch_data_to_primary_if_primary_is_oos_bool";
+
+    /**
+     * Controls the ping pong determination of opportunistic network.
+     * If opportunistic network is determined as out of service or below
+     * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT or
+     * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT within
+     * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG of switching to opportunistic network,
+     * it will be determined as ping pong situation by system app or 1st party app.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG =
+            "opportunistic_network_ping_pong_time_long";
+
+    /**
+     * Controls back off time in milli seconds for switching back to
+     * opportunistic subscription. This time will be added to
+     * {@link CarrierConfigManager#KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG} to
+     * determine hysteresis time if there is ping pong situation
+     * (determined by system app or 1st party app) between primary and opportunistic
+     * subscription. Ping ping situation is defined in
+     * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG.
+     * If ping pong situation continuous #KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG
+     * will be added to previously determined hysteresis time.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG =
+            "opportunistic_network_backoff_time_long";
+
+    /**
+     * Controls the max back off time in milli seconds for switching back to
+     * opportunistic subscription.
+     * This time will be the max hysteresis that can be determined irrespective of there is
+     * continuous ping pong situation or not as described in
+     * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG and
+     * #KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG.
+     */
+    public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG =
+            "opportunistic_network_max_backoff_time_long";
+
+    /** @hide */
+    public static class OpportunisticNetwork {
+        /**
+         * Prefix of all {@code OpportunisticNetwork.KEY_*} constants.
+         *
+         * @hide
+         */
+        public static final String PREFIX = "opportunistic.";
+
+        /**
+         * Controls SS-RSRP threshold in dBm at which 5G opportunistic network will be considered
+         * good enough for internet data. Note other factors may be considered for the final
+         * decision.
+         *
+         * <p>The value of {@link CellSignalStrengthNr#getSsRsrp()} will be compared with this
+         * threshold.
+         *
+         * @hide
+         */
+        public static final String KEY_ENTRY_THRESHOLD_SS_RSRP_INT =
+                PREFIX + "entry_threshold_ss_rsrp_int";
+
+        /**
+         * Similar to {@link #KEY_ENTRY_THRESHOLD_SS_RSRP_INT} but supports different
+         * thresholds for different 5G bands. For bands not specified here, the threshold
+         * will be {@link #KEY_ENTRY_THRESHOLD_SS_RSRP_INT}.
+         *
+         * <p>For each key-value in the bundle: the key is the band number in string, which
+         * shall be a decimal integer as defined in {@code NgranBands.BAND_*} constants;
+         * the value is the threshold in int.
+         *
+         * @hide
+         */
+        public static final String KEY_ENTRY_THRESHOLD_SS_RSRP_INT_BUNDLE =
+                PREFIX + "entry_threshold_ss_rsrp_int_bundle";
+
+        /**
+         * Controls SS-RSRQ threshold in dB at which 5G opportunistic network will be considered
+         * good enough for internet data. Note other factors may be considered for the final
+         * decision.
+         *
+         * <p>The value of {@link CellSignalStrengthNr#getSsRsrq()} will be compared with this
+         * threshold.
+         *
+         * @hide
+         */
+        public static final String KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE =
+                PREFIX + "entry_threshold_ss_rsrq_double";
+
+        /**
+         * Similar to {@link #KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE} but supports different
+         * thresholds for different 5G bands. For bands not specified here, the threshold
+         * will be {@link #KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE}.
+         *
+         * <p>For each key-value in the bundle: the key is the band number in string, which
+         * shall be a decimal integer as defined in {@code NgranBands.BAND_*} constants;
+         * the value is the threshold in double.
+         *
+         * @hide
+         */
+        public static final String KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE =
+                PREFIX + "entry_threshold_ss_rsrq_double_bundle";
+
+        /**
+         * Controls SS-RSRP threshold in dBm below which 5G opportunistic network available will not
+         * be considered good enough for internet data. Note other factors may be considered
+         * for the final decision.
+         *
+         * <p>The value of {@link CellSignalStrengthNr#getSsRsrp()} will be compared with this
+         * threshold.
+         *
+         * @hide
+         */
+        public static final String KEY_EXIT_THRESHOLD_SS_RSRP_INT =
+                PREFIX + "exit_threshold_ss_rsrp_int";
+
+        /**
+         * Similar to {@link #KEY_EXIT_THRESHOLD_SS_RSRP_INT} but supports different
+         * thresholds for different 5G bands. For bands not specified here, the threshold
+         * will be {@link #KEY_EXIT_THRESHOLD_SS_RSRP_INT}.
+         *
+         * <p>The syntax of its value is similar to
+         * {@link #KEY_ENTRY_THRESHOLD_SS_RSRP_INT_BUNDLE}.
+         *
+         * @hide
+         */
+        public static final String KEY_EXIT_THRESHOLD_SS_RSRP_INT_BUNDLE =
+                PREFIX + "exit_threshold_ss_rsrp_int_bundle";
+
+        /**
+         * Controls SS-RSRQ threshold in dB below which 5G opportunistic network available will not
+         * be considered good enough for internet data. Note other factors may be considered
+         * for the final decision.
+         *
+         * <p>The value of {@link CellSignalStrengthNr#getSsRsrq()} will be compared with this
+         * threshold.
+         *
+         * @hide
+         */
+        public static final String KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE =
+                PREFIX + "exit_threshold_ss_rsrq_double";
+
+        /**
+         * Similar to {@link #KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE} but supports different
+         * thresholds for different 5G bands. For bands not specified here, the threshold
+         * will be {@link #KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE}.
+         *
+         * <p>The syntax of its value is similar to
+         * {@link #KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE}.
+         *
+         * @hide
+         */
+        public static final String KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE =
+                PREFIX + "exit_threshold_ss_rsrq_double_bundle";
+
+        /**
+         * Controls hysteresis time in milliseconds for which will be waited before switching
+         * data to a 5G opportunistic network.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG =
+                PREFIX + "5g_data_switch_hysteresis_time_long";
+
+        /**
+         * Similar to {@link #KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG} but supports
+         * different values for different 5G bands. For bands not specified here, the threshold
+         * will be {@link #KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG}.
+         *
+         * <p>For each key-value in the bundle: the key is the band number in string, which
+         * shall be a decimal integer as defined in {@code NgranBands.BAND_*} constants;
+         * the value is the time in long.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG_BUNDLE =
+                PREFIX + "5g_data_switch_hysteresis_time_long_bundle";
+
+        /**
+         * Controls hysteresis time in milliseconds for which will be waited before switching from
+         * 5G opportunistic network to primary network.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG =
+                PREFIX + "5g_data_switch_exit_hysteresis_time_long";
+
+        /**
+         * Similar to {@link #KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG} but supports
+         * different values for different 5G bands. For bands not specified here, the threshold
+         * will be {@link #KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG}.
+         *
+         * <p>The syntax is similar to
+         * {@link KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG_BUNDLE}.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG_BUNDLE =
+                PREFIX + "5g_data_switch_exit_hysteresis_time_long_bundle";
+
+        /**
+         * Controls back off time in milliseconds for switching back to
+         * 5G opportunistic subscription. This time will be added to
+         * {@link #KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG} to
+         * determine hysteresis time if there is ping pong situation
+         * (determined by system app or 1st party app) between primary and 5G opportunistic
+         * subscription. Ping ping situation is defined in
+         * {@link #KEY_5G_PING_PONG_TIME_LONG}.
+         * If ping pong situation continuous {@link #KEY_5G_NETWORK_BACKOFF_TIME_LONG}
+         * will be added to previously determined hysteresis time.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_BACKOFF_TIME_LONG =
+                PREFIX + "5g_backoff_time_long";
+
+        /**
+         * Controls the max back off time in milliseconds for switching back to
+         * 5G opportunistic subscription.
+         * This time will be the max hysteresis that can be determined irrespective of there is
+         * continuous ping pong situation or not as described in
+         * {@link #KEY_5G_PING_PONG_TIME_LONG} and
+         * {@link #KEY_5G_BACKOFF_TIME_LONG}.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_MAX_BACKOFF_TIME_LONG =
+                PREFIX + "5g_max_backoff_time_long";
+
+        /**
+         * Controls the ping pong determination of 5G opportunistic network.
+         * If opportunistic network is determined as out of service or below
+         * {@link #KEY_EXIT_THRESHOLD_SS_RSRP_INT} or
+         * {@link #KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE} within
+         * the time specified by this carrier config of switching to opportunistic network,
+         * it will be determined as ping pong situation by system app or 1st party app.
+         *
+         * @hide
+         */
+        public static final String KEY_5G_PING_PONG_TIME_LONG =
+                PREFIX + "5g_ping_pong_time_long";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            // Default value is -111 dBm for all bands.
+            sDefaults.putInt(KEY_ENTRY_THRESHOLD_SS_RSRP_INT, -111);
+            sDefaults.putPersistableBundle(KEY_ENTRY_THRESHOLD_SS_RSRP_INT_BUNDLE,
+                                           PersistableBundle.EMPTY);
+            // Default value is -18.5 dB for all bands.
+            sDefaults.putDouble(KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE, -18.5);
+            sDefaults.putPersistableBundle(
+                    KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE,
+                    PersistableBundle.EMPTY);
+            // Default value is -120 dBm for all bands.
+            sDefaults.putInt(KEY_EXIT_THRESHOLD_SS_RSRP_INT, -120);
+            sDefaults.putPersistableBundle(KEY_EXIT_THRESHOLD_SS_RSRP_INT_BUNDLE,
+                                           PersistableBundle.EMPTY);
+            // Default value is -18.5 dB for all bands.
+            sDefaults.putDouble(KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE, -18.5);
+            sDefaults.putPersistableBundle(
+                    KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE,
+                    PersistableBundle.EMPTY);
+            // Default value is 2 seconds for all bands.
+            defaults.putLong(KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG, 2000);
+            defaults.putPersistableBundle(
+                    KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG_BUNDLE,
+                    PersistableBundle.EMPTY);
+            // Default value is 2 seconds for all bands.
+            defaults.putLong(KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000);
+            defaults.putPersistableBundle(
+                    KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG_BUNDLE,
+                    PersistableBundle.EMPTY);
+            // Default value is 10 seconds.
+            sDefaults.putLong(KEY_5G_BACKOFF_TIME_LONG, 10000);
+            // Default value is 60 seconds.
+            sDefaults.putLong(KEY_5G_MAX_BACKOFF_TIME_LONG, 60000);
+            // Default value is 60 seconds.
+            sDefaults.putLong(KEY_5G_PING_PONG_TIME_LONG, 60000);
+            return defaults;
+        }
+    }
+
+    /**
+     * Controls whether 4G opportunistic networks should be scanned for possible data switch.
+     *
+     * @hide
+     */
+    public static final String KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL =
+            "enabled_4g_opportunistic_network_scan_bool";
+
+    /**
+     * Only relevant when the device supports opportunistic networks but does not support
+     * simultaneous 5G+5G. Controls how long, in milliseconds, to wait before opportunistic network
+     * goes out of service before switching the 5G capability back to primary stack. The idea of
+     * waiting a few seconds is to minimize the calling of the expensive capability switching
+     * operation in the case where CBRS goes back into service shortly after going out of it.
+     *
+     * @hide
+     */
+    public static final String KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG =
+            "time_to_switch_back_to_primary_if_opportunistic_oos_long";
+
+    /**
+     * Only relevant when the device supports opportunistic networks but does not support
+     * simultaneous 5G+5G. Controls how long, in milliseconds, after 5G capability has switched back
+     * to primary stack due to opportunistic network being OOS. The idea is to minimizing the
+     * 'ping-ponging' effect where device is constantly witching capability back and forth between
+     * primary and opportunistic stack.
+     *
+     * @hide
+     */
+    public static final String
+            KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG =
+            "opportunistic_time_to_scan_after_capability_switch_to_primary_long";
+
+    /**
+     * Indicates zero or more emergency number prefix(es), because some carrier requires
+     * if users dial an emergency number address with a specific prefix, the combination of the
+     * prefix and the address is also a valid emergency number to dial. For example, an emergency
+     * number prefix is 318, and the emergency number is 911. Both 318911 and 911 can be dialed by
+     * users for emergency call. An empty array of string indicates that current carrier does not
+     * have this requirement.
+     */
+    public static final String KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY =
+            "emergency_number_prefix_string_array";
+
+    /**
+     * Indicates whether carrier treats "*67" or "*82" as a temporary mode CLIR.
+     * @hide
+     */
+    public static final String KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL =
+            "carrier_supports_caller_id_vertical_service_codes_bool";
+
+    /**
+     * Smart forwarding config. Smart forwarding is a feature to configure call forwarding to a
+     * different SIM in the device when one SIM is not reachable. The config here specifies a smart
+     * forwarding component that will launch UI for changing the configuration. An empty string
+     * indicates that no smart forwarding component is specified.
+     *
+     * Currently, only one non-empty configuration of smart forwarding component within system will
+     * be used when multiple SIMs are inserted.
+     *
+     * Empty string by default.
+     *
+     * @hide
+     */
+    public static final String KEY_SMART_FORWARDING_CONFIG_COMPONENT_NAME_STRING =
+            "smart_forwarding_config_component_name_string";
+
+    /**
+     * Indicates when a carrier has a primary subscription and an opportunistic subscription active,
+     * and when Internet data is switched to opportunistic network, whether to still show
+     * signal bar of primary network. By default it will be false, meaning whenever data
+     * is going over opportunistic network, signal bar will reflect signal strength and rat
+     * icon of that network.
+     */
+    public static final String KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN =
+            "always_show_primary_signal_bar_in_opportunistic_network_boolean";
+
+    /**
+     * Upon data switching between subscriptions within a carrier group, if switch depends on
+     * validation result, this value defines customized value of how long we wait for validation
+     * success before we fail and revoke the switch.
+     * Time out is in milliseconds.
+     */
+    public static final String KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG =
+            "data_switch_validation_timeout_long";
+
+    /**
+     * The minimum timeout of UDP port 4500 NAT / firewall entries on the Internet PDN of this
+     * carrier network. This will be used by Android platform VPNs to tune IPsec NAT keepalive
+     * interval. If this value is too low to provide uninterrupted inbound connectivity, then
+     * Android system VPNs may indicate to applications that the VPN cannot support long-lived
+     * TCP connections.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final String KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT =
+            "min_udp_port_4500_nat_timeout_sec_int";
+
+    /**
+     * The preferred IKE protocol for ESP packets.
+     *
+     * This will be used by Android platform VPNs to select preferred encapsulation type and IP
+     * protocol type. The possible customization values are:
+     *
+     * AUTO IP VERSION and ENCAPSULATION TYPE SELECTION : "0"
+     * IPv4 UDP                                         : "40"
+     * IPv6 ESP                                         : "61"
+     *
+     * See the {@code PREFERRED_IKE_PROTOCOL_} constants in
+     * {@link com.android.server.connectivity.Vpn}.
+     * @hide
+     */
+    public static final String KEY_PREFERRED_IKE_PROTOCOL_INT = "preferred_ike_protocol_int";
+
+    /**
+     * Specifies whether the system should prefix the EAP method to the anonymous identity.
+     * The following prefix will be added if this key is set to TRUE:
+     *   EAP-AKA: "0"
+     *   EAP-SIM: "1"
+     *   EAP-AKA_PRIME: "6"
+     */
+    public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool";
+
+    /**
+     * Indicates that GBA_ME should be used for GBA authentication, as defined in 3GPP TS 33.220.
+     * @hide
+     */
+    @SystemApi
+    public static final int GBA_ME = 1;
+
+    /**
+     * Indicates that GBA_U should be used for GBA authentication, as defined in 3GPP TS 33.220.
+     * @hide
+     */
+    @SystemApi
+    public static final int GBA_U = 2;
+
+    /**
+     * Indicates that GBA_Digest should be used for GBA authentication, as defined
+     * in 3GPP TS 33.220.
+     * @hide
+     */
+    @SystemApi
+    public static final int GBA_DIGEST = 3;
+
+    /**
+     * An integer representing the GBA mode to use for requesting credentials
+     * via {@link TelephonyManager#bootstrapAuthenticationRequest}.
+     *
+     * One of {@link #GBA_ME}, {@link #GBA_U}, or {@link #GBA_DIGEST}.
+     * @hide
+     */
+    @SystemApi
+    public static final String KEY_GBA_MODE_INT = "gba_mode_int";
+
+    /**
+     * An integer representing the organization code to be used when building the
+     * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication.
+     *
+     * See the {@code ORG_} constants in {@link UaSecurityProtocolIdentifier}.
+     * @hide
+     */
+    @SystemApi
+    public static final String KEY_GBA_UA_SECURITY_ORGANIZATION_INT =
+            "gba_ua_security_organization_int";
+
+    /**
+     * An integer representing the security protocol to be used when building the
+     * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication.
+     *
+     * See the {@code UA_SECURITY_PROTOCOL_} constants in {@link UaSecurityProtocolIdentifier}.
+     * @hide
+     */
+    @SystemApi
+    public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT = "gba_ua_security_protocol_int";
+
+    /**
+     * An integer representing the cipher suite to be used when building the
+     * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication.
+     *
+     * See the {@code TLS_} constants in {@link android.telephony.gba.TlsParams}.
+     * @hide
+     */
+    @SystemApi
+    public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT = "gba_ua_tls_cipher_suite_int";
+
+    /**
+     * The data stall recovery timers array in milliseconds, each element is the delay before
+     * performining next recovery action.
+     *
+     * The default value of timers array are: [180000ms, 180000ms, 180000ms, 180000ms] (3 minutes)
+     * Array[0]: It's the timer between RECOVERY_ACTION GET_DATA_CALL_LIST and CLEANUP, if data
+     * stall symptom still occurred, it will perform next recovery action after 180000ms.
+     * Array[1]: It's the timer between RECOVERY_ACTION CLEANUP and RE-REGISTER, if data stall
+     * symptom still occurred, it will perform next recovery action after 180000ms.
+     * Array[2]: It's the timer between RECOVERY_ACTION RE-REGISTER and RADIO_RESTART, if data stall
+     * symptom still occurred, it will perform next recovery action after 180000ms.
+     * Array[3]: It's the timer between RECOVERY_ACTION RADIO_RESTART and RESET_MODEM, if data stall
+     * symptom still occurred, it will perform next recovery action after 180000ms.
+     *
+     * See the {@code RECOVERY_ACTION_*} constants in
+     * {@link com.android.internal.telephony.data.DataStallRecoveryManager}
+     * @hide
+     */
+    public static final String KEY_DATA_STALL_RECOVERY_TIMERS_LONG_ARRAY =
+            "data_stall_recovery_timers_long_array";
+
+    /**
+     * The data stall recovery action boolean array, we use this array to determine if the
+     * data stall recovery action needs to be skipped.
+     *
+     * For example, if the carrier use the same APN for both of IA and default type,
+     * the data call will not disconnect in modem side (so the RECOVERY_ACTION_CLEANUP
+     * did not effect). In this case, we can config the boolean variable of action
+     * RECOVERY_ACTION_CLEANUP to true, then it can be ignored to speed up the recovery
+     * action procedure.
+     *
+     * The default value of boolean array are: [false, false, true, false, false]
+     * Array[0]: When performing the recovery action, we can use this boolean value to determine
+     * if we need to perform RECOVERY_ACTION_GET_DATA_CALL_LIST.
+     * Array[1]: If data stall symptom still occurred, we can use this boolean value to determine
+     * if we need to perform RECOVERY_ACTION_CLEANUP. For example, if the carrier use the same APN
+     * for both of IA and default type, the data call will not disconnect in modem side
+     * (so the RECOVERY_ACTION_CLEANUP did not effect). In this case, we can config the boolean
+     * variable of action RECOVERY_ACTION_CLEANUP to true, then it can be ignored to speed up the
+     * recovery action procedure.
+     * Array[2]: If data stall symptom still occurred, we can use this boolean value to determine
+     * if we need to perform RE-REGISTER.
+     * Array[3]: If data stall symptom still occurred, we can use this boolean value to determine
+     * if we need to perform RECOVERY_ACTION_RADIO_RESTART.
+     * Array[4]: If data stall symptom still occurred, we can use this boolean value to determine
+     * if we need to perform RECOVERY_ACTION_MODEM_RESET.
+     *
+     * See the {@code RECOVERY_ACTION_*} constants in
+     * {@link com.android.internal.telephony.data.DataStallRecoveryManager}
+     * @hide
+     */
+    public static final String KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY =
+            "data_stall_recovery_should_skip_bool_array";
+
+    /**
+     * String array containing the list of names for service numbers provided by carriers. This key
+     * should be used with {@link #KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY}. The names provided in
+     * this array will be mapped 1:1 with the numbers provided in the {@link
+     * #KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY} array.
+     *
+     * <p>The data would be considered valid if and only if:
+     *
+     * <ul>
+     *   <li>The number of items in both the arrays are equal
+     *   <li>The data added to the {@link #KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY} array is valid.
+     *       See {@link #KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY} for more information.
+     * </ul>
+     *
+     * <p>Example:
+     *
+     * <pre>{@code
+     * <string-array name="carrier_service_name_array" num="2">
+     *   <item value="Police"/>
+     *   <item value="Ambulance"/>
+     * </string-array>
+     * }</pre>
+     */
+    public static final String KEY_CARRIER_SERVICE_NAME_STRING_ARRAY = "carrier_service_name_array";
+
+    /**
+     * String array containing the list of service numbers provided by carriers. This key should be
+     * used with {@link #KEY_CARRIER_SERVICE_NAME_STRING_ARRAY}. The numbers provided in this array
+     * will be mapped 1:1 with the names provided in the {@link
+     * #KEY_CARRIER_SERVICE_NAME_STRING_ARRAY} array.
+     *
+     * <p>The data would be considered valid if and only if:
+     *
+     * <ul>
+     *   <li>The number of items in both the arrays are equal
+     *   <li>The item should contain dialable characters only which includes 0-9, -, *, #, (, ),
+     *       SPACE.
+     * </ul>
+     *
+     * <p>Example:
+     *
+     * <pre>{@code
+     * <string-array name="carrier_service_number_array" num="2">
+     *   <item value="*123"/>
+     *   <item value="+ (111) 111-111"/>
+     * </string-array>
+     * }</pre>
+     */
+    public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY =
+        "carrier_service_number_array";
+
+    /**
+     * Configs used by ImsServiceEntitlement.
+     */
+    public static final class ImsServiceEntitlement {
+        private ImsServiceEntitlement() {}
+
+        /** Prefix of all ImsServiceEntitlement.KEY_* constants. */
+        public static final String KEY_PREFIX = "imsserviceentitlement.";
+
+        /**
+         * The address of the entitlement configuration server.
+         *
+         * Reference: GSMA TS.43-v5, section 2.1 Default Entitlement Configuration Server.
+         */
+        public static final String KEY_ENTITLEMENT_SERVER_URL_STRING =
+                KEY_PREFIX + "entitlement_server_url_string";
+
+        /**
+         * For some carriers, end-users may be presented with a web portal of the carrier before
+         * being allowed to use the VoWiFi service.
+         * To support this feature, the app hosts a {@link android.webkit.WebView} in the foreground
+         * VoWiFi entitlement configuration flow to show the web portal.
+         *
+         * {@code true} - show the VoWiFi portal in a webview.
+         *
+         * Note: this is effective only if the {@link #KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING}
+         * is set to this app.
+         *
+         * Reference: GSMA TS.43-v5, section 3, VoWiFi entitlement configuration.
+         */
+        public static final String KEY_SHOW_VOWIFI_WEBVIEW_BOOL =
+                KEY_PREFIX + "show_vowifi_webview_bool";
+
+        /**
+         * For some carriers, the network is not provisioned by default to support
+         * IMS (VoLTE/VoWiFi/SMSoIP) service for all end users. Some type of network-side
+         * provisioning must then take place before offering the IMS service to the end-user.
+         *
+         * {@code true} - need this ImsServiceEntitlement app to do IMS (VoLTE/VoWiFi/SMSoIP)
+         * provisioning in the background before offering the IMS service to the end-user.
+         *
+         * Note: this is effective only if the carrier needs IMS provisioning, i.e.
+         * {@link #KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL} is set to true.
+         *
+         * Reference: GSMA TS.43-v5, section 3 - 5, VoWiFi/VoLTE/SMSoIP entitlement configuration.
+         */
+        public static final String KEY_IMS_PROVISIONING_BOOL = KEY_PREFIX + "ims_provisioning_bool";
+
+        /**
+         * The FCM sender ID for the carrier.
+         * Used to trigger a carrier network requested entitlement configuration
+         * via Firebase Cloud Messaging (FCM). Do not set if the carrier doesn't use FCM for network
+         * requested entitlement configuration.
+         *
+         * Reference: GSMA TS.43-v5, section 2.4, Network Requested Entitlement Configuration.
+         *
+         * @see <a href="https://firebase.google.com/docs/cloud-messaging/concept-options#senderid">
+         *     About FCM messages - Credentials</a>
+         */
+        public static final String KEY_FCM_SENDER_ID_STRING = KEY_PREFIX + "fcm_sender_id_string";
+
+        /**
+         * Indicates the supported protocol version in the parameter entitlement_version.
+         * The default value is 2. The possible value is 2 and 8.
+         *
+         * Reference: GSMA TS.43-v8 section 2.5 Protocol version control and
+         * Table 3. GET Parameters for Entitlement Configuration in section 2.3
+         * HTTP GET method Parameters.
+         * @hide
+         */
+        public static final String KEY_ENTITLEMENT_VERSION_INT =
+                KEY_PREFIX + "entitlement_version_int";
+
+        /**
+         * Controls the service entitlement status when receiving the VERS characteristic
+         * with both version and validity set to -1 or -2.
+         * If {@code true}, default service entitlement status is enabled.
+         * If {@code false}, default service entitlement status is disabled.
+         *
+         * Reference: GSMA TS.14-v8 section 2.1, overview
+         * @hide
+         */
+        public static final String KEY_DEFAULT_SERVICE_ENTITLEMENT_STATUS_BOOL =
+                KEY_PREFIX + "default_service_entitlement_status_bool";
+
+        /**
+         * Indicates if UE can skip service entitlement check when the user turns on Wi-Fi Calling.
+         * UE still shows Wi-Fi Calling emergency address update web view when the user clicks
+         * "Update Emergency Address" on the WiFi calling setting.
+         *
+         * Note: this is effective only if the {@link #KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING}
+         * is set to this app.
+         * @hide
+         */
+        public static final String KEY_SKIP_WFC_ACTIVATION_BOOL =
+                KEY_PREFIX + "skip_wfc_activation_bool";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putString(KEY_ENTITLEMENT_SERVER_URL_STRING, "");
+            defaults.putString(KEY_FCM_SENDER_ID_STRING, "");
+            defaults.putBoolean(KEY_SHOW_VOWIFI_WEBVIEW_BOOL, false);
+            defaults.putBoolean(KEY_IMS_PROVISIONING_BOOL, false);
+            defaults.putBoolean(KEY_DEFAULT_SERVICE_ENTITLEMENT_STATUS_BOOL, false);
+            defaults.putBoolean(KEY_SKIP_WFC_ACTIVATION_BOOL, false);
+            defaults.putInt(KEY_ENTITLEMENT_VERSION_INT, 2);
+            return defaults;
+        }
+    }
+
+    /**
+     * GPS configs. See the GNSS HAL documentation for more details.
+     */
+    public static final class Gps {
+        private Gps() {}
+
+        /** Prefix of all Gps.KEY_* constants. */
+        public static final String KEY_PREFIX = "gps.";
+
+        /**
+         * Location information during (and after) an emergency call is only provided over control
+         * plane signaling from the network.
+         * @hide
+         */
+        public static final int SUPL_EMERGENCY_MODE_TYPE_CP_ONLY = 0;
+
+        /**
+         * Location information during (and after) an emergency call is provided over the data
+         * plane and serviced by the framework GNSS service, but if it fails, the carrier also
+         * supports control plane backup signaling.
+         * @hide
+         */
+        public static final int SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK = 1;
+
+        /**
+         * Location information during (and after) an emergency call is provided over the data plane
+         * and serviced by the framework GNSS service only. There is no backup signalling over the
+         * control plane if it fails.
+         * @hide
+         */
+        public static final int SUPL_EMERGENCY_MODE_TYPE_DP_ONLY = 2;
+
+        /**
+         * Determine whether current lpp_mode used for E-911 needs to be kept persistently.
+         * {@code false} - not keeping the lpp_mode means using default configuration of gps.conf
+         *                 when sim is not presented.
+         * {@code true}  - current lpp_profile of carrier will be kepted persistently
+         *                 even after sim is removed. This is default.
+         */
+        public static final String KEY_PERSIST_LPP_MODE_BOOL = KEY_PREFIX + "persist_lpp_mode_bool";
+
+        /**
+         * SUPL server host for SET Initiated & non-ES Network-Initiated SUPL requests.
+         * Default to supl.google.com
+         * @hide
+         */
+        public static final String KEY_SUPL_HOST_STRING = KEY_PREFIX + "supl_host";
+
+        /**
+         * SUPL server port. Default to 7275.
+         * @hide
+         */
+        public static final String KEY_SUPL_PORT_STRING = KEY_PREFIX + "supl_port";
+
+        /**
+         * The SUPL version requested by Carrier. This is a bit mask
+         * with bits 0:7 representing a service indicator field, bits 8:15
+         * representing the minor version and bits 16:23 representing the
+         * major version. Default to 0x20000.
+         * @hide
+         */
+        public static final String KEY_SUPL_VER_STRING = KEY_PREFIX + "supl_ver";
+
+        /**
+         * SUPL_MODE configuration bit mask
+         * 1 - Mobile Station Based. This is default.
+         * 2 - Mobile Station Assisted.
+         * @hide
+         */
+        public static final String KEY_SUPL_MODE_STRING = KEY_PREFIX + "supl_mode";
+
+        /**
+         * Whether to limit responses to SUPL ES mode requests only during user emergency sessions
+         * (e.g. E911), and SUPL non-ES requests to only outside of non user emergency sessions.
+         * 0 - no.
+         * 1 - yes. This is default.
+         * @hide
+         */
+        public static final String KEY_SUPL_ES_STRING = KEY_PREFIX + "supl_es";
+
+        /**
+         * LTE Positioning Profile settings bit mask.
+         * 0 - Radio Resource Location Protocol in user plane and control plane. This is default.
+         * 1 - Enable LTE Positioning Protocol in user plane.
+         * 2 - Enable LTE Positioning Protocol in control plane.
+         * @hide
+         */
+        public static final String KEY_LPP_PROFILE_STRING = KEY_PREFIX + "lpp_profile";
+
+        /**
+         * Determine whether to use emergency PDN for emergency SUPL.
+         * 0 - no.
+         * 1 - yes. This is default.
+         * @hide
+         */
+        public static final String KEY_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL_STRING =
+                KEY_PREFIX + "use_emergency_pdn_for_emergency_supl";
+
+        /**
+         * A_GLONASS_POS_PROTOCOL_SELECT bit mask.
+         * 0 - Don't use A-GLONASS. This is default.
+         * 1 - Use A-GLONASS in Radio Resource Control(RRC) control-plane.
+         * 2 - Use A-GLONASS in Radio Resource Location user-plane.
+         * 4 - Use A-GLONASS in LTE Positioning Protocol User plane.
+         * @hide
+         */
+        public static final String KEY_A_GLONASS_POS_PROTOCOL_SELECT_STRING =
+                KEY_PREFIX + "a_glonass_pos_protocol_select";
+
+        /**
+         * GPS_LOCK configuration bit mask to specify GPS device behavior toward other services,
+         * when Location Settings are off.
+         * "0" - No lock.
+         * "1" - Lock Mobile Originated GPS functionalities.
+         * "2" - Lock Network initiated GPS functionalities.
+         * "3" - Lock both. This is default.
+         * @hide
+         */
+        public static final String KEY_GPS_LOCK_STRING = KEY_PREFIX + "gps_lock";
+
+        /**
+         * Control Plane / SUPL NI emergency extension time in seconds. Default to "0".
+         * @hide
+         */
+        public static final String KEY_ES_EXTENSION_SEC_STRING = KEY_PREFIX + "es_extension_sec";
+
+        /**
+         * Space separated list of Android package names of proxy applications representing
+         * the non-framework entities requesting location directly from GNSS without involving
+         * the framework, as managed by IGnssVisibilityControl.hal. For example,
+         * "com.example.mdt com.example.ims".
+         * @hide
+         */
+        public static final String KEY_NFW_PROXY_APPS_STRING = KEY_PREFIX + "nfw_proxy_apps";
+
+        /**
+         * Determines whether or not SUPL ES mode supports a control-plane mechanism to get a user's
+         * location in the event that data plane SUPL fails or is otherwise unavailable.
+         * <p>
+         * An integer value determines the support type of this carrier. If this carrier only
+         * supports data plane SUPL ES, then the value will be
+         * {@link #SUPL_EMERGENCY_MODE_TYPE_DP_ONLY}. If the carrier supports control plane fallback
+         * for emergency SUPL, the value will be {@link #SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK}.
+         * If the carrier does not support data plane SUPL using the framework, the value will be
+         * {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
+         * <p>
+         * The default value for this configuration is {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
+         * @hide
+         */
+        public static final String KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT =
+                KEY_PREFIX + "es_supl_control_plane_support_int";
+
+        /**
+         * A list of roaming PLMNs where SUPL ES mode does not support a control-plane mechanism to
+         * get a user's location in the event that data plane SUPL fails or is otherwise
+         * unavailable.
+         * <p>
+         * A string array of PLMNs that do not support a control-plane mechanism for getting a
+         * user's location for SUPL ES.
+         * @hide
+         */
+        public static final String KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY =
+                KEY_PREFIX + "es_supl_data_plane_only_roaming_plmn_string_array";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
+            defaults.putString(KEY_SUPL_HOST_STRING, "supl.google.com");
+            defaults.putString(KEY_SUPL_PORT_STRING, "7275");
+            defaults.putString(KEY_SUPL_VER_STRING, "0x20000");
+            defaults.putString(KEY_SUPL_MODE_STRING, "1");
+            defaults.putString(KEY_SUPL_ES_STRING, "1");
+            defaults.putString(KEY_LPP_PROFILE_STRING, "2");
+            defaults.putString(KEY_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL_STRING, "1");
+            defaults.putString(KEY_A_GLONASS_POS_PROTOCOL_SELECT_STRING, "0");
+            defaults.putString(KEY_GPS_LOCK_STRING, "3");
+            defaults.putString(KEY_ES_EXTENSION_SEC_STRING, "0");
+            defaults.putString(KEY_NFW_PROXY_APPS_STRING, "");
+            defaults.putInt(KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                    SUPL_EMERGENCY_MODE_TYPE_CP_ONLY);
+            defaults.putStringArray(KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY, null);
+            return defaults;
+        }
+    }
+
+    /**
+     * An int array containing CDMA enhanced roaming indicator values for Home (non-roaming)
+     * network.
+     * The default values come from 3GPP2 C.R1001 table 8.1-1.
+     * Enhanced Roaming Indicator Number Assignments
+     *
+     * @hide
+     */
+    public static final String KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY =
+            "cdma_enhanced_roaming_indicator_for_home_network_int_array";
+
+    /**
+     * Determines whether wifi calling location privacy policy is shown.
+     */
+    public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL =
+            "show_wfc_location_privacy_policy_bool";
+
+    /**
+     * Indicates use 3GPP application to replace 3GPP2 application even if it's a CDMA/CDMA-LTE
+     * phone, because some carriers' CSIM application is present but not supported.
+     * @hide
+     */
+    public static final String KEY_USE_USIM_BOOL = "use_usim_bool";
+
+    /**
+     * Determines whether the carrier wants to cancel the cs reject notification automatically
+     * when the voice registration state changes.
+     * If true, the notification will be automatically removed
+     *          when the voice registration state changes.
+     * If false, the notification will persist until the user dismisses it,
+     *           the SIM is removed, or the device is rebooted.
+     * @hide
+     */
+    public static final String KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION =
+            "carrier_auto_cancel_cs_notification";
+
+    /**
+     * Passing this value as {@link #KEY_SUBSCRIPTION_GROUP_UUID_STRING} will remove the
+     * subscription from a group instead of adding it to a group.
+     *
+     * <p>This value will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
+     */
+    public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000";
+
+    /**
+     * The UUID of a Group of related subscriptions in which to place the current subscription.
+     *
+     * A grouped subscription will behave for billing purposes and other UI purposes as though it
+     * is a transparent extension of other subscriptions in the group.
+     *
+     * <p>If set to {@link #REMOVE_GROUP_UUID_STRING}, then the subscription will be removed from
+     * its current group.
+     *
+     * <p>This key will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
+     */
+    public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING =
+            "subscription_group_uuid_string";
+
+    /**
+     * Controls the cellular usage setting.
+     *
+     * The usage setting indicates whether a device will remain attached to a network based on
+     * the primary use case for the service. A device will detach and search for a more-preferred
+     * network if the primary use case (voice or data) is not satisfied. Depending on the type
+     * of device, it may operate in a voice or data-centric mode by default.
+     *
+     * <p>Sets the usage setting in accordance with 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3.
+     * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221
+     * Annex A.
+     *
+     * Either omit this key or pass a value of
+     * {@link SubscriptionManager#USAGE_SETTING_UNKNOWN unknown} to preserve the current setting.
+     *
+     * <p>Devices that support configuration of the cellular usage setting, including devices
+     * with HAL capability to set the cellular usage setting, must honor this setting accordingly.
+     *
+     * {@link SubscriptionManager#USAGE_SETTING_DEFAULT default},
+     * {@link SubscriptionManager#USAGE_SETTING_VOICE_CENTRIC voice-centric},
+     * or {@link SubscriptionManager#USAGE_SETTING_DATA_CENTRIC data-centric}.
+     * {@see SubscriptionInfo#getUsageSetting}
+     *
+     */
+    public static final String KEY_CELLULAR_USAGE_SETTING_INT = "cellular_usage_setting_int";
+
+    /**
+     * Data switch validation minimal gap time, in milliseconds.
+     *
+     * Which means, if the same subscription on the same network (based on MCC+MNC+TAC+subId)
+     * was recently validated (within this time gap), and Telephony receives a request to switch to
+     * it again, Telephony will skip the validation part and switch to it as soon as connection
+     * is setup, as if it's already validated.
+     *
+     * If the network was validated within the gap but the latest validation result is false, the
+     * validation will not be skipped.
+     *
+     * If not set or set to 0, validation will never be skipped.
+     * The max acceptable value of this config is 24 hours.
+     *
+     * @hide
+     * @deprecated Use {@link #KEY_DATA_SWITCH_VALIDATION_MIN_INTERVAL_MILLIS_LONG} instead.
+     */
+    @Deprecated
+    public static final String KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG =
+            "data_switch_validation_min_gap_long";
+
+    /**
+     * Data switch validation minimal interval, in milliseconds.
+     *
+     * If a connection to the default (Internet) PDN for the current subscription is validated on
+     * a given operator within a given tracking area, re-validations to that matching operator will
+     * be skipped if they would occur within the specified interval. Instead, the connection will
+     * automatically considered validated.
+     *
+     * If the network was validated within the interval but the latest validation result was false,
+     * the validation will not be skipped. If not set or set to 0, validation will not be skipped.
+     *
+     * The valid range of value is between 0 millisecond and 24 hours, inclusive in both sides. The
+     * default value is 24 hours.
+     *
+     * @see android.net.NetworkCapabilities#NET_CAPABILITY_VALIDATED
+     */
+    public static final String KEY_DATA_SWITCH_VALIDATION_MIN_INTERVAL_MILLIS_LONG =
+            KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG;
+
+    /**
+     * A boolean property indicating whether this subscription should be managed as an opportunistic
+     * subscription.
+     *
+     * If true, then this subscription will be selected based on available coverage and will not be
+     * available for a user in settings menus for selecting macro network providers. If unset,
+     * defaults to “false”.
+     *
+     * <p>This key will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
+     */
+    public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL =
+            "is_opportunistic_subscription_bool";
+
+    /**
+     * The flatten string {@link android.content.ComponentName componentName} of carrier
+     * provisioning app receiver.
+     *
+     * <p>
+     * The RadioInfo activity(*#*#INFO#*#*) will broadcast an intent to this receiver when the
+     * "Carrier Provisioning Info" or "Trigger Carrier Provisioning" button clicked.
+     *
+     * <p>
+     * e.g., com.google.android.carrierPackageName/.CarrierReceiverName
+     *
+     * @hide
+     */
+    public static final String KEY_CARRIER_PROVISIONING_APP_STRING =
+            "carrier_provisioning_app_string";
+
+    /**
+     * Configs used by the IMS stack.
+     */
+    public static final class Ims {
+        /** Prefix of all Ims.KEY_* constants. */
+        public static final String KEY_PREFIX = "ims.";
+
+        /**
+         * Delay in milliseconds to turn off wifi when IMS is registered over wifi.
+         */
+        public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT =
+                KEY_PREFIX + "wifi_off_deferring_time_millis_int";
+
+        /**
+         * A boolean flag specifying whether or not this carrier requires one IMS registration for
+         * all IMS services (MMTEL and RCS).
+         * <p>
+         * If set to {@code true}, the IMS Service must use one IMS registration for all IMS
+         * services. If set to {@code false}, IMS services may use separate IMS registrations for
+         * MMTEL and RCS.
+         * <p>
+         * The default value for this configuration is {@code false}.
+         */
+        public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL =
+                KEY_PREFIX + "ims_single_registration_required_bool";
+
+        /**
+         * A boolean flag specifying whether or not this carrier supports the device notifying the
+         * network of its RCS capabilities using the SIP PUBLISH procedure defined for User
+         * Capability Exchange (UCE). See RCC.71, section 3 for more information.
+         * <p>
+         * If this key's value is set to false, the procedure for RCS contact capability exchange
+         * via SIP SUBSCRIBE/NOTIFY will also be disabled internally, and this key must also be set
+         * to false to ensure apps do not improperly think that capability exchange via SIP PUBLISH
+         * is enabled.
+         * <p> The default value for this key is {@code false}.
+         */
+        public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL =
+                KEY_PREFIX + "enable_presence_publish_bool";
+
+        /**
+         * Each string in this array contains a mapping between the service-id and version portion
+         * of the service-description element and the associated IMS feature tag(s) that are
+         * associated with each element (see RCC.07 Table 7).
+         * <p>
+         * Each string contains 3 parts, which define the mapping between service-description and
+         * feature tag(s) that must be present in the IMS REGISTER for the RCS service to be
+         * published as part of the RCS PUBLISH procedure:
+         * [service-id]|[version]|[desc]|[feature_tag];[feature_tag];...
+         * <ul>
+         *   <li>[service-id]: the service-id element associated with the RCS capability.</li>
+         *   <li>[version]: The version element associated with that service-id</li>
+         *   <li>[desc]: The optional desecription element associated with that service-id</li>
+         *   <li>[feature_tag];[feature_tag]: The list of all feature tags associated with this
+         *       capability that MUST ALL be present in the IMS registration for this this
+         *       capability to be published to the network.</li>
+         * </ul>
+         * <p>
+         * Features managed by the framework will be considered capable when the ImsService reports
+         * that those services are capable via the
+         * {@link MmTelFeature#notifyCapabilitiesStatusChanged(MmTelFeature.MmTelCapabilities)} or
+         * {@link RcsFeature#notifyCapabilitiesStatusChanged(RcsFeature.RcsImsCapabilities)} APIs.
+         * For RCS services not managed by the framework, the capability of these services are
+         * determined by looking at the feature tags associated with the IMS registration using the
+         * {@link ImsRegistrationAttributes} API and mapping them to the service-description map.
+         * <p>
+         * The framework contains a default value of this key, which is based off of RCC.07
+         * specification. Capabilities based of carrier extensions may be added to this list on a
+         * carrier-by-carrier basis as required in order to support additional services in the
+         * PUBLISH. If this list contains a service-id and version that overlaps with the default,
+         * it will override the framework default.
+         * @hide
+         */
+        public static final String KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY =
+                KEY_PREFIX + "publish_service_desc_feature_tag_map_override_string_array";
+
+        /**
+         * Flag indicating whether or not this carrier supports the exchange of phone numbers with
+         * the carrier's RCS presence server in order to retrieve the RCS capabilities of requested
+         * contacts used in the RCS User Capability Exchange (UCE) procedure. See RCC.71, section 3
+         * for more information.
+         * <p>
+         * When presence is supported, the device uses the SIP SUBSCRIBE/NOTIFY procedure internally
+         * to retrieve the requested RCS capabilities. See
+         * {@link android.telephony.ims.RcsUceAdapter} for more information on how RCS capabilities
+         * can be retrieved from the carrier's network.
+         */
+        public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL =
+                KEY_PREFIX + "enable_presence_capability_exchange_bool";
+
+        /**
+         * Flag indicating whether or not the carrier expects the RCS UCE service to periodically
+         * refresh the RCS capabilities cache of the user's contacts as well as request the
+         * capabilities of call contacts when the SIM card is first inserted or when a new contact
+         * is added, removed, or modified. This corresponds to the RCC.07 A.19
+         * "DISABLE INITIAL ADDRESS BOOK SCAN" parameter.
+         * <p>
+         * If this flag is disabled, the capabilities cache will not be refreshed internally at all
+         * and will only be updated if the cached capabilities are stale when an application
+         * requests them.
+         *
+         * @see RcsUceAdapter#isUceSettingEnabled() more information about this feature and how
+         * it is enabled by the user.
+         */
+        public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL =
+                KEY_PREFIX + "rcs_bulk_capability_exchange_bool";
+
+        /**
+         * Flag indicating whether or not the carrier supports capability exchange with a list of
+         * contacts. When {@code true}, the device will batch together multiple requests and
+         * construct a RLMI document in the SIP SUBSCRIBE request (see RFC 4662). If {@code false},
+         * the request will be split up into one SIP SUBSCRIBE request per contact.
+         */
+        public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL =
+                KEY_PREFIX + "enable_presence_group_subscribe_bool";
+
+        /**
+         * SIP SUBSCRIBE retry duration used when device doesn't receive a response to SIP
+         * SUBSCRIBE request.
+         * If this value is not defined or defined as negative value, the device does not retry
+         * the SIP SUBSCRIBE.
+         * If the value is 0 then device retries immediately upon timeout.
+         * If the value is > 0 then device waits for configured duration and retries after timeout
+         * is detected
+         * @hide
+         */
+        public static final String KEY_SUBSCRIBE_RETRY_DURATION_MILLIS_LONG =
+                KEY_PREFIX + "subscribe_retry_duration_millis_long";
+
+        /**
+         * Flag indicating whether or not to use SIP URI when send a presence subscribe.
+         * When {@code true}, the device sets the To and Contact header to be SIP URI using
+         * the TelephonyManager#getIsimDomain" API.
+         * If {@code false}, the device uses a TEL URI.
+         */
+        public static final String KEY_USE_SIP_URI_FOR_PRESENCE_SUBSCRIBE_BOOL =
+                KEY_PREFIX + "use_sip_uri_for_presence_subscribe_bool";
+
+        /**
+         * Flag indicating whether or not to use TEL URI when setting the entity uri field and
+         * contact element of each tuple.
+         *
+         * When {@code true}, the device sets the entity uri field and contact element to be
+         * TEL URI. This is done by first searching for the first TEL URI provided in
+         * p-associated-uri header. If there are no TEL URIs in the p-associated-uri header, we will
+         * convert the first SIP URI provided in the header to a TEL URI. If there are no URIs in
+         * the p-associated-uri header, we will then fall back to using the SIM card to generate the
+         * TEL URI.
+         * If {@code false}, the first URI provided in the p-associated-uri header is used,
+         * independent of the URI scheme. If there are no URIs available from p-associated-uri
+         * header, we will try to generate a SIP URI or TEL URI from the information provided by the
+         * SIM card, depending on the information available.
+         * @hide
+         */
+        public static final String KEY_USE_TEL_URI_FOR_PIDF_XML_BOOL =
+                KEY_PREFIX + "use_tel_uri_for_pidf_xml";
+
+        /**
+         * An integer key associated with the period of time in seconds the non-rcs capability
+         * information of each contact is cached on the device.
+         * <p>
+         * The rcs capability cache expiration sec is managed by
+         * {@code android.telephony.ims.ProvisioningManager} but non-rcs capability is managed by
+         * {@link CarrierConfigManager} since non-rcs capability will be provided via ACS or carrier
+         * config.
+         * <p>
+         * The default value is 2592000 secs (30 days), see RCC.07 Annex A.1.9.
+         */
+        public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT =
+                KEY_PREFIX + "non_rcs_capabilities_cache_expiration_sec_int";
+
+        /**
+         * Specifies the RCS feature tag allowed for the carrier.
+         *
+         * <p>The values refer to RCC.07 2.4.4.
+         */
+        public static final String KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY =
+                KEY_PREFIX + "rcs_feature_tag_allowed_string_array";
+
+        /**
+         * Flag indicating whether or not carrier forbids device send the RCS request when the
+         * device receive the network response with the SIP code 489 BAD EVENT.
+         * <p>
+         * The default value for this key is {@code false}.
+         * @hide
+         */
+        public static final String KEY_RCS_REQUEST_FORBIDDEN_BY_SIP_489_BOOL =
+                KEY_PREFIX + "rcs_request_forbidden_by_sip_489_bool";
+
+        /**
+         * Indicates the interval that SUBSCRIBE requests from applications will be retried at when
+         * the carrier network has responded to a previous request with a forbidden error.
+         * <p>
+         * The default value for this key is 20 minutes.
+         * @hide
+         */
+        public static final String KEY_RCS_REQUEST_RETRY_INTERVAL_MILLIS_LONG =
+                KEY_PREFIX + "rcs_request_retry_interval_millis_long";
+
+        /** SIP timer T1 as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_T1_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_t1_millis_int";
+
+        /** SIP timer T2 as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_T2_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_t2_millis_int";
+
+        /** SIP timer T4 as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_T4_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_t4_millis_int";
+
+        /** SIP timer B as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_B_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_b_millis_int";
+
+        /** SIP timer C as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_C_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_c_millis_int";
+
+        /** SIP timer D as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_D_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_d_millis_int";
+
+        /** SIP timer F as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_F_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_f_millis_int";
+
+        /** SIP timer H as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_H_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_h_millis_int";
+
+        /** SIP timer J as per 3GPP TS 24.229 Table 7.7.1 */
+        public static final String KEY_SIP_TIMER_J_MILLIS_INT =
+                KEY_PREFIX + "sip_timer_j_millis_int";
+
+        /** Specifies the SIP Server default port. */
+        public static final String KEY_SIP_SERVER_PORT_NUMBER_INT =
+                KEY_PREFIX + "sip_server_port_number_int";
+
+        /**
+         * Specify the “phone-context” parameter as defined in
+         * section 7.2A.10 in 3GPP TS 24.229.
+         */
+        public static final String KEY_PHONE_CONTEXT_DOMAIN_NAME_STRING =
+                KEY_PREFIX + "phone_context_domain_name_string";
+
+        /** @hide */
+        @IntDef({REQUEST_URI_FORMAT_TEL, REQUEST_URI_FORMAT_SIP})
+        public @interface RequestUriFormatType {}
+
+        /**
+         *  Request URI is of type TEL URI.
+         */
+        public static final int REQUEST_URI_FORMAT_TEL = 0;
+
+        /**
+         *  Request URI is of type SIP URI.
+         */
+        public static final int REQUEST_URI_FORMAT_SIP = 1;
+
+        /**
+         * Specify whether the request URI is SIP URI
+         * {@link #REQUEST_URI_FORMAT_SIP} or
+         * TEL URI {@link #REQUEST_URI_FORMAT_TEL}.
+         */
+        public static final String KEY_REQUEST_URI_TYPE_INT =
+                KEY_PREFIX + "request_uri_type_int";
+
+        /**
+         * Flag indicating whether Globally Routable User agent (GRUU)
+         * in supported HEADER is included or not.
+         *
+         * <p> Reference: RFC 5627.
+         */
+        public static final String KEY_GRUU_ENABLED_BOOL =
+                KEY_PREFIX + "gruu_enabled_bool";
+
+        /**
+         * Flag indicating whether to keep/release IMS PDN in case of
+         * moving to non VOPS area.
+         *
+         * <p>if {@code True}, keep IMS PDN in case of moving to non VOPS area.
+         * if {@code false}, otherwise.
+         */
+        public static final String KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL =
+                KEY_PREFIX + "keep_pdn_up_in_no_vops_bool";
+
+        /** @hide */
+        @IntDef({
+            PREFERRED_TRANSPORT_UDP,
+            PREFERRED_TRANSPORT_TCP,
+            PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP,
+            PREFERRED_TRANSPORT_TLS
+        })
+        public @interface PreferredTransportType {}
+
+        /** Preferred Transport is always UDP. */
+        public static final int PREFERRED_TRANSPORT_UDP = 0;
+
+        /** Preferred Transport is always TCP. */
+        public static final int PREFERRED_TRANSPORT_TCP = 1;
+
+        /**
+         *  Preferred Transport is both UDP and TCP and selected based
+         *  on MTU size specified in {@link #KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT}
+         *  and {@link #KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT}.
+         *
+         *  <p>Default transport is UDP. If message size is larger
+         *  than MTU, then TCP shall be used.
+         */
+        public static final int PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP = 2;
+
+        /** Preferred Transport is TLS. */
+        public static final int PREFERRED_TRANSPORT_TLS = 3;
+
+        /**
+         * Specify the preferred transport protocol for SIP messages.
+         *
+         * <p>Possible values are,
+         * {@link #PREFERRED_TRANSPORT_UDP},
+         * {@link #PREFERRED_TRANSPORT_TCP},
+         * {@link #PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP}
+         */
+        public static final String KEY_SIP_PREFERRED_TRANSPORT_INT =
+                KEY_PREFIX + "sip_preferred_transport_int";
+
+        /**
+         * Specify the maximum IPV4 MTU size of SIP message on Cellular.
+         *
+         * <p>If {@link #KEY_SIP_PREFERRED_TRANSPORT_INT} is
+         * {@link #PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP} and SIP message MTU size
+         * is more than this value, then SIP transport will be TCP, else the
+         * SIP transport is UDP.
+         */
+        public static final String KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT =
+                KEY_PREFIX + "ipv4_sip_mtu_size_cellular_int";
+
+        /**
+         * Specify the maximum IPV6 MTU size of SIP message on Cellular.
+         *
+         * <p>If {@link #KEY_SIP_PREFERRED_TRANSPORT_INT} is
+         * {@link #PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP} and SIP message MTU size
+         * is more than this value, then SIP transport will be TCP, else the
+         * SIP transport is UDP.
+         */
+        public static final String KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT =
+                KEY_PREFIX + "ipv6_sip_mtu_size_cellular_int";
+
+        /**
+         * This config determines whether IMS PDN needs to be enabled
+         * when VOPS support is not available in both home and roaming scenarios.
+         *
+         * <p>This is applicable before IMS PDN is up, to decide whether
+         * IMS PDN needs to be enabled based on VOPS support in home/roaming.
+         *
+         * <p>Possible values are,
+         * {@link #NETWORK_TYPE_HOME},
+         * {@link #NETWORK_TYPE_ROAMING}
+         * An empty array indicates IMS PDN depends on VOPS on both home
+         * and roaming scenarios.
+         */
+        public static final String KEY_IMS_PDN_ENABLED_IN_NO_VOPS_SUPPORT_INT_ARRAY =
+                KEY_PREFIX + "ims_pdn_enabled_in_no_vops_support_int_array";
+
+        /**
+         * Flag indicating whether IPSec enabled for SIP messages.
+         *
+         * <p> Reference: 3GPP TS 33.203 and RFC 3329.
+         */
+        public static final String KEY_SIP_OVER_IPSEC_ENABLED_BOOL =
+                KEY_PREFIX + "sip_over_ipsec_enabled_bool";
+
+        /** @hide */
+        @IntDef({IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5, IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1})
+        public @interface IpsecAuthenticationAlgorithmType {}
+
+        /** IPSec Authentication algorithm is HMAC-MD5. see Annex H of TS 33.203 */
+        public static final int IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5 = 0;
+
+        /** IPSec Authentication algorithm is HMAC-SHA1. see Annex H of TS 33.203 */
+        public static final int IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1 = 1;
+
+        /**
+         * List of supported IPSEC Authentication algorithms.
+         *
+         * <p>Possible values are,
+         * {@link #IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5},
+         * {@link #IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1}
+         */
+        public static final String KEY_IPSEC_AUTHENTICATION_ALGORITHMS_INT_ARRAY =
+                KEY_PREFIX + "ipsec_authentication_algorithms_int_array";
+
+        /** @hide */
+        @IntDef({
+            IPSEC_ENCRYPTION_ALGORITHM_NULL,
+            IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC,
+            IPSEC_ENCRYPTION_ALGORITHM_AES_CBC
+        })
+        public @interface IpsecEncryptionAlgorithmType {}
+
+        /** IPSec Encryption algorithm is NULL. see Annex H of TS 33.203 */
+        public static final int IPSEC_ENCRYPTION_ALGORITHM_NULL = 0;
+
+        /** IPSec Encryption algorithm is DES_EDE3_CBC. see Annex H of TS 33.203 */
+        public static final int IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC = 1;
+
+        /** IPSec Encryption algorithm is AES_CBC. see Annex H of TS 33.203 */
+        public static final int IPSEC_ENCRYPTION_ALGORITHM_AES_CBC = 2;
+
+        /**
+         * List of supported IPSEC encryption algorithms.
+         *
+         * <p>Possible values are,
+         * {@link #IPSEC_ENCRYPTION_ALGORITHM_NULL},
+         * {@link #IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC},
+         * {@link #IPSEC_ENCRYPTION_ALGORITHM_AES_CBC}
+         */
+        public static final String KEY_IPSEC_ENCRYPTION_ALGORITHMS_INT_ARRAY =
+                KEY_PREFIX + "ipsec_encryption_algorithms_int_array";
+
+        /**
+         * Expiry timer for IMS Registration in seconds.
+         * <p>Reference: RFC 3261 Section 20.19.
+         */
+        public static final String KEY_REGISTRATION_EXPIRY_TIMER_SEC_INT =
+                KEY_PREFIX + "registration_expiry_timer_sec_int";
+
+        /** Registration Retry Base-time as per RFC 5626 Section 4.5. */
+        public static final String KEY_REGISTRATION_RETRY_BASE_TIMER_MILLIS_INT =
+                KEY_PREFIX + "registration_retry_base_timer_millis_int";
+
+        /** Registration Retry max-time as per RFC 5626 Section 4.5. */
+        public static final String KEY_REGISTRATION_RETRY_MAX_TIMER_MILLIS_INT =
+                KEY_PREFIX + "registration_retry_max_timer_millis_int";
+
+        /**
+         * Flag indicating whether subscription to registration event package
+         * is supported or not.
+         */
+        public static final String KEY_REGISTRATION_EVENT_PACKAGE_SUPPORTED_BOOL =
+                KEY_PREFIX + "registration_event_package_supported_bool";
+
+        /**
+         * Expiry timer for SUBSCRIBE in seconds.
+         * <p>Reference: RFC 3261 Section 20.19.
+         */
+        public static final String KEY_REGISTRATION_SUBSCRIBE_EXPIRY_TIMER_SEC_INT =
+                KEY_PREFIX + "registration_subscribe_expiry_timer_sec_int";
+
+        /** @hide */
+        @IntDef({
+            GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI,
+            GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI,
+            GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR,
+            GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR
+        })
+        public @interface GeolocationPidfAllowedType {}
+
+        /**
+         * Indicates geolocation PIDF XML needs to be included for
+         * normal/non-emergency call scenario on WiFi
+         *
+         * <p>Geolocation for normal/non-emergency call should only include
+         * country code.
+         */
+        public static final int GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI = 1;
+
+        /**
+         * Indicates geolocation PIDF XML needs to be included for emergency
+         * call scenario on WiFi
+         */
+        public static final int GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI = 2;
+
+        /**
+         * Indicates geolocation PIDF XML needs to be included for normal/non-emergency
+         * call scenario on Cellular
+         *
+         * <p>Geolocation for normal/non-emergency call should only include
+         * country code.
+         */
+        public static final int GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR = 3;
+
+        /**
+         * Indicates geolocation PIDF XML needs to be included for emergency
+         * call scenario on Cellular
+         */
+        public static final int GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR = 4;
+
+        /**
+         * List of cases where geolocation PIDF XML needs to be included in the
+         * SIP REGISTER over WiFi and Cellular.
+         *
+         * <p>Possible values are,
+         * {@link #GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI},
+         * {@link #GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI},
+         * {@link #GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR},
+         * {@link #GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR}
+         *
+         * <p>An empty array indicates geolocation PIDF XML should not be included in
+         * the SIP REGISTER over WiFi and Cellular.
+         */
+        public static final String KEY_GEOLOCATION_PIDF_IN_SIP_REGISTER_SUPPORT_INT_ARRAY =
+                KEY_PREFIX + "geolocation_pidf_in_sip_register_support_int_array";
+
+        /**
+         * List of cases where geolocation PIDF XML needs to be included in the
+         * SIP INVITE over WiFi and Cellular.
+         *
+         * <p>Possible values are,
+         * {@link #GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI},
+         * {@link #GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI},
+         * {@link #GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR},
+         * {@link #GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR}
+         *
+         * <p>An empty array indicates geolocation PIDF XML should not be included
+         * in the SIP INVITE over WiFi and Cellular.
+         */
+        public static final String KEY_GEOLOCATION_PIDF_IN_SIP_INVITE_SUPPORT_INT_ARRAY =
+                KEY_PREFIX + "geolocation_pidf_in_sip_invite_support_int_array";
+
+        /**
+         * Specifies the IMS User Agent in template format.
+         *
+         * <p>Example: #MANUFACTURER#_#MODEL#_Android#AV#_#BUILD#".
+         * IMS Stack should internally substitute the tokens with the
+         * values from the respective android properties.
+         *
+         * <p>List of allowed tokens and the corresponding android properties are,
+         * <UL>
+         *   <LI>MANUFACTURER : ro.product.manufacturer</LI>
+         *   <LI>MODEL :  ro.product.model</LI>
+         *   <LI>AV : ro.build.version.release"</LI>
+         *   <LI>BUILD : ro.build.id</LI>
+         * </UL>
+         * <p> Vendor IMS Stack should strip any whitespace characters present
+         * in the android properties values before replacing the token.
+         *
+         * <p> An empty string is invalid as per IR92 section 2.6. This key is
+         * considered invalid if the format is violated. If the key is invalid or
+         * not configured, IMS stack should use internal default values.
+         */
+        public static final String KEY_IMS_USER_AGENT_STRING =
+                KEY_PREFIX + "ims_user_agent_string";
+
+        /** @hide */
+        @IntDef({
+            NETWORK_TYPE_HOME,
+            NETWORK_TYPE_ROAMING
+        })
+        public @interface NetworkType {}
+
+        /** Indicates HOME Network. */
+        public static final int NETWORK_TYPE_HOME = 0;
+
+        /** Indicates Roaming Network. */
+        public static final int NETWORK_TYPE_ROAMING = 1;
+
+        /** @hide */
+        @IntDef({
+            RTCP_INACTIVITY_ON_HOLD,
+            RTCP_INACTIVITY_ON_CONNECTED,
+            RTP_INACTIVITY_ON_CONNECTED,
+            E911_RTCP_INACTIVITY_ON_CONNECTED,
+            E911_RTP_INACTIVITY_ON_CONNECTED
+        })
+        public @interface MediaInactivityReason {}
+
+        /**  RTCP inactivity occurred when call is on HOLD. */
+        public static final int RTCP_INACTIVITY_ON_HOLD = 0;
+
+        /**  RTCP inactivity occurred when call is connected. */
+        public static final int RTCP_INACTIVITY_ON_CONNECTED = 1;
+
+        /**  RTP inactivity occurred when call is connected. */
+        public static final int RTP_INACTIVITY_ON_CONNECTED = 2;
+
+        /**  E911 RTCP inactivity occurred when call is connected. */
+        public static final int E911_RTCP_INACTIVITY_ON_CONNECTED = 3;
+
+        /**  E911 RTP inactivity occurred when call is connected. */
+        public static final int E911_RTP_INACTIVITY_ON_CONNECTED = 4;
+
+        /**
+         * List of different RAT technologies on which IMS
+         * is supported.
+         *
+         * <p>Possible values are,
+         * {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#IWLAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#UTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#GERAN}
+         */
+        public static final String KEY_SUPPORTED_RATS_INT_ARRAY =
+                KEY_PREFIX + "supported_rats_int_array";
+
+        /**
+         * A bundle which specifies the MMTEL capability and registration technology
+         * that requires provisioning. If a tuple is not present, the
+         * framework will not require that the tuple requires provisioning before
+         * enabling the capability.
+         * <p> Possible keys in this bundle are
+         * <ul>
+         *     <li>{@link #KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY}</li>
+         *     <li>{@link #KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY}</li>
+         *     <li>{@link #KEY_CAPABILITY_TYPE_UT_INT_ARRAY}</li>
+         *     <li>{@link #KEY_CAPABILITY_TYPE_SMS_INT_ARRAY}</li>
+         *     <li>{@link #KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT_ARRAY}</li>
+         * </ul>
+         * <p> The values are defined as {@code REGISTRATION_TECH_*} constants in
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase}.
+         *
+         * changing mmtel_requires_provisioning_bundle requires changes to
+         * carrier_volte_provisioning_required_bool and vice versa
+         * {@link Ims#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL}
+         */
+        public static final String KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE =
+                KEY_PREFIX + "mmtel_requires_provisioning_bundle";
+
+        /**
+         * List of different RAT technologies on which Provisioning for Voice calling (IR.92)
+         * is supported.
+         * <p>Possible values are,
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
+         * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE
+         */
+        public static final String KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY =
+                KEY_PREFIX + "capability_type_voice_int_array";
+
+        /**
+         * List of different RAT technologies on which Provisioning for Video Telephony (IR.94)
+         * is supported.
+         * <p>Possible values are,
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
+         * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO
+         */
+        public static final String KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY =
+                KEY_PREFIX + "capability_type_video_int_array";
+
+        /**
+         * List of different RAT technologies on which Provisioning for XCAP over Ut for
+         * supplementary services. (IR.92) is supported.
+         * <p>Possible values are,
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
+         * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT
+         */
+        public static final String KEY_CAPABILITY_TYPE_UT_INT_ARRAY =
+                KEY_PREFIX + "capability_type_ut_int_array";
+
+        /**
+         * List of different RAT technologies on which Provisioning for SMS (IR.92) is supported.
+         * <p>Possible values are,
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
+         * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS
+         */
+        public static final String KEY_CAPABILITY_TYPE_SMS_INT_ARRAY =
+                KEY_PREFIX + "capability_type_sms_int_array";
+
+        /**
+         * List of different RAT technologies on which Provisioning for Call Composer
+         * (section 2.4 of RCC.20) is supported.
+         * <p>Possible values are,
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
+         * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER
+         */
+        public static final String KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT_ARRAY =
+                KEY_PREFIX + "capability_type_call_composer_int_array";
+
+        /**
+         * A bundle which specifies the RCS capability and registration technology
+         * that requires provisioning. If a tuple is not present, the
+         * framework will not require that the tuple requires provisioning before
+         * enabling the capability.
+         * <p> Possible keys in this bundle are
+         * <ul>
+         *     <li>{@link #KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY}</li>
+         *     <li>{@link #KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY}</li>
+         * </ul>
+         * <p> The values are defined as {@code REGISTRATION_TECH_*} constants in
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase}.
+         */
+        public static final String KEY_RCS_REQUIRES_PROVISIONING_BUNDLE =
+                KEY_PREFIX + "rcs_requires_provisioning_bundle";
+
+        /**
+         * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
+         * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
+         * If not set, this RcsFeature should not service capability requests.
+         * <p>Possible values are,
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
+         */
+        public static final String KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY =
+                KEY_PREFIX + "capability_type_options_uce_int_array";
+
+        /**
+         * This carrier supports User Capability Exchange using a presence server as defined by the
+         * framework. If set, the RcsFeature should support capability exchange using a presence
+         * server. If not set, this RcsFeature should not publish capabilities or service capability
+         * requests using presence.
+         * <p>Possible values are,
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+         * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
+         */
+        public static final String KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY =
+                KEY_PREFIX + "capability_type_presence_uce_int_array";
+
+        /**
+         * Specifies the policy for disabling NR SA mode. Default value is
+         *{@link #SA_DISABLE_POLICY_NONE}.
+         * The value set as below:
+         * <ul>
+         * <li>0: {@link #SA_DISABLE_POLICY_NONE }</li>
+         * <li>1: {@link #SA_DISABLE_POLICY_WFC_ESTABLISHED }</li>
+         * <li>2: {@link #SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED  }</li>
+         * <li>3: {@link #SA_DISABLE_POLICY_VOWIFI_REGISTERED  }</li>
+         * </ul>
+         * @hide
+         */
+        public static final String KEY_NR_SA_DISABLE_POLICY_INT =
+                KEY_PREFIX + "sa_disable_policy_int";
+
+        /** @hide */
+        @IntDef({
+                NR_SA_DISABLE_POLICY_NONE,
+                NR_SA_DISABLE_POLICY_WFC_ESTABLISHED,
+                NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED,
+                NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED
+        })
+        public @interface NrSaDisablePolicy {}
+
+        /**
+         * Do not disables NR SA mode.
+         * @hide
+         */
+        public static final int NR_SA_DISABLE_POLICY_NONE = 0;
+
+        /**
+         * Disables NR SA mode when VoWiFi call is established in order to improve the delay or
+         * voice mute when the handover from ePDG to NR is not supported in UE or network.
+         * @hide
+         */
+        public static final int NR_SA_DISABLE_POLICY_WFC_ESTABLISHED = 1;
+
+        /**
+         * Disables NR SA mode when VoWiFi call is established when VoNR is disabled in order to
+         * improve the delay or voice mute when the handover from ePDG to NR is not supported
+         * in UE or network.
+         * @hide
+         */
+        public static final int NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED = 2;
+
+        /**
+         * Disables NR SA mode when IMS is registered over WiFi in order to improve the delay or
+         * voice mute when the handover from ePDG to NR is not supported in UE or network.
+         * @hide
+         */
+        public static final int NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED = 3;
+
+        /**
+         * This specifies whether the carrier support the global number format or not.
+         * {@link SubscriptionManager#getPhoneNumber(int)},
+         * {@link SubscriptionManager#getPhoneNumber(int, int)} with
+         * {@link SubscriptionManager#PHONE_NUMBER_SOURCE_IMS}
+         * In order to provide the phone number to the APIs, the framework extracts the phone
+         * number from the message received from the carrier server. If the carrier does not use
+         * global number format, the framework could not provide phone number.
+         * <p>
+         * If not set or set to false value, the framework handle only global number format URI.
+         * @hide
+         */
+        public static final String KEY_ALLOW_NON_GLOBAL_PHONE_NUMBER_FORMAT_BOOL =
+                KEY_PREFIX + "allow_non_global_phone_number_format_bool";
+
+        private Ims() {}
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000);
+            defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
+            defaults.putBoolean(KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false);
+            defaults.putStringArray(KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY,
+                    new String[0]);
+            defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
+            defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
+            defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, false);
+            defaults.putLong(KEY_SUBSCRIBE_RETRY_DURATION_MILLIS_LONG, -1);
+            defaults.putBoolean(KEY_USE_SIP_URI_FOR_PRESENCE_SUBSCRIBE_BOOL, false);
+            defaults.putInt(KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT, 30 * 24 * 60 * 60);
+            defaults.putBoolean(KEY_RCS_REQUEST_FORBIDDEN_BY_SIP_489_BOOL, false);
+            defaults.putLong(KEY_RCS_REQUEST_RETRY_INTERVAL_MILLIS_LONG, 20 * 60 * 1000);
+            defaults.putStringArray(KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY, new String[]{
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg\"",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.largemsg\"",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.deferred\"",
+                    "+g.gsma.rcs.cpm.pager-large",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.filetransfer\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.fthttp\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.ftsms\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.callcomposer\"",
+                    "+g.gsma.callcomposer",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.callunanswered\"",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.sharedmap\"",
+                    "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.sharedsketch\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geopush\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geosms\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.chatbot\"",
+                    "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.chatbot.sa\"",
+                    "+g.gsma.rcs.botversion=\"#=1,#=2\"",
+                    "+g.gsma.rcs.cpimext"});
+
+            /**
+             * @see #KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
+             */
+            defaults.putPersistableBundle(
+                    KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE, new PersistableBundle());
+            /**
+             * @see #KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
+             */
+            defaults.putPersistableBundle(
+                    KEY_RCS_REQUIRES_PROVISIONING_BUNDLE, new PersistableBundle());
+
+            defaults.putBoolean(KEY_GRUU_ENABLED_BOOL, false);
+            defaults.putBoolean(KEY_SIP_OVER_IPSEC_ENABLED_BOOL, true);
+            defaults.putBoolean(KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL, false);
+            defaults.putBoolean(KEY_REGISTRATION_EVENT_PACKAGE_SUPPORTED_BOOL, true);
+
+            defaults.putInt(KEY_SIP_TIMER_T1_MILLIS_INT, 2000);
+            defaults.putInt(KEY_SIP_TIMER_T2_MILLIS_INT, 16000);
+            defaults.putInt(KEY_SIP_TIMER_T4_MILLIS_INT, 17000);
+            defaults.putInt(KEY_SIP_TIMER_B_MILLIS_INT, 128000);
+            defaults.putInt(KEY_SIP_TIMER_C_MILLIS_INT, 210000);
+            defaults.putInt(KEY_SIP_TIMER_D_MILLIS_INT, 130000);
+            defaults.putInt(KEY_SIP_TIMER_F_MILLIS_INT, 128000);
+            defaults.putInt(KEY_SIP_TIMER_H_MILLIS_INT, 128000);
+            defaults.putInt(KEY_SIP_TIMER_J_MILLIS_INT, 128000);
+            defaults.putInt(KEY_SIP_SERVER_PORT_NUMBER_INT, 5060);
+            defaults.putInt(KEY_REQUEST_URI_TYPE_INT, REQUEST_URI_FORMAT_TEL);
+            defaults.putInt(KEY_SIP_PREFERRED_TRANSPORT_INT, PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP);
+            defaults.putInt(KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT, 1500);
+            defaults.putInt(KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT, 1500);
+            defaults.putInt(KEY_REGISTRATION_EXPIRY_TIMER_SEC_INT, 600000);
+            defaults.putInt(KEY_REGISTRATION_RETRY_BASE_TIMER_MILLIS_INT, 30000);
+            defaults.putInt(KEY_REGISTRATION_RETRY_MAX_TIMER_MILLIS_INT, 1800000);
+            defaults.putInt(KEY_REGISTRATION_SUBSCRIBE_EXPIRY_TIMER_SEC_INT, 600000);
+            defaults.putInt(KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_NONE);
+
+            defaults.putIntArray(
+                    KEY_IPSEC_AUTHENTICATION_ALGORITHMS_INT_ARRAY,
+                    new int[] {
+                        IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5,
+                        IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1
+                    });
+            defaults.putIntArray(
+                    KEY_IPSEC_ENCRYPTION_ALGORITHMS_INT_ARRAY,
+                    new int[] {
+                        IPSEC_ENCRYPTION_ALGORITHM_NULL,
+                        IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC,
+                        IPSEC_ENCRYPTION_ALGORITHM_AES_CBC
+                    });
+            defaults.putIntArray(
+                    KEY_IMS_PDN_ENABLED_IN_NO_VOPS_SUPPORT_INT_ARRAY,
+                    new int[] {
+                    });
+            defaults.putIntArray(
+                    KEY_GEOLOCATION_PIDF_IN_SIP_REGISTER_SUPPORT_INT_ARRAY,
+                    new int[] {
+                        GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI
+                    });
+            defaults.putIntArray(
+                    KEY_GEOLOCATION_PIDF_IN_SIP_INVITE_SUPPORT_INT_ARRAY,
+                    new int[] {
+                        GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI
+                    });
+            defaults.putIntArray(
+                    KEY_SUPPORTED_RATS_INT_ARRAY,
+                    new int[] {
+                        AccessNetworkType.NGRAN,
+                        AccessNetworkType.EUTRAN,
+                        AccessNetworkType.IWLAN
+                    });
+
+            defaults.putString(KEY_PHONE_CONTEXT_DOMAIN_NAME_STRING, "");
+            defaults.putString(KEY_IMS_USER_AGENT_STRING,
+                               "#MANUFACTURER#_#MODEL#_Android#AV#_#BUILD#");
+
+            defaults.putBoolean(KEY_ALLOW_NON_GLOBAL_PHONE_NUMBER_FORMAT_BOOL, false);
+
+            return defaults;
+        }
+    }
+
+    /**
+     * IMS Voice configs. This groups the configs required for IMS Voice - VoNR/VoLTE
+     *
+     * <p>Reference: IR.92
+     */
+    public static final class ImsVoice {
+        private ImsVoice() {}
+
+        /** Prefix of all imsvoice.KEY_* constants. */
+        public static final String KEY_PREFIX = "imsvoice.";
+
+        /**
+         * Flag specifying whether VoLTE should be available when on
+         * roaming network.
+         *
+         * <p>If {@code false}: hard disabled.
+         * If {@code true}: then depends on availability, etc.
+         */
+        public static final String KEY_CARRIER_VOLTE_ROAMING_AVAILABLE_BOOL =
+                KEY_PREFIX + "carrier_volte_roaming_available_bool";
+
+        /**
+         * Flag specifying whether to send vertical caller id service codes
+         * (*67 and *82) in the dialed string in the SIP:INVITE.
+         *
+         * <p>If {@code true}, vertical caller id service codes *67 and *82
+         * will be sent in the dialed string in the SIP:INVITE.
+         * If {@code false}, *67 and *82 will be removed.
+         */
+        public static final String KEY_INCLUDE_CALLER_ID_SERVICE_CODES_IN_SIP_INVITE_BOOL =
+                KEY_PREFIX + "include_caller_id_service_codes_in_sip_invite_bool";
+
+        /**
+         * Flag indicating whether Multi-end point setting is enabled or not.
+         */
+        public static final String KEY_MULTIENDPOINT_SUPPORTED_BOOL =
+                KEY_PREFIX + "multiendpoint_supported_bool";
+
+        /**
+         * Flag indicating whether Supported header field with the option tag
+         *  'timer' is enabled or not.
+         *
+         * <p>If {@code true}, session timer support is available.{@code false} otherwise.
+         *
+         * Reference: RFC 4028 Section 3
+         */
+        public static final String KEY_SESSION_TIMER_SUPPORTED_BOOL =
+                KEY_PREFIX + "session_timer_supported_bool";
+
+        /**
+         * Session-expires header field expressed in seconds as per
+         * RFC 4028 Section 3.
+         *
+         * <p>This establishes the upper bound for the session refresh interval.
+         */
+        public static final String KEY_SESSION_EXPIRES_TIMER_SEC_INT =
+                KEY_PREFIX + "session_expires_timer_sec_int";
+
+        /**
+         * Indicates the minimum value for the session interval in seconds.
+         * Represented as min-SE header field as per RFC 4028 Section 3.
+         *
+         * <p>This establishes the lower bound for the session refresh interval.
+         */
+        public static final String KEY_MINIMUM_SESSION_EXPIRES_TIMER_SEC_INT =
+                KEY_PREFIX + "minimum_session_expires_timer_sec_int";
+
+        /** @hide */
+        @IntDef({
+            SESSION_REFRESHER_TYPE_UNKNOWN,
+            SESSION_REFRESHER_TYPE_UAC,
+            SESSION_REFRESHER_TYPE_UAS
+        })
+        public @interface SessionRefresherType {}
+
+        /**
+         * Session Refresher entity is unknown. This means UE does not include the
+         * "refresher" parameter in the Session-Expires header field of
+         * the SIP INVITE request.
+         */
+        public static final int SESSION_REFRESHER_TYPE_UNKNOWN = 0;
+
+        /**
+         * Session Refresher entity is User Agent Client (UAC).
+         *
+         * <p>Type of "refresher" parameter in the Session-Expires header field
+         * of the SIP INVITE request is UAC.
+         */
+        public static final int SESSION_REFRESHER_TYPE_UAC = 1;
+
+        /**
+         * Session Refresher entity is User Agent Server (UAS).
+         *
+         * <p>Type of "refresher" parameter in the Session-Expires header field
+         * of the SIP INVITE request is UAS.
+         */
+        public static final int SESSION_REFRESHER_TYPE_UAS = 2;
+
+        /**
+         * Session Refresher entity as per RFC 4028 and IR.92 Section 2.2.8.
+         *
+         * <p>This determines,
+         * a) whether to include the "refresher" parameter
+         * b) Type of refresher" parameter
+         * in the Session-Expires header field of the SIP INVITE request.
+         *
+         * <p>Possible values are,
+         * {@link #SESSION_REFRESHER_TYPE_UNKNOWN},
+         * {@link #SESSION_REFRESHER_TYPE_UAC},
+         * {@link #SESSION_REFRESHER_TYPE_UAS}
+         */
+        public static final String KEY_SESSION_REFRESHER_TYPE_INT =
+                KEY_PREFIX + "session_refresher_type_int";
+
+        /** @hide */
+        @IntDef({
+            SESSION_PRIVACY_TYPE_HEADER,
+            SESSION_PRIVACY_TYPE_NONE,
+            SESSION_PRIVACY_TYPE_ID
+        })
+        public @interface SessionPrivacyType {}
+
+        /**
+         * Session privacy type is HEADER as per RFC 3323 Section 4.2.
+         */
+        public static final int SESSION_PRIVACY_TYPE_HEADER = 0;
+
+        /**
+         * Session privacy type is NONE as per RFC 3323 Section 4.2.
+         */
+        public static final int SESSION_PRIVACY_TYPE_NONE = 1;
+
+        /**
+         * Session privacy type is ID as per RFC 3325 Section 9.3.
+         */
+        public static final int SESSION_PRIVACY_TYPE_ID = 2;
+
+        /**
+         * Specify the session privacy type.
+         *
+         * <p>Reference: RFC 3323 Section 4.2, RFC 3325 Section 9.3.
+         *
+         * <p>Possible values are,
+         * {@link #SESSION_PRIVACY_TYPE_HEADER},
+         * {@link #SESSION_PRIVACY_TYPE_NONE},
+         * {@link #SESSION_PRIVACY_TYPE_ID}
+         */
+        public static final String KEY_SESSION_PRIVACY_TYPE_INT =
+                KEY_PREFIX + "session_privacy_type_int";
+
+        /**
+         * Flag indicating whether PRACK must be enabled for all 18x messages.
+         *
+         * <p>If {@code false}, only 18x responses with SDP are sent reliably.
+         * If {@code true},  SIP 18x responses (other than SIP 183 response)
+         * are sent reliably.
+         */
+        public static final String KEY_PRACK_SUPPORTED_FOR_18X_BOOL =
+                KEY_PREFIX + "prack_supported_for_18x_bool";
+
+        /** @hide */
+        @IntDef({
+            CONFERENCE_SUBSCRIBE_TYPE_IN_DIALOG,
+            CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG
+        })
+        public @interface ConferenceSubscribeType {}
+
+        /**
+         * The SIP SUBSCRIBE to conference state events is sent in the
+         * SIP INVITE dialog between the UE and the conference server.
+         *
+         * <p>Reference: IR.92 Section 2.3.3.
+         */
+        public static final int CONFERENCE_SUBSCRIBE_TYPE_IN_DIALOG = 0;
+
+        /**
+         * The SIP SUBSCRIBE to conference state events is sent out of
+         * the SIP INVITE dialog between the UE and the conference server.
+         *
+         * <p>Reference: IR.92 Section 2.3.3.
+         */
+        public static final int CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG = 1;
+
+        /**
+         * This is used to specify whether the SIP SUBSCRIBE to conference state events,
+         * is sent in or out of the SIP INVITE dialog between the UE and the
+         * conference server.
+         *
+         * <p>Reference: IR.92 Section 2.3.3.
+         *
+         * <p>Possible values are,
+         * {@link #CONFERENCE_SUBSCRIBE_TYPE_IN_DIALOG},
+         * {@link #CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG}
+         *
+         * An empty array indicates SUBSCRIBE to conference event package
+         * is not required.
+         */
+        public static final String KEY_CONFERENCE_SUBSCRIBE_TYPE_INT =
+                KEY_PREFIX + "conference_subscribe_type_int";
+
+        /**
+         * Flag specifying whether QoS preconditions are supported during call setup.
+         *
+         * <p>If {@code true}: QoS Preconditions are supported during call setup and
+         * 'precondition' tag is included in the SIP INVITE header and precondition
+         * parameters are sent in SDP as required.
+         * <p>If {@code false}: QoS Preconditions are not supported during call setup.
+         *
+         * <p>Reference: 3GPP TS 24.229
+         */
+        public static final String KEY_VOICE_QOS_PRECONDITION_SUPPORTED_BOOL =
+                KEY_PREFIX + "voice_qos_precondition_supported_bool";
+
+        /**
+         * Flag specifying whether voice is allowed on default bearer.
+         *
+         * <p>If {@code true}: voice packets can be sent on default bearer. {@code false} otherwise.
+         */
+        public static final String KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL =
+                KEY_PREFIX + "voice_on_default_bearer_supported_bool";
+
+        /**
+         * Specifies the dedicated bearer wait time during call establishment.
+         *
+         * <p>If dedicated bearer is not established within this time and if
+         * {@link #KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL} is false, then call setup would fail.
+         * <p>If dedicated bearer is not established within this time and if
+         * {@link #KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL} is true, then the media is allowed
+         * on default bearer.
+         */
+        public static final String KEY_DEDICATED_BEARER_WAIT_TIMER_MILLIS_INT =
+                KEY_PREFIX + "dedicated_bearer_wait_timer_millis_int";
+
+        /** @hide */
+        @IntDef({
+            BASIC_SRVCC_SUPPORT,
+            ALERTING_SRVCC_SUPPORT,
+            PREALERTING_SRVCC_SUPPORT,
+            MIDCALL_SRVCC_SUPPORT
+        })
+        public @interface SrvccType {}
+
+        /**
+         * Indicates support for basic SRVCC, typically 1 active call
+         * as detailed in IR.92 Section A.3.
+         */
+        public static final int BASIC_SRVCC_SUPPORT = 0;
+
+        /**
+         * SRVCC access transfer for calls in alerting phase as per 3GPP 24.237
+         * and IR.64 Section 4.4.
+         * Media feature tag used: g.3gpp.srvcc-alerting.
+         */
+        public static final int ALERTING_SRVCC_SUPPORT = 1;
+
+        /**
+         * SRVCC access transfer for calls in pre-alerting phase as per 3GPP 24.237.
+         * Media feature tag used: g.3gpp.ps2cs-srvcc-orig-pre-alerting.
+         */
+        public static final int PREALERTING_SRVCC_SUPPORT = 2;
+
+        /**
+         * SRVCC access transfer for calls in mid-call phase as per 3GPP 24.237.
+         * and IR.64 Section 4.4.
+         * <p>This means UE supports the MSC server assisted mid-call feature.
+         * Media feature tag used: g.3gpp.mid-call.
+         */
+        public static final int MIDCALL_SRVCC_SUPPORT = 3;
+
+        /**
+         * List of different SRVCC types supported as defined in 3GPP 24.237.
+         *
+         * <p> Possible values are,
+         * {@link #BASIC_SRVCC_SUPPORT},
+         * {@link #ALERTING_SRVCC_SUPPORT},
+         * {@link #PREALERTING_SRVCC_SUPPORT},
+         * {@link #MIDCALL_SRVCC_SUPPORT}
+         *
+         * <p> Reference: IR.64, 3GPP 24.237, 3GPP 23.216
+         */
+        public static final String KEY_SRVCC_TYPE_INT_ARRAY =
+                KEY_PREFIX + "srvcc_type_int_array";
+
+        /**
+         * Specifies the ringing timer for Mobile terminated calls.
+         *
+         * <p>Ringing timer starts when the device sends SIP 180 Ringing in
+         * response to a received SIP INVITE. If Ringing timer expires,
+         * the device sends SIP 486 response.
+         */
+        public static final String KEY_RINGING_TIMER_MILLIS_INT =
+                KEY_PREFIX + "ringing_timer_millis_int";
+
+        /**
+         * Specifies the ringback timer for Mobile originated calls.
+         *
+         * <p>Ringback timer starts when the device receives SIP 180 Ringing
+         * in response to its SIP INVITE. If Ringback timer expires,
+         * the device sends SIP CANCEL.
+         */
+        public static final String KEY_RINGBACK_TIMER_MILLIS_INT =
+                KEY_PREFIX + "ringback_timer_millis_int";
+
+        /**
+         * Specifies the timeout value for RTP inactivity for audio media.
+         * <p>On timer expiry, call will end.
+         * See {@link #KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY} for more
+         * details.
+         * <p> Value of 0 means this timer is not enabled.
+         */
+        public static final String KEY_AUDIO_RTP_INACTIVITY_TIMER_MILLIS_INT =
+                KEY_PREFIX + "audio_rtp_inactivity_timer_millis_int";
+
+        /**
+         * Specifies the timeout value for RTCP inactivity for audio media.
+         * <p>On timer expiry, call will end.
+         * See {@link #KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY} for more
+         * details.
+         * <p> Value of 0 means this timer is not enabled.
+         */
+        public static final String KEY_AUDIO_RTCP_INACTIVITY_TIMER_MILLIS_INT =
+                KEY_PREFIX + "audio_rtcp_inactivity_timer_millis_int";
+
+        /**
+         * Used to specify the conference factory URI.
+         *
+         * <p>If this is empty, then conference URI is generated from MCC/MNC as
+         * specified in clause 13.10 of 3GPP 23.003.
+         */
+        public static final String KEY_CONFERENCE_FACTORY_URI_STRING =
+                KEY_PREFIX + "conference_factory_uri_string";
+
+        /** @hide */
+        @IntDef({
+            SESSION_REFRESH_METHOD_INVITE,
+            SESSION_REFRESH_METHOD_UPDATE_PREFERRED
+        })
+        public @interface SessionRefreshMethod {}
+
+        /**
+         * SIP INVITE is used for Session Refresh
+         */
+        public static final int SESSION_REFRESH_METHOD_INVITE = 0;
+
+        /**
+         * Both SIP INVITE and UPDATE are used for session refresh.
+         *
+         * <p>SIP UPDATE will be used if UPDATE is in 'Allow' header.
+         * If UPDATE is not in 'Allow' header, then INVITE will be used.
+         */
+        public static final int SESSION_REFRESH_METHOD_UPDATE_PREFERRED = 1;
+
+        /**
+         * This is used to specify the method used for session refresh.
+         *
+         * <p>Possible values are,
+         * {@link #SESSION_REFRESH_METHOD_INVITE},
+         * {@link #SESSION_REFRESH_METHOD_UPDATE_PREFERRED}
+         */
+        public static final String KEY_SESSION_REFRESH_METHOD_INT =
+                KEY_PREFIX + "session_refresh_method_int";
+
+        /**
+         * Flag specifying whether the 'From' header field is used for determination of
+         * the originating party identity in Originating Identification Presentation(OIP)
+         * service.
+         *
+         * <p>If {@code true}: Indicates that the 'From' header field is used for
+         * determination of the originating party identity in OIP.
+         * {@code false} otherwise.
+         */
+        public static final String KEY_OIP_SOURCE_FROM_HEADER_BOOL =
+                KEY_PREFIX + "oip_source_from_header_bool";
+
+        /**
+         * Specifies the timer value for INVITE to the first 1xx response
+         * (including 100 trying). If no response is received at timer expiry,
+         * call is redialed over CS.
+         *
+         * <p> Reference: 24.173 Table L.1
+         */
+        public static final String KEY_MO_CALL_REQUEST_TIMEOUT_MILLIS_INT =
+                KEY_PREFIX + "mo_call_request_timeout_millis_int";
+
+        /**
+         * List of various reasons of media inactivity for which
+         * voice/emergency call will end.
+         *
+         * <p>Possible values are,
+         * {@link Ims#RTCP_INACTIVITY_ON_HOLD},
+         * {@link Ims#RTCP_INACTIVITY_ON_CONNECTED},
+         * {@link Ims#RTP_INACTIVITY_ON_CONNECTED}
+         * {@link Ims#E911_RTCP_INACTIVITY_ON_CONNECTED},
+         * {@link Ims#E911_RTP_INACTIVITY_ON_CONNECTED}
+         */
+        public static final String KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY =
+                KEY_PREFIX + "audio_inactivity_call_end_reasons_int_array";
+
+        /**
+         * Specifies the AS (Application Specific) SDP modifier for audio media.
+         *
+         * <p>This value is expressed in kilobits per second.
+         * Reference: RFC 3556 Section 2.
+         */
+        public static final String KEY_AUDIO_AS_BANDWIDTH_KBPS_INT =
+                KEY_PREFIX + "audio_as_bandwidth_kbps_int";
+
+        /**
+         * Specifies the RS SDP modifier for audio media. This indicates the RTCP
+         * bandwidth allocated to active data senders for audio media.
+         *
+         * <p>This value is expressed in bits per second.
+         * Reference: RFC 3556 Section 2.
+         */
+        public static final String KEY_AUDIO_RS_BANDWIDTH_BPS_INT =
+                KEY_PREFIX + "audio_rs_bandwidth_bps_int";
+
+        /**
+         * Specifies the RR SDP modifier for audio media. This indicates the RTCP
+         * bandwidth allocated to receivers for audio media.
+         *
+         * <p>This value is expressed in bits per second.
+         * Reference: RFC 3556 Section 2.
+         */
+        public static final String KEY_AUDIO_RR_BANDWIDTH_BPS_INT =
+                KEY_PREFIX + "audio_rr_bandwidth_bps_int";
+
+        /**
+         * Specifies the Audio Codec capability. This contains a list of payload types
+         * representing different audio codec instances.
+         *
+         * <p> The priority of the codecs is EVS, AMRWB, AMRNB,  DTMF WB, DTMF NB
+         * from highest to lowest. In each individual codec, the priority is determined
+         * by the order of the payload types from highest to lowest.
+         *
+         * <p>Possible keys in this bundle are,
+         * <UL>
+         *     <LI>{@link #KEY_EVS_PAYLOAD_TYPE_INT_ARRAY}</LI>
+         *     <LI>{@link #KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY}</LI>
+         *     <LI>{@link #KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY}</LI>
+         *     <LI>{@link #KEY_DTMFWB_PAYLOAD_TYPE_INT_ARRAY}</LI>
+         *     <LI>{@link #KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY}</LI>
+         * </UL>
+         * <p>To specify payload descriptions for each of the audio payload types, see
+         * <UL>
+         *     <LI>{@link #KEY_EVS_PAYLOAD_DESCRIPTION_BUNDLE}</LI>
+         *     <LI>{@link #KEY_AMRNB_PAYLOAD_DESCRIPTION_BUNDLE}</LI>
+         *     <LI>{@link #KEY_AMRWB_PAYLOAD_DESCRIPTION_BUNDLE}</LI>
+         * </UL>
+         */
+        public static final String KEY_AUDIO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE =
+                KEY_PREFIX + "audio_codec_capability_payload_types_bundle";
+
+        /**
+         * A list of integers representing the different payload types
+         * in EVS codec in priority order from highest to lowest.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_EVS_PAYLOAD_TYPE_INT_ARRAY =
+                KEY_PREFIX + "evs_payload_type_int_array";
+
+        /**
+         * A list of integers representing the different payload types
+         * in AMR-WB codec in priority order from highest to lowest.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY =
+                KEY_PREFIX + "amrwb_payload_type_int_array";
+
+        /**
+         * A list of integers representing the different payload types
+         * in AMR-NB codec in priority order from highest to lowest.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY =
+                KEY_PREFIX + "amrnb_payload_type_int_array";
+
+        /**
+         * A list of integers representing the different payload types
+         * in DTMF WB codec in priority order from highest to lowest.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_DTMFWB_PAYLOAD_TYPE_INT_ARRAY =
+                KEY_PREFIX + "dtmfwb_payload_type_int_array";
+
+        /**
+         * A list of integers representing the different payload types
+         * in DTMF NB codec in priority order from highest to lowest.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY =
+                KEY_PREFIX + "dtmfnb_payload_type_int_array";
+
+        /**
+         * This indicates the threshold for RTP packet loss rate in percentage. If measured packet
+         * loss rate crosses this, a callback with {@link MediaQualityStatus} will be invoked to
+         * listeners.
+         * See {@link android.telephony.TelephonyCallback.MediaQualityStatusChangedListener}
+         *
+         * <p/>
+         * Valid threshold range : 0 ~ 100
+         *
+         * @hide
+         */
+        public static final String KEY_VOICE_RTP_PACKET_LOSS_RATE_THRESHOLD_INT =
+                KEY_PREFIX + "rtp_packet_loss_rate_threshold_int";
+
+        /**
+         * This indicates the threshold for RTP jitter value in milliseconds (RFC3550). If measured
+         * jitter value crosses this, a callback with {@link MediaQualityStatus} will be invoked
+         * to listeners.
+         * See {@link android.telephony.TelephonyCallback.MediaQualityStatusChangedListener}
+         *
+         * <p/>
+         * Valid threshold range : 0 ~ 10000
+         *
+         * @hide
+         */
+        public static final String KEY_VOICE_RTP_JITTER_THRESHOLD_MILLIS_INT =
+                KEY_PREFIX + "rtp_jitter_threshold_millis_int";
+
+        /**
+         * This indicates the threshold for RTP inactivity time in milliseconds. If measured
+         * inactivity timer crosses this, a callback with {@link MediaQualityStatus} will be invoked
+         * to listeners.
+         * See {@link android.telephony.TelephonyCallback.MediaQualityStatusChangedListener}
+         *
+         * <p/>
+         * Valid threshold range : 0 ~ 60000
+         *
+         * @hide
+         */
+        public static final String KEY_VOICE_RTP_INACTIVITY_TIME_THRESHOLD_MILLIS_LONG =
+                KEY_PREFIX + "rtp_inactivity_time_threshold_millis_long";
+
+        /** @hide */
+        @IntDef({
+            BANDWIDTH_EFFICIENT,
+            OCTET_ALIGNED
+        })
+        public @interface AmrPayloadFormat {}
+
+        /** AMR NB/WB Payload format is bandwidth-efficient. */
+        public static final int BANDWIDTH_EFFICIENT = 0;
+
+        /** AMR NB/WB Payload format is octet-aligned. */
+        public static final int OCTET_ALIGNED = 1;
+
+        /**
+         * Specifies the payload format of the AMR-NB/AMR-WB codec.
+         *
+         * <p>Possible values are,
+         * {@link #BANDWIDTH_EFFICIENT},
+         * {@link #OCTET_ALIGNED}
+
+         * <p>If value is not specified, payload format is
+         * {@link #BANDWIDTH_EFFICIENT}.
+         *
+         * <p>Reference: RFC 4867 Section 8.1.
+         */
+        public static final String KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT =
+                KEY_PREFIX + "amr_codec_attribute_payload_format_int";
+
+        /**
+         * Restricts the active mode set to a subset of all modes in the codec.
+         *
+         * <p>This attribute is optional. If value is set, then session mode
+         * set is restricted to the modes specified in this list. If this value
+         * is not specified, then all available modes in the codec are allowed.
+         * This attribute is applicable for AMR-WB, AMR-NB,
+         * and EVS codec (operating in AMR-WB IO Mode).
+         *
+         * <p>Possible values are subset of,
+         * [0,1,2,3,4,5,6,7,8] - AMRWB with the modes representing nine speech codec modes
+         * with bit rates of 6.6, 8.85, 12.65, 14.25,  15.85, 18.25, 19.85, 23.05, 23.85 kbps.
+         * [0,1,2,3,4,5,6,7] - AMRNB with the modes representing eight speech codec modes
+         * with bit rates of 4.75, 5.15, 5.90, 6.70, 7.40, 7.95, 10.2, 12.2 kbps.
+         *
+         * <p>If value is not specified, then it means device supports all
+         * modes in the codec but not included in SDP.
+         *
+         * <p>Reference: RFC 4867 Section 8.1, 3GPP 26.445 A.3.1
+         */
+        public static final String KEY_AMR_CODEC_ATTRIBUTE_MODESET_INT_ARRAY =
+                KEY_PREFIX + "amr_codec_attribute_modeset_int_array";
+
+        /**
+         * Specifies the codec attributes of different payload types in
+         * the AMR NarrowBand (AMR-NB) codec.
+         *
+         * <p> The keys in this bundle are payload types specified
+         * in {@link #KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY}.
+         *
+         * <p>Codec attributes allowed as part of AMR-NB codec bundle are,
+         * <UL>
+         *     <LI>{@link #KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT}</LI>
+         *     <LI>{@link #KEY_AMR_CODEC_ATTRIBUTE_MODESET_INT_ARRAY}</LI>
+         * </UL>
+         *
+         * <p> If this bundle is not configured and AMRNB payload type is added
+         * in {@link #KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY}, then default
+         * values as in the individual codec attribute to be used
+         * for that payload type.
+         */
+        public static final String KEY_AMRNB_PAYLOAD_DESCRIPTION_BUNDLE =
+                KEY_PREFIX + "amrnb_payload_description_bundle";
+
+        /**
+         * Specifies the codec attributes of different payload types in
+         * the AMR WideBand (AMR-WB) codec.
+         *
+         * <p> The keys in this bundle are payload types specified
+         * in {@link #KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY}.
+         *
+         * <p>Codec attributes allowed as part of AMR-NB codec bundle are,
+         * <UL>
+         *     <LI>{@link #KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT}</LI>
+         *     <LI>{@link #KEY_AMR_CODEC_ATTRIBUTE_MODESET_INT_ARRAY}</LI>
+         * </UL>
+         *
+         * <p> If this bundle is not configured and AMRWB payload type is added
+         * in {@link #KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY}, then default
+         * values as in the individual codec attribute to be used
+         * for that payload type.
+         */
+        public static final String KEY_AMRWB_PAYLOAD_DESCRIPTION_BUNDLE =
+                KEY_PREFIX + "amrwb_payload_description_bundle";
+
+        /** @hide */
+        @IntDef({
+            EVS_OPERATIONAL_MODE_PRIMARY,
+            EVS_OPERATIONAL_MODE_AMRWB_IO
+        })
+        public @interface EvsOperationalMode {}
+
+        /**  Indicates the EVS primary mode. 3GPP 26.445 Section 3.1 */
+        public static final int EVS_OPERATIONAL_MODE_PRIMARY = 0;
+
+        /** Indicates the EVS AMR-WB IO mode. 3GPP 26.445 Section 3.1 */
+        public static final int EVS_OPERATIONAL_MODE_AMRWB_IO = 1;
+
+        /**
+         * Specifies if the EVS mode used is EVS primary mode
+         * or EVS AMR-WB IO mode.
+         *
+         * <p>Possible values are,
+         * {@link #EVS_OPERATIONAL_MODE_PRIMARY},
+         * {@link #EVS_OPERATIONAL_MODE_AMRWB_IO}
+         *
+         * <p>If this is not present, then {@link #EVS_OPERATIONAL_MODE_PRIMARY} is used.
+         * <p>Reference: 3GPP 26.445 Section 3.1.
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_MODE_SWITCH_INT =
+                KEY_PREFIX + "evs_codec_attribute_mode_switch_int";
+
+        /** @hide */
+        @IntDef({
+            EVS_ENCODED_BW_TYPE_NB,
+            EVS_ENCODED_BW_TYPE_WB,
+            EVS_ENCODED_BW_TYPE_SWB,
+            EVS_ENCODED_BW_TYPE_FB,
+            EVS_ENCODED_BW_TYPE_NB_WB,
+            EVS_ENCODED_BW_TYPE_NB_WB_SWB,
+            EVS_ENCODED_BW_TYPE_NB_WB_SWB_FB,
+            EVS_ENCODED_BW_TYPE_WB_SWB,
+            EVS_ENCODED_BW_TYPE_WB_SWB_FB
+        })
+        public @interface EvsEncodedBwType {}
+
+        /**
+         * EVS encoded Bandwidth is Narrow Band (NB).
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_NB = 0;
+
+        /**
+         * EVS encoded Bandwidth is Wide Band (WB).
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_WB = 1;
+
+        /**
+         * EVS encoded Bandwidth is Super WideBand (SWB).
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_SWB = 2;
+
+        /**
+         * EVS encoded Bandwidth is Full Band (FB).
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_FB = 3;
+
+        /**
+         * EVS encoded Bandwidth is in the range NB,WB.
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_NB_WB = 4;
+
+        /**
+         * EVS encoded Bandwidth is in the range NB,WB,SWB.
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_NB_WB_SWB = 5;
+
+        /**
+         * EVS encoded Bandwidth is in the range NB,WB,SWB,FB.
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_NB_WB_SWB_FB = 6;
+
+        /**
+         * EVS encoded Bandwidth is in the range WB,SWB.
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_WB_SWB = 7;
+
+        /**
+         * EVS encoded Bandwidth is in the range WB,SWB,FB.
+         * Reference: 3GPP 26.441 Table 1.
+         */
+        public static final int EVS_ENCODED_BW_TYPE_WB_SWB_FB = 8;
+
+        /**
+         * Specifies the EVS codec encoding bandwidth options.
+         *
+         * Possible values are,
+         * {@link #EVS_ENCODED_BW_TYPE_NB},
+         * {@link #EVS_ENCODED_BW_TYPE_WB},
+         * {@link #EVS_ENCODED_BW_TYPE_SWB},
+         * {@link #EVS_ENCODED_BW_TYPE_FB},
+         * {@link #EVS_ENCODED_BW_TYPE_NB_WB},
+         * {@link #EVS_ENCODED_BW_TYPE_NB_WB_SWB},
+         * {@link #EVS_ENCODED_BW_TYPE_NB_WB_SWB_FB},
+         * {@link #EVS_ENCODED_BW_TYPE_WB_SWB},
+         * {@link #EVS_ENCODED_BW_TYPE_WB_SWB_FB}
+         *
+         * If this key is not specified, then the behavior is same as
+         * value {@link #EVS_ENCODED_BW_TYPE_NB_WB_SWB}
+         *
+         * <p>Reference: 3GPP 26.441 Table 1.
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_BANDWIDTH_INT =
+                KEY_PREFIX + "evs_codec_attribute_bandwidth_int";
+
+        /** @hide */
+        @IntDef({
+            EVS_PRIMARY_MODE_BITRATE_5_9_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_7_2_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_8_0_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_9_6_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_13_2_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_16_4_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_24_4_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_32_0_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_48_0_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_64_0_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_96_0_KBPS,
+            EVS_PRIMARY_MODE_BITRATE_128_0_KBPS
+        })
+        public @interface EvsPrimaryModeBitRate {}
+
+        /** EVS primary mode with bitrate 5.9 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_5_9_KBPS = 0;
+
+        /** EVS primary mode with bitrate 7.2 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_7_2_KBPS = 1;
+
+        /** EVS primary mode with bitrate 8.0 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_8_0_KBPS = 2;
+
+        /** EVS primary mode with bitrate 9.6 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_9_6_KBPS = 3;
+
+        /** EVS primary mode with bitrate 13.2 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_13_2_KBPS = 4;
+
+        /** EVS primary mode with bitrate 16.4 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_16_4_KBPS = 5;
+
+        /** EVS primary mode with bitrate 24.4 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_24_4_KBPS = 6;
+
+        /** EVS primary mode with bitrate 32.0 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_32_0_KBPS = 7;
+
+        /** EVS primary mode with bitrate 48.0 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_48_0_KBPS = 8;
+
+        /** EVS primary mode with bitrate 64.0 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_64_0_KBPS = 9;
+
+        /** EVS primary mode with bitrate 96.0 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_96_0_KBPS = 10;
+
+        /** EVS primary mode with bitrate 128.0 kbps */
+        public static final int EVS_PRIMARY_MODE_BITRATE_128_0_KBPS = 11;
+
+        /**
+         * Specifies the range of source codec bit-rate for EVS Primary mode
+         * in the session. This is expressed in kilobits per second and
+         * applicable for both the send and the receive directions.
+         *
+         * <p>The range is specified as integer aray of size 2,
+         * represented as [low, high], where low <= high
+         *
+         * <p>Possible values for low and high are,
+         * {@link #EVS_PRIMARY_MODE_BITRATE_5_9_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_7_2_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_8_0_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_9_6_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_13_2_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_16_4_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_24_4_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_32_0_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_48_0_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_64_0_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_96_0_KBPS},
+         * {@link #EVS_PRIMARY_MODE_BITRATE_128_0_KBPS}
+         *
+         * If this key is not specified, then the behavior is same as
+         * value {@link #EVS_PRIMARY_MODE_BITRATE_24_4_KBPS}
+         *
+         * <p>Reference: 3GPP 26.445 Section A.3.1
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_BITRATE_INT_ARRAY =
+                KEY_PREFIX + "evs_codec_attribute_bitrate_int_array";
+
+        /**
+         * Specifies the Channel aware mode (ch-aw-recv) for the receive direction.
+         * This is applicable for EVS codec.
+         *
+         * <p> Permissible values are -1, 0, 2, 3, 5, and 7.
+         * If this key is not specified, then the behavior is same as value 0
+         * (channel aware mode disabled).
+         * <p> If this key is configured, then device is expected to send
+         * this parameter in the SDP offer.
+         *
+         * <p>Reference: 3GPP TS 26.445 section 4.4.5, 3GPP 26.445 Section A.3.1
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_CH_AW_RECV_INT =
+                KEY_PREFIX + "evs_codec_attribute_ch_aw_recv_int";
+
+        /**
+         * Specifies whether to limit the session to header-full format.
+         * This applies to both directions in the session. This attribute
+         * is applicable for EVS codec.
+         *
+         * <p>Permissible values are 0, 1
+         * If hf-only is 1, only Header-Full format is used and hf-only is
+         * included in the SDP.
+         * <p>If hf-only is 0, both Compact and Header-Full formats are used
+         * and hf-only is included in the SDP.
+         * <p>If this key is not present, then both Compact
+         * and Header-Full formats are used and hf-only is not included in
+         * the SDP.
+         * <p> If this key is configured, then device is expected to send
+         * this parameter in the SDP offer if operator required it.
+         *
+         * <p>Reference: 3GPP 26.445 Section A.3.1.
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_HF_ONLY_INT =
+                KEY_PREFIX + "evs_codec_attribute_hf_only_int";
+
+        /**
+         * Specifies whether DTX (Discontinuous transmission) is enabled
+         * or not. This applies to both directions in the session.
+         * This attribute is applicable for EVS codec and can be used
+         * in both EVS Primary mode and EVS AMR-WB IO mode.
+         *
+         * <p>If {@code true}: Indicates DTX is enabled.
+         * If {@code false}: Indicates DTX is disabled.
+         *
+         * <p>If this is not present, then default value of {@code true}
+         * will apply.
+         * <p>Reference: 3GPP TS 26.445 Section A.3.1.
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_DTX_BOOL =
+                KEY_PREFIX + "evs_codec_attribute_dtx_bool";
+
+        /**
+         * This is used if further restriction is required on DTX in the
+         * receive direction. This attribute is applicable for EVS codec
+         * and can be used in both EVS Primary mode and EVS AMR-WB IO mode.
+         *
+         * <p> If this value is true or not present, then DTX setting is
+         * dependent on {@link #KEY_EVS_CODEC_ATTRIBUTE_DTX_BOOL}.
+         *
+         * <p> If this is not present, then default value of {@code true}
+         * will apply.
+         *
+         * <p>Reference: 3GPP TS 26.445 Section A.3.1.
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_DTX_RECV_BOOL =
+                KEY_PREFIX + "evs_codec_attribute_dtx_recv_bool";
+
+        /**
+         * Specifies the number of audio channels.
+         * If this is not present, then default value of 1 will apply.
+         *
+         * <p>Reference: RFC 3551
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_CHANNELS_INT =
+                KEY_PREFIX + "evs_codec_attribute_channels_int";
+
+        /**
+         * Indicates whether the Codec Mode Request (CMR) is supported
+         * for the session.
+         * This attribute is applicable for EVS codec in Primary Mode only.
+         *
+         * <p>Possible values are -1, 0, 1. If this key is not present,
+         * then behavior as per value 0 is applicable.
+         *
+         * <p>Reference: 3GPP 26.445 Section A.3.1, 3GPP 26.114 Table 6.2a
+         */
+        public static final String KEY_EVS_CODEC_ATTRIBUTE_CMR_INT =
+                KEY_PREFIX + "codec_attribute_cmr_int";
+
+        /**
+         * Specifies the number of frame-blocks. This indicates the frame-block period
+         * at which codec mode changes are allowed for the sender. This attribute is
+         * applicable for EVS codec in AMR-WB IO mode and AMR-WB.
+         *
+         * <p>Possible values are 1, 2.
+         * If the key is not present, behavior as per value 1 is applicable and this
+         * parameter is not included in SDP.
+         *
+         * <p>Reference: RFC 4867 Section 8.1.
+         */
+        public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_PERIOD_INT =
+                KEY_PREFIX + "codec_attribute_mode_change_period_int";
+
+        /**
+         * Specifies if the client is capable to transmit with a restricted mode
+         * change period. This attribute is applicable for EVS codec in
+         * AMR-WB IO mode and AMR-WB.
+         *
+         * <p>Possible values are 1, 2. If this key is not present,
+         * then behavior as per value 1 is applicable and this
+         * parameter is not included in SDP.
+         *
+         * <p>Reference: RFC 4867 Section 8.1.
+         */
+        public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_CAPABILITY_INT =
+                KEY_PREFIX + "codec_attribute_mode_change_capability_int";
+
+        /**
+         * Specifies the allowed mode changes for the sender in the active mode set.
+         * This attribute is applicable for EVS codec in AMR-WB IO mode
+         * and AMR-WB.
+         *
+         * <p>Possible values are 0, 1. If value is 1, then the sender should only
+         * perform mode changes to the neighboring modes in the active codec mode set.
+         * If value is 0, then mode changes between any two modes
+         * in the active codec mode set is allowed.
+         * If the key is not present, behavior as per value 0 is applicable and this
+         * parameter is not included in SDP.
+         *
+         * <p>Reference: RFC 4867 Section 8.1.
+         */
+        public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_NEIGHBOR_INT =
+                KEY_PREFIX + "codec_attribute_mode_change_neighbor_int";
+
+        /**
+         * Specifies the codec attributes of different payload types in
+         * the EVS codec.
+         *
+         * <p> The keys in this bundle are payload types specified
+         * in {@link #KEY_EVS_PAYLOAD_TYPE_INT_ARRAY}.
+         *
+         * <p>Codec attributes allowed as part of EVS codec are,
+         * <UL>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_BANDWIDTH_INT}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_BITRATE_INT_ARRAY}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_CH_AW_RECV_INT}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_HF_ONLY_INT}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_DTX_BOOL}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_DTX_RECV_BOOL}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_MODE_SWITCH_INT}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_CMR_INT}</LI>
+         *     <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_CHANNELS_INT}</LI>
+         *     <LI>{@link #KEY_CODEC_ATTRIBUTE_MODE_CHANGE_PERIOD_INT}</LI>
+         *     <LI>{@link #KEY_CODEC_ATTRIBUTE_MODE_CHANGE_CAPABILITY_INT}</LI>
+         *     <LI>{@link #KEY_CODEC_ATTRIBUTE_MODE_CHANGE_NEIGHBOR_INT}</LI>
+         * </UL>
+         */
+        public static final String KEY_EVS_PAYLOAD_DESCRIPTION_BUNDLE =
+                KEY_PREFIX + "evs_payload_description_bundle";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_CARRIER_VOLTE_ROAMING_AVAILABLE_BOOL, true);
+            defaults.putBoolean(KEY_INCLUDE_CALLER_ID_SERVICE_CODES_IN_SIP_INVITE_BOOL, false);
+            defaults.putBoolean(KEY_MULTIENDPOINT_SUPPORTED_BOOL, false);
+            defaults.putBoolean(KEY_SESSION_TIMER_SUPPORTED_BOOL, true);
+            defaults.putBoolean(KEY_OIP_SOURCE_FROM_HEADER_BOOL, false);
+            defaults.putBoolean(KEY_PRACK_SUPPORTED_FOR_18X_BOOL, false);
+            defaults.putBoolean(KEY_VOICE_QOS_PRECONDITION_SUPPORTED_BOOL, true);
+            defaults.putBoolean(KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL, false);
+
+            defaults.putInt(KEY_SESSION_REFRESHER_TYPE_INT, SESSION_REFRESHER_TYPE_UAC);
+            defaults.putInt(KEY_SESSION_PRIVACY_TYPE_INT, SESSION_PRIVACY_TYPE_HEADER);
+            defaults.putInt(KEY_SESSION_REFRESH_METHOD_INT,
+                            SESSION_REFRESH_METHOD_UPDATE_PREFERRED);
+            defaults.putInt(KEY_CONFERENCE_SUBSCRIBE_TYPE_INT,
+                            CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG);
+            defaults.putInt(KEY_AUDIO_RTP_INACTIVITY_TIMER_MILLIS_INT, 20000);
+            defaults.putInt(KEY_AUDIO_RTCP_INACTIVITY_TIMER_MILLIS_INT, 20000);
+            defaults.putInt(KEY_DEDICATED_BEARER_WAIT_TIMER_MILLIS_INT, 8000);
+            defaults.putInt(KEY_RINGING_TIMER_MILLIS_INT, 90000);
+            defaults.putInt(KEY_RINGBACK_TIMER_MILLIS_INT, 90000);
+            defaults.putInt(KEY_MO_CALL_REQUEST_TIMEOUT_MILLIS_INT, 5000);
+            defaults.putInt(KEY_SESSION_EXPIRES_TIMER_SEC_INT, 1800);
+            defaults.putInt(KEY_MINIMUM_SESSION_EXPIRES_TIMER_SEC_INT, 90);
+            defaults.putInt(KEY_AUDIO_AS_BANDWIDTH_KBPS_INT, 41);
+            defaults.putInt(KEY_AUDIO_RS_BANDWIDTH_BPS_INT, 600);
+            defaults.putInt(KEY_AUDIO_RR_BANDWIDTH_BPS_INT, 2000);
+            defaults.putInt(KEY_VOICE_RTP_PACKET_LOSS_RATE_THRESHOLD_INT, 40);
+            defaults.putInt(KEY_VOICE_RTP_JITTER_THRESHOLD_MILLIS_INT, 120);
+            defaults.putLong(KEY_VOICE_RTP_INACTIVITY_TIME_THRESHOLD_MILLIS_LONG, 5000);
+
+            defaults.putIntArray(
+                    KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY,
+                    new int[] {
+                        Ims.RTCP_INACTIVITY_ON_CONNECTED,
+                        Ims.RTP_INACTIVITY_ON_CONNECTED,
+                        Ims.E911_RTCP_INACTIVITY_ON_CONNECTED,
+                        Ims.RTCP_INACTIVITY_ON_HOLD
+                    });
+
+            defaults.putIntArray(
+                    KEY_SRVCC_TYPE_INT_ARRAY,
+                    new int[] {
+                        BASIC_SRVCC_SUPPORT,
+                        ALERTING_SRVCC_SUPPORT,
+                        PREALERTING_SRVCC_SUPPORT,
+                        MIDCALL_SRVCC_SUPPORT
+                    });
+
+            defaults.putString(KEY_CONFERENCE_FACTORY_URI_STRING, "");
+
+            PersistableBundle audio_codec_capability_payload_types = new PersistableBundle();
+
+            audio_codec_capability_payload_types.putIntArray(
+                    KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY,
+                    new int[] { 97, 98 });
+
+            audio_codec_capability_payload_types.putIntArray(
+                    KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY,
+                    new int[] { 99, 100 });
+
+            audio_codec_capability_payload_types.putIntArray(
+                    KEY_DTMFWB_PAYLOAD_TYPE_INT_ARRAY,
+                    new int[] { 101 });
+
+            audio_codec_capability_payload_types.putIntArray(
+                    KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY,
+                    new int[] { 102 });
+
+            defaults.putPersistableBundle(
+                    KEY_AUDIO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE,
+                    audio_codec_capability_payload_types);
+
+            /* Setting defaults for AMRWB */
+            PersistableBundle all_amrwb_payload_bundles = new PersistableBundle();
+            PersistableBundle amrwb_bundle_instance1 = new PersistableBundle();
+
+            all_amrwb_payload_bundles.putPersistableBundle(
+                    "97", /* Same value of payload type as in KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY */
+                    amrwb_bundle_instance1);
+
+            PersistableBundle amrwb_bundle_instance2 = new PersistableBundle();
+
+            amrwb_bundle_instance2.putInt(KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT,
+                                          OCTET_ALIGNED);
+
+            all_amrwb_payload_bundles.putPersistableBundle(
+                    "98", /* Same value of payload type as in KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY */
+                    amrwb_bundle_instance2);
+
+            defaults.putPersistableBundle(
+                    KEY_AMRWB_PAYLOAD_DESCRIPTION_BUNDLE,
+                    all_amrwb_payload_bundles);
+
+            /* Setting defaults for AMRNB */
+            PersistableBundle all_amrnb_payload_bundles = new PersistableBundle();
+            PersistableBundle amrnb_bundle_instance1 = new PersistableBundle();
+
+            all_amrnb_payload_bundles.putPersistableBundle(
+                    "99", /* Same value of payload type as in KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY */
+                    amrnb_bundle_instance1);
+
+            PersistableBundle amrnb_bundle_instance2 = new PersistableBundle();
+
+            amrnb_bundle_instance2.putInt(KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT,
+                                          OCTET_ALIGNED);
+
+            all_amrnb_payload_bundles.putPersistableBundle(
+                    "100", /* Same value of payload type as in KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY */
+                    amrnb_bundle_instance2);
+
+            defaults.putPersistableBundle(
+                    KEY_AMRNB_PAYLOAD_DESCRIPTION_BUNDLE,
+                    all_amrnb_payload_bundles);
+
+            return defaults;
+        }
+    }
+
+    /**
+     * IMS SMS configs. This groups the configs specific for SMS over IMS
+     */
+    public static final class ImsSms {
+        private ImsSms() {}
+
+        /** Prefix of all imssms.KEY_* constants. */
+        public static final String KEY_PREFIX = "imssms.";
+
+        /**
+         * Flag specifying if SMS over IMS support is available or not.
+         *
+         * <p>If {@code true}: SMS over IMS support available.
+         * {@code false}: otherwise.
+         */
+        public static final String KEY_SMS_OVER_IMS_SUPPORTED_BOOL =
+                KEY_PREFIX + "sms_over_ims_supported_bool";
+
+        /**
+         * Flag specifying whether to allow SMS CSFB in case of
+         * SMS over PS failure.
+         *
+         * <p>If {@code true}: allow SMS CSFB in case of SMS over PS failure.
+         * {@code false} otherwise.
+         */
+        public static final String KEY_SMS_CSFB_RETRY_ON_FAILURE_BOOL =
+                KEY_PREFIX + "sms_csfb_retry_on_failure_bool";
+
+        /** @hide */
+        @IntDef({
+            SMS_FORMAT_3GPP,
+            SMS_FORMAT_3GPP2
+        })
+        public @interface SmsFormat {}
+
+        /** SMS format is 3GPP. */
+        public static final int SMS_FORMAT_3GPP = 0;
+
+        /** SMS format is 3GPP2. */
+        public static final int SMS_FORMAT_3GPP2 = 1;
+
+        /**
+         * Specifies the SMS over IMS format.
+         *
+         * <p>Possible values are,
+         * {@link #SMS_FORMAT_3GPP},
+         * {@link #SMS_FORMAT_3GPP2}
+         */
+        public static final String KEY_SMS_OVER_IMS_FORMAT_INT =
+                KEY_PREFIX + "sms_over_ims_format_int";
+
+        /**
+         * List of different RAT technologies on which SMS over IMS
+         * is supported.
+         *
+         * <p>Possible values are,
+         * {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#IWLAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#UTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#GERAN}
+         */
+        public static final String KEY_SMS_OVER_IMS_SUPPORTED_RATS_INT_ARRAY =
+                KEY_PREFIX + "sms_over_ims_supported_rats_int_array";
+
+        /**
+         * Maximum Retry Count for Failure, If the Retry Count exceeds this value,
+         * it must display to User Interface as sending failed
+         */
+        public static final String KEY_SMS_MAX_RETRY_COUNT_INT =
+                KEY_PREFIX + "sms_max_retry_count_int";
+
+        /**
+         * Maximum Retry Count for SMS over IMS on Failure, If the Retry Count exceeds this value,
+         * and if the retry count is less than {@link #KEY_SMS_MAX_RETRY_COUNT_INT}
+         * sending SMS should fallback to CS
+         */
+        public static final String KEY_SMS_MAX_RETRY_OVER_IMS_COUNT_INT =
+                KEY_PREFIX + "sms_max_retry_over_ims_count_int";
+
+        /**
+         * Delay Timer Value in milliseconds
+         * Retry SMS over IMS after this Timer expires
+         */
+        public static final String KEY_SMS_OVER_IMS_SEND_RETRY_DELAY_MILLIS_INT =
+                KEY_PREFIX + "sms_over_ims_send_retry_delay_millis_int";
+
+        /**
+         * TR1 Timer Value in milliseconds,
+         * Waits for RP-Ack from network for MO SMS.
+         */
+        public static final String KEY_SMS_TR1_TIMER_MILLIS_INT =
+                KEY_PREFIX + "sms_tr1_timer_millis_int";
+
+        /**
+         * TR2 Timer Value in milliseconds,
+         * Waits for RP-Ack from Transfer Layer for MT SMS.
+         */
+        public static final String KEY_SMS_TR2_TIMER_MILLIS_INT =
+                KEY_PREFIX + "sms_tr2_timer_millis_int";
+
+        /**
+         * SMS RP-Cause Values for which SMS should be retried over IMS
+         *
+         * <p>Possible values are,
+         * {@link SmsManager#SMS_RP_CAUSE_UNALLOCATED_NUMBER}
+         * {@link SmsManager#SMS_RP_CAUSE_OPERATOR_DETERMINED_BARRING}
+         * {@link SmsManager#SMS_RP_CAUSE_CALL_BARRING}
+         * {@link SmsManager#SMS_RP_CAUSE_RESERVED}
+         * {@link SmsManager#SMS_RP_CAUSE_SHORT_MESSAGE_TRANSFER_REJECTED}
+         * {@link SmsManager#SMS_RP_CAUSE_DESTINATION_OUT_OF_ORDER}
+         * {@link SmsManager#SMS_RP_CAUSE_UNIDENTIFIED_SUBSCRIBER}
+         * {@link SmsManager#SMS_RP_CAUSE_FACILITY_REJECTED}
+         * {@link SmsManager#SMS_RP_CAUSE_UNKNOWN_SUBSCRIBER}
+         * {@link SmsManager#SMS_RP_CAUSE_NETWORK_OUT_OF_ORDER}
+         * {@link SmsManager#SMS_RP_CAUSE_TEMPORARY_FAILURE}
+         * {@link SmsManager#SMS_RP_CAUSE_CONGESTION}
+         * {@link SmsManager#SMS_RP_CAUSE_RESOURCES_UNAVAILABLE}
+         * {@link SmsManager#SMS_RP_CAUSE_FACILITY_NOT_SUBSCRIBED}
+         * {@link SmsManager#SMS_RP_CAUSE_FACILITY_NOT_IMPLEMENTED}
+         * {@link SmsManager#SMS_RP_CAUSE_INVALID_MESSAGE_REFERENCE_VALUE}
+         * {@link SmsManager#SMS_RP_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE}
+         * {@link SmsManager#SMS_RP_CAUSE_INVALID_MANDATORY_INFORMATION}
+         * {@link SmsManager#SMS_RP_CAUSE_MESSAGE_TYPE_NON_EXISTENT}
+         * {@link SmsManager#SMS_RP_CAUSE_MESSAGE_INCOMPATIBLE_WITH_PROTOCOL_STATE}
+         * {@link SmsManager#SMS_RP_CAUSE_INFORMATION_ELEMENT_NON_EXISTENT}
+         * {@link SmsManager#SMS_RP_CAUSE_PROTOCOL_ERROR}
+         * {@link SmsManager#SMS_RP_CAUSE_INTERWORKING_UNSPECIFIED
+         */
+        public static final String KEY_SMS_RP_CAUSE_VALUES_TO_RETRY_OVER_IMS_INT_ARRAY =
+                KEY_PREFIX + "sms_rp_cause_values_to_retry_over_ims_int_array";
+
+        /**
+         * SMS RP-Cause Values for which Sending SMS should fallback
+         */
+        public static final String KEY_SMS_RP_CAUSE_VALUES_TO_FALLBACK_INT_ARRAY =
+                KEY_PREFIX + "sms_rp_cause_values_to_fallback_int_array";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_SMS_OVER_IMS_SUPPORTED_BOOL, true);
+            defaults.putBoolean(KEY_SMS_CSFB_RETRY_ON_FAILURE_BOOL, true);
+
+            defaults.putInt(KEY_SMS_OVER_IMS_FORMAT_INT, SMS_FORMAT_3GPP);
+
+            defaults.putInt(KEY_SMS_MAX_RETRY_COUNT_INT, 3);
+            defaults.putInt(KEY_SMS_MAX_RETRY_OVER_IMS_COUNT_INT, 3);
+            defaults.putInt(KEY_SMS_OVER_IMS_SEND_RETRY_DELAY_MILLIS_INT,
+                    2000);
+            defaults.putInt(KEY_SMS_TR1_TIMER_MILLIS_INT, 130000);
+            defaults.putInt(KEY_SMS_TR2_TIMER_MILLIS_INT, 15000);
+
+            defaults.putIntArray(
+                    KEY_SMS_RP_CAUSE_VALUES_TO_RETRY_OVER_IMS_INT_ARRAY,
+                    new int[] {
+                        SmsManager.SMS_RP_CAUSE_TEMPORARY_FAILURE
+                    });
+            defaults.putIntArray(
+                    KEY_SMS_RP_CAUSE_VALUES_TO_FALLBACK_INT_ARRAY,
+                    new int[] {
+                        SmsManager.SMS_RP_CAUSE_UNALLOCATED_NUMBER,
+                        SmsManager.SMS_RP_CAUSE_OPERATOR_DETERMINED_BARRING,
+                        SmsManager.SMS_RP_CAUSE_CALL_BARRING,
+                        SmsManager.SMS_RP_CAUSE_RESERVED,
+                        SmsManager.SMS_RP_CAUSE_SHORT_MESSAGE_TRANSFER_REJECTED,
+                        SmsManager.SMS_RP_CAUSE_DESTINATION_OUT_OF_ORDER,
+                        SmsManager.SMS_RP_CAUSE_UNIDENTIFIED_SUBSCRIBER,
+                        SmsManager.SMS_RP_CAUSE_FACILITY_REJECTED,
+                        SmsManager.SMS_RP_CAUSE_UNKNOWN_SUBSCRIBER,
+                        SmsManager.SMS_RP_CAUSE_NETWORK_OUT_OF_ORDER,
+                        SmsManager.SMS_RP_CAUSE_CONGESTION,
+                        SmsManager.SMS_RP_CAUSE_RESOURCES_UNAVAILABLE,
+                        SmsManager.SMS_RP_CAUSE_FACILITY_NOT_SUBSCRIBED,
+                        SmsManager.SMS_RP_CAUSE_FACILITY_NOT_IMPLEMENTED,
+                        SmsManager.SMS_RP_CAUSE_INVALID_MESSAGE_REFERENCE_VALUE,
+                        SmsManager.SMS_RP_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE,
+                        SmsManager.SMS_RP_CAUSE_INVALID_MANDATORY_INFORMATION,
+                        SmsManager.SMS_RP_CAUSE_MESSAGE_TYPE_NON_EXISTENT,
+                        SmsManager.SMS_RP_CAUSE_MESSAGE_INCOMPATIBLE_WITH_PROTOCOL_STATE,
+                        SmsManager.SMS_RP_CAUSE_INFORMATION_ELEMENT_NON_EXISTENT,
+                        SmsManager.SMS_RP_CAUSE_PROTOCOL_ERROR,
+                        SmsManager.SMS_RP_CAUSE_INTERWORKING_UNSPECIFIED
+                    });
+
+            defaults.putIntArray(
+                    KEY_SMS_OVER_IMS_SUPPORTED_RATS_INT_ARRAY,
+                    new int[] {
+                        AccessNetworkType.EUTRAN,
+                        AccessNetworkType.IWLAN
+                    });
+
+            return defaults;
+        }
+    }
+
+    /**
+     * IMS RTT configs. This groups the configs specific for text media,
+     * RTT (Real Time Text).
+     */
+    public static final class ImsRtt {
+        private ImsRtt() {}
+
+        /** Prefix of all imsrtt.KEY_* constants. */
+        public static final String KEY_PREFIX = "imsrtt.";
+
+        /**
+         * Flag specifying whether text media is allowed on default bearer.
+         *
+         * <p>If {@code true}: text media can be sent on default bearer.
+         * {@code false} otherwise.
+         */
+        public static final String KEY_TEXT_ON_DEFAULT_BEARER_SUPPORTED_BOOL =
+                KEY_PREFIX + "text_on_default_bearer_supported_bool";
+
+        /**
+         * Flag specifying whether QoS preconditions are supported for text.
+         *
+         * <p>If {@code true}: QoS Preconditions are supported.
+         * {@code false} otherwise.
+         * <p>Reference: 3GPP TS 24.229
+         */
+        public static final String KEY_TEXT_QOS_PRECONDITION_SUPPORTED_BOOL =
+                KEY_PREFIX + "text_qos_precondition_supported_bool";
+
+        /**
+         * Specifies the AS (Application Specific) SDP modifier for text media.
+         *
+         * <p>Expressed in kilobits per second as per RFC 3556 Section 2.
+         */
+        public static final String KEY_TEXT_AS_BANDWIDTH_KBPS_INT =
+                KEY_PREFIX + "text_as_bandwidth_kbps_int";
+
+        /**
+         * Specifies the RS (RTCP bandwidth-Sender) SDP modifier for text media.
+         *
+         * <p>This indicates the RTCP bandwidth allocated to active data senders
+         * for text media.
+         *
+         * <p>Expressed in bits per second as per RFC 3556 Section 2.
+         */
+        public static final String KEY_TEXT_RS_BANDWIDTH_BPS_INT =
+                KEY_PREFIX + "text_rs_bandwidth_bps_int";
+
+        /**
+         * Specifies the RR (RTCP bandwidth-Receiver) SDP modifier for
+         * text media.
+         *
+         * <p>This indicates the RTCP bandwidth allocated to receivers
+         * for text media.
+         *
+         * <p>Expressed in bits per second as per RFC 3556 Section 2.
+         */
+        public static final String KEY_TEXT_RR_BANDWIDTH_BPS_INT =
+                KEY_PREFIX + "text_rr_bandwidth_bps_int";
+
+        /**
+         * Specifies the Text Codec capability.
+         *
+         * <p>Possible keys in this bundle are,
+         * <UL>
+         *     <LI>{@link #KEY_T140_PAYLOAD_TYPE_INT}</LI>
+         *     <LI>{@link #KEY_RED_PAYLOAD_TYPE_INT}</LI>
+         * </UL>
+         */
+        public static final String KEY_TEXT_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE =
+                KEY_PREFIX + "text_codec_capability_payload_types_bundle";
+
+        /** Integer representing payload type for T140 codec.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_T140_PAYLOAD_TYPE_INT =
+                KEY_PREFIX + "t140_payload_type_int";
+
+        /** Integer representing payload type for RED/redundancy codec.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_RED_PAYLOAD_TYPE_INT =
+                KEY_PREFIX + "red_payload_type_int";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_TEXT_ON_DEFAULT_BEARER_SUPPORTED_BOOL, false);
+            defaults.putBoolean(KEY_TEXT_QOS_PRECONDITION_SUPPORTED_BOOL, true);
+
+            defaults.putInt(KEY_TEXT_AS_BANDWIDTH_KBPS_INT, 4);
+            defaults.putInt(KEY_TEXT_RS_BANDWIDTH_BPS_INT, 100);
+            defaults.putInt(KEY_TEXT_RR_BANDWIDTH_BPS_INT, 300);
+
+            PersistableBundle text_codec_capability_payload_types = new PersistableBundle();
+
+            text_codec_capability_payload_types.putInt(
+                    KEY_RED_PAYLOAD_TYPE_INT,
+                    112);
+
+            text_codec_capability_payload_types.putInt(
+                    KEY_T140_PAYLOAD_TYPE_INT,
+                    111);
+
+            defaults.putPersistableBundle(
+                    KEY_TEXT_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE,
+                    text_codec_capability_payload_types);
+
+            return defaults;
+        }
+    }
+
+    /**
+     * Emergency Call/E911. This groups the configs specific for emergency call
+     * over IMS.
+     *
+     * <p> Reference: 3GPP 24.229, 3GPP 23.167 Annex H, 3GPP 24.301.
+     */
+    public static final class ImsEmergency {
+        private ImsEmergency() {}
+
+        /** Prefix of all imsemergency.KEY_* constants. */
+        public static final String KEY_PREFIX = "imsemergency.";
+
+        /**
+         * Flag specifying whether UE would retry E911 call on
+         * IMS PDN if emergency PDN setup failed.
+         *
+         * <p>If {@code true}: Allow UE to retry emergency call on
+         * IMS PDN if emergency PDN setup failed.{@code false} otherwise.
+         */
+        public static final String KEY_RETRY_EMERGENCY_ON_IMS_PDN_BOOL =
+                KEY_PREFIX + "retry_emergency_on_ims_pdn_bool";
+
+        /**
+         * Flag specifying whether UE should enter Emergency CallBack Mode(ECBM)
+         * after E911 call is ended.
+         *
+         * <p>If {@code true}: Enter ECBM mode after E911 call is ended.
+         * {@code false} otherwise.
+         */
+        public static final String KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL =
+                KEY_PREFIX + "emergency_callback_mode_supported_bool";
+
+        /**
+         * Flag specifying whether QoS preconditions are supported for emergency
+         * call setup.
+         *
+         * <p>If {@code true}: QoS Preconditions are supported.
+         * {@code false} otherwise.
+         *
+         * <p>Reference: 3GPP TS 24.229
+         */
+        public static final String KEY_EMERGENCY_QOS_PRECONDITION_SUPPORTED_BOOL =
+                KEY_PREFIX + "emergency_qos_precondition_supported_bool";
+
+        /**
+         * List of different RAT technologies on which emergency call using IMS
+         * is supported.
+         *
+         * <p>Possible values are,
+         * {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#IWLAN}
+         */
+        public static final String KEY_EMERGENCY_OVER_IMS_SUPPORTED_RATS_INT_ARRAY =
+                KEY_PREFIX + "emergency_over_ims_supported_rats_int_array";
+
+        /**
+         * Specifies the maximum time from deciding that an emergency service is to
+         * be established until completion of the emergency registration procedure.
+         * Upon timer expiry, the UE considers the emergency REGISTER request or
+         * the emergency call attempt as failed.
+         */
+        public static final String KEY_EMERGENCY_REGISTRATION_TIMER_MILLIS_INT =
+                KEY_PREFIX + "emergency_registration_timer_millis_int";
+
+        /**
+         * This setting will be specify the wait time for refreshing
+         * geolocation information before dialing emergency call.
+         */
+        public static final String KEY_REFRESH_GEOLOCATION_TIMEOUT_MILLIS_INT =
+                KEY_PREFIX + "refresh_geolocation_timeout_millis_int";
+
+        /**
+         * List of 3GPP access network technologies where e911 over IMS is supported
+         * in the home network and domestic 3rd-party networks. The order in the list represents
+         * the preference. The domain selection service shall scan the network type in the order
+         * of the preference.
+         *
+         * <p>Possible values are,
+         * {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
+         *
+         * The default value for this key is
+         * {{@link AccessNetworkConstants.AccessNetworkType#EUTRAN},
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String
+                KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY = KEY_PREFIX
+                        + "emergency_over_ims_supported_3gpp_network_types_int_array";
+
+        /**
+         * List of 3GPP access network technologies where e911 over IMS is supported
+         * in the roaming network and non-domestic 3rd-party networks. The order in the list
+         * represents the preference. The domain selection service shall scan the network type
+         * in the order of the preference.
+         *
+         * <p>Possible values are,
+         * {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
+         *
+         * The default value for this key is
+         * {{@link AccessNetworkConstants.AccessNetworkType#EUTRAN},
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String
+                KEY_EMERGENCY_OVER_IMS_ROAMING_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY = KEY_PREFIX
+                        + "emergency_over_ims_roaming_supported_3gpp_network_types_int_array";
+
+        /**
+         * List of CS access network technologies where circuit-switched emergency calls are
+         * supported in the home network and domestic 3rd-party networks. The order in the list
+         * represents the preference. The domain selection service shall scan the network type
+         * in the order of the preference.
+         *
+         * <p>Possible values are,
+         * {@link AccessNetworkConstants.AccessNetworkType#GERAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#UTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#CDMA2000}
+         *
+         * The default value for this key is
+         * {{@link AccessNetworkConstants.AccessNetworkType#UTRAN},
+         * {@link AccessNetworkConstants.AccessNetworkType#GERAN}}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY =
+                KEY_PREFIX + "emergency_over_cs_supported_access_network_types_int_array";
+
+        /**
+         * List of CS access network technologies where circuit-switched emergency calls are
+         * supported in the roaming network and non-domestic 3rd-party networks. The order
+         * in the list represents the preference. The domain selection service shall scan
+         * the network type in the order of the preference.
+         *
+         * <p>Possible values are,
+         * {@link AccessNetworkConstants.AccessNetworkType#GERAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#UTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#CDMA2000}
+         *
+         * The default value for this key is
+         * {{@link AccessNetworkConstants.AccessNetworkType#UTRAN},
+         * {@link AccessNetworkConstants.AccessNetworkType#GERAN}}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String
+                KEY_EMERGENCY_OVER_CS_ROAMING_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY = KEY_PREFIX
+                        + "emergency_over_cs_roaming_supported_access_network_types_int_array";
+
+        /** @hide */
+        @IntDef({
+            DOMAIN_CS,
+            DOMAIN_PS_3GPP,
+            DOMAIN_PS_NON_3GPP
+        })
+        public @interface EmergencyDomain {}
+
+        /**
+         * Circuit switched domain.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final int DOMAIN_CS = 1;
+
+        /**
+         * Packet switched domain over 3GPP networks.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final int DOMAIN_PS_3GPP = 2;
+
+        /**
+         * Packet switched domain over non-3GPP networks such as Wi-Fi.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final int DOMAIN_PS_NON_3GPP = 3;
+
+        /**
+         * Specifies the emergency call domain preference for the home network.
+         * The domain selection service shall choose the domain in the order
+         * for attempting the emergency call
+         *
+         * <p>Possible values are,
+         * {@link #DOMAIN_CS}
+         * {@link #DOMAIN_PS_3GPP}
+         * {@link #DOMAIN_PS_NON_3GPP}.
+         *
+         * The default value for this key is
+         * {{@link #DOMAIN_PS_3GPP},
+         * {@link #DOMAIN_CS},
+         * {@link #DOMAIN_PS_NON_3GPP}}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY =
+                KEY_PREFIX + "emergency_domain_preference_int_array";
+
+        /**
+         * Specifies the emergency call domain preference for the roaming network.
+         * The domain selection service shall choose the domain in the order
+         * for attempting the emergency call.
+         *
+         * <p>Possible values are,
+         * {@link #DOMAIN_CS}
+         * {@link #DOMAIN_PS_3GPP}
+         * {@link #DOMAIN_PS_NON_3GPP}.
+         *
+         * The default value for this key is
+         * {{@link #DOMAIN_PS_3GPP},
+         * {@link #DOMAIN_CS},
+         * {@link #DOMAIN_PS_NON_3GPP}}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_EMERGENCY_DOMAIN_PREFERENCE_ROAMING_INT_ARRAY =
+                KEY_PREFIX + "emergency_domain_preference_roaming_int_array";
+
+        /**
+         * Specifies whether the emergency call shall be preferred over IMS or not
+         * irrespective of IMS registration status.
+         * If the value of the config is {@code true} then emergency calls shall prefer IMS
+         * when device is combined-attached in LTE network and IMS is not registered.
+         * If the value of the config is {@code false} then emergency calls use CS domain
+         * in the same scenario.
+         *
+         * The default value for this key is {@code false}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL =
+                KEY_PREFIX + "prefer_ims_emergency_when_voice_calls_on_cs_bool";
+
+        /** @hide */
+        @IntDef({
+            VOWIFI_REQUIRES_NONE,
+            VOWIFI_REQUIRES_SETTING_ENABLED,
+            VOWIFI_REQUIRES_VALID_EID,
+        })
+        public @interface VoWiFiRequires {}
+
+        /**
+         * Default value.
+         * If {@link ImsWfc#KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL} is {@code true},
+         * VoWi-Fi emergency call shall be attempted if Wi-Fi network is connected.
+         * Otherwise, it shall be attempted if IMS is registered over Wi-Fi.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final int VOWIFI_REQUIRES_NONE = 0;
+
+        /**
+         * VoWi-Fi emergency call shall be attempted on IMS over Wi-Fi if Wi-Fi network is connected
+         * and Wi-Fi calling setting is enabled. This value is applicable if the value of
+         * {@link ImsWfc#KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL} is {@code true}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final int VOWIFI_REQUIRES_SETTING_ENABLED = 1;
+
+        /**
+         * VoWi-Fi emergency call shall be attempted on IMS over Wi-Fi if Wi-Fi network is connected
+         * and Wi-Fi calling is activated successfully. The device shall have the valid
+         * Entitlement ID if the user activates VoWi-Fi emergency calling successfully.
+         * This value is applicable if the value of
+         * {@link ImsWfc#KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL} is {@code true}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final int VOWIFI_REQUIRES_VALID_EID = 2;
+
+        /**
+         * Specifies the condition when emergency call shall be attempted on IMS over Wi-Fi.
+         *
+         * <p>Possible values are,
+         * {@link #VOWIFI_REQUIRES_NONE}
+         * {@link #VOWIFI_REQUIRES_SETTING_ENABLED}
+         * {@link #VOWIFI_REQUIRES_VALID_EID}
+         *
+         * The default value for this key is {@link #VOWIFI_REQUIRES_NONE}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT =
+                KEY_PREFIX + "emergency_vowifi_requires_condition_int";
+
+        /**
+         * Specifies maximum number of emergency call retries over Wi-Fi.
+         * This is valid only when {@link #DOMAIN_PS_NON_3GPP} is included in
+         * {@link #KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY} or
+         * {@link #KEY_EMERGENCY_DOMAIN_PREFERENCE_ROAMING_INT_ARRAY}.
+         *
+         * The default value for this key is 1.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT =
+                KEY_PREFIX + "maximum_number_of_emergency_tries_over_vowifi_int";
+
+        /**
+         * Emergency scan timer to wait for scan results from radio before attempting the call
+         * over Wi-Fi. On timer expiry, if emergency call on Wi-Fi is allowed and possible,
+         * telephony shall cancel the scan and place the call on Wi-Fi. If emergency call on Wi-Fi
+         * is not possible, then domain selection continues to wait for the scan result from the
+         * radio. If an emergency scan result is received before the timer expires, the timer shall
+         * be stopped and no dialing over Wi-Fi will be tried. If this value is set to 0, then
+         * the timer is never started and domain selection waits for the scan result from the radio.
+         *
+         * The default value for the timer is 10 seconds.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_EMERGENCY_SCAN_TIMER_SEC_INT =
+                KEY_PREFIX + "emergency_scan_timer_sec_int";
+
+        /**
+         * The timer to wait for the call completion on the cellular network before attempting the
+         * call over Wi-Fi. On timer expiry, if emergency call on Wi-Fi is allowed and possible,
+         * telephony shall cancel the scan on the cellular network and place the call on Wi-Fi.
+         * If dialing over cellular network is ongoing when timer expires, dialing over Wi-Fi
+         * will be requested only when the ongoing dialing fails. If emergency call on Wi-Fi is not
+         * possible, then domain selection continues to try dialing from the radio and the timer
+         * remains expired. Later when calling over Wi-Fi is possible and dialing over cellular
+         * networks fails, calling over Wi-Fi will be requested. The timer shall be restarted from
+         * initial state if calling over Wi-Fi fails.
+         * If this value is set to {@link #REDIAL_TIMER_DISABLED}, then the timer will never be
+         * started.
+         *
+         * The default value for the timer is {@link #REDIAL_TIMER_DISABLED}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT =
+                KEY_PREFIX + "maximum_cellular_search_timer_sec_int";
+
+        /** @hide */
+        @IntDef(prefix = "SCAN_TYPE_",
+            value = {
+                SCAN_TYPE_NO_PREFERENCE,
+                SCAN_TYPE_FULL_SERVICE,
+                SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE})
+        public @interface EmergencyScanType {}
+
+        /**
+         * No specific preference given to the modem. Modem can return an emergency
+         * capable network either with limited service or full service.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final int SCAN_TYPE_NO_PREFERENCE = 0;
+
+        /**
+         * Modem will attempt to camp on a network with full service only.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final int SCAN_TYPE_FULL_SERVICE = 1;
+
+        /**
+         * Telephony shall attempt full service scan first.
+         * If a full service network is not found, telephony shall attempt a limited service scan.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final int SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE = 2;
+
+        /**
+         * Specifies the preferred emergency network scanning type.
+         *
+         * <p>Possible values are,
+         * {@link #SCAN_TYPE_NO_PREFERENCE}
+         * {@link #SCAN_TYPE_FULL_SERVICE}
+         * {@link #SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE}
+         *
+         * The default value for this key is {@link #SCAN_TYPE_NO_PREFERENCE}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT =
+                KEY_PREFIX + "emergency_network_scan_type_int";
+
+        /**
+         * Specifies the time by which a call should be set up on the current network
+         * once the call is routed on the network. If the call cannot be set up by timer expiry,
+         * call shall be re-dialed on the next available network.
+         * If this value is set to 0, the timer shall be disabled.
+         *
+         * The default value for this key is 0.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT =
+                KEY_PREFIX + "emergency_call_setup_timer_on_current_network_sec_int";
+
+        /**
+         * Specifies if emergency call shall be attempted on IMS only when IMS is registered.
+         * This is applicable only for the case PS is in service.
+         *
+         * The default value for this key is {@code false}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL =
+                KEY_PREFIX + "emergency_requires_ims_registration_bool";
+
+        /**
+         * Specifies if LTE is preferred when re-scanning networks after the failure of dialing
+         * over NR. If not, CS will be preferred.
+         *
+         * The default value for this key is {@code false}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL =
+                KEY_PREFIX + "emergency_lte_preferred_after_nr_failed_bool";
+
+        /**
+         * Specifies the numbers to be dialed over CDMA network in case of dialing over CS network.
+         *
+         * The default value for this key is an empty string array.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY =
+                KEY_PREFIX + "emergency_cdma_preferred_numbers_string_array";
+
+        /**
+         * Specifies if emergency call shall be attempted on IMS over cellular network
+         * only when VoLTE is enabled.
+         *
+         * The default value for this key is {@code false}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL =
+                KEY_PREFIX + "emergency_requires_volte_enabled_bool";
+
+        /**
+         * This values indicates that the cross SIM redialing timer and maximum celluar search
+         * timer shall be disabled.
+         *
+         * @see #KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT
+         * @see #KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT
+         * @see #KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final int REDIAL_TIMER_DISABLED = 0;
+
+        /**
+         * A timer to guard the max attempting time on current SIM slot so that modem will not
+         * stuck in current SIM slot for long time. On timer expiry, if emergency call on the
+         * other SIM slot is preferable, telephony shall cancel the emergency call and place the
+         * call on the other SIM slot. If this value is set to {@link #REDIAL_TIMER_DISABLED}, then
+         * the timer will never be started and domain selection continues on the current SIM slot.
+         * This value should be greater than the value of {@link #KEY_EMERGENCY_SCAN_TIMER_SEC_INT}.
+         *
+         * The default value for the timer is 120 seconds.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT =
+                KEY_PREFIX + "cross_stack_redial_timer_sec_int";
+
+        /**
+         * If emergency calls are only allowed with normal-registered service and UE should get
+         * normal service in a short time with acquired band information, telephony
+         * expects dialing emergency call will be completed in a short time.
+         * If dialing is not completed with in a certain timeout, telephony shall place on
+         * another SIM slot. If this value is set to {@link #REDIAL_TIMER_DISABLED}, then the timer
+         * will never be started and domain selection continues on the current SIM slot.
+         * The timer shall be started for the first trial of each subscription and shall be ignored
+         * in the roaming networks and non-domestic networks.
+         *
+         * The default value for the timer is {@link #REDIAL_TIMER_DISABLED}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT =
+                KEY_PREFIX + "quick_cross_stack_redial_timer_sec_int";
+
+        /**
+         * Indicates whether the quick cross stack redial timer will be triggered only when
+         * the device is registered to the network.
+         *
+         * The default value is {@code true}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL =
+                KEY_PREFIX + "start_quick_cross_stack_redial_timer_when_registered_bool";
+
+        /**
+         * Indicates whether limited service only scanning will be requested after VoLTE fails.
+         * This value is applicable if the value of
+         * {@link #KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT} is any of {@link #SCAN_TYPE_NO_PREFERENCE}
+         * or {@link #SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE}.
+         *
+         * The default value is {@code false}.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final String
+                KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL =
+                    KEY_PREFIX + "scan_limited_service_after_volte_failure_bool";
+
+        /**
+         * This config defines {@link ImsReasonInfo} code with which the emergency call
+         * shall be retried.
+         *
+         * <p>
+         * If the reason code is one of the following, the emergency call shall be retried
+         * regardless of this configuration.
+         * <ul>
+         * <li>{@link ImsReasonInfo#CODE_LOCAL_CALL_CS_RETRY_REQUIRED}</li>
+         * <li>{@link ImsReasonInfo#CODE_LOCAL_NOT_REGISTERED}</li>
+         * <li>{@link ImsReasonInfo#CODE_SIP_ALTERNATE_EMERGENCY_CALL}</li>
+         * </ul>
+         * <p>
+         *
+         * This config is empty by default.
+         *
+         * @hide
+         */
+        public static final String KEY_IMS_REASONINFO_CODE_TO_RETRY_EMERGENCY_INT_ARRAY =
+                KEY_PREFIX + "ims_reasoninfo_code_to_retry_emergency_int_array";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_RETRY_EMERGENCY_ON_IMS_PDN_BOOL, false);
+            defaults.putBoolean(KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL, false);
+            defaults.putBoolean(KEY_EMERGENCY_QOS_PRECONDITION_SUPPORTED_BOOL, true);
+
+            defaults.putIntArray(
+                    KEY_EMERGENCY_OVER_IMS_SUPPORTED_RATS_INT_ARRAY,
+                    new int[] {
+                        AccessNetworkType.EUTRAN,
+                        AccessNetworkType.IWLAN
+                    });
+
+            defaults.putInt(KEY_EMERGENCY_REGISTRATION_TIMER_MILLIS_INT, 10000);
+            defaults.putInt(KEY_REFRESH_GEOLOCATION_TIMEOUT_MILLIS_INT, 5000);
+
+            defaults.putIntArray(
+                    KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+                    new int[] {
+                        AccessNetworkType.EUTRAN,
+                    });
+
+            defaults.putIntArray(
+                    KEY_EMERGENCY_OVER_IMS_ROAMING_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+                    new int[] {
+                        AccessNetworkType.EUTRAN,
+                    });
+
+            defaults.putIntArray(
+                    KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,
+                    new int[] {
+                        AccessNetworkType.UTRAN,
+                        AccessNetworkType.GERAN,
+                    });
+
+            defaults.putIntArray(
+                    KEY_EMERGENCY_OVER_CS_ROAMING_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,
+                    new int[] {
+                        AccessNetworkType.UTRAN,
+                        AccessNetworkType.GERAN,
+                    });
+
+            defaults.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY,
+                    new int[] {
+                        DOMAIN_PS_3GPP,
+                        DOMAIN_CS,
+                        DOMAIN_PS_NON_3GPP
+                    });
+            defaults.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_ROAMING_INT_ARRAY,
+                    new int[] {
+                        DOMAIN_PS_3GPP,
+                        DOMAIN_CS,
+                        DOMAIN_PS_NON_3GPP
+                    });
+
+            defaults.putBoolean(KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL, false);
+            defaults.putInt(KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT, VOWIFI_REQUIRES_NONE);
+            defaults.putInt(KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT, 1);
+            defaults.putInt(KEY_EMERGENCY_SCAN_TIMER_SEC_INT, 10);
+            defaults.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, REDIAL_TIMER_DISABLED);
+            defaults.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT, SCAN_TYPE_NO_PREFERENCE);
+            defaults.putInt(KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT, 0);
+            defaults.putBoolean(KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL, false);
+            defaults.putBoolean(KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL, false);
+            defaults.putBoolean(KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL, false);
+            defaults.putStringArray(KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY,
+                    new String[0]);
+            defaults.putInt(KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT, 120);
+            defaults.putInt(KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT, REDIAL_TIMER_DISABLED);
+            defaults.putBoolean(KEY_START_QUICK_CROSS_STACK_REDIAL_TIMER_WHEN_REGISTERED_BOOL,
+                    true);
+            defaults.putBoolean(KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL, false);
+            defaults.putIntArray(KEY_IMS_REASONINFO_CODE_TO_RETRY_EMERGENCY_INT_ARRAY,
+                    new int[0]);
+
+            return defaults;
+        }
+    }
+
+    /**
+     * IMS Video Telephony configs. This groups the configs that are specific for video call.
+     */
+    public static final class ImsVt {
+        private ImsVt() {}
+
+        /** Prefix of all imsvt.KEY_* constants. */
+        public static final String KEY_PREFIX = "imsvt.";
+
+        /**
+         * Flag specifying whether video media is allowed on default bearer.
+         *
+         * <p>If {@code true}: video media can be sent on default bearer.
+         * {@code false} otherwise.
+         */
+        public static final String KEY_VIDEO_ON_DEFAULT_BEARER_SUPPORTED_BOOL =
+                KEY_PREFIX + "video_on_default_bearer_supported_bool";
+
+        /**
+         * Specifies the timeout value for no video RTP packets received.
+         * <p>On timer expiry, VT call can downgrade to voice call or end
+         * or continue depending on the operator requirement.
+         */
+        public static final String KEY_VIDEO_RTP_INACTIVITY_TIMER_MILLIS_INT =
+                KEY_PREFIX + "video_rtp_inactivity_timer_millis_int";
+
+        /**
+         * Specifies the timeout value for no video RTCP packets received.
+         * <p>On timer expiry, VT call can downgrade to voice call or end
+         * or continue depending on the operator requirement.
+         */
+        public static final String KEY_VIDEO_RTCP_INACTIVITY_TIMER_MILLIS_INT =
+                KEY_PREFIX + "video_rtcp_inactivity_timer_millis_int";
+
+        /**
+         * Specifies the AS (Application Specific) SDP modifier for video media.
+         *
+         * <p>Expressed in kilobits per second as per RFC 3556 Section 2.
+         */
+        public static final String KEY_VIDEO_AS_BANDWIDTH_KBPS_INT =
+                KEY_PREFIX + "video_as_bandwidth_kbps_int";
+
+        /**
+         * Specifies the RS (RTCP bandwidth-Sender) SDP modifier for video media.
+         *
+         * <p>This indicates the RTCP bandwidth allocated to active data senders
+         * for video media.
+         *
+         * <p>Expressed in bits per second as per RFC 3556 Section 2.
+         */
+        public static final String KEY_VIDEO_RS_BANDWIDTH_BPS_INT =
+                KEY_PREFIX + "video_rs_bandwidth_bps_int";
+
+        /**
+         * Specifies the RR (RTCP bandwidth-Receiver) SDP modifier
+         * for video media.
+         *
+         * <p>This indicates the RTCP bandwidth allocated to receivers
+         * for video media.
+         *
+         * <p>Expressed in bits per second as per RFC 3556 Section 2.
+         */
+        public static final String KEY_VIDEO_RR_BANDWIDTH_BPS_INT =
+                KEY_PREFIX + "video_rr_bandwidth_bps_int";
+
+        /**
+         * Specifies the differentiated services code point (DSCP) value
+         * for Video RTP.
+         *
+         * <p>Reference: RFC 4594 Section 1.4.4
+         */
+        public static final String KEY_VIDEO_RTP_DSCP_INT =
+                KEY_PREFIX + "video_rtp_dscp_int";
+
+        /**
+         * Flag specifying whether QoS preconditions are supported for Video.
+         *
+         * <p>If {@code true}: QoS Preconditions are supported.
+         * {@code false} otherwise.
+         * <p>Reference: 3GPP TS 24.229
+         */
+        public static final String KEY_VIDEO_QOS_PRECONDITION_SUPPORTED_BOOL =
+                KEY_PREFIX + "video_qos_precondition_supported_bool";
+
+        /**
+         * Specifies the Video Codec capability. This contains a list of
+         * payload types representing different Video codec instances.
+
+         * <p>Possible key(s) in this bundle are,
+         * <UL>
+         *     <LI>{@link #KEY_H264_PAYLOAD_TYPE_INT_ARRAY}</LI>
+         * </UL>
+         * <p>To specify payload descriptions for each of the payload types, see
+         * <UL>
+         *     <LI>{@link #KEY_H264_PAYLOAD_DESCRIPTION_BUNDLE}</LI>
+         * </UL>
+         */
+        public static final String KEY_VIDEO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE =
+                KEY_PREFIX + "video_codec_capability_payload_types_bundle";
+
+        /**
+         * A list of integers representing the different payload types
+         * in H264 video codec in priority order from highest to lowest.
+         * <p>Payload type is an integer in dynamic payload type range 96-127
+         * as per RFC RFC 3551 Section 6.
+         */
+        public static final String KEY_H264_PAYLOAD_TYPE_INT_ARRAY =
+                KEY_PREFIX + "h264_payload_type_int_array";
+
+        /**
+         * Specifies the codec attributes of different payload types
+         * representing H264 video codec instances.
+         *
+         * <p> The allowed payload types of the video codecs are specified in,
+         * {@link #KEY_H264_PAYLOAD_TYPE_INT_ARRAY}.
+         *
+         * <p>Codec attributes allowed as part of H264 codec bundle are,
+         * <UL>
+         *     <LI>{@link #KEY_H264_VIDEO_CODEC_ATTRIBUTE_PROFILE_LEVEL_ID_STRING}</LI>
+         *     <LI>{@link #KEY_VIDEO_CODEC_ATTRIBUTE_PACKETIZATION_MODE_INT}</LI>
+         *     <LI>{@link #KEY_VIDEO_CODEC_ATTRIBUTE_FRAME_RATE_INT}</LI>
+         *     <LI>{@link #KEY_VIDEO_CODEC_ATTRIBUTE_RESOLUTION_INT_ARRAY}</LI>
+         * </UL>
+         *
+         * <p>If this bundle is not configured and
+         * {@link #KEY_H264_PAYLOAD_TYPE_INT_ARRAY} is not empty,
+         * then default values as in the individual codec attributes to
+         * be used for that payload type.
+         * <p>If the codec attributes in a particular codec instance bundle
+         * is not valid together, then that codec instance should not be used.
+         */
+        public static final String KEY_H264_PAYLOAD_DESCRIPTION_BUNDLE =
+                KEY_PREFIX + "h264_payload_description_bundle";
+
+        /**
+         * Specifies the packetization mode of the video codec.
+         *
+         * <p>Permissible values are 0 (Single NAL unit mode),
+         * 1(Non-interleaved mode).
+         *
+         * <p>If this key is not specified or invalid, then the following
+         * default value to be used.
+         * <UL>
+         *   <LI>For H264: 1(Non-interleaved mode)</LI>
+         * <UL>
+         *
+         * <p>Reference: RFC 6184 Section 5.4
+         */
+        public static final String KEY_VIDEO_CODEC_ATTRIBUTE_PACKETIZATION_MODE_INT =
+                KEY_PREFIX + "video_codec_attribute_packetization_mode_int";
+
+        /**
+         * Specifies the maximum frame rate the offerer wishes to receive.
+         * This gives the maximum video frame rate in frames/sec.
+         *
+         * <p>If this key is not specified or invalid, then the following
+         * default value to be used.
+         * <UL>
+         *   <LI>For H264: 15 </LI>
+         * <UL>
+         * <p>Reference: RFC 4566 Section 6, 3GPP 26.114 Section 6.2.3.2
+         */
+        public static final String KEY_VIDEO_CODEC_ATTRIBUTE_FRAME_RATE_INT =
+                KEY_PREFIX + "video_codec_attribute_frame_rate_int";
+
+        /**
+         * Specifies the maximum resolution allowed for the video codec
+         * instance.
+         *
+         * <p>This is specified as an array of two integers, with
+         * index 0 : Width,
+         * index 1 : Height
+         *
+         * <p>If this key is not specified or invalid as per the video codec,
+         * then the following default value to be used.
+         * <UL>
+         *   <LI>For H264: 240 (WIDTH) x 320 (HEIGHT) </LI>
+         * <UL>
+         * <p>Reference: RFC 4566 Section 6, 3GPP 26.114 Section 6.2.3.2
+         *
+         */
+        public static final String KEY_VIDEO_CODEC_ATTRIBUTE_RESOLUTION_INT_ARRAY =
+                KEY_PREFIX + "video_codec_attribute_resolution_int_array";
+
+        /**
+         * Specifies the profile level id of the H264 video codec.
+         * This value is represented as "profile-level-id" in the SDP offer
+         * as per RFC 6184 Section 8.1.
+         *
+         * <p>If this key is not specified or invalid as per the video codec,
+         * then default value of 42C00C to be used.
+         *
+         * <p>Reference: RFC 6184 Section 8.1, ITU-T Recommendation H.264
+         */
+        public static final String KEY_H264_VIDEO_CODEC_ATTRIBUTE_PROFILE_LEVEL_ID_STRING =
+                KEY_PREFIX + "h264_video_codec_attribute_profile_level_id_string";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_VIDEO_ON_DEFAULT_BEARER_SUPPORTED_BOOL, false);
+            defaults.putBoolean(KEY_VIDEO_QOS_PRECONDITION_SUPPORTED_BOOL, true);
+
+            defaults.putInt(KEY_VIDEO_RTP_INACTIVITY_TIMER_MILLIS_INT, 0);
+            defaults.putInt(KEY_VIDEO_RTCP_INACTIVITY_TIMER_MILLIS_INT, 0);
+
+            defaults.putInt(KEY_VIDEO_AS_BANDWIDTH_KBPS_INT, 960);
+            defaults.putInt(KEY_VIDEO_RS_BANDWIDTH_BPS_INT, 8000);
+            defaults.putInt(KEY_VIDEO_RR_BANDWIDTH_BPS_INT, 6000);
+            defaults.putInt(KEY_VIDEO_RTP_DSCP_INT, 40);
+
+            PersistableBundle video_codec_capability_payload_types = new PersistableBundle();
+
+            video_codec_capability_payload_types.putIntArray(
+                    KEY_H264_PAYLOAD_TYPE_INT_ARRAY,
+                    new int[] { 99, 100 });
+
+            defaults.putPersistableBundle(
+                    KEY_VIDEO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE,
+                    video_codec_capability_payload_types);
+
+            PersistableBundle all_h264_payload_bundles = new PersistableBundle();
+
+            /* Setting default codec attributes for individual H264 profiles*/
+
+            /* For H264 profile-level-id: 42C00C, frame rate:15, Resolution: 240x320 */
+            PersistableBundle h264_bundle_instance1 = new PersistableBundle();
+            all_h264_payload_bundles.putPersistableBundle(
+                    "99", /* Same value of payload type as in KEY_H264_PAYLOAD_TYPE_INT_ARRAY */
+                    h264_bundle_instance1);
+
+            /* For H264 profile-level-id: 42C00C, packetisation mode:0, frame rate:15,
+             * Resolution: 240x320 */
+            PersistableBundle h264_bundle_instance2 = new PersistableBundle();
+            h264_bundle_instance2.putInt(
+                    KEY_VIDEO_CODEC_ATTRIBUTE_PACKETIZATION_MODE_INT,
+                    0);
+
+            all_h264_payload_bundles.putPersistableBundle(
+                    "100", /* Same value of payload type as in KEY_H264_PAYLOAD_TYPE_INT_ARRAY */
+                    h264_bundle_instance2);
+
+            defaults.putPersistableBundle(
+                    KEY_H264_PAYLOAD_DESCRIPTION_BUNDLE,
+                    all_h264_payload_bundles);
+
+            return defaults;
+        }
+    }
+
+    /**
+     * WiFi Calling. This groups the configs specific for Voice over WiFi/WFC call.
+     */
+    public static final class ImsWfc {
+        private ImsWfc() {}
+
+        /** Prefix of all imswfc.KEY_* constants. */
+        public static final String KEY_PREFIX = "imswfc.";
+
+        /**
+         *  List of MDNs for which Geo-location PIDF XML with country info
+         *  needs to included for normal calls involving short code.
+         */
+        public static final String KEY_PIDF_SHORT_CODE_STRING_ARRAY =
+                KEY_PREFIX + "pidf_short_code_string_array";
+
+        /**
+         * Flag specifying whether emergency call over VoWiFi is requested over
+         * emergency PDN or IMS PDN.
+         *
+         * <p>If {@code false}: E911 call uses IMS PDN for E911 call over VoWiFi.
+         * If {@code true}: E911 call uses Emergency PDN for E911 call over VoWiFi.
+         */
+        public static final String KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL =
+                KEY_PREFIX + "emergency_call_over_emergency_pdn_bool";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+
+            defaults.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, false);
+            defaults.putStringArray(KEY_PIDF_SHORT_CODE_STRING_ARRAY, new String[0]);
+
+            return defaults;
+        }
+    }
+
+    /**
+     *   IMS supplementary services configs. This groups the configs required for
+     *   supplementary services (SS) like XCAP over UT,
+     *   Unstructured Supplementary Service Data(USSD).
+     */
+    public static final class ImsSs {
+        private ImsSs() {}
+
+        /** Prefix of all imsss.KEY_* constants. */
+        public static final String KEY_PREFIX = "imsss.";
+
+        /**
+         * Flag that controls whether XCAP over UT status need to be
+         * dependent on IMS registration.
+         *
+         * <p>If {@code true}: XCAP over UT status need to be
+         * dependent on IMS registration.
+         * {@code false} otherwise.
+         */
+        public static final String KEY_UT_REQUIRES_IMS_REGISTRATION_BOOL =
+                KEY_PREFIX + "ut_requires_ims_registration_bool";
+
+        /**
+         * Flag that controls whether XCAP over UT is supported
+         * when on roaming network.
+         *
+         * <p>If {@code true}: XCAP over UT is supported when on
+         * roaming network.
+         * {@code false} otherwise.
+         */
+        public static final String KEY_UT_SUPPORTED_WHEN_ROAMING_BOOL =
+                KEY_PREFIX + "ut_supported_when_roaming_bool";
+
+        /**
+         * Flag that controls whether Circuit Switched Fallback (CSFB)
+         * option is available when XCAP over UT fails.
+         *
+         * <p>If {@code false}:  XCAP over UT only with no CSFB option.
+         * If XCAP over UT fails, return error.
+         * if {@code true}, Use CSFB if XCAP over UT fails.
+         */
+        public static final String KEY_USE_CSFB_ON_XCAP_OVER_UT_FAILURE_BOOL =
+                KEY_PREFIX + "use_csfb_on_xcap_over_ut_failure_bool";
+
+        /**
+         * Flag that controls whether XCAP over UT is enabled or not
+         * when PS data is turned off.
+         *
+         * <p>If {@code true}: XCAP over UT is enabled when PS data is off.
+         * {@code false}: Otherwise.
+         *
+         * Reference: IR.92 Section 5.5.1
+         */
+        public static final String KEY_UT_SUPPORTED_WHEN_PS_DATA_OFF_BOOL =
+                KEY_PREFIX + "ut_supported_when_ps_data_off_bool";
+
+        /**
+         * Flag that controls whether network initiated USSD over IMS is
+         * supported by the UE.
+         *
+         * <p>If {@code true}:  Support Available.{@code false}: Otherwise.
+         * Reference: 3GPP 24.390.
+         */
+        public static final String KEY_NETWORK_INITIATED_USSD_OVER_IMS_SUPPORTED_BOOL =
+                KEY_PREFIX + "network_initiated_ussd_over_ims_supported_bool";
+
+        /**
+         * Specifies the 'XCAP over UT' IP Type when device is
+         * on Home Network.
+         *
+         * <p>Possible values are,
+         * {@link ApnSetting#PROTOCOL_IPV4V6},
+         * {@link ApnSetting#PROTOCOL_IP},
+         * {@link ApnSetting#PROTOCOL_IPV6}
+         *
+         * If key is invalid or not configured, the default value
+         * {@link ApnSetting#PROTOCOL_IPV4V6} will apply.
+         */
+        public static final String KEY_UT_IPTYPE_HOME_INT =
+                KEY_PREFIX + "ut_iptype_home_int";
+
+        /**
+         * Specifies the 'XCAP over UT' IP Type when device is roaming.
+         *
+         * <p>Possible values are,
+         * {@link ApnSetting#PROTOCOL_IPV4V6},
+         * {@link ApnSetting#PROTOCOL_IP},
+         * {@link ApnSetting#PROTOCOL_IPV6}
+
+         * If key is invalid or not configured, the default value
+         * {@link ApnSetting#PROTOCOL_IPV4V6} will apply.
+         */
+        public static final String KEY_UT_IPTYPE_ROAMING_INT =
+                KEY_PREFIX + "ut_iptype_roaming_int";
+
+        /**
+         * Specifies the XCAP Application Server fully qualified domain name (FQDN).
+         * <p> Reference: 24.623 Section 5.2.3.
+         */
+        public static final String KEY_UT_AS_SERVER_FQDN_STRING =
+                KEY_PREFIX + "ut_as_server_fqdn_string";
+
+        /**
+         * Specifies the XCAP Application Server Remote port.
+         * As XCAP is a usage of HTTP, the default value is same as HTTP, i.e. 80.
+         */
+        public static final String KEY_UT_AS_SERVER_PORT_INT =
+                KEY_PREFIX + "ut_as_server_port_int";
+
+        /**
+         * Specifies the preferred transport to be used for XCAP over UT.
+         *
+         * <p>Possible values are,
+         * {@link Ims#PREFERRED_TRANSPORT_TCP},
+         * {@link Ims#PREFERRED_TRANSPORT_TLS}
+         *
+         * <p>If key is invalid or not configured, the default value
+         * {@link Ims#PREFERRED_TRANSPORT_TCP} will apply.
+         */
+        public static final String KEY_UT_TRANSPORT_TYPE_INT =
+                KEY_PREFIX + "ut_transport_type_int";
+
+        /** @hide */
+        @IntDef({
+            SUPPLEMENTARY_SERVICE_CW,
+            SUPPLEMENTARY_SERVICE_CF_ALL,
+            SUPPLEMENTARY_SERVICE_CF_CFU,
+            SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING,
+            SUPPLEMENTARY_SERVICE_CF_CFB,
+            SUPPLEMENTARY_SERVICE_CF_CFNRY,
+            SUPPLEMENTARY_SERVICE_CF_CFNRC,
+            SUPPLEMENTARY_SERVICE_CF_CFNL,
+            SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP,
+            SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP,
+            SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR,
+            SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR,
+            SUPPLEMENTARY_SERVICE_CB_ALL,
+            SUPPLEMENTARY_SERVICE_CB_OBS,
+            SUPPLEMENTARY_SERVICE_CB_BAOC,
+            SUPPLEMENTARY_SERVICE_CB_BOIC,
+            SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC,
+            SUPPLEMENTARY_SERVICE_CB_IBS,
+            SUPPLEMENTARY_SERVICE_CB_BAIC,
+            SUPPLEMENTARY_SERVICE_CB_BIC_ROAM,
+            SUPPLEMENTARY_SERVICE_CB_ACR,
+            SUPPLEMENTARY_SERVICE_CB_BIL
+        })
+        public @interface SsType {}
+
+        /** Communication Waiting (CW) support as per 3GPP 24.615. */
+        public static final int SUPPLEMENTARY_SERVICE_CW = 0;
+
+        /**
+         * Call Diversion - All call forwarding support as per 3GPP 24.604.
+         *
+         * <p>This value is associated with MMI support service code 002
+         * as indicated in TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_ALL = 1;
+
+        /**
+         * Call Diversion - All Unconditional call forwarding support (CFU) as
+         * per 3GPP 24.604.
+         *
+         * <p>This value is associated with MMI support service code 21
+         * as indicated in TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_CFU = 2;
+
+        /**
+         * Call Diversion - All conditional call forwarding support as
+         * per 3GPP 24.604.
+         *
+         * <p>This value is associated with MMI support service code 004
+         * as indicated in TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING = 3;
+
+        /**
+         * Call Diversion - Call forwarding on mobile subscriber busy (CFB)
+         * support as per 3GPP 24.604.
+         *
+         * <p>This value is associated with MMI support service code 67
+         * as indicated in TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_CFB = 4;
+
+        /**
+         * Call Diversion - Call forwarding on no reply (CFNRY)
+         * support as per 3GPP 24.604.
+         *
+         * <p>This value is associated with MMI support service code 61
+         * as indicated in TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_CFNRY = 5;
+
+        /**
+         * Call Diversion - Call forwarding on mobile subscriber not reachable
+         * (CFNRC) support as per 3GPP 24.604.
+         *
+         * <p>This value is associated with MMI support service code 62
+         * as indicated in TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_CFNRC = 6;
+
+        /**
+         * Communication Forwarding on Not Logged-in (CFNL).
+         * support as per 3GPP 24.604 Section 4.2.1.7
+         *
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CF_CFNL = 7;
+
+        /**
+         * Originating Identification Presentation (OIP) support
+         * as per 3GPP 24.607.
+         *
+         */
+        public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP = 8;
+
+        /**
+         * Terminating Identification Presentation (TIP) support
+         * as per 3GPP 24.608.
+         */
+        public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP = 9;
+
+        /**
+         * Originating Identification Restriction (OIR) support
+         * as per 3GPP 24.607.
+         */
+        public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR = 10;
+
+        /**
+         * Terminating Identification Restriction (TIR) support
+         * as per 3GPP 24.608.
+         */
+        public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR = 11;
+
+        /**
+         * Call Barring - All barring services,
+         * This value is associated with MMI support service code 330
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_ALL = 12;
+
+        /**
+         * Call Barring - Outgoing barring services,
+         * This value is associated with MMI support service code 333
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_OBS = 13;
+
+        /**
+         * Call Barring - Barring of all outgoing calls (BAOC)
+         * support as per 3GPP TS 24.611.
+         *
+         * <p>This value is associated with MMI support service code 33
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_BAOC = 14;
+
+        /**
+         * Call Barring - Barring of outgoing international calls
+         * (BOIC) support as per 3GPP TS 24.611.
+         *
+         * <p>This value is associated with MMI support service code 331
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_BOIC = 15;
+
+        /**
+         * Call Barring - Barring of outgoing international calls
+         * except those directed to the home PLMN country (BOIC-EXHC) support
+         * as per 3GPP TS 24.611.
+         *
+         * <p>This value is associated with MMI support service code 332
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC = 16;
+
+        /**
+         * Call Barring - Incoming barring services,
+         * This value is associated with MMI support service code 353
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_IBS = 17;
+
+        /**
+         * Call Barring - Barring of all incoming calls (BAIC)
+         * support as per 3GPP TS 24.611.
+         *
+         * <p>This value is associated with MMI support service code 35
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_BAIC = 18;
+
+        /**
+         * Call Barring - Barring of incoming calls when roaming outside
+         * the home PLMN country (BIC-ROAM) support as per 3GPP TS 24.611.
+         *
+         * <p>This value is associated with MMI support service code 351
+         * as indicated TS 22.030 Table B.1
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_BIC_ROAM = 19;
+
+        /**
+         * Call Barring - Anonymous Call Rejection/Barring of all anonymous
+         * incoming number support as per 3GPP TS 24.611.
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_ACR = 20;
+
+        /**
+         * Call Barring - Barring list of incoming numbers support.
+         */
+        public static final int SUPPLEMENTARY_SERVICE_CB_BIL = 21;
+
+        /**
+         * List of UT services that are Server based.
+         *
+         * <p>Possible values are,
+         * <UL>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CW}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_ALL}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFU}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFB}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFNRY}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFNRC}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFNL}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_ALL}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_OBS}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_IBS}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BAOC}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BOIC}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BAIC}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BIC_ROAM}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_ACR}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BIL}</LI>
+         * </UL>
+         */
+        public static final String KEY_UT_SERVER_BASED_SERVICES_INT_ARRAY =
+                KEY_PREFIX + "ut_server_based_services_int_array";
+
+        /**
+         * List of UT services that are terminal based.
+         *
+         * By default, all services are server based and defined in
+         * {@link #KEY_UT_SERVER_BASED_SERVICES_INT_ARRAY}.
+         * Adding here will override that service setting to terminal based.
+         *
+         * <p>Possible values are,
+         * <UL>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_CW}</LI>
+         *     <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR}</LI>
+         * </UL>
+         */
+        public static final String KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY =
+                KEY_PREFIX + "ut_terminal_based_services_int_array";
+
+        /**
+         * List of different RAT technologies on which XCAP over UT
+         * is supported.
+         *
+         * <p>Possible values are,
+         * {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#IWLAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#UTRAN}
+         * {@link AccessNetworkConstants.AccessNetworkType#GERAN}
+         */
+        public static final String KEY_XCAP_OVER_UT_SUPPORTED_RATS_INT_ARRAY =
+                KEY_PREFIX + "xcap_over_ut_supported_rats_int_array";
+
+        /** @hide */
+        @IntDef({
+            CALL_WAITING_SYNC_NONE,
+            CALL_WAITING_SYNC_USER_CHANGE,
+            CALL_WAITING_SYNC_FIRST_POWER_UP,
+            CALL_WAITING_SYNC_FIRST_CHANGE,
+            CALL_WAITING_SYNC_IMS_ONLY
+        })
+        public @interface CwSyncType {}
+
+        /**
+         * Do not synchronize the user's call waiting setting with the network. Call waiting is
+         * always enabled on the carrier network and the user setting for call waiting is applied
+         * on the terminal side. If the user disables call waiting, the call will be rejected on
+         * the terminal.
+         */
+        public static final int CALL_WAITING_SYNC_NONE = 0;
+
+        /**
+         * The change of user’s setting is always passed to the carrier network
+         * and then synchronized to the terminal based call waiting solution over IMS.
+         * If changing the service over the carrier network is not successful,
+         * the setting over IMS shall not be changed.
+         */
+        public static final int CALL_WAITING_SYNC_USER_CHANGE = 1;
+
+        /**
+         * Activate call waiting on the carrier network when the device boots or a subscription
+         * using this carrier is loaded. Call waiting is always considered enabled on the carrier
+         * network and the user setting for call waiting is applied on the terminal side only. If
+         * the user disables call waiting, the call will be rejected on the terminal.
+         * The mismatch between CS calls and IMS calls can happen when the network based call
+         * waiting service is in disabled state in the legacy 3G/2G networks while it's enabled
+         * in the terminal side.
+         */
+        public static final int CALL_WAITING_SYNC_FIRST_POWER_UP = 2;
+
+        /**
+         * Activate call waiting on the carrier network when the user enables call waiting the
+         * first time. Call waiting is then always considered enabled on the carrier network. If
+         * the user disables call waiting, the setting will only be applied to the terminal based
+         * call waiting service and the call will be rejected on the terminal.
+         * The mismatch between CS calls and IMS calls can happen when the network based call
+         * waiting service is in disabled state in the legacy 3G/2G networks while it's enabled
+         * in the terminal side. However, if the user retrieves the setting again when the device
+         * is in the legacy 3G/2G networks, the correct state will be shown to the user.
+         */
+        public static final int CALL_WAITING_SYNC_FIRST_CHANGE = 3;
+
+        /**
+         * Do not synchronize the call waiting service state between the carrier network and
+         * the terminal based IMS call waiting service. If the user changes the call waiting setting
+         * when IMS is registered, the change will only be applied to the terminal based call
+         * waiting service. If IMS is not registered when call waiting is changed, synchronize this
+         * setting with the carrier network.
+         */
+        public static final int CALL_WAITING_SYNC_IMS_ONLY = 4;
+
+        /** @hide */
+        public static final int CALL_WAITING_SYNC_MAX = CALL_WAITING_SYNC_IMS_ONLY;
+
+        /**
+         * Flag indicating the way to synchronize the setting between CS and IMS.
+         *
+         * <p>Possible values are,
+         * {@link #CALL_WAITING_SYNC_NONE},
+         * {@link #CALL_WAITING_SYNC_USER_CHANGE},
+         * {@link #CALL_WAITING_SYNC_FIRST_POWER_UP},
+         * {@link #CALL_WAITING_SYNC_FIRST_CHANGE},
+         * {@link #CALL_WAITING_SYNC_IMS_ONLY}.
+         *
+         * This configuration is valid only when
+         * {@link #KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY} includes
+         * {@link #SUPPLEMENTARY_SERVICE_CW}.
+         *
+         * <p>If key is invalid or not configured, the default value
+         * {@link #CALL_WAITING_SYNC_FIRST_CHANGE} will apply.
+         */
+        public static final String KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT =
+                KEY_PREFIX + "terminal_based_call_waiting_sync_type_int";
+
+        /**
+         * Flag indicating whether the user setting for terminal-based call waiting
+         * is enabled by default or not.
+         * This configuration is valid only when
+         * {@link #KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY} includes
+         * {@link #SUPPLEMENTARY_SERVICE_CW}.
+         *
+         * The default value for this key is {@code true}.
+         */
+        public static final String KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL =
+                KEY_PREFIX + "terminal_based_call_waiting_default_enabled_bool";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putBoolean(KEY_UT_REQUIRES_IMS_REGISTRATION_BOOL, false);
+            defaults.putBoolean(KEY_USE_CSFB_ON_XCAP_OVER_UT_FAILURE_BOOL, true);
+            defaults.putBoolean(KEY_UT_SUPPORTED_WHEN_PS_DATA_OFF_BOOL, true);
+            defaults.putBoolean(KEY_NETWORK_INITIATED_USSD_OVER_IMS_SUPPORTED_BOOL, true);
+            defaults.putBoolean(KEY_UT_SUPPORTED_WHEN_ROAMING_BOOL, true);
+
+            defaults.putInt(KEY_UT_IPTYPE_HOME_INT, ApnSetting.PROTOCOL_IPV4V6);
+            defaults.putInt(KEY_UT_IPTYPE_ROAMING_INT, ApnSetting.PROTOCOL_IPV4V6);
+            defaults.putInt(KEY_UT_AS_SERVER_PORT_INT, 80);
+            defaults.putInt(KEY_UT_TRANSPORT_TYPE_INT, Ims.PREFERRED_TRANSPORT_TCP);
+
+            defaults.putIntArray(
+                    KEY_UT_SERVER_BASED_SERVICES_INT_ARRAY,
+                    new int[] {
+                        SUPPLEMENTARY_SERVICE_CW,
+                        SUPPLEMENTARY_SERVICE_CF_ALL,
+                        SUPPLEMENTARY_SERVICE_CF_CFU,
+                        SUPPLEMENTARY_SERVICE_CF_CFNRC,
+                        SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING,
+                        SUPPLEMENTARY_SERVICE_CF_CFB,
+                        SUPPLEMENTARY_SERVICE_CF_CFNRY,
+                        SUPPLEMENTARY_SERVICE_CF_CFNL,
+                        SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP,
+                        SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP,
+                        SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR,
+                        SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR,
+                        SUPPLEMENTARY_SERVICE_CB_ALL,
+                        SUPPLEMENTARY_SERVICE_CB_OBS,
+                        SUPPLEMENTARY_SERVICE_CB_IBS,
+                        SUPPLEMENTARY_SERVICE_CB_BAOC,
+                        SUPPLEMENTARY_SERVICE_CB_BOIC,
+                        SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC,
+                        SUPPLEMENTARY_SERVICE_CB_BAIC,
+                        SUPPLEMENTARY_SERVICE_CB_BIC_ROAM,
+                        SUPPLEMENTARY_SERVICE_CB_ACR,
+                        SUPPLEMENTARY_SERVICE_CB_BIL
+                    });
+            defaults.putIntArray(KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY, new int[0]);
+
+            defaults.putIntArray(
+                    KEY_XCAP_OVER_UT_SUPPORTED_RATS_INT_ARRAY,
+                    new int[] {
+                        AccessNetworkType.EUTRAN,
+                        AccessNetworkType.IWLAN,
+                        AccessNetworkType.NGRAN
+                    });
+            defaults.putString(KEY_UT_AS_SERVER_FQDN_STRING, "");
+            defaults.putBoolean(KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL, true);
+            defaults.putInt(KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT,
+                    CALL_WAITING_SYNC_FIRST_CHANGE);
+
+            return defaults;
+        }
+    }
+
+    /**
+     * This groups the BSF (BootStrapping Function) related configs.
+     * Reference: 3GPP TS 24.109.
+     */
+    public static final class Bsf {
+        private Bsf() {}
+
+        /** Prefix of all bsf.KEY_* constants. */
+        public static final String KEY_PREFIX = "bsf.";
+
+        /** Specifies the fully qualified domain name (FQDN) of BSF Server
+         * as per 3GPP 24.109.
+         */
+        public static final String KEY_BSF_SERVER_FQDN_STRING =
+                KEY_PREFIX + "bsf_server_fqdn_string";
+
+        /**
+         * Specifies the port number of the BSF server as per 3GPP 24.109.
+         * This is usually default port number of HTTP, i.e. 80.
+         */
+        public static final String KEY_BSF_SERVER_PORT_INT =
+                KEY_PREFIX + "bsf_server_port_int";
+
+        /**
+         * Specifies the transport type used in communication with
+         * BSF server.
+         *
+         * <p>Possible values are,
+         * {@link Ims#PREFERRED_TRANSPORT_TCP},
+         * {@link Ims#PREFERRED_TRANSPORT_TLS}
+         *
+         * <p>If key is invalid or not configured, the default value
+         * {@link Ims#PREFERRED_TRANSPORT_TCP} will apply.
+         */
+        public static final String KEY_BSF_TRANSPORT_TYPE_INT =
+                KEY_PREFIX + "bsf_transport_type_int";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+
+            defaults.putInt(KEY_BSF_SERVER_PORT_INT, 80);
+            defaults.putInt(KEY_BSF_TRANSPORT_TYPE_INT, Ims.PREFERRED_TRANSPORT_TCP);
+            defaults.putString(KEY_BSF_SERVER_FQDN_STRING, "");
+
+            return defaults;
+        }
+    }
+
+    /**
+     * Configs used for epdg tunnel bring up.
+     *
+     * @see <a href="https://tools.ietf.org/html/rfc7296">RFC 7296, Internet Key Exchange Protocol
+     *     Version 2 (IKEv2)</a>
+     */
+    public static final class Iwlan {
+        /** Prefix of all Epdg.KEY_* constants. */
+        public static final String KEY_PREFIX = "iwlan.";
+
+        /**
+         * Time in seconds after which the child security association session is terminated if rekey
+         * procedure is not successful. If not set or set to <= 0, the default value is 3600
+         * seconds.
+         */
+        public static final String KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT =
+                KEY_PREFIX + "child_sa_rekey_hard_timer_sec_int";
+
+        /**
+         * Time in seconds after which the child session rekey procedure is started. If not set or
+         * set to <= 0, default value is 3000 seconds.
+         */
+        public static final String KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT =
+                KEY_PREFIX + "child_sa_rekey_soft_timer_sec_int";
+
+        /**
+         * Supported DH groups for IKE negotiation. Possible values are:
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_NONE},
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1024_BIT_MODP},
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1536_BIT_MODP},
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_2048_BIT_MODP},
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_3072_BIT_MODP},
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_4096_BIT_MODP}
+         */
+        public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY =
+                KEY_PREFIX + "diffie_hellman_groups_int_array";
+
+        /**
+         * Time in seconds after which a dead peer detection (DPD) request is sent. If not set or
+         * set to <= 0, default value is 120 seconds.
+         */
+        public static final String KEY_DPD_TIMER_SEC_INT = KEY_PREFIX + "dpd_timer_sec_int";
+
+        /**
+         * Method used to authenticate epdg server. Possible values are {@link
+         * #AUTHENTICATION_METHOD_EAP_ONLY}, {@link #AUTHENTICATION_METHOD_CERT}
+         */
+        public static final String KEY_EPDG_AUTHENTICATION_METHOD_INT =
+                KEY_PREFIX + "epdg_authentication_method_int";
+
+        /**
+         * A priority list of ePDG addresses to be used. Possible values are {@link
+         * #EPDG_ADDRESS_STATIC}, {@link #EPDG_ADDRESS_PLMN}, {@link #EPDG_ADDRESS_PCO}, {@link
+         * #EPDG_ADDRESS_CELLULAR_LOC}, {@link #EPDG_ADDRESS_VISITED_COUNTRY}
+         */
+        public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY =
+                KEY_PREFIX + "epdg_address_priority_int_array";
+
+        /**
+         * A priority list of PLMN to be used in EPDG_ADDRESS_PLMN. Possible values are {@link
+         * #EPDG_PLMN_RPLMN}, {@link #EPDG_PLMN_HPLMN}, {@link #EPDG_PLMN_EHPLMN_ALL}, {@link
+         * #EPDG_PLMN_EHPLMN_FIRST}
+         *
+         * @hide
+         */
+        public static final String KEY_EPDG_PLMN_PRIORITY_INT_ARRAY =
+                KEY_PREFIX + "epdg_plmn_priority_int_array";
+
+        /** Epdg static IP address or FQDN */
+        public static final String KEY_EPDG_STATIC_ADDRESS_STRING =
+                KEY_PREFIX + "epdg_static_address_string";
+
+        /** Epdg static IP address or FQDN for roaming */
+        public static final String KEY_EPDG_STATIC_ADDRESS_ROAMING_STRING =
+                KEY_PREFIX + "epdg_static_address_roaming_string";
+
+        /**
+         * Enables the use of multiple IKE SA proposals, encompassing both carrier-preferred
+         * ciphers and all supported ciphers from 3GPP TS 33.210 and RFC 8221,
+         * as defined in RFC 7296.
+         */
+        @FlaggedApi(Flags.FLAG_ENABLE_MULTIPLE_SA_PROPOSALS)
+        public static final String KEY_SUPPORTS_IKE_SESSION_MULTIPLE_SA_PROPOSALS_BOOL =
+                KEY_PREFIX + "supports_ike_session_multiple_sa_proposals_bool";
+
+        /**
+         * Enables the use of multiple Child SA proposals, encompassing both carrier-preferred
+         * ciphers and all supported ciphers from 3GPP TS 33.210 and RFC 8221,
+         * as defined in RFC 7296.
+         */
+        @FlaggedApi(Flags.FLAG_ENABLE_MULTIPLE_SA_PROPOSALS)
+        public static final String KEY_SUPPORTS_CHILD_SESSION_MULTIPLE_SA_PROPOSALS_BOOL =
+                KEY_PREFIX + "supports_child_session_multiple_sa_proposals_bool";
+
+        /**
+         * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of child
+         * session. Possible values are:
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
+         */
+        public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY =
+                KEY_PREFIX + "child_session_aes_cbc_key_size_int_array";
+
+        /**
+         * List of supported key sizes for AES Counter (CTR) encryption mode of child session.
+         * Possible values are:
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
+         */
+        public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY =
+                KEY_PREFIX + "child_session_aes_ctr_key_size_int_array";
+
+        /**
+         * List of supported key sizes for AES Galois/Counter Mode (GCM) encryption mode
+         * of child session.
+         * Possible values are:
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
+         */
+        @FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS)
+        public static final String KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY =
+                KEY_PREFIX + "child_session_aes_gcm_key_size_int_array";
+
+        /**
+         * List of supported encryption algorithms for child session. Possible values are
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC},
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CTR}
+         */
+        public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY =
+                KEY_PREFIX + "supported_child_session_encryption_algorithms_int_array";
+
+        /**
+         * List of supported AEAD algorithms for child session. Possible values are
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_8},
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_12},
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_16}
+         */
+        @FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS)
+        public static final String KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY =
+                KEY_PREFIX + "supported_child_session_aead_algorithms_int_array";
+
+        /**
+         * Time in seconds after which the IKE session is terminated if rekey procedure is not
+         * successful. If not set or set to <= 0, default value is 3600 seconds.
+         */
+        public static final String KEY_IKE_REKEY_HARD_TIMER_SEC_INT =
+                KEY_PREFIX + "ike_rekey_hard_timer_in_sec";
+
+        /**
+         * Time in seconds after which the IKE session rekey procedure is started. If not set or set
+         * to <= 0, default value is 3000 seconds.
+         */
+        public static final String KEY_IKE_REKEY_SOFT_TIMER_SEC_INT =
+                KEY_PREFIX + "ike_rekey_soft_timer_sec_int";
+
+        /**
+         * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of IKE
+         * session. Possible values:
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
+         */
+        public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY =
+                KEY_PREFIX + "ike_session_encryption_aes_cbc_key_size_int_array";
+
+        /**
+         * List of supported key sizes for AES Counter (CTR) encryption mode of IKE session.
+         * Possible values -
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
+         */
+         public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY =
+                 KEY_PREFIX + "ike_session_encryption_aes_ctr_key_size_int_array";
+
+        /**
+         * List of supported key sizes for AES Galois/Counter Mode (GCM) encryption mode
+         * of IKE session.
+         * Possible values -
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
+         */
+        @FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS)
+        public static final String KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY =
+                KEY_PREFIX + "ike_session_encryption_aes_gcm_key_size_int_array";
+
+        /**
+         * List of supported encryption algorithms for IKE session. Possible values are
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC},
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CTR}
+         */
+        public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY =
+                KEY_PREFIX + "supported_ike_session_encryption_algorithms_int_array";
+
+        /**
+         * List of supported AEAD algorithms for IKE session. Possible values are
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_8},
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_12},
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_GCM_16}
+         */
+        @FlaggedApi(Flags.FLAG_ENABLE_AEAD_ALGORITHMS)
+        public static final String KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY =
+                KEY_PREFIX + "supported_ike_session_aead_algorithms_int_array";
+
+        /**
+         * List of supported integrity algorithms for IKE session. Possible values are
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_NONE},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA1_96},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_AES_XCBC_96},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_256_128},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_384_192},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_512_256}
+         */
+        public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY =
+                KEY_PREFIX + "supported_integrity_algorithms_int_array";
+
+        /** Maximum number of retries for tunnel establishment. */
+        public static final String KEY_MAX_RETRIES_INT = KEY_PREFIX + "max_retries_int";
+
+        /**
+         * Time in seconds after which a NATT keep alive message is sent. If not set or set to <= 0,
+         * default value is 20 seconds.
+         */
+        public static final String KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT =
+                KEY_PREFIX + "natt_keep_alive_timer_sec_int";
+
+        /** List of '-' separated MCC/MNCs used to create ePDG FQDN as per 3GPP TS 23.003 */
+        public static final String KEY_MCC_MNCS_STRING_ARRAY = KEY_PREFIX + "mcc_mncs_string_array";
+
+        /**
+         * List of supported pseudo random function algorithms for IKE session. Possible values are
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_HMAC_SHA1},
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_AES128_XCBC},
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_256},
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_384},
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_512}
+         */
+        public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY =
+                KEY_PREFIX + "supported_prf_algorithms_int_array";
+
+        /**
+         * List of IKE message retransmission timeouts in milliseconds, where each timeout
+         * is the waiting time before next retry, except the last timeout which is the waiting time
+         * before terminating the IKE Session. Min list length = 1, Max
+         * list length = 10 Min timeout = 500 ms, Max timeout = 1800000 ms
+         */
+        public static final String KEY_RETRANSMIT_TIMER_MSEC_INT_ARRAY =
+                KEY_PREFIX + "retransmit_timer_sec_int_array";
+
+        /**
+         * Specifies the local identity type for IKE negotiations. Possible values are {@link
+         * #ID_TYPE_FQDN}, {@link #ID_TYPE_RFC822_ADDR}, {@link #ID_TYPE_KEY_ID}
+         */
+        public static final String KEY_IKE_LOCAL_ID_TYPE_INT = KEY_PREFIX + "ike_local_id_type_int";
+
+        /**
+         * Specifies the remote identity type for IKE negotiations. Possible values are {@link
+         * #ID_TYPE_FQDN}, {@link #ID_TYPE_RFC822_ADDR}, {@link #ID_TYPE_KEY_ID}
+         */
+        public static final String KEY_IKE_REMOTE_ID_TYPE_INT =
+                KEY_PREFIX + "ike_remote_id_type_int";
+
+        /** Controls if KE payload should be added during child session local rekey procedure. */
+        public static final String KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL =
+                KEY_PREFIX + "add_ke_to_child_session_rekey_bool";
+
+        /** Specifies the PCO id for IPv6 Epdg server address */
+        public static final String KEY_EPDG_PCO_ID_IPV6_INT = KEY_PREFIX + "epdg_pco_id_ipv6_int";
+
+        /** Specifies the PCO id for IPv4 Epdg server address */
+        public static final String KEY_EPDG_PCO_ID_IPV4_INT = KEY_PREFIX + "epdg_pco_id_ipv4_int";
+
+        /** Controls if the IKE tunnel setup supports EAP-AKA fast reauth */
+        public static final String KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL =
+                KEY_PREFIX + "supports_eap_aka_fast_reauth_bool";
+
+        /**
+         * Type of IP preference used to prioritize ePDG servers. Possible values are
+         * {@link #EPDG_ADDRESS_IPV4_PREFERRED}, {@link #EPDG_ADDRESS_IPV6_PREFERRED},
+         * {@link #EPDG_ADDRESS_IPV4_ONLY}
+         */
+        public static final String KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT =
+                KEY_PREFIX + "epdg_address_ip_type_preference_int";
+
+        /** @hide */
+        @IntDef({AUTHENTICATION_METHOD_EAP_ONLY, AUTHENTICATION_METHOD_CERT})
+        public @interface AuthenticationMethodType {}
+
+        /**
+         * Certificate sent from the server is ignored. Only Extensible Authentication Protocol
+         * (EAP) is used to authenticate the server. EAP_ONLY_AUTH payload is added to IKE_AUTH
+         * request if supported.
+         *
+         * @see <a href="https://tools.ietf.org/html/rfc5998">RFC 5998</a>
+         */
+        public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0;
+        /** Server is authenticated using its certificate. */
+        public static final int AUTHENTICATION_METHOD_CERT = 1;
+
+        /** @hide */
+        @IntDef({
+            EPDG_ADDRESS_STATIC,
+            EPDG_ADDRESS_PLMN,
+            EPDG_ADDRESS_PCO,
+            EPDG_ADDRESS_CELLULAR_LOC,
+            EPDG_ADDRESS_VISITED_COUNTRY
+        })
+        public @interface EpdgAddressType {}
+
+        /** Use static epdg address. */
+        public static final int EPDG_ADDRESS_STATIC = 0;
+        /** Construct the epdg address using plmn. */
+        public static final int EPDG_ADDRESS_PLMN = 1;
+        /**
+         * Use the epdg address received in protocol configuration options (PCO) from the network.
+         */
+        public static final int EPDG_ADDRESS_PCO = 2;
+        /** Use cellular location to chose epdg server */
+        public static final int EPDG_ADDRESS_CELLULAR_LOC = 3;
+        /** Use Visited Country FQDN rule*/
+        public static final int EPDG_ADDRESS_VISITED_COUNTRY = 4;
+
+        /** @hide */
+        @IntDef({
+                EPDG_PLMN_RPLMN,
+                EPDG_PLMN_HPLMN,
+                EPDG_PLMN_EHPLMN_ALL,
+                EPDG_PLMN_EHPLMN_FIRST
+        })
+        public @interface EpdgAddressPlmnType {}
+
+        /**
+         * Use the Registered PLMN
+         * @hide
+         */
+        public static final int EPDG_PLMN_RPLMN = 0;
+        /**
+         * Use the PLMN derived from IMSI
+         * @hide
+         */
+        public static final int EPDG_PLMN_HPLMN = 1;
+        /**
+         * Use all EHPLMN from SIM EF files
+         * @hide
+         */
+        public static final int EPDG_PLMN_EHPLMN_ALL = 2;
+        /**
+         * Use the first EHPLMN from SIM EF files
+         * @hide
+         */
+        public static final int EPDG_PLMN_EHPLMN_FIRST = 3;
+
+        /** @hide */
+        @IntDef({ID_TYPE_FQDN, ID_TYPE_RFC822_ADDR, ID_TYPE_KEY_ID})
+        public @interface IkeIdType {}
+
+        /**
+         * Ike Identification Fully Qualified Domain Name
+         *
+         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.5">RFC 7296, Internet Key
+         *     Exchange Protocol Version 2 (IKEv2)</a>
+         */
+        public static final int ID_TYPE_FQDN = 2;
+        /**
+         * Ike Identification Fully Qualified RFC 822 email address.
+         *
+         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.5">RFC 7296, Internet Key
+         *     Exchange Protocol Version 2 (IKEv2)</a>
+         */
+        public static final int ID_TYPE_RFC822_ADDR = 3;
+        /**
+         * Ike Identification opaque octet stream for vendor specific information
+         *
+         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.5">RFC 7296, Internet Key
+         *     Exchange Protocol Version 2 (IKEv2)</a>
+         */
+        public static final int ID_TYPE_KEY_ID = 11;
+
+        /** @hide */
+        @IntDef({
+                EPDG_ADDRESS_IPV4_PREFERRED,
+                EPDG_ADDRESS_IPV6_PREFERRED,
+                EPDG_ADDRESS_IPV4_ONLY,
+                EPDG_ADDRESS_IPV6_ONLY,
+                EPDG_ADDRESS_SYSTEM_PREFERRED
+        })
+        public @interface EpdgAddressIpPreference {}
+
+        /** Prioritize IPv4 ePDG addresses. */
+        public static final int EPDG_ADDRESS_IPV4_PREFERRED = 0;
+
+        /** Prioritize IPv6 ePDG addresses */
+        public static final int EPDG_ADDRESS_IPV6_PREFERRED = 1;
+
+        /** Use IPv4 ePDG addresses only. */
+        public static final int EPDG_ADDRESS_IPV4_ONLY = 2;
+
+        /** Use IPv6 ePDG addresses only.
+         * @hide
+         */
+        public static final int EPDG_ADDRESS_IPV6_ONLY = 3;
+
+        /** Follow the priority from DNS resolution results, which are sorted by using RFC6724
+         * algorithm.
+         *
+         * @see <a href="https://tools.ietf.org/html/rfc6724#section-6">RFC 6724, Default Address
+         *     Selection for Internet Protocol Version 6 (IPv6)</a>
+         * @hide
+         */
+        public static final int EPDG_ADDRESS_SYSTEM_PREFERRED = 4;
+
+        private Iwlan() {}
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putInt(KEY_IKE_REKEY_SOFT_TIMER_SEC_INT, 7200);
+            defaults.putInt(KEY_IKE_REKEY_HARD_TIMER_SEC_INT, 14400);
+            defaults.putInt(KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT, 3600);
+            defaults.putInt(KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT, 7200);
+            defaults.putBoolean(KEY_SUPPORTS_IKE_SESSION_MULTIPLE_SA_PROPOSALS_BOOL, false);
+            defaults.putBoolean(KEY_SUPPORTS_CHILD_SESSION_MULTIPLE_SA_PROPOSALS_BOOL, false);
+            defaults.putIntArray(
+                    KEY_RETRANSMIT_TIMER_MSEC_INT_ARRAY, new int[] {500, 1000, 2000, 4000, 8000});
+            defaults.putInt(KEY_DPD_TIMER_SEC_INT, 120);
+            defaults.putInt(KEY_MAX_RETRIES_INT, 3);
+            defaults.putIntArray(
+                    KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY,
+                    new int[] {
+                        SaProposal.DH_GROUP_1024_BIT_MODP,
+                        SaProposal.DH_GROUP_1536_BIT_MODP,
+                        SaProposal.DH_GROUP_2048_BIT_MODP
+                    });
+            defaults.putIntArray(
+                    KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
+                    new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC});
+            defaults.putIntArray(
+                    KEY_SUPPORTED_IKE_SESSION_AEAD_ALGORITHMS_INT_ARRAY, new int[] {});
+            defaults.putIntArray(
+                    KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
+                    new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC});
+            defaults.putIntArray(
+                    KEY_SUPPORTED_CHILD_SESSION_AEAD_ALGORITHMS_INT_ARRAY, new int[] {});
+            defaults.putIntArray(
+                    KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY,
+                    new int[] {
+                        SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96,
+                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96,
+                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
+                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
+                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256,
+                    });
+            defaults.putIntArray(
+                    KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY,
+                    new int[] {
+                        SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1,
+                        SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC,
+                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256,
+                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384,
+                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512
+                    });
+
+            defaults.putInt(KEY_EPDG_AUTHENTICATION_METHOD_INT, AUTHENTICATION_METHOD_EAP_ONLY);
+            defaults.putString(KEY_EPDG_STATIC_ADDRESS_STRING, "");
+            defaults.putString(KEY_EPDG_STATIC_ADDRESS_ROAMING_STRING, "");
+            // will be used after b/158036773 is fixed
+            defaults.putInt(KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT, 20);
+            defaults.putIntArray(
+                    KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY,
+                    new int[] {
+                      SaProposal.KEY_LEN_AES_128,
+                      SaProposal.KEY_LEN_AES_192,
+                      SaProposal.KEY_LEN_AES_256});
+            defaults.putIntArray(
+                    KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY,
+                    new int[] {
+                      SaProposal.KEY_LEN_AES_128,
+                      SaProposal.KEY_LEN_AES_192,
+                      SaProposal.KEY_LEN_AES_256});
+            defaults.putIntArray(
+                    KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY,
+                    new int[] {
+                      SaProposal.KEY_LEN_AES_128,
+                      SaProposal.KEY_LEN_AES_192,
+                      SaProposal.KEY_LEN_AES_256});
+            defaults.putIntArray(
+                    KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY,
+                    new int[] {
+                      SaProposal.KEY_LEN_AES_128,
+                      SaProposal.KEY_LEN_AES_192,
+                      SaProposal.KEY_LEN_AES_256});
+            defaults.putIntArray(
+                    KEY_IKE_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY, new int[] {});
+            defaults.putIntArray(
+                    KEY_CHILD_SESSION_AES_GCM_KEY_SIZE_INT_ARRAY, new int[] {});
+            defaults.putIntArray(
+                    KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
+                    new int[] {EPDG_ADDRESS_PLMN, EPDG_ADDRESS_STATIC});
+            defaults.putIntArray(
+                    KEY_EPDG_PLMN_PRIORITY_INT_ARRAY,
+                    new int[]{
+                            EPDG_PLMN_RPLMN,
+                            EPDG_PLMN_HPLMN,
+                            EPDG_PLMN_EHPLMN_ALL});
+            defaults.putStringArray(KEY_MCC_MNCS_STRING_ARRAY, new String[0]);
+            defaults.putInt(KEY_IKE_LOCAL_ID_TYPE_INT, ID_TYPE_RFC822_ADDR);
+            defaults.putInt(KEY_IKE_REMOTE_ID_TYPE_INT, ID_TYPE_FQDN);
+            defaults.putBoolean(KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL, false);
+            defaults.putInt(KEY_EPDG_PCO_ID_IPV6_INT, 0);
+            defaults.putInt(KEY_EPDG_PCO_ID_IPV4_INT, 0);
+            defaults.putBoolean(KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL, false);
+            defaults.putInt(KEY_EPDG_ADDRESS_IP_TYPE_PREFERENCE_INT, EPDG_ADDRESS_IPV4_PREFERRED);
+            return defaults;
+        }
+    }
+
+    /**
+     * A list of 4 GSM RSSI thresholds above which a signal level is considered POOR,
+     * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
+     *
+     * Note that the min and max thresholds are fixed at -113 and -51, as set in 3GPP TS 27.007
+     * section 8.5.
+     * <p>
+     * See CellSignalStrengthGsm#GSM_RSSI_MAX and CellSignalStrengthGsm#GSM_RSSI_MIN. Any signal
+     * level outside these boundaries is considered invalid.
+     * @hide
+     */
+    public static final String KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY = "gsm_rssi_thresholds_int_array";
+
+    /**
+     * An interval in dB for {@link SignalThresholdInfo#SIGNAL_MEASUREMENT_TYPE_RSSI} measurement
+     * type defining the required magnitude change between reports.
+     *
+     * <p>The default value is 2 and the minimum allowed value is 0. If no value or negative value
+     * is set, the default value 2 is used.
+     * @hide
+     */
+    public static final String KEY_GERAN_RSSI_HYSTERESIS_DB_INT = "geran_rssi_hysteresis_db_int";
+
+    /**
+     * Determines whether Wireless Priority Service call is supported over IMS.
+     *
+     * See Wireless Priority Service from https://www.fcc.gov/general/wireless-priority-service-wps
+     * @hide
+     */
+    public static final String KEY_SUPPORT_WPS_OVER_IMS_BOOL = "support_wps_over_ims_bool";
+
+    /**
+     * The two digital number pattern of MMI code which is defined by carrier.
+     * If the dial number matches this pattern, it will be dialed out normally not USSD.
+     *
+     * @hide
+     */
+    public static final String KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY =
+            "mmi_two_digit_number_pattern_string_array";
+
+    /**
+     * Holds the list of carrier certificate hashes, followed by optional package names.
+     * Format: "sha1/256" or "sha1/256:package1,package2,package3..."
+     * Note that each carrier has its own hashes.
+     */
+    public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY =
+            "carrier_certificate_string_array";
+
+    /**
+     * Flag specifying whether the incoming call number and the conference participant number
+     * should be formatted to national number for Japan.
+     * @return {@code true} convert to the national format, {@code false} otherwise.
+     * e.g. "+819012345678" -> "09012345678"
+     * @hide
+     */
+    public static final String KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL =
+            "format_incoming_number_to_national_for_jp_bool";
+
+    /**
+     * DisconnectCause array to play busy tone. Value should be array of
+     * {@link android.telephony.DisconnectCause}.
+     */
+    public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY =
+            "disconnect_cause_play_busytone_int_array";
+
+    /**
+     * Flag specifying whether to prevent sending CLIR activation("*31#") and deactivation("#31#")
+     * code only without dialing number.
+     * When {@code true}, these are prevented, {@code false} otherwise.
+     */
+    public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL =
+            "prevent_clir_activation_and_deactivation_code_bool";
+
+    /**
+     * Flag specifying whether to show forwarded number on call-in-progress screen.
+     * When true, forwarded number is shown.
+     * When false, forwarded number is not shown.
+     */
+    public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL = "show_forwarded_number_bool";
+
+    /**
+     * The list of originating address of missed incoming call SMS. If the SMS has originator
+     * matched, the SMS will be treated as special SMS for notifying missed incoming call to the
+     * user.
+     *
+     * @hide
+     */
+    public static final String KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY =
+            "missed_incoming_call_sms_originator_string_array";
+
+    /**
+     * Network capability priority for determine the satisfy order in telephony. The priority is
+     * from the lowest 0 to the highest 100. The long-lived network shall have the lowest priority.
+     * This allows other short-lived requests like MMS requests to be established. Emergency request
+     * always has the highest priority.
+     *
+     * @hide
+     */
+    public static final String KEY_TELEPHONY_NETWORK_CAPABILITY_PRIORITIES_STRING_ARRAY =
+            "telephony_network_capability_priorities_string_array";
+
+    /**
+     * Defines the rules for data setup retry.
+     *
+     * The syntax of the retry rule:
+     * 1. Retry based on {@link NetworkCapabilities}. Note that only APN-type network capabilities
+     *    are supported. If the capabilities are not specified, then the retry rule only applies
+     *    to the current failed APN used in setup data call request.
+     * "capabilities=[netCaps1|netCaps2|...], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
+     *
+     * 2. Retry based on {@link DataFailCause}
+     * "fail_causes=[cause1|cause2|cause3|..], [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
+     *
+     * 3. Retry based on {@link NetworkCapabilities} and {@link DataFailCause}. Note that only
+     *    APN-type network capabilities are supported.
+     * "capabilities=[netCaps1|netCaps2|...], fail_causes=[cause1|cause2|cause3|...],
+     *     [retry_interval=n1|n2|n3|n4...], [maximum_retries=n]"
+     *
+     * 4. Permanent fail causes (no timer-based retry) on the current failed APN. Retry interval
+     *    is specified for retrying the next available APN.
+     * "permanent_fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|65543|65547|
+     *     2252|2253|2254, retry_interval=2500"
+     *
+     * For example,
+     * "capabilities=eims, retry_interval=1000, maximum_retries=20" means if the attached
+     * network request is emergency, then retry data network setup every 1 second for up to 20
+     * times.
+     *
+     * "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2500|3000|"
+     * "5000|10000|15000|20000|40000|60000|120000|240000|600000|1200000|1800000"
+     * "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s,
+     * 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries.
+     *
+     * @hide
+     */
+    public static final String KEY_TELEPHONY_DATA_SETUP_RETRY_RULES_STRING_ARRAY =
+            "telephony_data_setup_retry_rules_string_array";
+
+    /**
+     * Defines the rules for data handover retry.
+     *
+     * The syntax of the retry rule:
+     * 1. Retry when handover fails.
+     * "retry_interval=[n1|n2|n3|...], [maximum_retries=n]"
+     *
+     * For example,
+     * "retry_interval=1000|3000|5000, maximum_retries=10" means handover retry will happen in 1s,
+     * 3s, 5s, 5s, 5s....up to 10 times.
+     *
+     * 2. Retry when handover fails with certain fail causes.
+     * "retry_interval=[n1|n2|n3|...], fail_causes=[cause1|cause2|cause3|...], [maximum_retries=n]
+     *
+     * For example,
+     * "retry_interval=1000, maximum_retries=3, fail_causes=5" means handover retry every 1 second
+     * for up to 3 times when handover fails with the cause 5.
+     *
+     * "maximum_retries=0, fail_causes=6|10|67" means handover retry should not happen for those
+     * causes.
+     *
+     * @hide
+     */
+    public static final String KEY_TELEPHONY_DATA_HANDOVER_RETRY_RULES_STRING_ARRAY =
+            "telephony_data_handover_retry_rules_string_array";
+
+    /**
+     * Indicates whether delay tearing down IMS data network until voice call ends.
+     * @hide
+     */
+    public static final String KEY_DELAY_IMS_TEAR_DOWN_UNTIL_CALL_END_BOOL =
+            "delay_ims_tear_down_until_call_end_bool";
+
+    /**
+     * The patterns of missed incoming call sms. This is the regular expression used for
+     * matching the missed incoming call's date, time, and caller id. The pattern should match
+     * fields for at least month, day, hour, and minute. Year is optional although it is encouraged.
+     *
+     * An usable pattern should look like this:
+     * ^(?<month>0[1-9]|1[012])\/(?<day>0[1-9]|1[0-9]|2[0-9]|3[0-1]) (?<hour>[0-1][0-9]|2[0-3]):
+     * (?<minute>[0-5][0-9])\s*(?<callerId>[0-9]+)\s*$
+     *
+     * @hide
+     */
+    public static final String KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY =
+            "missed_incoming_call_sms_pattern_string_array";
+
+    /**
+     * A PersistableBundle that contains a list of key-value pairs, where the values are integer
+     * arrays.
+     * <p>
+     * Keys are the PLMNs of satellite providers as strings and values are integer arrays of
+     * supported services with the following value:
+     * <ul>
+     * <li>1 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VOICE}</li>
+     * <li>2 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_DATA}</li>
+     * <li>3 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_SMS}</li>
+     * <li>4 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VIDEO}</li>
+     * <li>5 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_EMERGENCY}</li>
+     * <li>6 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_MMS}</li>
+     * </ul>
+     * <p>
+     * An example config for two PLMNs "123411" and "123412":
+     * <pre>{@code
+     * <carrier_config>
+     *   <pbundle_as_map name="carrier_supported_satellite_services_per_provider_bundle">
+     *     <int-array name = "123411" num = "2">
+     *       <item value = "3"/>
+     *       <item value = "5"/>
+     *     </int-array>
+     *     <int-array name = "123412" num = "1">
+     *       <item value = "3"/>
+     *     </int-array>
+     *   </pbundle_as_map>
+     * </carrier_config>
+     * }</pre>
+     * <p>
+     * This config is empty by default.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE =
+            "carrier_supported_satellite_services_per_provider_bundle";
+
+    /**
+     * This config enables modem to scan satellite PLMNs specified as per
+     * {@link #KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE} and attach to same
+     * in case cellular networks are not enabled. This will need specific agreement between
+     * satellite provider and the carrier before enabling this flag.
+     *
+     * The default value is false.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL =
+            "satellite_attach_supported_bool";
+
+    /**
+     * The carrier-enabled satellite connection hysteresis time in seconds for which the device
+     * continues in satellite mode after it loses the connection with the satellite network.
+     * <p>
+     * If the device is in satellite mode, the following actions will be taken by the device:
+     * <ul>
+     * <li>System UI will continue showing the satellite icon.</li>
+     * <li>When there is an ongoing emergency call, and the IMS is not registered, and cellular
+     * service is not available, and the device is in satellite mode, a timer with a duration
+     * defined by the overlay config
+     * {@code config_emergency_call_wait_for_connection_timeout_millis} will be started. When the
+     * timer expires, Telephony will send the event
+     * {@link TelephonyManager#EVENT_DISPLAY_EMERGENCY_MESSAGE} to Dialer, which will then prompt
+     * users to switch to using satellite emergency messaging.</li>
+     * </ul>
+     * <p>
+     * The default value is 300 seconds.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final String KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT =
+            "satellite_connection_hysteresis_sec_int";
+
+    /**
+     * This threshold is used when connected to a non-terrestrial LTE network.
+     * A list of 4 NTN LTE RSRP thresholds above which a signal level is considered POOR,
+     * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
+     *
+     * Note that the min and max thresholds are fixed at -140 and -44, as explained in
+     * TS 136.133 9.1.4 - RSRP Measurement Report Mapping.
+     * <p>
+     * See SignalStrength#MAX_LTE_RSRP and SignalStrength#MIN_LTE_RSRP. Any signal level outside
+     * these boundaries is considered invalid.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final String KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY =
+            "ntn_lte_rsrp_thresholds_int_array";
+
+    /**
+     * This threshold is used when connected to a non-terrestrial LTE network.
+     * A list of 4 customized NTN LTE Reference Signal Received Quality (RSRQ) thresholds.
+     *
+     * Reference: TS 136.133 v12.6.0 section 9.1.7 - RSRQ Measurement Report Mapping.
+     *
+     * 4 threshold integers must be within the boundaries [-34 dB, 3 dB], and the levels are:
+     *     "NONE: [-34, threshold1)"
+     *     "POOR: [threshold1, threshold2)"
+     *     "MODERATE: [threshold2, threshold3)"
+     *     "GOOD:  [threshold3, threshold4)"
+     *     "EXCELLENT:  [threshold4, 3]"
+     *
+     * This key is considered invalid if the format is violated. If the key is invalid or
+     * not configured, a default value set will apply.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final String KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY =
+            "ntn_lte_rsrq_thresholds_int_array";
+
+    /**
+     * This threshold is used when connected to a non-terrestrial LTE network.
+     * A list of 4 customized NTN LTE Reference Signal Signal to Noise Ratio (RSSNR) thresholds.
+     *
+     * 4 threshold integers must be within the boundaries [-20 dB, 30 dB], and the levels are:
+     *     "NONE: [-20, threshold1)"
+     *     "POOR: [threshold1, threshold2)"
+     *     "MODERATE: [threshold2, threshold3)"
+     *     "GOOD:  [threshold3, threshold4)"
+     *     "EXCELLENT:  [threshold4, 30]"
+     *
+     * This key is considered invalid if the format is violated. If the key is invalid or
+     * not configured, a default value set will apply.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final String KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY =
+            "ntn_lte_rssnr_thresholds_int_array";
+
+    /**
+     * This threshold is used when connected to a non-terrestrial LTE network.
+     * Bit-field integer to determine whether to use Reference Signal Received Power (RSRP),
+     * Reference Signal Received Quality (RSRQ), or/and Reference Signal Signal to Noise Ratio
+     * (RSSNR) for the number of NTN LTE signal bars and signal criteria reporting enabling.
+     *
+     * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and
+     * not be used for calculating signal level. If multiple measures are set bit, the parameter
+     * whose value is smallest is used to indicate the signal level.
+     * <UL>
+     *  <LI>RSRP = 1 << 0</LI>
+     *  <LI>RSRQ = 1 << 1</LI>
+     *  <LI>RSSNR = 1 << 2</LI>
+     * </UL>
+     * <p> The value of this key must be bitwise OR of CellSignalStrengthLte#USE_RSRP,
+     * CellSignalStrengthLte#USE_RSRQ, CellSignalStrengthLte#USE_RSSNR.
+     *
+     * <p> For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
+     * If the key is invalid or not configured, a default value (RSRP = 1 << 0) will apply.
+     *
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final String KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT =
+            "parameters_used_for_ntn_lte_signal_bar_int";
+
+    /**
+     * Indicating whether plmns associated with carrier satellite can be exposed to user when
+     * manually scanning available cellular network.
+     * If key is {@code true}, satellite plmn should not be exposed to user and should be
+     * automatically set, {@code false} otherwise. Default value is {@code true}.
+     *
+     * @hide
+     */
+    public static final String KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL =
+            "remove_satellite_plmn_in_manual_network_scan_bool";
+
+    /**
+     * Determine whether to override roaming Wi-Fi Calling preference when device is connected to
+     * non-terrestrial network.
+     * {@code true}  - roaming preference cannot be changed by user independently.
+     *                 Roaming preference is overridden to
+     *                 {@link com.android.ims.ImsConfig.WfcModeFeatureValueConstants#WIFI_PREFERRED}
+     * {@code false} - roaming preference can be changed by user independently and is not
+     *                 overridden when device is connected to non-terrestrial network.
+     * @hide
+     */
+    public static final String KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL =
+            "override_wfc_roaming_mode_while_using_ntn_bool";
+
+    /**
+     * An integer key holds the time interval for refreshing or re-querying the satellite
+     * entitlement status from the entitlement server to ensure it is the latest.
+     *
+     * The default value is 7 days.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT =
+            "satellite_entitlement_status_refresh_days_int";
+
+    /**
+     * This configuration enables device to query the entitlement server to get the satellite
+     * configuration.
+     * This will need agreement the carrier before enabling this flag.
+     *
+     * The default value is false.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL =
+            "satellite_entitlement_supported_bool";
+
+    /**
+     * Indicates the appName that is used when querying the entitlement server for satellite.
+     *
+     * The default value is androidSatmode.
+     *
+     * Reference: GSMA TS.43-v11, 2.8.5 Fast Authentication and Token Management.
+     * `app_name` is an optional attribute in the request and may vary depending on the carrier
+     * requirement.
+     * @hide
+     */
+    public static final String KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING =
+            "satellite_entitlement_app_name_string";
+
+    /**
+     * URL to redirect user to get more information about the carrier support for satellite.
+     *
+     * The default value is empty string.
+     *
+     * @hide
+     */
+    public static final String KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING =
+            "satellite_information_redirect_url_string";
+    /**
+     * Indicate whether a carrier supports emergency messaging. When this config is {@code  false},
+     * emergency call to satellite T911 handover will be disabled.
+     *
+     * This will need agreement with carriers before enabling this flag.
+     *
+     * The default value is false.
+     *
+     * @hide
+     */
+    public static final String KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL =
+            "emergency_messaging_supported_bool";
+
+    /**
+     * An integer key holds the timeout duration in milliseconds used to determine whether to hand
+     * over an emergency call to satellite T911.
+     *
+     * The timer is started when there is an ongoing emergency call, and the IMS is not registered,
+     * and cellular service is not available. When the timer expires,
+     * {@link com.android.internal.telephony.satellite.SatelliteSOSMessageRecommender} will send the
+     * event {@link TelephonyManager#EVENT_DISPLAY_EMERGENCY_MESSAGE} to Dialer, which will then
+     * prompt user to switch to using satellite emergency messaging.
+     *
+     * The default value is 30 seconds.
+     *
+     * @hide
+     */
+    public static final String KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT =
+            "emergency_call_to_satellite_t911_handover_timeout_millis_int";
+
+    /**
+     * An int array that contains default capabilities for carrier enabled satellite roaming.
+     * If any PLMN is provided from the entitlement server, and it is not listed in
+     * {@link #KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE}, default capabilities
+     * will be used instead.
+     * <p>
+     * The default capabilities are
+     * {@link NetworkRegistrationInfo#SERVICE_TYPE_SMS}, and
+     * {@link NetworkRegistrationInfo#SERVICE_TYPE_MMS}
+     *
+     * @hide
+     */
+    public static final String KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY =
+            "carrier_roaming_satellite_default_services_int_array";
+
+    /**
+     * Indicating whether DUN APN should be disabled when the device is roaming. In that case,
+     * the default APN (i.e. internet) will be used for tethering.
+     *
+     * This config is only available when using Preset APN(not user edited) as Preferred APN.
+     *
+     * @hide
+     */
+    public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL =
+            "disable_dun_apn_while_roaming_with_preset_apn_bool";
+
+    /**
+     * Where there is no preferred APN, specifies the carrier's default preferred APN.
+     * Specifies the {@link android.provider.Telephony.Carriers.APN} of the default preferred apn.
+     *
+     * This config is only available with Preset APN(not user edited).
+     *
+     * @hide
+     */
+    public static final String KEY_DEFAULT_PREFERRED_APN_NAME_STRING =
+            "default_preferred_apn_name_string";
+
+    /**
+     * Indicates if the carrier supports call composer.
+     */
+    public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
+
+    /**
+     * Indicates if the carrier supports a business call composer.
+     */
+    @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_BUSINESS_CALL_COMPOSER)
+    public static final String KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL =
+            "supports_business_call_composer_bool";
+
+    /**
+     * Indicates the carrier server url that serves the call composer picture.
+     */
+    public static final String KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING =
+            "call_composer_picture_server_url_string";
+
+    /**
+     * Determines the default RTT mode.
+     *
+     * Upon first boot, when the user has not yet set a value for their preferred RTT mode,
+     * the value of this config will be sent to the IMS stack. Valid values are the same as for
+     * {@link Settings.Secure#RTT_CALLING_MODE}.
+     *
+     * @hide
+     */
+    public static final String KEY_DEFAULT_RTT_MODE_INT = "default_rtt_mode_int";
+
+    /**
+     * Indicates whether RTT is supported while roaming.
+     */
+    public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL =
+            "rtt_supported_while_roaming_bool";
+
+    /**
+     * Indicates if auto-configuration server is used for the RCS config
+     * Reference: GSMA RCC.14
+     */
+    public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
+
+    /**
+     * Indicates temporarily unmetered mobile data is supported by the carrier.
+     * @hide
+     */
+    public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL =
+            "network_temp_not_metered_supported_bool";
+
+    /**
+     * Boolean indicating whether the SIM PIN can be stored and verified
+     * seamlessly after an unattended reboot.
+     *
+     * The device configuration value {@code config_allow_pin_storage_for_unattended_reboot}
+     * ultimately controls whether this carrier configuration option is used. Where
+     * {@code config_allow_pin_storage_for_unattended_reboot} is false, the value of this
+     * carrier configuration is ignored.
+     *
+     * @hide
+     */
+    public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL =
+            "store_sim_pin_for_unattended_reboot_bool";
+
+    /**
+     * Allow whether the user can use the "Allow 2G" toggle in Settings.
+     *
+     * If {@code true} then the toggle is disabled (i.e. grayed out).
+     *
+     * Used to trade privacy/security against potentially reduced carrier coverage for some
+     * carriers.
+     *
+     * @removed This config option is no longer supported as it was hiding a security feature
+     * from users. Setting this option will not change the behavior of the Settings menu starting
+     * in Android V.
+     */
+    @Deprecated
+    public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool";
+
+    /**
+     * Indicates the allowed APN types that can be used for LTE initial attach. The order of APN
+     * types in the configuration is the order of APN types that will be used for initial attach.
+     * Empty list indicates that no APN types are allowed for initial attach.
+     *
+     * @hide
+     */
+    public static final String KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY =
+            "allowed_initial_attach_apn_types_string_array";
+
+    /**
+     * Indicates whether or not the carrier will provision merged carrier Wi-Fi offload networks.
+     * Such networks are considered part of the core carrier network.
+     *
+     * This configuration will be use to gate whether such configurations are allowed to the carrier
+     * and correspondingly enable UI elements which are required for such configurations.
+     */
+    public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL =
+            "carrier_provisions_wifi_merged_networks_bool";
+
+    /**
+     * Determines whether or not to use (IP) data connectivity as a supplemental condition to
+     * control the visibility of the no-calling indicator for this carrier in the System UI. Setting
+     * the configuration to true may make sense for carriers that provide OTT calling.
+     *
+     * Config = true: show no-calling indication only if telephony does not have voice registration
+     *                and if no (IP) data connectivity is available.
+     * Config = false: show no-calling indication only if telephony does not have voice
+     *                 registration.
+     */
+    public static final String KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL =
+            "use_ip_for_calling_indicator_bool";
+
+    /**
+     * Determine whether or not to display a call strength indicator for this carrier in the System
+     * UI. Disabling the indication may be reasonable if the carrier's calling is not integrated
+     * into the Android telephony stack (e.g. it is OTT).
+     *
+     * true: Use telephony APIs to detect the current networking medium of calling and display a
+     *       UI indication based on the current strength (e.g. signal level) of that medium.
+     * false: Do not display the call strength indicator.
+     */
+    public static final String KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL =
+            "display_call_strength_indicator_bool";
+
+    /**
+     * Determine whether or not to display no data notification when data setup is permanently
+     * failed.
+     *
+     * @hide
+     */
+    public static final String KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL =
+            "display_no_data_notification_on_permanent_failure_bool";
+
+    /**
+     * Boolean indicating if the VoNR setting is visible in the Call Settings menu.
+     * If this flag is set and VoNR is enabled for this carrier (see {@link #KEY_VONR_ENABLED_BOOL})
+     * the VoNR setting menu will be visible. If {@link #KEY_VONR_ENABLED_BOOL} or
+     * this setting is false, the menu will be gone.
+     *
+     * Enabled by default.
+     *
+     */
+    public static final String KEY_VONR_SETTING_VISIBILITY_BOOL = "vonr_setting_visibility_bool";
+
+    /**
+     * Flag specifying whether VoNR should be enabled for carrier.
+     * If true, VoNr will be enabled. If false, hard disabled.
+     *
+     * Disabled by default.
+     *
+     */
+    public static final String KEY_VONR_ENABLED_BOOL = "vonr_enabled_bool";
+
+    /**
+     * Boolean indicating the default VoNR user preference setting.
+     * If true, the VoNR setting will be enabled. If false, it will be disabled initially.
+     *
+     * Enabled by default.
+     *
+     */
+    public static final String KEY_VONR_ON_BY_DEFAULT_BOOL = "vonr_on_by_default_bool";
+
+    /**
+     * Determine whether unthrottle data retry when tracking area code (TAC/LAC) from cell changes
+     *
+     * @hide
+     */
+    public static final String KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL =
+            "unthrottle_data_retry_when_tac_changes_bool";
+
+    /**
+     * A list of premium capabilities the carrier supports. Applications can prompt users to
+     * purchase these premium capabilities from their carrier for a performance boost.
+     * Valid values are any of {@link TelephonyManager}'s {@code PREMIUM_CAPABILITY_*} constants.
+     *
+     * This is empty by default, indicating that no premium capabilities are supported.
+     *
+     * @see TelephonyManager#isPremiumCapabilityAvailableForPurchase(int)
+     * @see TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)
+     */
+    public static final String KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY =
+            "supported_premium_capabilities_int_array";
+
+    /**
+     * The amount of time in milliseconds the notification for a performance boost via
+     * premium capabilities will be visible to the user after
+     * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
+     * requests user action to purchase the boost from the carrier. Once the timeout expires,
+     * the performance boost notification will be automatically dismissed and the request will fail
+     * with {@link TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT}.
+     *
+     * The default value is 30 minutes.
+     */
+    public static final String KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG =
+            "premium_capability_notification_display_timeout_millis_long";
+
+    /**
+     * The amount of time in milliseconds that the notification for a performance boost via
+     * premium capabilities should be blocked when
+     * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
+     * returns a failure due to user action or timeout.
+     * The maximum number of performance boost notifications to show the user are defined in
+     * {@link #KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT} and
+     * {@link #KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT}.
+     *
+     * The default value is 30 minutes.
+     *
+     * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED
+     * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT
+     */
+    public static final String
+            KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG =
+            "premium_capability_notification_backoff_hysteresis_time_millis_long";
+
+    /**
+     * The maximum number of times in a day that we display the notification for a performance boost
+     * via premium capabilities when
+     * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
+     * returns a failure due to user action or timeout.
+     *
+     * The default value is 2 times.
+     *
+     * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED
+     * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT
+     */
+    public static final String KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT =
+            "premium_capability_maximum_daily_notification_count_int";
+
+    /**
+     * The maximum number of times in a month that we display the notification for a performance
+     * boost via premium capabilities when
+     * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
+     * returns a failure due to user action or timeout.
+     *
+     * The default value is 10 times.
+     *
+     * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED
+     * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT
+     */
+    public static final String KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT =
+            "premium_capability_maximum_monthly_notification_count_int";
+
+    /**
+     * The amount of time in milliseconds that the purchase request should be throttled when
+     * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
+     * returns a failure due to the carrier.
+     *
+     * The default value is 30 minutes.
+     *
+     * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR
+     * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED
+     */
+    public static final String
+            KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG =
+            "premium_capability_purchase_condition_backoff_hysteresis_time_millis_long";
+
+    /**
+     * The amount of time in milliseconds within which the network must set up a slicing
+     * configuration for the premium capability after
+     * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
+     * returns {@link TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS}.
+     * During the setup time, calls to
+     * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)} will return
+     * {@link TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP}.
+     * If the network fails to set up a slicing configuration for the premium capability within the
+     * setup time, subsequent purchase requests will be allowed to go through again.
+     *
+     * The default value is 5 minutes.
+     */
+    public static final String KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG =
+            "premium_capability_network_setup_time_millis_long";
+
+    /**
+     * The URL to redirect to when the user clicks on the notification for a performance boost via
+     * premium capabilities after applications call
+     * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}.
+     * If the URL is empty or invalid, the purchase request will return
+     * {@link TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED}.
+     *
+     * This is empty by default.
+     */
+    public static final String KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING =
+            "premium_capability_purchase_url_string";
+
+    /**
+     * Whether to allow premium capabilities to be purchased when the device is connected to LTE.
+     * If this is {@code true}, applications can call
+     * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
+     * when connected to {@link TelephonyManager#NETWORK_TYPE_LTE} to purchase and use
+     * premium capabilities.
+     * If this is {@code false}, applications can only purchase and use premium capabilities when
+     * connected to {@link TelephonyManager#NETWORK_TYPE_NR}.
+     *
+     * This is {@code false} by default.
+     */
+    public static final String KEY_PREMIUM_CAPABILITY_SUPPORTED_ON_LTE_BOOL =
+            "premium_capability_supported_on_lte_bool";
+
+    /**
+     * IWLAN handover rules that determine whether handover is allowed or disallowed between
+     * cellular and IWLAN.
+     *
+     * Rule syntax: "source=[GERAN|UTRAN|EUTRAN|NGRAN|IWLAN|UNKNOWN], target=[GERAN|UTRAN|EUTRAN
+     * |NGRAN|IWLAN], type=[allowed|disallowed], roaming=[true|false], capabilities=[INTERNET|MMS
+     * |FOTA|IMS|CBS|SUPL|EIMS|XCAP|DUN]"
+     *
+     * Note that UNKNOWN can be only specified in the source access network and can be only used
+     * in the disallowed rule.
+     *
+     * The handover rules will be matched in the order. Here are some sample rules.
+     * <string-array name="iwlan_handover_rules" num="5">
+     *     <!-- Handover from IWLAN to 2G/3G is not allowed -->
+     *     <item value="source=IWLAN, target=GERAN|UTRAN, type=disallowed"/>
+     *     <!-- Handover from 2G/3G to IWLAN is not allowed -->
+     *     <item value="source=GERAN|UTRAN, target:IWLAN, type=disallowed"/>
+     *     <!-- Handover from IWLAN to 3G/4G/5G is not allowed if the device is roaming. -->
+     *     <item value="source=IWLAN, target=UTRAN|EUTRAN|NGRAN, roaming=true, type=disallowed"/>
+     *     <!-- Handover from 4G to IWLAN is not allowed if the device has capability in either IMS
+     *     or EIMS-->
+     *     <item value="source=EUTRAN, target=IWLAN, type=disallowed, capabilities=IMS|EIMS"/>
+     *     <!-- Handover is always allowed in any condition. -->
+     *     <item value="source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN|UNKNOWN,
+     *         target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"/>
+     * </string-array>
+     *
+     * When handover is not allowed, frameworks will tear down the data network on source transport,
+     * and then setup a new one on the target transport when Qualified Network Service changes the
+     * preferred access networks for particular APN types.
+     *
+     * @hide
+     */
+    public static final String KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY =
+            "iwlan_handover_policy_string_array";
+
+    /**
+     * Score table for {@link TelephonyManager#MOBILE_DATA_POLICY_AUTO_DATA_SWITCH}. The score is
+     * used in conjunction with a tolerance value defined in resource config
+     * {@code auto_data_switch_score_tolerance}, greater than which device will switch to the sub
+     * with higher score.
+     * Possible keys are network type name string(also see {@link #KEY_BANDWIDTH_STRING_ARRAY}).
+     * Value should be "score_level_0, score_level_1, score_level_2, score_level_3,score_level_4".
+     * Each network type must have 5 scores correspond to {@link CellSignalStrength}, where score is
+     * a non-negative integer. A score of 0 is treated the same as data out of service.
+     *
+     * For NR (5G), the following network names should be used:
+     * - NR_NSA: NR NSA, sub-6 frequencies
+     * - NR_NSA_MMWAVE: NR NSA, mmwave frequencies
+     * - NR_SA: NR SA, sub-6 frequencies
+     * - NR_SA_MMWAVE: NR SA, mmwave frequencies
+     *
+     * @hide
+     */
+    public static final String KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_BUNDLE =
+            "auto_data_switch_rat_signal_score_string_bundle";
+
+    // TODO(b/316183370): replace @code with @link in javadoc after feature is released
+    /**
+     * An array of cellular services supported by a subscription.
+     *
+     * <p>Permissible values include:
+     * <ul>
+     *   <li>{@code SubscriptionManager#SERVICE_CAPABILITY_VOICE} for voice services</li>
+     *   <li>{@code SubscriptionManager#SERVICE_CAPABILITY_SMS} for SMS services</li>
+     *   <li>{@code SubscriptionManager#SERVICE_CAPABILITY_DATA} for data services</li>
+     * </ul>
+     *
+     * <p>Carrier-specific factors may influence how these services are supported. Therefore,
+     * modifying this carrier configuration might not always enable the specified services. These
+     * capability bitmasks should be considered as indicators of a carrier's preferred services
+     * to enhance user experience, rather than as absolute platform guarantees.
+     *
+     * <p>Device-level service capabilities, defined by
+     * {@code TelephonyManager#isDeviceVoiceCapable} and
+     * {@code TelephonyManager#isDeviceSmsCapable}, take precedence over these subscription-level
+     * settings. For instance, a device where {@code TelephonyManager#isDeviceVoiceCapable} returns
+     * false may not be able to make voice calls, even if subscribed to a service marked as
+     * voice-capable.
+     *
+     * <p>To determine a subscription's cellular service capabilities, use
+     * {@code SubscriptionInfo#getServiceCapabilities()}. To track changes in services, register
+     * a {@link SubscriptionManager.OnSubscriptionsChangedListener} and invoke the
+     * same method in its callback.
+     *
+     * <p>Emergency service availability may not depend on the cellular service capabilities.
+     * For example, emergency calls might be possible on a subscription even if it lacks
+     * {@code SubscriptionManager#SERVICE_CAPABILITY_VOICE}.
+     *
+     * <p>If unset, the default value is “[1, 2, 3]” (supports all cellular services).
+     *
+     * @see TelephonyManager#isDeviceVoiceCapable
+     * @see TelephonyManager#isDeviceSmsCapable
+     * @see SubscriptionInfo#getServiceCapabilities()
+     * @see SubscriptionManager.OnSubscriptionsChangedListener
+     */
+    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+    public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY =
+            "cellular_service_capabilities_int_array";
+   /**
+     * Transition delay from BT to Cellular on Wear.
+     * Specifies delay when transitioning away from BT.
+     * This minimizes the duration of the netTransitionWakelock held by ConnectivityService
+     * whenever the primary/default network disappears, while still allowing some amount of time
+     * for BT to reconnect before we enable cell.
+     *
+     * If set as -1 then value from resources will be used
+     *
+     * @hide
+     */
+    public static final String KEY_WEAR_CONNECTIVITY_BT_TO_CELL_DELAY_MS_INT =
+            "proxy_connectivity_delay_cell";
+
+    /**
+     * Transition delay from BT to Cellular on Wear.
+     * If wifi connected it extends delay that has been started for BT to Cellular transition
+     * to avoid Wifi thrashing turning Cell radio and causing higher battery drain.
+     *
+     * If set as -1 then value from resources will be used
+     *
+     * @hide
+     */
+    public static final String KEY_WEAR_CONNECTIVITY_EXTEND_BT_TO_CELL_DELAY_ON_WIFI_MS_INT =
+            "wifi_connectivity_extend_cell_delay";
+
+    /** The default value for every variable. */
+    private static final PersistableBundle sDefaults;
+
+    static {
+        sDefaults = new PersistableBundle();
+        sDefaults.putString(KEY_CARRIER_CONFIG_VERSION_STRING, "");
+        sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL, false);
+        sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
+        sDefaults.putBoolean(KEY_AUTO_RETRY_FAILED_WIFI_EMERGENCY_CALL, false);
+        sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
+        sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false);
+        sDefaults.putStringArray(KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_ALLOW_LOCAL_DTMF_TONES_BOOL, true);
+        sDefaults.putBoolean(KEY_PLAY_CALL_RECORDING_TONE_BOOL, false);
+        sDefaults.putBoolean(KEY_APN_EXPAND_BOOL, true);
+        sDefaults.putBoolean(KEY_AUTO_RETRY_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
+        sDefaults.putInt(KEY_CARRIER_USSD_METHOD_INT, USSD_OVER_CS_PREFERRED);
+        sDefaults.putBoolean(KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL, false);
+        sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
+        sDefaults.putBoolean(KEY_ALLOW_MERGING_RTT_CALLS_BOOL, false);
+        sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true);
+        sDefaults.putString(KEY_DEFAULT_VM_NUMBER_STRING, "");
+        sDefaults.putString(KEY_DEFAULT_VM_NUMBER_ROAMING_STRING, "");
+        sDefaults.putString(KEY_DEFAULT_VM_NUMBER_ROAMING_AND_IMS_UNREGISTERED_STRING, "");
+        sDefaults.putBoolean(KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL, false);
+        sDefaults.putBoolean(KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true);
+        sDefaults.putBoolean(KEY_VILTE_DATA_IS_METERED_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL, false);
+        sDefaults.putBoolean(KEY_ENABLE_CROSS_SIM_CALLING_ON_OPPORTUNISTIC_DATA_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_PROMOTE_WFC_ON_CALL_FAIL_BOOL, false);
+        sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT, 2);
+        sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT, 2);
+        sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_VOWIFI_TTY_SUPPORTED_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
+        sDefaults.putBoolean(KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL,
+                false);
+        sDefaults.putBoolean(KEY_USE_ONLY_DIALED_SIM_ECC_LIST_BOOL, false);
+        sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WLAN_CLASS_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WWAN_CLASS_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WWAN_CLASS_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WLAN_CLASS_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, "");
+        sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, "");
+        sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING, "");
+        sDefaults.putInt(KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT, 64);
+        sDefaults.putBoolean(KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL, false);
+        sDefaults.putBoolean(KEY_DTMF_TYPE_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true);
+        sDefaults.putBoolean(KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL, false);
+        sDefaults.putBoolean(KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+        sDefaults.putBoolean(KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_SINGLE_OPERATOR_ROW_IN_CHOOSE_NETWORK_SETTING_BOOL, true);
+        sDefaults.putBoolean(KEY_SHOW_SPN_FOR_HOME_IN_CHOOSE_NETWORK_SETTING_BOOL, false);
+        sDefaults.putBoolean(KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL, false);
+        sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false);
+
+        sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false);
+        sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false);
+        sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL, true);
+        sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL, true);
+        sDefaults.putInt(KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT, SERVICE_CLASS_VOICE);
+        sDefaults.putBoolean(KEY_SUPPORT_SS_OVER_CDMA_BOOL, false);
+        sDefaults.putBoolean(KEY_CALL_FORWARDING_VISIBILITY_BOOL, true);
+        sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL, true);
+        sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNANSWERED_SUPPORTED_BOOL, true);
+        sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_BUSY_SUPPORTED_BOOL, true);
+        sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL, true);
+        sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL, true);
+        sDefaults.putBoolean(KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL, false);
+        sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
+        sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
+        sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
+        sDefaults.putBoolean(KEY_PREFER_2G_BOOL, false);
+        sDefaults.putBoolean(KEY_PREFER_3G_VISIBILITY_BOOL, true);
+        sDefaults.putBoolean(KEY_4G_ONLY_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_APN_SETTING_CDMA_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_CDMA_CHOICES_BOOL, false);
+        sDefaults.putBoolean(KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL, true);
+        sDefaults.putBoolean(KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL, true);
+        sDefaults.putBoolean(KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL, true);
+        sDefaults.putBoolean(KEY_USE_HFA_FOR_PROVISIONING_BOOL, false);
+        sDefaults.putBoolean(KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL, true);
+        sDefaults.putBoolean(KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL, false);
+        sDefaults.putBoolean(KEY_HIDE_VOICEMAIL_NUMBER_SETTING_BOOL, false);
+        sDefaults.putBoolean(KEY_USE_OTASP_FOR_PROVISIONING_BOOL, false);
+        sDefaults.putBoolean(KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL, false);
+        sDefaults.putBoolean(KEY_VOICE_PRIVACY_DISABLE_UI_BOOL, false);
+        sDefaults.putBoolean(KEY_WORLD_PHONE_BOOL, false);
+        sDefaults.putBoolean(KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_TETHERING_BOOL, true);
+        sDefaults.putBoolean(KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL, false);
+        sDefaults.putIntArray(KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY, new int[0]);
+        sDefaults.putInt(KEY_VOLTE_REPLACEMENT_RAT_INT, 0);
+        sDefaults.putString(KEY_DEFAULT_SIM_CALL_MANAGER_STRING, "");
+        sDefaults.putString(KEY_VVM_DESTINATION_NUMBER_STRING, "");
+        sDefaults.putInt(KEY_VVM_PORT_NUMBER_INT, 0);
+        sDefaults.putString(KEY_VVM_TYPE_STRING, "");
+        sDefaults.putBoolean(KEY_VVM_CELLULAR_DATA_REQUIRED_BOOL, false);
+        sDefaults.putString(KEY_VVM_CLIENT_PREFIX_STRING, "//VVM");
+        sDefaults.putBoolean(KEY_VVM_SSL_ENABLED_BOOL, false);
+        sDefaults.putStringArray(KEY_VVM_DISABLED_CAPABILITIES_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_VVM_LEGACY_MODE_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_VVM_PREFETCH_BOOL, true);
+        sDefaults.putString(KEY_CARRIER_VVM_PACKAGE_NAME_STRING, "");
+        sDefaults.putStringArray(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, true);
+        sDefaults.putBoolean(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false);
+        sDefaults.putBoolean(KEY_CI_ACTION_ON_SYS_UPDATE_BOOL, false);
+        sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING, "");
+        sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, "");
+        sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING, "");
+        sDefaults.putBoolean(KEY_CSP_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_ALLOW_ADDING_APNS_BOOL, true);
+        sDefaults.putStringArray(KEY_READ_ONLY_APN_TYPES_STRING_ARRAY, new String[] {"dun"});
+        sDefaults.putStringArray(KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY, null);
+        sDefaults.putAll(Apn.getDefaults());
+
+        sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
+        sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
+        sDefaults.putInt(KEY_DEFAULT_MTU_INT, 1500);
+        sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG, 3000);
+        sDefaults.putString(KEY_CARRIER_ERI_FILE_NAME_STRING, "eri.xml");
+        sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200);
+        sDefaults.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{"default", "mms", "dun", "supl", "enterprise"});
+        sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
+                new String[]{"default", "mms", "dun", "supl", "enterprise"});
+        sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
+                new int[] {TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_1xRTT,
+                        TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_EVDO_A,
+                        TelephonyManager.NETWORK_TYPE_EVDO_B});
+        sDefaults.putIntArray(KEY_CAPABILITIES_EXEMPT_FROM_SINGLE_DC_CHECK_INT_ARRAY,
+                new int[] {NetworkCapabilities.NET_CAPABILITY_IMS});
+        sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
+        sDefaults.putString(KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
+        sDefaults.putString(KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING, null);
+        sDefaults.putString(KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING, null);
+        sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_DIAL_STRING_REPLACE_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_INTERNATIONAL_ROAMING_DIAL_STRING_REPLACE_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_FORCE_HOME_NETWORK_BOOL, false);
+        sDefaults.putInt(KEY_GSM_DTMF_TONE_DELAY_INT, 0);
+        sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
+        sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
+        sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false);
+        sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, true);
+        sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
+        sDefaults.putBoolean(KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL, true);
+        sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL, true);
+        sDefaults.putBoolean(KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false);
+        sDefaults.putBoolean(KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL, false);
+        sDefaults.putInt(KEY_IMS_CONFERENCE_SIZE_LIMIT_INT, 5);
+        sDefaults.putBoolean(KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL, true);
+        sDefaults.putBoolean(KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true);
+        sDefaults.putBoolean(KEY_HIDE_ENHANCED_4G_LTE_BOOL, false);
+        sDefaults.putBoolean(KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL, true);
+        sDefaults.putBoolean(KEY_HIDE_IMS_APN_BOOL, false);
+        sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
+        sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL, false);
+        sDefaults.putStringArray(KEY_ENABLE_APPS_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_EDITABLE_WFC_MODE_BOOL, true);
+        sDefaults.putStringArray(KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY, null);
+        sDefaults.putInt(KEY_WFC_SPN_FORMAT_IDX_INT, 0);
+        sDefaults.putInt(KEY_WFC_DATA_SPN_FORMAT_IDX_INT, 0);
+        sDefaults.putInt(KEY_WFC_FLIGHT_MODE_SPN_FORMAT_IDX_INT, -1);
+        sDefaults.putBoolean(KEY_WFC_SPN_USE_ROOT_LOCALE, false);
+        sDefaults.putString(KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING, "");
+        sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
+        sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
+        sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
+        sDefaults.putBoolean(KEY_WFC_CARRIER_NAME_OVERRIDE_BY_PNN_BOOL, false);
+        sDefaults.putInt(KEY_CROSS_SIM_SPN_FORMAT_INT, 1);
+        sDefaults.putInt(KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT, -1);
+        sDefaults.putStringArray(KEY_SPDI_OVERRIDE_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_PNN_OVERRIDE_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_OPL_OVERRIDE_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_EHPLMN_OVERRIDE_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_ALLOW_ERI_BOOL, false);
+        sDefaults.putBoolean(KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL, false);
+        sDefaults.putString(KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_CALL_SCREENING_APP_STRING, "");
+        sDefaults.putString(KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING, null);
+        sDefaults.putBoolean(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL, false);
+        sDefaults.putString(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING, "");
+        sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
+        sDefaults.putInt(KEY_FDN_NUMBER_LENGTH_LIMIT_INT, 20);
+        sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, true);
+
+        // MMS defaults
+        sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL, true);
+        sDefaults.putBoolean(KEY_MMS_APPEND_TRANSACTION_ID_BOOL, false);
+        sDefaults.putBoolean(KEY_MMS_GROUP_MMS_ENABLED_BOOL, true);
+        sDefaults.putBoolean(KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_MMS_MMS_ENABLED_BOOL, true);
+        sDefaults.putBoolean(KEY_MMS_MMS_READ_REPORT_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_MMS_MULTIPART_SMS_ENABLED_BOOL, true);
+        sDefaults.putBoolean(KEY_MMS_NOTIFY_WAP_MMSC_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_MMS_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES_BOOL, false);
+        sDefaults.putBoolean(KEY_MMS_SHOW_CELL_BROADCAST_APP_LINKS_BOOL, true);
+        sDefaults.putBoolean(KEY_MMS_SMS_DELIVERY_REPORT_ENABLED_BOOL, true);
+        sDefaults.putBoolean(KEY_MMS_SUPPORT_HTTP_CHARSET_HEADER_BOOL, false);
+        sDefaults.putBoolean(KEY_MMS_SUPPORT_MMS_CONTENT_DISPOSITION_BOOL, true);
+        sDefaults.putBoolean(KEY_MMS_CLOSE_CONNECTION_BOOL, false);
+        sDefaults.putInt(KEY_MMS_ALIAS_MAX_CHARS_INT, 48);
+        sDefaults.putInt(KEY_MMS_ALIAS_MIN_CHARS_INT, 2);
+        sDefaults.putInt(KEY_MMS_HTTP_SOCKET_TIMEOUT_INT, 60 * 1000);
+        sDefaults.putInt(KEY_MMS_MAX_IMAGE_HEIGHT_INT, 480);
+        sDefaults.putInt(KEY_MMS_MAX_IMAGE_WIDTH_INT, 640);
+        sDefaults.putInt(KEY_MMS_MAX_MESSAGE_SIZE_INT, 300 * 1024);
+        sDefaults.putInt(KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT, -1);
+        sDefaults.putInt(KEY_MMS_RECIPIENT_LIMIT_INT, Integer.MAX_VALUE);
+        sDefaults.putInt(KEY_MMS_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD_INT, -1);
+        sDefaults.putInt(KEY_MMS_SMS_TO_MMS_TEXT_THRESHOLD_INT, -1);
+        sDefaults.putInt(KEY_MMS_SUBJECT_MAX_LENGTH_INT, 40);
+        sDefaults.putInt(KEY_MMS_NETWORK_RELEASE_TIMEOUT_MILLIS_INT, 5 * 1000);
+        sDefaults.putInt(KEY_MMS_MAX_NTN_PAYLOAD_SIZE_BYTES_INT, 3 * 1000);
+        sDefaults.putString(KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING, "");
+        sDefaults.putString(KEY_MMS_HTTP_PARAMS_STRING, "");
+        sDefaults.putString(KEY_MMS_NAI_SUFFIX_STRING, "");
+        sDefaults.putString(KEY_MMS_UA_PROF_TAG_NAME_STRING, "x-wap-profile");
+        sDefaults.putString(KEY_MMS_UA_PROF_URL_STRING, "");
+        sDefaults.putString(KEY_MMS_USER_AGENT_STRING, "");
+        sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true);
+        sDefaults.putInt(KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT, 0);
+        sDefaults.putBoolean(KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, true);
+        sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false);
+        sDefaults.putBoolean(KEY_USE_RCS_SIP_OPTIONS_BOOL, false);
+        sDefaults.putBoolean(KEY_FORCE_IMEI_BOOL, false);
+        sDefaults.putInt(
+                KEY_CDMA_ROAMING_MODE_INT, TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT);
+        sDefaults.putBoolean(KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL, true);
+        sDefaults.putString(KEY_RCS_CONFIG_SERVER_URL_STRING, "");
+
+        // Carrier Signalling Receivers
+        sDefaults.putString(KEY_CARRIER_SETUP_APP_STRING, "");
+        sDefaults.putStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                new String[]{
+                        "com.android.carrierdefaultapp/.CarrierDefaultBroadcastReceiver:"
+                                + "com.android.internal.telephony.CARRIER_SIGNAL_RESET"
+                });
+        sDefaults.putStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_CARRIER_APP_REQUIRED_DURING_SIM_SETUP_BOOL, false);
+
+        // Default carrier app configurations
+        sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY,
+                new String[]{
+                        "9, 4, 1"
+                        //9: CARRIER_ACTION_REGISTER_NETWORK_AVAIL
+                        //4: CARRIER_ACTION_DISABLE_METERED_APNS
+                        //1: CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION
+                });
+        sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET, new String[]{
+                "6, 8"
+                //6: CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS
+                //8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
+                });
+        sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE,
+                new String[] {
+                        false + ": 7", //7: CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER
+                        true + ": 8"  //8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
+                });
+        sDefaults.putStringArray(KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY, null);
+
+        sDefaults.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        sDefaults.putLong(KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        sDefaults.putBoolean(KEY_DATA_WARNING_NOTIFICATION_BOOL, true);
+        sDefaults.putBoolean(KEY_LIMITED_SIM_FUNCTION_NOTIFICATION_FOR_DSDS_BOOL, false);
+        sDefaults.putLong(KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        sDefaults.putBoolean(KEY_DATA_LIMIT_NOTIFICATION_BOOL, true);
+        sDefaults.putBoolean(KEY_DATA_RAPID_NOTIFICATION_BOOL, true);
+
+        // Rat families: {GPRS, EDGE}, {EVDO, EVDO_A, EVDO_B}, {UMTS, HSPA, HSDPA, HSUPA, HSPAP},
+        // {LTE, LTE_CA}
+        // Order is important - lowest precedence first
+        sDefaults.putStringArray(KEY_RATCHET_RAT_FAMILIES,
+                new String[]{"1,2", "7,8,12", "3,11,9,10,15", "14,19"});
+        sDefaults.putBoolean(KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL, false);
+        sDefaults.putBoolean(KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL, false);
+        sDefaults.putBoolean(KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL, true);
+        sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_ALLOW_HOLD_VIDEO_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_WIFI_CALLS_CAN_BE_HD_AUDIO, true);
+        sDefaults.putBoolean(KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO, true);
+        sDefaults.putBoolean(KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO, false);
+        sDefaults.putBoolean(KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL, true);
+
+        sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL, false);
+        sDefaults.putInt(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT, 0);
+        sDefaults.putBoolean(KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, false);
+        sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_BLOCKING_PAY_PHONE_OPTION_BOOL, false);
+        sDefaults.putBoolean(KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL, false);
+        sDefaults.putBoolean(KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL, false);
+        sDefaults.putBoolean(KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL, false);
+        sDefaults.putBoolean(KEY_HIDE_DIGITS_HELPER_TEXT_ON_STK_INPUT_SCREEN_BOOL, true);
+        sDefaults.putInt(KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, -1);
+        sDefaults.putInt(KEY_EMERGENCY_NOTIFICATION_DELAY_INT, -1);
+        sDefaults.putBoolean(KEY_ALLOW_USSD_REQUESTS_VIA_TELEPHONY_MANAGER_BOOL, true);
+        sDefaults.putBoolean(KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
+        sDefaults.putBoolean(KEY_DISPLAY_VOICEMAIL_NUMBER_AS_DEFAULT_CALL_FORWARDING_NUMBER_BOOL,
+                false);
+        sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false);
+        sDefaults.putBoolean(KEY_HIDE_PRESET_APN_DETAILS_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false);
+        sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_SUPPORT_IMS_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
+        sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
+        sDefaults.putStringArray(KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY, null);
+        sDefaults.putIntArray(KEY_NRARFCNS_RSRP_BOOST_INT_ARRAY, null);
+        sDefaults.putStringArray(KEY_BOOSTED_NRARFCNS_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
+        sDefaults.putBoolean(KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false);
+        sDefaults.putInt(IMSI_KEY_AVAILABILITY_INT, 0);
+        sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null);
+        sDefaults.putString(IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING, null);
+        sDefaults.putString(IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING, null);
+        sDefaults.putBoolean(KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL,
+                false);
+        sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_SHOW_ROAMING_INDICATOR_BOOL, true);
+        sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
+        sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
+        sDefaults.putBoolean(KEY_TTY_SUPPORTED_BOOL, true);
+        sDefaults.putBoolean(KEY_RTT_AUTO_UPGRADE_BOOL, false);
+        sDefaults.putBoolean(KEY_RTT_SUPPORTED_FOR_VT_BOOL, false);
+        sDefaults.putBoolean(KEY_RTT_UPGRADE_SUPPORTED_BOOL, false);
+        sDefaults.putBoolean(KEY_RTT_DOWNGRADE_SUPPORTED_BOOL, false);
+        sDefaults.putBoolean(KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL, false);
+        sDefaults.putBoolean(KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL, false);
+        sDefaults.putBoolean(KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_VT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_RTT_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true);
+        sDefaults.putInt(KEY_NO_REPLY_TIMER_FOR_CFNRY_SEC_INT, 20);
+        sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL, false);
+        sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
+        sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_4GLTE_FOR_LTE_DATA_ICON_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL, false);
+        sDefaults.putString(KEY_OPERATOR_NAME_FILTER_PATTERN_STRING, "");
+        sDefaults.putString(KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING, "");
+        sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true);
+        sDefaults.putBoolean(KEY_SHOW_5G_SLICE_ICON_BOOL, true);
+        sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
+        sDefaults.putInt(KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 0);
+        sDefaults.putBoolean(KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL, false);
+        sDefaults.putBoolean(KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL, false);
+        sDefaults.putIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
+                new int[]{CARRIER_NR_AVAILABILITY_NSA, CARRIER_NR_AVAILABILITY_SA});
+        sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);
+        sDefaults.putBoolean(KEY_SUPPORT_TDSCDMA_BOOL, false);
+        sDefaults.putStringArray(KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_WORLD_MODE_ENABLED_BOOL, false);
+        sDefaults.putString(KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING, "");
+        sDefaults.putBoolean(KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
+        sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL, false);
+        sDefaults.putStringArray(KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_EXCLUDED_MCCS_STRING_ARRAY,
+                new String[0]);
+        sDefaults.putStringArray(
+                KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_INCLUDED_MCC_MNCS_STRING_ARRAY,
+                new String[0]);
+        sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+                // Boundaries: [-140 dBm, -44 dBm]
+                new int[] {
+                        -128, /* SIGNAL_STRENGTH_POOR */
+                        -118, /* SIGNAL_STRENGTH_MODERATE */
+                        -108, /* SIGNAL_STRENGTH_GOOD */
+                        -98,  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sDefaults.putIntArray(KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY,
+                // Boundaries: [-34 dB, 3 dB]
+                new int[] {
+                        -20, /* SIGNAL_STRENGTH_POOR */
+                        -17, /* SIGNAL_STRENGTH_MODERATE */
+                        -14, /* SIGNAL_STRENGTH_GOOD */
+                        -11  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sDefaults.putIntArray(KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY,
+                // Boundaries: [-20 dBm, 30 dBm]
+                new int[] {
+                        -3, /* SIGNAL_STRENGTH_POOR */
+                        1,  /* SIGNAL_STRENGTH_MODERATE */
+                        5,  /* SIGNAL_STRENGTH_GOOD */
+                        13  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sDefaults.putIntArray(KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY,
+                // Boundaries: [-120 dBm, -25 dBm]
+                new int[] {
+                        -115, /* SIGNAL_STRENGTH_POOR */
+                        -105, /* SIGNAL_STRENGTH_MODERATE */
+                        -95,  /* SIGNAL_STRENGTH_GOOD */
+                        -85   /* SIGNAL_STRENGTH_GREAT */
+                });
+        // TODO(b/249896055): On enabling ECNO measurement part for Signal Bar level indication
+        // system functionality, below values to be rechecked.
+        sDefaults.putIntArray(KEY_WCDMA_ECNO_THRESHOLDS_INT_ARRAY,
+                // Boundaries: [-24 dBm, 1 dBm]
+                new int[] {
+                        -24, /* SIGNAL_STRENGTH_POOR */
+                        -14, /* SIGNAL_STRENGTH_MODERATE */
+                        -6,  /* SIGNAL_STRENGTH_GOOD */
+                        1    /* SIGNAL_STRENGTH_GREAT */
+                });
+        sDefaults.putIntArray(KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY,
+                // Boundaries: [-140 dB, -44 dB]
+                new int[] {
+                    -110, /* SIGNAL_STRENGTH_POOR */
+                    -90,  /* SIGNAL_STRENGTH_MODERATE */
+                    -80,  /* SIGNAL_STRENGTH_GOOD */
+                    -65,  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sDefaults.putIntArray(KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY,
+                // Boundaries: [-43 dB, 20 dB]
+                new int[] {
+                    -31, /* SIGNAL_STRENGTH_POOR */
+                    -19, /* SIGNAL_STRENGTH_MODERATE */
+                    -7,  /* SIGNAL_STRENGTH_GOOD */
+                    6    /* SIGNAL_STRENGTH_GREAT */
+                });
+        sDefaults.putIntArray(KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY,
+                // Boundaries: [-23 dB, 40 dB]
+                new int[] {
+                    -5, /* SIGNAL_STRENGTH_POOR */
+                    5,  /* SIGNAL_STRENGTH_MODERATE */
+                    15, /* SIGNAL_STRENGTH_GOOD */
+                    30  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sDefaults.putInt(KEY_GERAN_RSSI_HYSTERESIS_DB_INT, 2);
+        sDefaults.putInt(KEY_UTRAN_RSCP_HYSTERESIS_DB_INT, 2);
+        sDefaults.putInt(KEY_EUTRAN_RSRP_HYSTERESIS_DB_INT, 2);
+        sDefaults.putInt(KEY_EUTRAN_RSRQ_HYSTERESIS_DB_INT, 2);
+        sDefaults.putInt(KEY_EUTRAN_RSSNR_HYSTERESIS_DB_INT, 2);
+        sDefaults.putInt(KEY_NGRAN_SSRSRP_HYSTERESIS_DB_INT, 2);
+        sDefaults.putInt(KEY_NGRAN_SSRSRQ_HYSTERESIS_DB_INT, 2);
+        sDefaults.putInt(KEY_NGRAN_SSSINR_HYSTERESIS_DB_INT, 2);
+        sDefaults.putInt(KEY_UTRAN_ECNO_HYSTERESIS_DB_INT, 2);
+        sDefaults.putInt(KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT,
+                CellSignalStrengthNr.USE_SSRSRP);
+        sDefaults.putBoolean(KEY_SIGNAL_STRENGTH_NR_NSA_USE_LTE_AS_PRIMARY_BOOL, true);
+        sDefaults.putStringArray(KEY_BANDWIDTH_STRING_ARRAY, new String[]{
+                "GPRS:24,24", "EDGE:70,18", "UMTS:115,115", "CDMA:14,14",
+                "1xRTT:30,30", "EvDo_0:750,48", "EvDo_A:950,550", "HSDPA:4300,620",
+                "HSUPA:4300,1800", "HSPA:4300,1800", "EvDo_B:1500,550", "eHRPD:750,48",
+                "iDEN:14,14", "LTE:30000,15000", "HSPA+:13000,3400", "GSM:24,24",
+                "TD_SCDMA:115,115", "LTE_CA:30000,15000", "NR_NSA:47000,18000",
+                "NR_NSA_MMWAVE:145000,60000", "NR_SA:145000,60000", "NR_SA_MMWAVE:145000,60000"});
+        sDefaults.putBoolean(KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPLINK_BOOL, false);
+        sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "rssi");
+        sDefaults.putLong(
+                KEY_UNDELIVERED_SMS_MESSAGE_EXPIRATION_TIME, (long) (60 * 60 * 1000) * 24 * 7);
+        sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false);
+        sDefaults.putBoolean(KEY_CALL_FORWARDING_OVER_UT_WARNING_BOOL, false);
+        sDefaults.putBoolean(KEY_CALL_BARRING_OVER_UT_WARNING_BOOL, false);
+        sDefaults.putBoolean(KEY_CALLER_ID_OVER_UT_WARNING_BOOL, false);
+        sDefaults.putBoolean(KEY_CALL_WAITING_OVER_UT_WARNING_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL, true);
+        sDefaults.putBoolean(KEY_SUPPORT_EMERGENCY_DIALER_SHORTCUT_BOOL, true);
+        sDefaults.putBoolean(KEY_USE_CALL_FORWARDING_USSD_BOOL, false);
+        sDefaults.putBoolean(KEY_USE_CALLER_ID_USSD_BOOL, false);
+        sDefaults.putBoolean(KEY_USE_CALL_WAITING_USSD_BOOL, false);
+        sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
+        sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
+                "connected_mmwave:5G,connected:5G,connected_rrc_idle:5G,not_restricted_rrc_idle:5G,"
+                        + "not_restricted_rrc_con:5G");
+        sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, "");
+        sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, "");
+        sDefaults.putInt(KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT, 0);
+        sDefaults.putBoolean(KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL, false);
+        sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL, true);
+        sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_PLMN_CHANGE_BOOL, false);
+        /* Default value is 1 hour. */
+        sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000);
+        sDefaults.putIntArray(KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY, new int[0]);
+        sDefaults.putInt(KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0);
+        sDefaults.putBoolean(KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL, true);
+        sDefaults.putBoolean(KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL, false);
+        sDefaults.putStringArray(KEY_UNMETERED_NETWORK_TYPES_STRING_ARRAY, new String[] {
+                "NR_NSA", "NR_NSA_MMWAVE", "NR_SA", "NR_SA_MMWAVE"});
+        sDefaults.putStringArray(KEY_ROAMING_UNMETERED_NETWORK_TYPES_STRING_ARRAY, new String[0]);
+        sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_WIFI_CALLING_ICON_IN_STATUS_BAR_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL, false);
+        sDefaults.putString(KEY_SMDP_SERVER_ADDRESS_STRING, "");
+        sDefaults.putInt(KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT, 5);
+        sDefaults.putInt(KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT, 60);
+        sDefaults.putIntArray(KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY, new int[] {0});
+        sDefaults.putBoolean(KEY_OPPORTUNISTIC_ESIM_DOWNLOAD_VIA_WIFI_ONLY_BOOL, false);
+        /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
+        /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT, -118);
+        /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_GOOD */
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 5);
+        /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_MODERATE */
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, 1);
+        /* Default value is 1024 kbps */
+        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_BANDWIDTH_INT, 1024);
+        /* Default value is 10 seconds */
+        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_ENTRY_OR_EXIT_HYSTERESIS_TIME_LONG, 10000);
+        /* Default value is 10 seconds. */
+        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG, 10000);
+        /* Default value is 3 seconds. */
+        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 3000);
+        sDefaults.putAll(OpportunisticNetwork.getDefaults());
+        sDefaults.putBoolean(KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL, true);
+        sDefaults.putBoolean(KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL, true);
+        /* Default value is 60 seconds. */
+        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG, 60000);
+        /* Default value is 10 seconds. */
+        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG, 10000);
+        /* Default value is 60 seconds. */
+        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG, 60000);
+        sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true);
+        sDefaults.putLong(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000L);
+        sDefaults.putLong(KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
+                120000L);
+        sDefaults.putAll(ImsServiceEntitlement.getDefaults());
+        sDefaults.putAll(Gps.getDefaults());
+        sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
+                new int[] {
+                        1 /* Roaming Indicator Off */
+                });
+        sDefaults.putStringArray(KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY, new String[0]);
+        sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL, false);
+        sDefaults.putBoolean(KEY_USE_USIM_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, false);
+        sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, true);
+        sDefaults.putString(KEY_SMART_FORWARDING_CONFIG_COMPONENT_NAME_STRING, "");
+        sDefaults.putBoolean(KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN,
+                false);
+        sDefaults.putString(KEY_SUBSCRIPTION_GROUP_UUID_STRING, "");
+        sDefaults.putBoolean(KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL, false);
+        sDefaults.putIntArray(KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY,
+                new int[] {
+                        -107, /* SIGNAL_STRENGTH_POOR */
+                        -103, /* SIGNAL_STRENGTH_MODERATE */
+                        -97,  /* SIGNAL_STRENGTH_GOOD */
+                        -89,  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
+        sDefaults.putAll(Ims.getDefaults());
+        sDefaults.putAll(ImsVoice.getDefaults());
+        sDefaults.putAll(ImsSms.getDefaults());
+        sDefaults.putAll(ImsRtt.getDefaults());
+        sDefaults.putAll(ImsEmergency.getDefaults());
+        sDefaults.putAll(ImsVt.getDefaults());
+        sDefaults.putAll(ImsWfc.getDefaults());
+        sDefaults.putAll(ImsSs.getDefaults());
+        sDefaults.putAll(Bsf.getDefaults());
+        sDefaults.putAll(Iwlan.getDefaults());
+        sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[0]);
+        sDefaults.putBoolean(KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL, false);
+        if (Flags.doNotOverridePreciseLabel()) {
+            sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY, new int[]{});
+        } else {
+            sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
+                    new int[]{4 /* BUSY */});
+        }
+        sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
+        sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 5000);
+        sDefaults.putStringArray(KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY, new String[0]);
+        sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
+                CellSignalStrengthLte.USE_RSRP);
+        sDefaults.putInt(KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT, 300);
+        sDefaults.putInt(KEY_PREFERRED_IKE_PROTOCOL_INT, -1);
+        // Default wifi configurations.
+        sDefaults.putAll(Wifi.getDefaults());
+        sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
+        sDefaults.putInt(KEY_GBA_MODE_INT, GBA_ME);
+        sDefaults.putInt(KEY_GBA_UA_SECURITY_ORGANIZATION_INT,
+                UaSecurityProtocolIdentifier.ORG_3GPP);
+        sDefaults.putInt(KEY_GBA_UA_SECURITY_PROTOCOL_INT,
+                UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT);
+        sDefaults.putInt(KEY_GBA_UA_TLS_CIPHER_SUITE_INT, TlsParams.TLS_NULL_WITH_NULL_NULL);
+
+        sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false);
+        sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_INTERVAL_MILLIS_LONG,
+                TimeUnit.DAYS.toMillis(1));
+        sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY,
+                new String[0]);
+
+        // Do not modify the priority unless you know what you are doing. This will have significant
+        // impacts on the order of data network setup.
+        sDefaults.putStringArray(
+                KEY_TELEPHONY_NETWORK_CAPABILITY_PRIORITIES_STRING_ARRAY, new String[] {
+                        "eims:90", "supl:80", "mms:70", "xcap:70", "cbs:50", "mcx:50", "fota:50",
+                        "ims:40", "rcs:40", "dun:30", "enterprise:20", "internet:20",
+                        "prioritize_bandwidth:20", "prioritize_latency:20"
+                });
+        sDefaults.putStringArray(
+                KEY_TELEPHONY_DATA_SETUP_RETRY_RULES_STRING_ARRAY, new String[] {
+                        "capabilities=eims, retry_interval=1000, maximum_retries=20",
+                        // Permanent fail causes. When setup data call fails with the following
+                        // fail causes, telephony data frameworks will stop timer-based retry on
+                        // the failed APN until power cycle, APM, or some special events. Note that
+                        // even timer-based retry is not performed, condition-based (RAT changes,
+                        // registration state changes) retry can still happen.
+                        "permanent_fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|"
+                                + "-3|65543|65547|2252|2253|2254, retry_interval=2500",
+                        "capabilities=mms|supl|cbs|rcs, retry_interval=2000",
+                        "capabilities=internet|enterprise|dun|ims|fota|xcap|mcx|"
+                                + "prioritize_bandwidth|prioritize_latency, retry_interval="
+                                + "2500|3000|5000|10000|15000|20000|40000|60000|120000|240000|"
+                                + "600000|1200000|1800000, maximum_retries=20"
+                });
+        sDefaults.putStringArray(
+                KEY_TELEPHONY_DATA_HANDOVER_RETRY_RULES_STRING_ARRAY, new String[] {
+                        "retry_interval=1000|2000|4000|8000|16000, maximum_retries=5"
+                });
+        sDefaults.putBoolean(KEY_DELAY_IMS_TEAR_DOWN_UNTIL_CALL_END_BOOL, false);
+        sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
+        sDefaults.putPersistableBundle(
+                KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
+                PersistableBundle.EMPTY);
+        sDefaults.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false);
+        sDefaults.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 300);
+        sDefaults.putIntArray(KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+                // Boundaries: [-140 dBm, -44 dBm]
+                new int[]{
+                        -128, /* SIGNAL_STRENGTH_POOR */
+                        -118, /* SIGNAL_STRENGTH_MODERATE */
+                        -108, /* SIGNAL_STRENGTH_GOOD */
+                        -98  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sDefaults.putIntArray(KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY,
+                // Boundaries: [-34 dB, 3 dB]
+                new int[]{
+                        -20, /* SIGNAL_STRENGTH_POOR */
+                        -17, /* SIGNAL_STRENGTH_MODERATE */
+                        -14, /* SIGNAL_STRENGTH_GOOD */
+                        -11  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sDefaults.putIntArray(KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY,
+                // Boundaries: [-20 dBm, 30 dBm]
+                new int[] {
+                        -3, /* SIGNAL_STRENGTH_POOR */
+                        1, /* SIGNAL_STRENGTH_MODERATE */
+                        5,  /* SIGNAL_STRENGTH_GOOD */
+                        13   /* SIGNAL_STRENGTH_GREAT */
+                });
+        sDefaults.putInt(KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT,
+                CellSignalStrengthLte.USE_RSRP);
+        sDefaults.putBoolean(KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true);
+        sDefaults.putBoolean(KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL, true);
+        sDefaults.putInt(KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT, 7);
+        sDefaults.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
+        sDefaults.putString(KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING, "androidSatmode");
+        sDefaults.putString(KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING, "");
+        sDefaults.putIntArray(KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY,
+                new int[] {
+                        NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+                        NetworkRegistrationInfo.SERVICE_TYPE_MMS
+                });
+        sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
+        sDefaults.putBoolean(KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL, false);
+        sDefaults.putInt(KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT,
+                (int) TimeUnit.SECONDS.toMillis(30));
+        sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
+        sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL, false);
+        sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, "");
+        sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false);
+        sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true);
+        sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0);
+        sDefaults.putBoolean(KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, true);
+        sDefaults.putBoolean(KEY_HIDE_ENABLE_2G, false);
+        sDefaults.putStringArray(KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY,
+                new String[]{"ia", "default"});
+        sDefaults.putBoolean(KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL, false);
+        sDefaults.putBoolean(KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL, false);
+        sDefaults.putBoolean(KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL, true);
+        sDefaults.putString(KEY_CARRIER_PROVISIONING_APP_STRING, "");
+        sDefaults.putBoolean(KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL, false);
+        sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false);
+        sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, true);
+        sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_VONR_ON_BY_DEFAULT_BOOL, true);
+        sDefaults.putIntArray(KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY, new int[0]);
+        sDefaults.putLong(KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG,
+                TimeUnit.MINUTES.toMillis(30));
+        sDefaults.putLong(KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG,
+                TimeUnit.MINUTES.toMillis(30));
+        sDefaults.putInt(KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT, 2);
+        sDefaults.putInt(KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT, 10);
+        sDefaults.putLong(
+                KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG,
+                TimeUnit.MINUTES.toMillis(30));
+        sDefaults.putLong(KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG,
+                TimeUnit.MINUTES.toMillis(5));
+        sDefaults.putString(KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING, null);
+        sDefaults.putBoolean(KEY_PREMIUM_CAPABILITY_SUPPORTED_ON_LTE_BOOL, false);
+        sDefaults.putStringArray(KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY, new String[]{
+                "source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, "
+                        + "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"});
+        PersistableBundle auto_data_switch_rat_signal_score_string_bundle = new PersistableBundle();
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "NR_SA_MMWAVE", new int[]{10000, 13227, 16000, 18488, 20017});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "NR_NSA_MMWAVE", new int[]{8000, 10227, 12488, 15017, 15278});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "LTE", new int[]{3731, 5965, 8618, 11179, 13384});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "LTE_CA", new int[]{3831, 6065, 8718, 11379, 13484});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "NR_SA", new int[]{5288, 6795, 6955, 7562, 9713});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "NR_NSA", new int[]{5463, 6827, 8029, 9007, 9428});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "UMTS", new int[]{100, 169, 183, 192, 300});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "eHRPD", new int[]{10, 400, 600, 800, 1000});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "TD_SCDMA", new int[]{1, 50, 100, 500, 1000});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "iDEN", new int[]{1, 2, 10, 50, 100});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "EvDo_B", new int[]{1000, 1495, 2186, 2532, 2600});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "HSPA+", new int[]{1619, 2500, 3393, 4129, 4212});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "HSPA", new int[]{1000, 1495, 2186, 2532, 2544});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "HSUPA", new int[]{1500, 1919, 2132, 2362, 2704});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "HSDPA", new int[]{1500, 1732, 4000, 7000, 8000});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "EvDo_A", new int[]{600, 840, 1200, 1300, 1400});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "EvDo_0", new int[]{300, 600, 1000, 1500, 2000});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "1xRTT", new int[]{50, 60, 70, 80, 90});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "EDGE", new int[]{154, 169, 183, 192, 267});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "GPRS", new int[]{15, 30, 40, 45, 50});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "CDMA", new int[]{1, 50, 100, 300, 2000});
+        auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+                "GSM", new int[]{1, 2, 10, 50, 100});
+        sDefaults.putPersistableBundle(KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_BUNDLE,
+                auto_data_switch_rat_signal_score_string_bundle);
+        sDefaults.putInt(KEY_CELLULAR_USAGE_SETTING_INT,
+                SubscriptionManager.USAGE_SETTING_UNKNOWN);
+        // Default data stall recovery configurations.
+        sDefaults.putLongArray(KEY_DATA_STALL_RECOVERY_TIMERS_LONG_ARRAY,
+                new long[] {180000, 180000, 180000, 180000});
+        sDefaults.putBooleanArray(KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY,
+                new boolean[] {false, false, true, false, false});
+        sDefaults.putStringArray(KEY_CARRIER_SERVICE_NAME_STRING_ARRAY, new String[0]);
+        sDefaults.putStringArray(KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY, new String[0]);
+        sDefaults.putIntArray(KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY, new int[]{1, 2, 3});
+        sDefaults.putInt(KEY_WEAR_CONNECTIVITY_BT_TO_CELL_DELAY_MS_INT, -1);
+        sDefaults.putInt(KEY_WEAR_CONNECTIVITY_EXTEND_BT_TO_CELL_DELAY_ON_WIFI_MS_INT, -1);
+    }
+
+    /**
+     * Wi-Fi configs used in WiFi Module.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Wifi {
+        /** Prefix of all Wifi.KEY_* constants. */
+        public static final String KEY_PREFIX = "wifi.";
+        /**
+        * It contains the maximum client count definition that the carrier sets.
+        * The default is 0, which means that the carrier hasn't set a requirement.
+        */
+        public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT =
+                KEY_PREFIX + "hotspot_maximum_client_count";
+
+        /**
+         * This configuration is intended to be a narrow exception for provisioning
+         * {@link android.net.wifi.WifiNetworkSuggestion} of widely-known carrier networks that do
+         * not support using randomized MAC address.
+         * Carrier provisioned {@link android.net.wifi.WifiNetworkSuggestion} with SSIDs included
+         * in this list will have MAC randomization disabled.
+         *
+         * Note: the SSIDs in the list are expected to be interpreted as is - do not add double
+         * quotes to the SSIDs.
+         */
+        public static final String KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED =
+                KEY_PREFIX + "suggestion_ssid_list_with_mac_randomization_disabled";
+
+        /**
+         * Avoid SoftAp in 5GHz if cellular is on unlicensed 5Ghz using License Assisted Access
+         * (LAA).
+         */
+        public static final String KEY_AVOID_5GHZ_SOFTAP_FOR_LAA_BOOL =
+                KEY_PREFIX + "avoid_5ghz_softap_for_laa_bool";
+
+        /**
+         * Avoid Wifi Direct in 5GHz if cellular is on unlicensed 5Ghz using License Assisted
+         * Access (LAA).
+         */
+        public static final String KEY_AVOID_5GHZ_WIFI_DIRECT_FOR_LAA_BOOL =
+                KEY_PREFIX + "avoid_5ghz_wifi_direct_for_laa_bool";
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            defaults.putInt(KEY_HOTSPOT_MAX_CLIENT_COUNT, 0);
+            defaults.putStringArray(KEY_SUGGESTION_SSID_LIST_WITH_MAC_RANDOMIZATION_DISABLED,
+                    new String[0]);
+            defaults.putBoolean(KEY_AVOID_5GHZ_SOFTAP_FOR_LAA_BOOL, false);
+            defaults.putBoolean(KEY_AVOID_5GHZ_WIFI_DIRECT_FOR_LAA_BOOL, false);
+
+            return defaults;
+        }
+
+        private Wifi() {}
+    }
+
+    /**
+     * Gets the configuration values for a particular subscription, which is associated with a
+     * specific SIM card. If an invalid subId is used, the returned config will contain default
+     * values. After using this method to get the configuration bundle,
+     * {@link #isConfigForIdentifiedCarrier(PersistableBundle)} should be called to confirm whether
+     * any carrier specific configuration has been applied.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app
+     * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}).
+     *
+     * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
+     * @return A {@link PersistableBundle} containing the config for the given subId, or default
+     *         values for an invalid subId.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
+     * @deprecated Use {@link #getConfigForSubId(int, String...)} instead.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @Nullable
+    @Deprecated
+    public PersistableBundle getConfigForSubId(int subId) {
+        try {
+            ICarrierConfigLoader loader = getICarrierConfigLoader();
+            if (loader == null) {
+                Rlog.w(TAG, "Error getting config for subId " + subId
+                        + " ICarrierConfigLoader is null");
+                return null;
+            }
+            return loader.getConfigForSubIdWithFeature(subId, mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Error getting config for subId " + subId + ": " + ex);
+        }
+        return null;
+    }
+
+    /**
+     * Gets the configuration values of the specified keys for a particular subscription.
+     *
+     * <p>If an invalid subId is used, the returned configuration will contain default values for
+     * the specified keys. If the value for the key can't be found, the returned configuration will
+     * filter the key out.
+     *
+     * <p>After using this method to get the configuration bundle,
+     * {@link #isConfigForIdentifiedCarrier(PersistableBundle)} should be called to confirm whether
+     * any carrier specific configuration has been applied.
+     *
+     * <p>Note that on success, the key/value for {@link #KEY_CARRIER_CONFIG_VERSION_STRING} and
+     * {@link #KEY_CARRIER_CONFIG_APPLIED_BOOL} are always in the returned bundle, no matter if they
+     * were explicitly requested.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app
+     * has carrier privileges on the specified subscription (see
+     * {@link TelephonyManager#hasCarrierPrivileges()}).
+     *
+     * @param subId The subscription ID on which the carrier config should be retrieved.
+     * @param keys The carrier config keys to retrieve values.
+     * @return A {@link PersistableBundle} with key/value mapping for the specified configuration
+     * on success, or an empty (but never null) bundle on failure (for example, when the calling app
+     * has no permission).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            "carrier privileges",
+    })
+    @NonNull
+    public PersistableBundle getConfigForSubId(int subId, @NonNull String... keys) {
+        Objects.requireNonNull(keys, "Config keys should be non-null");
+        for (String key : keys) {
+            Objects.requireNonNull(key, "Config key should be non-null");
+        }
+
+        try {
+            ICarrierConfigLoader loader = getICarrierConfigLoader();
+            if (loader == null) {
+                Rlog.w(TAG, "Error getting config for subId " + subId
+                        + " ICarrierConfigLoader is null");
+                throw new IllegalStateException("Carrier config loader is not available.");
+            }
+            return loader.getConfigSubsetForSubIdWithFeature(subId, mContext.getOpPackageName(),
+                    mContext.getAttributionTag(), keys);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Error getting config for subId " + subId + ": " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return new PersistableBundle();
+    }
+
+    /**
+     * Overrides the carrier config of the provided subscription ID with the provided values.
+     *
+     * Any further queries to carrier config from any process will return the overridden values
+     * after this method returns. The overrides are effective for the lifetime of the phone process
+     * until the user passes in {@code null} for {@code overrideValues}. This removes all previous
+     * overrides and sets the carrier config back to production values.
+     *
+     * May throw an {@link IllegalArgumentException} if {@code overrideValues} contains invalid
+     * values for the specified config keys.
+     *
+     * NOTE: This API is meant for testing purposes only.
+     *
+     * @param subscriptionId The subscription ID for which the override should be done.
+     * @param overrideValues Key-value pairs of the values that are to be overridden. If set to
+     *                       {@code null}, this will remove all previous overrides and set the
+     *                       carrier configuration back to production values.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    public void overrideConfig(int subscriptionId, @Nullable PersistableBundle overrideValues) {
+        overrideConfig(subscriptionId, overrideValues, false);
+    }
+
+    /**
+     * Overrides the carrier config of the provided subscription ID with the provided values.
+     *
+     * Any further queries to carrier config from any process will return the overridden values
+     * after this method returns. The overrides are effective until the user passes in {@code null}
+     * for {@code overrideValues}. This removes all previous overrides and sets the carrier config
+     * back to production values.
+     *
+     * The overrides is stored persistently and will survive a reboot if {@code persistent} is true.
+     *
+     * May throw an {@link IllegalArgumentException} if {@code overrideValues} contains invalid
+     * values for the specified config keys.
+     *
+     * NOTE: This API is meant for testing purposes only.
+     *
+     * @param subscriptionId The subscription ID for which the override should be done.
+     * @param overrideValues Key-value pairs of the values that are to be overridden. If set to
+     *                       {@code null}, this will remove all previous overrides and set the
+     *                       carrier configuration back to production values.
+     * @param persistent     Determines whether the override should be persistent.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void overrideConfig(int subscriptionId, @Nullable PersistableBundle overrideValues,
+            boolean persistent) {
+        try {
+            ICarrierConfigLoader loader = getICarrierConfigLoader();
+            if (loader == null) {
+                Rlog.w(TAG, "Error setting config for subId " + subscriptionId
+                        + " ICarrierConfigLoader is null");
+                return;
+            }
+            loader.overrideConfig(subscriptionId, overrideValues, persistent);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Error setting config for subId " + subscriptionId + ": " + ex);
+        }
+    }
+
+    /**
+     * Gets the configuration values for the default subscription. After using this method to get
+     * the configuration bundle, {@link #isConfigForIdentifiedCarrier(PersistableBundle)} should be
+     * called to confirm whether any carrier specific configuration has been applied.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app
+     * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}).
+     *
+     * @see #getConfigForSubId
+     * @see #getConfig(String...)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
+     * @deprecated use {@link #getConfig(String...)} instead.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @Nullable
+    @Deprecated
+    public PersistableBundle getConfig() {
+        return getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
+    }
+
+    /**
+     * Gets the configuration values of the specified config keys applied for the default
+     * subscription.
+     *
+     * <p>If the value for the key can't be found, the returned bundle will filter the key out.
+     *
+     * <p>After using this method to get the configuration bundle, {@link
+     * #isConfigForIdentifiedCarrier(PersistableBundle)} should be called to confirm whether any
+     * carrier specific configuration has been applied.
+     *
+     * <p>Note that on success, the key/value for {@link #KEY_CARRIER_CONFIG_VERSION_STRING} and
+     * {@link #KEY_CARRIER_CONFIG_APPLIED_BOOL} are always in the returned bundle, no matter if
+     * they were explicitly requested.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app
+     * has carrier privileges for the default subscription (see
+     * {@link TelephonyManager#hasCarrierPrivileges()}).
+     *
+     * @param keys The config keys to retrieve values
+     * @return A {@link PersistableBundle} with key/value mapping for the specified carrier
+     * configs on success, or an empty (but never null) bundle on failure.
+     * @see #getConfigForSubId(int, String...)
+     * @see SubscriptionManager#getDefaultSubscriptionId()
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            "carrier privileges",
+    })
+    @NonNull
+    public PersistableBundle getConfig(@NonNull String... keys) {
+        return getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId(), keys);
+    }
+
+    /**
+     * Determines whether a configuration {@link PersistableBundle} obtained from
+     * {@link #getConfig()} or {@link #getConfigForSubId(int)} corresponds to an identified carrier.
+     *
+     * <p>When an app receives the {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED}
+     * broadcast which informs it that the carrier configuration has changed, it is possible
+     * that another reload of the carrier configuration has begun since the intent was sent.
+     * In this case, the carrier configuration the app fetches (e.g. via {@link #getConfig()})
+     * may not represent the configuration for the current carrier. It should be noted that it
+     * does not necessarily mean the configuration belongs to current carrier when this function
+     * return true because it may belong to another previous identified carrier. Users should
+     * always call {@link #getConfig()} or {@link #getConfigForSubId(int)} after receiving the
+     * broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED}.
+     *
+     * <p>After using {@link #getConfig()} or {@link #getConfigForSubId(int)} an app should always
+     * use this method to confirm whether any carrier specific configuration has been applied.
+     * Especially when an app misses the broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED} but it
+     * still needs to get the current configuration, it must use this method to verify whether the
+     * configuration is default or carrier overridden.
+     *
+     * @param bundle the configuration bundle to be checked.
+     * @return boolean true if any carrier specific configuration bundle has been applied, false
+     * otherwise or the bundle is null.
+     */
+    public static boolean isConfigForIdentifiedCarrier(PersistableBundle bundle) {
+        return bundle != null && bundle.getBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL);
+    }
+
+    /**
+     * Calling this method triggers telephony services to fetch the current carrier configuration.
+     *
+     * <p>Normally this does not need to be called because the platform reloads config on its own.
+     * This should be called by a carrier service app if it wants to update config at an arbitrary
+     * moment.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or the calling app
+     * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}).
+     *
+     * <p>This method returns before the reload has completed, and {@link
+     * android.service.carrier.CarrierService#onLoadConfig} will be called from an arbitrary thread.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void notifyConfigChangedForSubId(int subId) {
+        try {
+            ICarrierConfigLoader loader = getICarrierConfigLoader();
+            if (loader == null) {
+                Rlog.w(TAG, "Error reloading config for subId=" + subId
+                        + " ICarrierConfigLoader is null");
+                return;
+            }
+            loader.notifyConfigChangedForSubId(subId);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Error reloading config for subId=" + subId + ": " + ex);
+        }
+    }
+
+    /**
+     * Request the carrier config loader to update the config for phoneId.
+     *
+     * <p>Depending on simState, the config may be cleared or loaded from config app. This is only
+     * used by SubscriptionInfoUpdater.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void updateConfigForPhoneId(int phoneId, String simState) {
+        try {
+            ICarrierConfigLoader loader = getICarrierConfigLoader();
+            if (loader == null) {
+                Rlog.w(TAG, "Error updating config for phoneId=" + phoneId
+                        + " ICarrierConfigLoader is null");
+                return;
+            }
+            loader.updateConfigForPhoneId(phoneId, simState);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Error updating config for phoneId=" + phoneId + ": " + ex);
+        }
+    }
+
+    /**
+     * Gets the package name for a default carrier service.
+     * @return the package name for a default carrier service; empty string if not available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public String getDefaultCarrierServicePackageName() {
+        try {
+            ICarrierConfigLoader loader = getICarrierConfigLoader();
+            if (loader == null) {
+                Rlog.w(TAG, "getDefaultCarrierServicePackageName ICarrierConfigLoader is null");
+                return "";
+            }
+            return loader.getDefaultCarrierServicePackageName();
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getDefaultCarrierServicePackageName ICarrierConfigLoader is null" + ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return "";
+    }
+
+    /**
+     * Returns a new bundle with the default value for every supported configuration variable.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @SuppressLint("RequiresPermission")
+    public static PersistableBundle getDefaultConfig() {
+        return new PersistableBundle(sDefaults);
+    }
+
+    /** @hide */
+    @Nullable
+    private ICarrierConfigLoader getICarrierConfigLoader() {
+        return ICarrierConfigLoader.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getCarrierConfigServiceRegisterer()
+                        .get());
+    }
+
+    /**
+     * Gets the configuration values for a component using its prefix.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app
+     * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}).
+     *
+     * @param prefix prefix of the component.
+     * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
+     *
+     * @see #getConfigForSubId
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @Nullable
+    public PersistableBundle getConfigByComponentForSubId(@NonNull String prefix, int subId) {
+        PersistableBundle configs = getConfigForSubId(subId);
+
+        if (configs == null) {
+            return null;
+        }
+
+        PersistableBundle ret = new PersistableBundle();
+        for (String configKey : configs.keySet()) {
+            if (configKey.startsWith(prefix)) {
+                addConfig(configKey, configs.get(configKey), ret);
+            }
+        }
+
+        return ret;
+    }
+
+    private void addConfig(String key, Object value, PersistableBundle configs) {
+        if (value instanceof String) {
+            configs.putString(key, (String) value);
+        } else if (value instanceof String[]) {
+            configs.putStringArray(key, (String[]) value);
+        } else if (value instanceof Integer) {
+            configs.putInt(key, (Integer) value);
+        } else if (value instanceof Long) {
+            configs.putLong(key, (Long) value);
+        } else if (value instanceof Double) {
+            configs.putDouble(key, (Double) value);
+        } else if (value instanceof Boolean) {
+            configs.putBoolean(key, (Boolean) value);
+        } else if (value instanceof int[]) {
+            configs.putIntArray(key, (int[]) value);
+        } else if (value instanceof double[]) {
+            configs.putDoubleArray(key, (double[]) value);
+        } else if (value instanceof boolean[]) {
+            configs.putBooleanArray(key, (boolean[]) value);
+        } else if (value instanceof long[]) {
+            configs.putLongArray(key, (long[]) value);
+        } else if (value instanceof PersistableBundle) {
+            configs.putPersistableBundle(key, (PersistableBundle) value);
+        }
+    }
+
+    /**
+     * Listener interface to get a notification when the carrier configurations have changed.
+     *
+     * Use this listener to receive timely updates when the carrier configuration changes. System
+     * components should prefer this listener over {@link #ACTION_CARRIER_CONFIG_CHANGED}
+     * whenever possible.
+     *
+     * To register the listener, call
+     * {@link #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener)}.
+     * To unregister, call
+     * {@link #unregisterCarrierConfigChangeListener(CarrierConfigChangeListener)}.
+     *
+     * Note that on registration, registrants will NOT receive a notification on last carrier config
+     * change. Only carrier configs change AFTER the registration will be sent to registrants. And
+     * unlike {@link #ACTION_CARRIER_CONFIG_CHANGED}, notification wouldn't send when the device is
+     * unlocked. Registrants only receive the notification when there has been real carrier config
+     * changes.
+     *
+     * @see #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener)
+     * @see #unregisterCarrierConfigChangeListener(CarrierConfigChangeListener)
+     * @see #ACTION_CARRIER_CONFIG_CHANGED
+     * @see #getConfig(String...)
+     * @see #getConfigForSubId(int, String...)
+     */
+    public interface CarrierConfigChangeListener {
+        /**
+         * Called when carrier configurations have changed.
+         *
+         * @param logicalSlotIndex  The logical SIM slot index on which to monitor and get
+         *                          notification. It is guaranteed to be valid.
+         * @param subscriptionId    The subscription on the SIM slot. May be
+         *                          {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+         * @param carrierId         The optional carrier Id, may be
+         *                          {@link TelephonyManager#UNKNOWN_CARRIER_ID}.
+         *                          See {@link TelephonyManager#getSimCarrierId()}.
+         * @param specificCarrierId The optional fine-grained carrierId, may be {@link
+         *                          TelephonyManager#UNKNOWN_CARRIER_ID}. A specific carrierId may
+         *                          be different from the carrierId above in a MVNO scenario. See
+         *                          detail in {@link TelephonyManager#getSimSpecificCarrierId()}.
+         */
+        void onCarrierConfigChanged(int logicalSlotIndex, int subscriptionId, int carrierId,
+                int specificCarrierId);
+    }
+
+    /**
+     * Register a {@link CarrierConfigChangeListener} to get a notification when carrier
+     * configurations have changed.
+     *
+     * @param executor The executor on which the listener will be called.
+     * @param listener The CarrierConfigChangeListener called when carrier configs has changed.
+     */
+    public void registerCarrierConfigChangeListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull CarrierConfigChangeListener listener) {
+        Objects.requireNonNull(executor, "Executor should be non-null.");
+        Objects.requireNonNull(listener, "Listener should be non-null.");
+
+        TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class);
+        if (trm == null) {
+            throw new IllegalStateException("Telephony registry service is null");
+        }
+        trm.addCarrierConfigChangedListener(executor, listener);
+    }
+
+    /**
+     * Unregister the {@link CarrierConfigChangeListener} to stop notification on carrier
+     * configurations change.
+     *
+     * @param listener The CarrierConfigChangeListener which was registered with method
+     * {@link #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener)}.
+     */
+    public void unregisterCarrierConfigChangeListener(
+            @NonNull CarrierConfigChangeListener listener) {
+        Objects.requireNonNull(listener, "Listener should be non-null.");
+
+        TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class);
+        if (trm == null) {
+            throw new IllegalStateException("Telephony registry service is null");
+        }
+        trm.removeCarrierConfigChangedListener(listener);
+    }
+
+    /**
+     * Get subset of specified carrier configuration if available or empty bundle, without throwing
+     * {@link RuntimeException} to caller.
+     *
+     * <p>This is a system internally used only utility to reduce the repetitive logic.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app
+     * has carrier privileges on the specified subscription (see
+     * {@link TelephonyManager#hasCarrierPrivileges()}).
+     *
+     * @param context Context used to get the CarrierConfigManager service.
+     * @param subId The subscription ID to get the config from.
+     * @param keys The config keys the client is interested in.
+     * @return Config bundle with key/value for the specified keys or empty bundle when failed
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            "carrier privileges",
+    })
+    @NonNull
+    public static PersistableBundle getCarrierConfigSubset(
+            @NonNull Context context, int subId, @NonNull String... keys) {
+        PersistableBundle configs = null;
+        CarrierConfigManager ccm = context.getSystemService(CarrierConfigManager.class);
+        try {
+            configs = ccm.getConfigForSubId(subId, keys);
+        } catch (RuntimeException exception) {
+            Rlog.w(TAG, "CarrierConfigLoader is not available.");
+        }
+        return configs != null ? configs : new PersistableBundle();
+    }
+}
diff --git a/android-35/android/telephony/CarrierInfo.java b/android-35/android/telephony/CarrierInfo.java
new file mode 100644
index 0000000..da77a45
--- /dev/null
+++ b/android-35/android/telephony/CarrierInfo.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2024 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;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.telephony.Rlog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CarrierInfo that is used to represent the carrier lock information details.
+ *
+ * @hide
+ */
+public final class CarrierInfo implements Parcelable {
+
+    /**
+     * Used to create a {@link CarrierInfo} from a {@link Parcel}.
+     *
+     * @hide
+     */
+    public static final @android.annotation.NonNull Creator<CarrierInfo> CREATOR =
+            new Creator<CarrierInfo>() {
+                /**
+                 * Create a new instance of the Parcelable class, instantiating it
+                 * from the given Parcel whose data had previously been written by
+                 * {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.
+                 *
+                 * @param source The Parcel to read the object's data from.
+                 * @return Returns a new instance of the Parcelable class.
+                 */
+                @Override
+                public CarrierInfo createFromParcel(Parcel source) {
+                    return new CarrierInfo(source);
+                }
+
+                /**
+                 * Create a new array of the Parcelable class.
+                 *
+                 * @param size Size of the array.
+                 * @return Returns an array of the Parcelable class, with every entry
+                 * initialized to null.
+                 */
+                @Override
+                public CarrierInfo[] newArray(int size) {
+                    return new CarrierInfo[size];
+                }
+
+            };
+    @NonNull
+    private String mMcc;
+    @NonNull
+    private String mMnc;
+    @Nullable
+    private String mSpn;
+    @Nullable
+    private String mGid1;
+    @Nullable
+    private String mGid2;
+    @Nullable
+    private String mImsiPrefix;
+    /** Ehplmn is String combination of MCC,MNC */
+    @Nullable
+    private List<String> mEhplmn;
+    @Nullable
+    private String mIccid;
+    @Nullable
+    private String mImpi;
+
+    /** @hide */
+    @NonNull
+    public String getMcc() {
+        return mMcc;
+    }
+
+    /** @hide */
+    @NonNull
+    public String getMnc() {
+        return mMnc;
+    }
+
+    /** @hide */
+    @Nullable
+    public String getSpn() {
+        return mSpn;
+    }
+
+    /** @hide */
+    @Nullable
+    public String getGid1() {
+        return mGid1;
+    }
+
+    /** @hide */
+    @Nullable
+    public String getGid2() {
+        return mGid2;
+    }
+
+    /** @hide */
+    @Nullable
+    public String getImsiPrefix() {
+        return mImsiPrefix;
+    }
+
+    /** @hide */
+    @Nullable
+    public String getIccid() {
+        return mIccid;
+    }
+
+    /** @hide */
+    @Nullable
+    public String getImpi() {
+        return mImpi;
+    }
+
+    /**
+     * Returns the list of EHPLMN.
+     *
+     * @return List of String that represent Ehplmn.
+     * @hide
+     */
+    @NonNull
+    public List<String> getEhplmn() {
+        return mEhplmn;
+    }
+
+    /** @hide */
+    public CarrierInfo(@NonNull String mcc, @NonNull String mnc, @Nullable String spn,
+            @Nullable String gid1, @Nullable String gid2, @Nullable String imsi,
+            @Nullable String iccid, @Nullable String impi, @Nullable List<String> plmnArrayList) {
+        mMcc = mcc;
+        mMnc = mnc;
+        mSpn = spn;
+        mGid1 = gid1;
+        mGid2 = gid2;
+        mImsiPrefix = imsi;
+        mIccid = iccid;
+        mImpi = impi;
+        mEhplmn = plmnArrayList;
+    }
+
+    /**
+     * Describe the kinds of special objects contained in this Parcelable
+     * instance's marshaled representation. For example, if the object will
+     * include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
+     * the return value of this method must include the
+     * {@link #CONTENTS_FILE_DESCRIPTOR} bit.
+     *
+     * @return a bitmask indicating the set of special object types marshaled
+     * by this Parcelable object instance.
+     * @hide
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Flatten this object in to a Parcel.
+     *
+     * @param dest  The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written.
+     *              May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+     * @hide
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString8(mMcc);
+        dest.writeString8(mMnc);
+        dest.writeString8(mSpn);
+        dest.writeString8(mGid1);
+        dest.writeString8(mGid2);
+        dest.writeString8(mImsiPrefix);
+        dest.writeString8(mIccid);
+        dest.writeString8(mImpi);
+        dest.writeStringList(mEhplmn);
+    }
+
+    /** @hide */
+    public CarrierInfo(Parcel in) {
+        mEhplmn = new ArrayList<String>();
+        mMcc = in.readString8();
+        mMnc = in.readString8();
+        mSpn = in.readString8();
+        mGid1 = in.readString8();
+        mGid2 = in.readString8();
+        mImsiPrefix = in.readString8();
+        mIccid = in.readString8();
+        mImpi = in.readString8();
+        in.readStringList(mEhplmn);
+    }
+
+
+    /** @hide */
+    @android.annotation.NonNull
+    @Override
+    public String toString() {
+        return "CarrierInfo MCC = " + mMcc + "   MNC = " + mMnc + "  SPN = " + mSpn + "   GID1 = "
+                + mGid1 + "   GID2 = " + mGid2 + "   IMSI = " + getPrintableImsi() + "   ICCID = "
+                + SubscriptionInfo.getPrintableId(mIccid) + "  IMPI = " + mImpi + "  EHPLMN = [ "
+                + getEhplmn_toString() + " ]";
+    }
+
+    private String getEhplmn_toString() {
+        return String.join("  ", mEhplmn);
+    }
+
+    private String getPrintableImsi() {
+        boolean enablePiiLog = Rlog.isLoggable("CarrierInfo", Log.VERBOSE);
+        return ((mImsiPrefix != null && mImsiPrefix.length() > 6) ? mImsiPrefix.substring(0, 6)
+                + Rlog.pii(enablePiiLog, mImsiPrefix.substring(6)) : mImsiPrefix);
+    }
+}
diff --git a/android-35/android/telephony/CarrierRestrictionRules.java b/android-35/android/telephony/CarrierRestrictionRules.java
new file mode 100644
index 0000000..d5db612
--- /dev/null
+++ b/android-35/android/telephony/CarrierRestrictionRules.java
@@ -0,0 +1,601 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.carrier.CarrierIdentifier;
+import android.telephony.TelephonyManager.CarrierRestrictionStatus;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Contains the list of carrier restrictions.
+ * Allowed list: it indicates the list of carriers that are allowed.
+ * Excluded list: it indicates the list of carriers that are excluded.
+ * Default carrier restriction: it indicates the default behavior and the priority between the two
+ * lists:
+ *  - not allowed: the device only allows usage of carriers that are present in the allowed list
+ *    and not present in the excluded list. This implies that if a carrier is not present in either
+ *    list, it is not allowed.
+ *  - allowed: the device allows all carriers, except those present in the excluded list and not
+ *    present in the allowed list. This implies that if a carrier is not present in either list,
+ *    it is allowed.
+ * MultiSim policy: it indicates the behavior in case of devices with two or more SIM cards.
+ *  - MULTISIM_POLICY_NONE: the same configuration is applied to all SIM slots independently. This
+ *    is the default value if none is set.
+ *  - MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT: it indicates that any SIM card can be used
+ *    as far as one SIM card matching the configuration is present in the device.
+ *
+ * Both lists support the character '?' as wild character. For example, an entry indicating
+ * MCC=310 and MNC=??? will match all networks with MCC=310.
+ *
+ * Example 1: Allowed list contains MCC and MNC of operator A. Excluded list contains operator B,
+ *            which has same MCC and MNC, but also GID1 value. The priority allowed list is set
+ *            to true. Only SIM cards of operator A are allowed, but not those of B or any other
+ *            operator.
+ * Example 2: Allowed list contains MCC and MNC of operator A. Excluded list contains an entry
+ *            with same MCC, and '???' as MNC. The priority allowed list is set to false.
+ *            SIM cards of operator A and all SIM cards with a different MCC value are allowed.
+ *            SIM cards of operators with same MCC value and different MNC are not allowed.
+ * @hide
+ */
+@SystemApi
+public final class CarrierRestrictionRules implements Parcelable {
+    /**
+     * The device only allows usage of carriers that are present in the allowed list and not
+     * present in the excluded list. This implies that if a carrier is not present in either list,
+     * it is not allowed.
+     */
+    public static final int CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED = 0;
+
+    /**
+     * The device allows all carriers, except those present in the excluded list and not present
+     * in the allowed list. This implies that if a carrier is not present in either list, it is
+     * allowed.
+     */
+    public static final int CARRIER_RESTRICTION_DEFAULT_ALLOWED = 1;
+
+    /** The same configuration is applied to all SIM slots independently. */
+    public static final int MULTISIM_POLICY_NONE = 0;
+
+    /**
+     * Indicates that any SIM card can be used as far as one valid card is present in the device.
+     * For the modem, a SIM card is valid when its content (i.e. MCC, MNC, GID, SPN) matches the
+     * carrier restriction configuration.
+     */
+    public static final int MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT = 1;
+
+    /**
+     * Indicates that the SIM lock policy applies uniformly to all sim slots.
+     * @hide
+     */
+    public static final int MULTISIM_POLICY_APPLY_TO_ALL_SLOTS = 2;
+
+    /**
+     * The SIM lock configuration applies exclusively to sim slot 1, leaving
+     * all other sim slots unlocked irrespective of the SIM card in slot 1
+     * @hide
+     */
+    public static final int MULTISIM_POLICY_APPLY_TO_ONLY_SLOT_1 = 3;
+
+    /**
+     * Valid sim cards must be present on sim slot1 in order
+     * to use other sim slots.
+     * @hide
+     */
+    public static final int MULTISIM_POLICY_VALID_SIM_MUST_PRESENT_ON_SLOT_1 = 4;
+
+    /**
+     * Valid sim card must be present on slot1 and it must be in full service
+     * in order to use other sim slots.
+     * @hide
+     */
+    public static final int MULTISIM_POLICY_ACTIVE_SERVICE_ON_SLOT_1_TO_UNBLOCK_OTHER_SLOTS = 5;
+
+    /**
+     * Valid sim card be present on any slot and it must be in full service
+     * in order to use other sim slots.
+     * @hide
+     */
+    public static final int MULTISIM_POLICY_ACTIVE_SERVICE_ON_ANY_SLOT_TO_UNBLOCK_OTHER_SLOTS = 6;
+
+    /**
+     * Valid sim cards must be present on all slots. If any SIM cards become
+     * invalid then device would set other SIM cards as invalid as well.
+     * @hide
+     */
+    public static final int MULTISIM_POLICY_ALL_SIMS_MUST_BE_VALID = 7;
+
+    /**
+     * In case there is no match policy listed above.
+     * @hide
+     */
+    public static final int MULTISIM_POLICY_SLOT_POLICY_OTHER = 8;
+
+
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "MULTISIM_POLICY_",
+            value = {MULTISIM_POLICY_NONE,
+                    MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT,
+                    MULTISIM_POLICY_APPLY_TO_ALL_SLOTS,
+                    MULTISIM_POLICY_APPLY_TO_ONLY_SLOT_1,
+                    MULTISIM_POLICY_VALID_SIM_MUST_PRESENT_ON_SLOT_1,
+                    MULTISIM_POLICY_ACTIVE_SERVICE_ON_SLOT_1_TO_UNBLOCK_OTHER_SLOTS,
+                    MULTISIM_POLICY_ACTIVE_SERVICE_ON_ANY_SLOT_TO_UNBLOCK_OTHER_SLOTS,
+                    MULTISIM_POLICY_ALL_SIMS_MUST_BE_VALID,
+                    MULTISIM_POLICY_SLOT_POLICY_OTHER
+            })
+    public @interface MultiSimPolicy {}
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CARRIER_RESTRICTION_DEFAULT_",
+            value = {CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED, CARRIER_RESTRICTION_DEFAULT_ALLOWED})
+    public @interface CarrierRestrictionDefault {}
+
+    /* Wild character for comparison */
+    private static final char WILD_CHARACTER = '?';
+
+    private List<CarrierIdentifier> mAllowedCarriers;
+    private List<CarrierIdentifier> mExcludedCarriers;
+    private List<CarrierInfo> mAllowedCarrierInfo;
+    private List<CarrierInfo> mExcludedCarrierInfo;
+    @CarrierRestrictionDefault
+    private int mCarrierRestrictionDefault;
+    @MultiSimPolicy
+    private int mMultiSimPolicy;
+    @CarrierRestrictionStatus
+    private int mCarrierRestrictionStatus;
+    private boolean mUseCarrierLockInfo;
+
+    private CarrierRestrictionRules() {
+        mAllowedCarriers = new ArrayList<CarrierIdentifier>();
+        mExcludedCarriers = new ArrayList<CarrierIdentifier>();
+        mAllowedCarrierInfo = new ArrayList<CarrierInfo>();
+        mExcludedCarrierInfo = new ArrayList<CarrierInfo>();
+        mCarrierRestrictionDefault = CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED;
+        mMultiSimPolicy = MULTISIM_POLICY_NONE;
+        mCarrierRestrictionStatus = TelephonyManager.CARRIER_RESTRICTION_STATUS_UNKNOWN;
+        mUseCarrierLockInfo = false;
+    }
+
+    private CarrierRestrictionRules(Parcel in) {
+        mAllowedCarriers = new ArrayList<CarrierIdentifier>();
+        mExcludedCarriers = new ArrayList<CarrierIdentifier>();
+        mAllowedCarrierInfo = new ArrayList<CarrierInfo>();
+        mExcludedCarrierInfo = new ArrayList<CarrierInfo>();
+        in.readTypedList(mAllowedCarriers, CarrierIdentifier.CREATOR);
+        in.readTypedList(mExcludedCarriers, CarrierIdentifier.CREATOR);
+        mCarrierRestrictionDefault = in.readInt();
+        mMultiSimPolicy = in.readInt();
+        mCarrierRestrictionStatus = in.readInt();
+        if (Flags.carrierRestrictionRulesEnhancement()) {
+            in.readTypedList(mAllowedCarrierInfo, CarrierInfo.CREATOR);
+            in.readTypedList(mExcludedCarrierInfo, CarrierInfo.CREATOR);
+            mUseCarrierLockInfo = in.readBoolean();
+        }
+    }
+
+    /**
+     * Creates a new builder for this class
+     * @hide
+     */
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Indicates if all carriers are allowed
+     */
+    public boolean isAllCarriersAllowed() {
+        if (Flags.carrierRestrictionStatus() && mCarrierRestrictionStatus
+                == TelephonyManager.CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED) {
+            return true;
+        }
+        if (Flags.carrierRestrictionRulesEnhancement() && mUseCarrierLockInfo) {
+            return (mAllowedCarrierInfo.isEmpty() && mExcludedCarrierInfo.isEmpty()
+                    && mCarrierRestrictionDefault == CARRIER_RESTRICTION_DEFAULT_ALLOWED);
+        }
+        return (mAllowedCarriers.isEmpty() && mExcludedCarriers.isEmpty()
+                && mCarrierRestrictionDefault == CARRIER_RESTRICTION_DEFAULT_ALLOWED);
+    }
+
+    /**
+     * Retrieves list of allowed carriers
+     *
+     * @return the list of allowed carriers
+     */
+    public @NonNull List<CarrierIdentifier> getAllowedCarriers() {
+        return mAllowedCarriers;
+    }
+
+    /**
+     * Retrieves list of excluded carriers
+     *
+     * @return the list of excluded carriers
+     */
+    public @NonNull List<CarrierIdentifier> getExcludedCarriers() {
+        return mExcludedCarriers;
+    }
+
+    /**
+     * Retrieves list of excluded carrierInfos
+     *
+     * @return the list of excluded carrierInfos
+     * @hide
+     */
+    public @NonNull List<CarrierInfo> getExcludedCarriersInfoList() {
+        return mExcludedCarrierInfo;
+    }
+
+    /**
+     * Retrieves list of excluded carrierInfos
+     *
+     * @return the list of excluded carrierInfos
+     * @hide
+     */
+    public @NonNull List<CarrierInfo> getAllowedCarriersInfoList() {
+        return mAllowedCarrierInfo;
+    }
+    /**
+     * Retrieves the default behavior of carrier restrictions
+     */
+    public @CarrierRestrictionDefault int getDefaultCarrierRestriction() {
+        return mCarrierRestrictionDefault;
+    }
+
+    /**
+     * @return The policy used for multi-SIM devices
+     */
+    public @MultiSimPolicy int getMultiSimPolicy() {
+        return mMultiSimPolicy;
+    }
+
+    /**
+     * Tests an array of carriers with the carrier restriction configuration. The list of carrier
+     * ids passed as argument does not need to be the same as currently present in the device.
+     *
+     * @param carrierIds list of {@link CarrierIdentifier}, one for each SIM slot on the device
+     * @return a list of boolean with the same size as input, indicating if each
+     * {@link CarrierIdentifier} is allowed or not.
+     */
+    public @NonNull List<Boolean> areCarrierIdentifiersAllowed(
+            @NonNull List<CarrierIdentifier> carrierIds) {
+        ArrayList<Boolean> result = new ArrayList<>(carrierIds.size());
+
+        // First calculate the result for each slot independently
+        for (int i = 0; i < carrierIds.size(); i++) {
+            boolean inAllowedList = isCarrierIdInList(carrierIds.get(i), mAllowedCarriers);
+            boolean inExcludedList = isCarrierIdInList(carrierIds.get(i), mExcludedCarriers);
+            if (mCarrierRestrictionDefault == CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED) {
+                result.add((inAllowedList && !inExcludedList) ? true : false);
+            } else {
+                result.add((inExcludedList && !inAllowedList) ? false : true);
+            }
+        }
+        // Apply the multi-slot policy, if needed.
+        if (mMultiSimPolicy == MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT) {
+            for (boolean b : result) {
+                if (b) {
+                    result.replaceAll(x -> true);
+                    break;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Indicates if a certain carrier {@code id} is present inside a {@code list}
+     *
+     * @return true if the carrier {@code id} is present, false otherwise
+     */
+    private static boolean isCarrierIdInList(CarrierIdentifier id, List<CarrierIdentifier> list) {
+        for (CarrierIdentifier listItem : list) {
+            // Compare MCC and MNC
+            if (!patternMatch(id.getMcc(), listItem.getMcc())
+                    || !patternMatch(id.getMnc(), listItem.getMnc())) {
+                continue;
+            }
+
+            // Compare SPN. Comparison is on the complete strings, case insensitive and with wild
+            // characters.
+            String listItemValue = convertNullToEmpty(listItem.getSpn());
+            String idValue = convertNullToEmpty(id.getSpn());
+            if (!listItemValue.isEmpty()) {
+                if (!patternMatch(idValue, listItemValue)) {
+                    continue;
+                }
+            }
+
+            // The IMSI of the configuration can be shorter than actual IMSI in the SIM card.
+            listItemValue = convertNullToEmpty(listItem.getImsi());
+            idValue = convertNullToEmpty(id.getImsi());
+            if (!patternMatch(
+                    idValue.substring(0, Math.min(idValue.length(), listItemValue.length())),
+                    listItemValue)) {
+                continue;
+            }
+
+            // The GID1 of the configuration can be shorter than actual GID1 in the SIM card.
+            listItemValue = convertNullToEmpty(listItem.getGid1());
+            idValue = convertNullToEmpty(id.getGid1());
+            if (!patternMatch(
+                    idValue.substring(0, Math.min(idValue.length(), listItemValue.length())),
+                    listItemValue)) {
+                continue;
+            }
+
+            // The GID2 of the configuration can be shorter than actual GID2 in the SIM card.
+            listItemValue = convertNullToEmpty(listItem.getGid2());
+            idValue = convertNullToEmpty(id.getGid2());
+            if (!patternMatch(
+                    idValue.substring(0, Math.min(idValue.length(), listItemValue.length())),
+                    listItemValue)) {
+                continue;
+            }
+
+            // Valid match was found in the list
+            return true;
+        }
+        return false;
+    }
+
+    private static String convertNullToEmpty(String value) {
+        return Objects.toString(value, "");
+    }
+
+    /**
+     * Performs a case insensitive string comparison against a given pattern. The character '?'
+     * is used in the pattern as wild character in the comparison. The string must have the same
+     * length as the pattern.
+     *
+     * @param str string to match
+     * @param pattern string containing the pattern
+     * @return true in case of match, false otherwise
+     */
+    private static boolean patternMatch(String str, String pattern) {
+        if (str.length() != pattern.length()) {
+            return false;
+        }
+        String lowerCaseStr = str.toLowerCase(Locale.ROOT);
+        String lowerCasePattern = pattern.toLowerCase(Locale.ROOT);
+
+        for (int i = 0; i < lowerCasePattern.length(); i++) {
+            if (lowerCasePattern.charAt(i) != lowerCaseStr.charAt(i)
+                    && lowerCasePattern.charAt(i) != WILD_CHARACTER) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Get the carrier restriction status of the device.
+     * The return value of the API is as follows.
+     * <ul>
+     *      <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER}
+     *      if the caller and the device locked by the network are same</li>
+     *      <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_RESTRICTED} if the
+     *      caller and the device locked by the network are different</li>
+     *      <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED} if the
+     *      device is not locked</li>
+     *      <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_UNKNOWN} if the device
+     *      locking state is unavailable or radio does not supports the feature</li>
+     * </ul>
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_RESTRICTION_STATUS)
+    public @CarrierRestrictionStatus int getCarrierRestrictionStatus() {
+        return mCarrierRestrictionStatus;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeTypedList(mAllowedCarriers);
+        out.writeTypedList(mExcludedCarriers);
+        out.writeInt(mCarrierRestrictionDefault);
+        out.writeInt(mMultiSimPolicy);
+        out.writeInt(mCarrierRestrictionStatus);
+        if (Flags.carrierRestrictionRulesEnhancement()) {
+            out.writeTypedList(mAllowedCarrierInfo);
+            out.writeTypedList(mExcludedCarrierInfo);
+            out.writeBoolean(mUseCarrierLockInfo);
+        }
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable.Creator}
+     */
+    public static final @android.annotation.NonNull Creator<CarrierRestrictionRules> CREATOR =
+            new Creator<CarrierRestrictionRules>() {
+        @Override
+        public CarrierRestrictionRules createFromParcel(Parcel in) {
+            return new CarrierRestrictionRules(in);
+        }
+
+        @Override
+        public CarrierRestrictionRules[] newArray(int size) {
+            return new CarrierRestrictionRules[size];
+        }
+    };
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "CarrierRestrictionRules(allowed:" + mAllowedCarriers + ", excluded:"
+                + mExcludedCarriers + ", default:" + mCarrierRestrictionDefault
+                + ", MultiSim policy:" + mMultiSimPolicy + getCarrierInfoList() +
+                "  mIsCarrierLockInfoSupported = " + mUseCarrierLockInfo + ")";
+    }
+
+    private String getCarrierInfoList() {
+        if (Flags.carrierRestrictionRulesEnhancement()) {
+            return ",  allowedCarrierInfoList:" + mAllowedCarrierInfo
+                    + ", excludedCarrierInfoList:" + mExcludedCarrierInfo;
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     * Builder for a {@link CarrierRestrictionRules}.
+     */
+    public static final class Builder {
+        private final CarrierRestrictionRules mRules;
+
+        public Builder() {
+            mRules = new CarrierRestrictionRules();
+        }
+
+        /** build command */
+        public @NonNull CarrierRestrictionRules build() {
+            return mRules;
+        }
+
+        /**
+         * Indicate that all carriers are allowed.
+         */
+        public @NonNull Builder setAllCarriersAllowed() {
+            mRules.mAllowedCarriers.clear();
+            mRules.mExcludedCarriers.clear();
+            mRules.mCarrierRestrictionDefault = CARRIER_RESTRICTION_DEFAULT_ALLOWED;
+            if (Flags.carrierRestrictionRulesEnhancement()) {
+                mRules.mCarrierRestrictionStatus =
+                        TelephonyManager.CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED;
+                mRules.mAllowedCarrierInfo.clear();
+                mRules.mExcludedCarrierInfo.clear();
+                mRules.mUseCarrierLockInfo = false;
+            }
+            return this;
+        }
+
+        /**
+         * Set list of allowed carriers.
+         *
+         * @param allowedCarriers list of allowed carriers
+         */
+        public @NonNull Builder setAllowedCarriers(
+                @NonNull List<CarrierIdentifier> allowedCarriers) {
+            mRules.mAllowedCarriers = new ArrayList<CarrierIdentifier>(allowedCarriers);
+            return this;
+        }
+
+        /**
+         * Set list of excluded carriers.
+         *
+         * @param excludedCarriers list of excluded carriers
+         */
+        public @NonNull Builder setExcludedCarriers(
+                @NonNull List<CarrierIdentifier> excludedCarriers) {
+            mRules.mExcludedCarriers = new ArrayList<CarrierIdentifier>(excludedCarriers);
+            return this;
+        }
+
+        /**
+         * Set the default behavior of the carrier restrictions
+         *
+         * @param carrierRestrictionDefault prioritized carrier list
+         */
+        public @NonNull Builder setDefaultCarrierRestriction(
+                @CarrierRestrictionDefault int carrierRestrictionDefault) {
+            mRules.mCarrierRestrictionDefault = carrierRestrictionDefault;
+            return this;
+        }
+
+        /**
+         * Set the policy to be used for multi-SIM devices
+         *
+         * @param multiSimPolicy multi SIM policy
+         */
+        public @NonNull Builder setMultiSimPolicy(@MultiSimPolicy int multiSimPolicy) {
+            mRules.mMultiSimPolicy = multiSimPolicy;
+            return this;
+        }
+
+        /**
+         * Set the device's carrier restriction status
+         *
+         * @param carrierRestrictionStatus device restriction status
+         * @hide
+         */
+        public @NonNull
+        Builder setCarrierRestrictionStatus(int carrierRestrictionStatus) {
+            mRules.mCarrierRestrictionStatus = carrierRestrictionStatus;
+            return this;
+        }
+
+        /**
+         * Set list of allowed carrierInfo
+         *
+         * @param allowedCarrierInfo list of allowed CarrierInfo
+         * @hide
+         */
+        public @NonNull Builder setAllowedCarrierInfo(
+                @NonNull List<CarrierInfo> allowedCarrierInfo) {
+            mRules.mAllowedCarrierInfo = new ArrayList<CarrierInfo>(allowedCarrierInfo);
+            return this;
+        }
+
+        /**
+         * Set list of allowed carrierInfo
+         *
+         * @param excludedCarrierInfo list of allowed CarrierInfo
+         * @hide
+         */
+        public @NonNull Builder setExcludedCarrierInfo(
+                @NonNull List<CarrierInfo> excludedCarrierInfo) {
+            mRules.mExcludedCarrierInfo = new ArrayList<CarrierInfo>(excludedCarrierInfo);
+            return this;
+        }
+
+        /**
+         * set whether the HAL radio supports the advanced carrier lock features or not.
+         *
+         * @param carrierLockInfoSupported advanced carrierInfo changes supported or not
+         * @hide
+         */
+        public @NonNull Builder setCarrierLockInfoFeature(boolean carrierLockInfoSupported) {
+            mRules.mUseCarrierLockInfo = carrierLockInfoSupported;
+            return this;
+        }
+    }
+}
diff --git a/android-35/android/telephony/CbGeoUtils.java b/android-35/android/telephony/CbGeoUtils.java
new file mode 100644
index 0000000..806bac0
--- /dev/null
+++ b/android-35/android/telephony/CbGeoUtils.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2019 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;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.telephony.Rlog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * This utils class is used for geo-fencing of CellBroadcast messages and is used by the cell
+ * broadcast module.
+ *
+ * The coordinates used by this utils class are latitude and longitude, but some algorithms in this
+ * class only use them as coordinates on plane, so the calculation will be inaccurate. So don't use
+ * this class for anything other then geo-targeting of cellbroadcast messages.
+ *
+ * More information regarding cell broadcast geo-fencing logic is laid out in 3GPP TS 23.041 and
+ * ATIS-0700041.
+ * @hide
+ */
+@SystemApi
+public class CbGeoUtils {
+
+    /**
+     * This class is never instantiated
+     * @hide
+     */
+    private CbGeoUtils() {}
+
+    /** Geometric interface. */
+    public interface Geometry {
+        /**
+         * Determines if the given point {@code p} is inside the geometry.
+         * @param p point in latitude, longitude format.
+         * @return {@code True} if the given point is inside the geometry.
+         */
+        boolean contains(@NonNull LatLng p);
+    }
+
+    /**
+     * Tolerance for determining if the value is 0. If the absolute value of a value is less than
+     * this tolerance, it will be treated as 0.
+     * @hide
+     */
+    public static final double EPS = 1e-7;
+
+    /**
+     * The radius of earth.
+     * @hide
+     */
+    public static final int EARTH_RADIUS_METER = 6371 * 1000;
+
+    private static final String TAG = "CbGeoUtils";
+
+    // The TLV tags of WAC, defined in ATIS-0700041 5.2.3 WAC tag coding.
+    /** @hide */
+    public static final int GEO_FENCING_MAXIMUM_WAIT_TIME = 0x01;
+    /** @hide */
+    public static final int GEOMETRY_TYPE_POLYGON = 0x02;
+    /** @hide */
+    public static final int GEOMETRY_TYPE_CIRCLE = 0x03;
+
+    // The identifier of geometry in the encoded string.
+    /** @hide */
+    private static final String CIRCLE_SYMBOL = "circle";
+    /** @hide */
+    private static final String POLYGON_SYMBOL = "polygon";
+
+    /** A point represented by (latitude, longitude). */
+    public static class LatLng {
+        public final double lat;
+        public final double lng;
+
+        /**
+         * Constructor.
+         * @param lat latitude, range [-90, 90]
+         * @param lng longitude, range [-180, 180]
+         */
+        public LatLng(double lat, double lng) {
+            this.lat = lat;
+            this.lng = lng;
+        }
+
+        /**
+         * @param p the point to subtract
+         * @return the result of the subtraction
+         */
+        @NonNull
+        public LatLng subtract(@NonNull LatLng p) {
+            return new LatLng(lat - p.lat, lng - p.lng);
+        }
+
+        /**
+         * Calculate the distance in meters between this point and the given point {@code p}.
+         * @param p the point used to calculate the distance.
+         * @return the distance in meters.
+         */
+        public double distance(@NonNull LatLng p) {
+            double dlat = Math.sin(0.5 * Math.toRadians(lat - p.lat));
+            double dlng = Math.sin(0.5 * Math.toRadians(lng - p.lng));
+            double x = dlat * dlat
+                    + dlng * dlng * Math.cos(Math.toRadians(lat)) * Math.cos(Math.toRadians(p.lat));
+            return 2 * Math.atan2(Math.sqrt(x), Math.sqrt(1 - x)) * EARTH_RADIUS_METER;
+        }
+
+        @Override
+        public String toString() {
+            return "(" + lat + "," + lng + ")";
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+
+            if (!(o instanceof LatLng)) {
+                return false;
+            }
+
+            LatLng l = (LatLng) o;
+            return lat == l.lat && lng == l.lng;
+        }
+    }
+
+    /**
+     * A class representing a simple polygon with at least 3 points. This is used for geo-fencing
+     * logic with cell broadcasts. More information regarding cell broadcast geo-fencing logic is
+     * laid out in 3GPP TS 23.041 and ATIS-0700041.
+     */
+    public static class Polygon implements Geometry {
+        /**
+         * In order to reduce the loss of precision in floating point calculations, all vertices
+         * of the polygon are scaled. Set the value of scale to 1000 can take into account the
+         * actual distance accuracy of 1 meter if the EPS is 1e-7 during the calculation.
+         */
+        private static final double SCALE = 1000.0;
+
+        private final List<LatLng> mVertices;
+        private final List<Point> mScaledVertices;
+        private final LatLng mOrigin;
+
+        /**
+         * Constructs a simple polygon from the given vertices. The adjacent two vertices are
+         * connected to form an edge of the polygon. The polygon has at least 3 vertices, and the
+         * last vertices and the first vertices must be adjacent.
+         *
+         * The longitude difference in the vertices should be less than 180 degrees.
+         */
+        public Polygon(@NonNull List<LatLng> vertices) {
+            mVertices = vertices;
+
+            // Find the point with smallest longitude as the mOrigin point.
+            int idx = 0;
+            for (int i = 1; i < vertices.size(); i++) {
+                if (vertices.get(i).lng < vertices.get(idx).lng) {
+                    idx = i;
+                }
+            }
+            mOrigin = vertices.get(idx);
+
+            mScaledVertices = vertices.stream()
+                    .map(latLng -> convertAndScaleLatLng(latLng))
+                    .collect(Collectors.toList());
+        }
+
+        /**
+         * Return the list of vertices which compose the polygon.
+         */
+        public @NonNull List<LatLng> getVertices() {
+            return mVertices;
+        }
+
+        /**
+         * Check if the given LatLng is inside the polygon.
+         *
+         * If a LatLng is on the edge of the polygon, it is also considered to be inside the
+         * polygon.
+         */
+        @Override
+        public boolean contains(@NonNull LatLng latLng) {
+            // This method counts the number of times the polygon winds around the point P, A.K.A
+            // "winding number". The point is outside only when this "winding number" is 0.
+
+            Point p = convertAndScaleLatLng(latLng);
+
+            int n = mScaledVertices.size();
+            int windingNumber = 0;
+            for (int i = 0; i < n; i++) {
+                Point a = mScaledVertices.get(i);
+                Point b = mScaledVertices.get((i + 1) % n);
+
+                // CCW is counterclockwise
+                // CCW = ab x ap
+                // CCW > 0 -> ap is on the left side of ab
+                // CCW == 0 -> ap is on the same line of ab
+                // CCW < 0 -> ap is on the right side of ab
+                int ccw = sign(crossProduct(b.subtract(a), p.subtract(a)));
+
+                if (ccw == 0) {
+                    if (Math.min(a.x, b.x) <= p.x && p.x <= Math.max(a.x, b.x)
+                            && Math.min(a.y, b.y) <= p.y && p.y <= Math.max(a.y, b.y)) {
+                        return true;
+                    }
+                } else {
+                    if (sign(a.y - p.y) <= 0) {
+                        // upward crossing
+                        if (ccw > 0 && sign(b.y - p.y) > 0) {
+                            ++windingNumber;
+                        }
+                    } else {
+                        // downward crossing
+                        if (ccw < 0 && sign(b.y - p.y) <= 0) {
+                            --windingNumber;
+                        }
+                    }
+                }
+            }
+            return windingNumber != 0;
+        }
+
+        /**
+         * Move the given point {@code latLng} to the coordinate system with {@code mOrigin} as the
+         * origin and scale it. {@code mOrigin} is selected from the vertices of a polygon, it has
+         * the smallest longitude value among all of the polygon vertices.
+         *
+         * @param latLng the point need to be converted and scaled.
+         * @Return a {@link Point} object.
+         */
+        private Point convertAndScaleLatLng(LatLng latLng) {
+            double x = latLng.lat - mOrigin.lat;
+            double y = latLng.lng - mOrigin.lng;
+
+            // If the point is in different hemispheres(western/eastern) than the mOrigin, and the
+            // edge between them cross the 180th meridian, then its relative coordinates will be
+            // extended.
+            // For example, suppose the longitude of the mOrigin is -178, and the longitude of the
+            // point to be converted is 175, then the longitude after the conversion is -8.
+            // calculation: (-178 - 8) - (-178).
+            if (sign(mOrigin.lng) != 0 && sign(mOrigin.lng) != sign(latLng.lng)) {
+                double distCross0thMeridian = Math.abs(mOrigin.lng) + Math.abs(latLng.lng);
+                if (sign(distCross0thMeridian * 2 - 360) > 0) {
+                    y = sign(mOrigin.lng) * (360 - distCross0thMeridian);
+                }
+            }
+            return new Point(x * SCALE, y * SCALE);
+        }
+
+        private static double crossProduct(Point a, Point b) {
+            return a.x * b.y - a.y * b.x;
+        }
+
+        /** @hide */
+        static final class Point {
+            public final double x;
+            public final double y;
+
+            Point(double x, double y) {
+                this.x = x;
+                this.y = y;
+            }
+
+            public Point subtract(Point p) {
+                return new Point(x - p.x, y - p.y);
+            }
+        }
+
+        @Override
+        public String toString() {
+            String str = "Polygon: ";
+            if (TelephonyUtils.IS_DEBUGGABLE) {
+                str += mVertices;
+            }
+            return str;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+
+            if (!(o instanceof Polygon)) {
+                return false;
+            }
+
+            Polygon p = (Polygon) o;
+            if (mVertices.size() != p.mVertices.size()) {
+                return false;
+            }
+            for (int i = 0; i < mVertices.size(); i++) {
+                if (!mVertices.get(i).equals(p.mVertices.get(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+    }
+
+    /**
+     * A class represents a {@link Geometry} in the shape of a Circle. This is used for handling
+     * geo-fenced cell broadcasts. More information regarding cell broadcast geo-fencing logic is
+     * laid out in 3GPP TS 23.041 and ATIS-0700041.
+     */
+    public static class Circle implements Geometry {
+        private final LatLng mCenter;
+        private final double mRadiusMeter;
+
+        /**
+         * Construct a Circle given a center point and a radius in meters.
+         *
+         * @param center the latitude and longitude of the center of the circle
+         * @param radiusInMeters the radius of the circle in meters
+         */
+        public Circle(@NonNull LatLng center, double radiusInMeters) {
+            this.mCenter = center;
+            this.mRadiusMeter = radiusInMeters;
+        }
+
+        /**
+         * Return the latitude and longitude of the center of the circle;
+         */
+        public @NonNull LatLng getCenter() {
+            return mCenter;
+        }
+
+        /**
+         * Return the radius of the circle in meters.
+         */
+        public double getRadius() {
+            return mRadiusMeter;
+        }
+
+        /**
+         * Check if the given LatLng is inside the circle.
+         *
+         * If a LatLng is on the edge of the circle, it is also considered to be inside the circle.
+         */
+        @Override
+        public boolean contains(@NonNull LatLng latLng) {
+            return mCenter.distance(latLng) <= mRadiusMeter;
+        }
+
+        @Override
+        public String toString() {
+            String str = "Circle: ";
+            if (TelephonyUtils.IS_DEBUGGABLE) {
+                str += mCenter + ", radius = " + mRadiusMeter;
+            }
+
+            return str;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+
+            if (!(o instanceof Circle)) {
+                return false;
+            }
+
+            Circle c = (Circle) o;
+            return mCenter.equals(c.mCenter)
+                    && Double.compare(mRadiusMeter, c.mRadiusMeter) == 0;
+        }
+    }
+
+    /**
+     * Parse the geometries from the encoded string {@code str}. The string must follow the
+     * geometry encoding specified by {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+     * @hide
+     */
+    @NonNull
+    public static List<Geometry> parseGeometriesFromString(@NonNull String str) {
+        List<Geometry> geometries = new ArrayList<>();
+        for (String geometryStr : str.split("\\s*;\\s*")) {
+            String[] geoParameters = geometryStr.split("\\s*\\|\\s*");
+            switch (geoParameters[0]) {
+                case CIRCLE_SYMBOL:
+                    geometries.add(new Circle(parseLatLngFromString(geoParameters[1]),
+                            Double.parseDouble(geoParameters[2])));
+                    break;
+                case POLYGON_SYMBOL:
+                    List<LatLng> vertices = new ArrayList<>(geoParameters.length - 1);
+                    for (int i = 1; i < geoParameters.length; i++) {
+                        vertices.add(parseLatLngFromString(geoParameters[i]));
+                    }
+                    geometries.add(new Polygon(vertices));
+                    break;
+                default:
+                    Rlog.e(TAG, "Invalid geometry format " + geometryStr);
+            }
+        }
+        return geometries;
+    }
+
+    /**
+     * Encode a list of geometry objects to string. The encoding format is specified by
+     * {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+     *
+     * @param geometries the list of geometry objects need to be encoded.
+     * @return the encoded string.
+     * @hide
+     */
+    @NonNull
+    public static String encodeGeometriesToString(List<Geometry> geometries) {
+        if (geometries == null || geometries.isEmpty()) return "";
+        return geometries.stream()
+                .map(geometry -> encodeGeometryToString(geometry))
+                .filter(encodedStr -> !TextUtils.isEmpty(encodedStr))
+                .collect(Collectors.joining(";"));
+    }
+
+
+    /**
+     * Encode the geometry object to string. The encoding format is specified by
+     * {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+     * @param geometry the geometry object need to be encoded.
+     * @return the encoded string.
+     * @hide
+     */
+    @NonNull
+    private static String encodeGeometryToString(@NonNull Geometry geometry) {
+        StringBuilder sb = new StringBuilder();
+        if (geometry instanceof Polygon) {
+            sb.append(POLYGON_SYMBOL);
+            for (LatLng latLng : ((Polygon) geometry).getVertices()) {
+                sb.append("|");
+                sb.append(latLng.lat);
+                sb.append(",");
+                sb.append(latLng.lng);
+            }
+        } else if (geometry instanceof Circle) {
+            sb.append(CIRCLE_SYMBOL);
+            Circle circle = (Circle) geometry;
+
+            // Center
+            sb.append("|");
+            sb.append(circle.getCenter().lat);
+            sb.append(",");
+            sb.append(circle.getCenter().lng);
+
+            // Radius
+            sb.append("|");
+            sb.append(circle.getRadius());
+        } else {
+            Rlog.e(TAG, "Unsupported geometry object " + geometry);
+            return null;
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Parse {@link LatLng} from {@link String}. Latitude and longitude are separated by ",".
+     * Example: "13.56,-55.447".
+     *
+     * @param str encoded lat/lng string.
+     * @Return {@link LatLng} object.
+     * @hide
+     */
+    @NonNull
+    public static LatLng parseLatLngFromString(@NonNull String str) {
+        String[] latLng = str.split("\\s*,\\s*");
+        return new LatLng(Double.parseDouble(latLng[0]), Double.parseDouble(latLng[1]));
+    }
+
+    /**
+     * @Return the sign of the given value {@code value} with the specified tolerance. Return 1
+     * means the sign is positive, -1 means negative, 0 means the value will be treated as 0.
+     * @hide
+     */
+    public static int sign(double value) {
+        if (value > EPS) return 1;
+        if (value < -EPS) return -1;
+        return 0;
+    }
+}
diff --git a/android-35/android/telephony/CellBroadcastIdRange.java b/android-35/android/telephony/CellBroadcastIdRange.java
new file mode 100644
index 0000000..abee80f
--- /dev/null
+++ b/android-35/android/telephony/CellBroadcastIdRange.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Describes a particular cell broadcast message identifier range.
+ * @hide
+ */
+@SystemApi
+public final class CellBroadcastIdRange implements Parcelable {
+
+    @IntRange(from = 0, to = 0xFFFF)
+    private int mStartId;
+    @IntRange(from = 0, to = 0xFFFF)
+    private int mEndId;
+    private int mType;
+    private boolean mIsEnabled;
+
+    /**
+     * Create a new CellBroacastRange
+     *
+     * @param startId first message identifier as specified in TS 23.041 (3GPP)
+     * or C.R1001-G (3GPP2). The value must be between 0 and 0xFFFF.
+     * @param endId last message identifier as specified in TS 23.041 (3GPP)
+     * or C.R1001-G (3GPP2). The value must be between 0 and 0xFFFF.
+     * @param type the message format as defined in {@link SmsCbMessage}
+     * @param isEnabled whether the range is enabled
+     *
+     * @throws IllegalArgumentException if endId < startId or invalid value
+     */
+    public CellBroadcastIdRange(@IntRange(from = 0, to = 0xFFFF) int startId,
+            @IntRange(from = 0, to = 0xFFFF) int endId,
+            @android.telephony.SmsCbMessage.MessageFormat int type, boolean isEnabled)
+            throws IllegalArgumentException {
+        if (startId < 0 || endId < 0 || startId > 0xFFFF || endId > 0xFFFF) {
+            throw new IllegalArgumentException("invalid id");
+        }
+        if (endId < startId) {
+            throw new IllegalArgumentException("endId must be greater than or equal to startId");
+        }
+        mStartId = startId;
+        mEndId = endId;
+        mType = type;
+        mIsEnabled = isEnabled;
+    }
+
+    /**
+     * Return the first message identifier of this range as specified in TS 23.041 (3GPP)
+     * or C.R1001-G (3GPP2)
+     */
+    @IntRange(from = 0, to = 0xFFFF)
+    public int getStartId() {
+        return mStartId;
+    }
+
+    /**
+     * Return the last message identifier of this range as specified in TS 23.041 (3GPP)
+     * or C.R1001-G (3GPP2)
+     */
+    @IntRange(from = 0, to = 0xFFFF)
+    public int getEndId() {
+        return mEndId;
+    }
+
+    /**
+     * Return the message format of this range as defined in {@link SmsCbMessage}
+     */
+    public @android.telephony.SmsCbMessage.MessageFormat int getType() {
+        return mType;
+    }
+
+    /**
+     * Return whether the range is enabled
+     */
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mStartId);
+        out.writeInt(mEndId);
+        out.writeInt(mType);
+        out.writeBoolean(mIsEnabled);
+    }
+
+    /**
+     * {@link Parcelable.Creator}
+     *
+     */
+    public static final @NonNull Parcelable.Creator<CellBroadcastIdRange> CREATOR =
+            new Creator<CellBroadcastIdRange>() {
+                @NonNull
+                @Override
+                public CellBroadcastIdRange createFromParcel(Parcel in) {
+                    int startId = in.readInt();
+                    int endId = in.readInt();
+                    int type = in.readInt();
+                    boolean isEnabled = in.readBoolean();
+
+                    return new CellBroadcastIdRange(startId, endId, type, isEnabled);
+                }
+
+                @NonNull
+                @Override
+                public CellBroadcastIdRange[] newArray(int size) {
+                    return new CellBroadcastIdRange[size];
+                }
+            };
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mStartId, mEndId, mType, mIsEnabled);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof CellBroadcastIdRange)) {
+            return false;
+        }
+
+        CellBroadcastIdRange other = (CellBroadcastIdRange) obj;
+
+        return mStartId == other.mStartId && mEndId == other.mEndId && mType == other.mType
+                && mIsEnabled == other.mIsEnabled;
+    }
+
+    @Override
+    public String toString() {
+        return "CellBroadcastIdRange[" + mStartId + ", " + mEndId + ", " + mType + ", "
+                + mIsEnabled + "]";
+    }
+}
diff --git a/android-35/android/telephony/CellBroadcastIntents.java b/android-35/android/telephony/CellBroadcastIntents.java
new file mode 100644
index 0000000..b9ad773
--- /dev/null
+++ b/android-35/android/telephony/CellBroadcastIntents.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Telephony;
+
+/**
+ * A static helper class used to send Intents with prepopulated flags.
+ * <p>
+ * This is intended to be used by the CellBroadcastService and does nothing if the caller does not
+ * have permission to broadcast {@link Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION}.
+ *
+ * @hide
+ */
+@SystemApi
+public class CellBroadcastIntents {
+    private static final String LOG_TAG = "CellBroadcastIntents";
+
+    private static final String EXTRA_MESSAGE = "message";
+
+    /**
+     * Broadcast intent action for notifying area information has been updated. broadcast is also
+     * sent when the user turns off area info alerts. The information
+     * can be retrieved by {@link CellBroadcastService#getCellBroadcastAreaInfo(int)}. The
+     * associated SIM slot index of updated area information can be retrieved through the extra
+     * {@link SubscriptionManager#EXTRA_SLOT_INDEX}.
+     *
+     * @see SubscriptionManager#EXTRA_SLOT_INDEX
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_AREA_INFO_UPDATED =
+            "android.telephony.action.AREA_INFO_UPDATED";
+
+    /**
+     * @hide
+     */
+    private CellBroadcastIntents() {
+    }
+
+    /**
+     * Broadcasts an SMS_CB_RECEIVED_ACTION intent which can be received by background
+     * BroadcastReceivers. This is only intended to be used by the CellBroadcastService and will
+     * do nothing if the caller does not have permission to broadcast
+     * {@link Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION}.
+     *
+     * @param context            The context from which to send the broadcast
+     * @param user               The user from which to send the broadcast
+     * @param smsCbMessage       The SmsCbMessage to include with the intent
+     * @param resultReceiver     Your own BroadcastReceiver to treat as the final receiver of the
+     *                           broadcast.
+     * @param scheduler          A custom Handler with which to schedule the resultReceiver
+     *                           callback; if null it will be scheduled in the Context's main
+     *                           thread.
+     * @param initialCode        An initial value for the result code.  Often Activity.RESULT_OK.
+     * @param slotIndex          The slot index to include in the intent
+     */
+    public static void sendSmsCbReceivedBroadcast(@NonNull Context context,
+            @Nullable UserHandle user, @NonNull SmsCbMessage smsCbMessage,
+            @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+            int initialCode, int slotIndex) {
+        Intent backgroundIntent = new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION);
+        backgroundIntent.putExtra(EXTRA_MESSAGE, smsCbMessage);
+        putPhoneIdAndSubIdExtra(context, backgroundIntent, slotIndex);
+
+        String receiverPermission = Manifest.permission.RECEIVE_SMS;
+        String receiverAppOp = AppOpsManager.OPSTR_RECEIVE_SMS;
+        if (user != null) {
+            context.createContextAsUser(user, 0).sendOrderedBroadcast(backgroundIntent,
+                    receiverPermission, receiverAppOp, resultReceiver, scheduler, initialCode,
+                    null, null);
+        } else {
+            context.sendOrderedBroadcast(backgroundIntent, receiverPermission,
+                    receiverAppOp, resultReceiver, scheduler, initialCode, null, null);
+        }
+    }
+
+    /**
+     * Put the phone ID and sub ID into an intent as extras.
+     */
+    private static void putPhoneIdAndSubIdExtra(Context context, Intent intent, int phoneId) {
+        int subId = SubscriptionManager.getSubscriptionId(phoneId);
+        if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            intent.putExtra("subscription", subId);
+            intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
+        }
+        intent.putExtra("phone", phoneId);
+        intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
+    }
+}
diff --git a/android-35/android/telephony/CellBroadcastService.java b/android-35/android/telephony/CellBroadcastService.java
new file mode 100644
index 0000000..14de2f2
--- /dev/null
+++ b/android-35/android/telephony/CellBroadcastService.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2019 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;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+import android.telephony.cdma.CdmaSmsCbProgramData;
+
+import com.android.internal.util.FastPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A service which exposes the cell broadcast handling module to the system.
+ * <p>
+ * To extend this class, you must declare the service in your manifest file to require the
+ * {@link android.Manifest.permission#BIND_CELL_BROADCAST_SERVICE} permission and include an intent
+ * filter with the {@link #CELL_BROADCAST_SERVICE_INTERFACE}.
+ * Implementations of this service should run in the phone process and with its UID.
+ * <p>
+ * For example:
+ * <pre>{@code
+ * <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ *       android:sharedUserId="android.uid.phone">
+ *   <service android:name=".MyCellBroadcastService"
+ *         android:label="@string/service_name"
+ *         android:process="com.android.phone"
+ *         android:exported="true"
+ *         android:permission="android.permission.BIND_CELL_BROADCAST_SERVICE">
+ *     <intent-filter>
+ *           <action android:name="android.telephony.CellBroadcastService" />
+ *     </intent-filter>
+ *   </service>
+ * </manifest>
+ * }</pre>
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class CellBroadcastService extends Service {
+
+    public static final String CELL_BROADCAST_SERVICE_INTERFACE =
+            "android.telephony.CellBroadcastService";
+
+    private final ICellBroadcastService.Stub mStubWrapper;
+
+    public CellBroadcastService() {
+        mStubWrapper = new ICellBroadcastServiceWrapper();
+    }
+
+    /**
+     * Handle a GSM cell broadcast SMS message forwarded from the system.
+     *
+     * @param slotIndex the index of the slot which received the message
+     * @param message   the SMS PDU
+     */
+    public abstract void onGsmCellBroadcastSms(int slotIndex, @NonNull byte[] message);
+
+    /**
+     * Handle a CDMA cell broadcast SMS message forwarded from the system.
+     *
+     * @param slotIndex       the index of the slot which received the message
+     * @param bearerData      the CDMA SMS bearer data
+     * @param serviceCategory the CDMA SCPT service category
+     */
+    public abstract void onCdmaCellBroadcastSms(int slotIndex, @NonNull byte[] bearerData,
+            @CdmaSmsCbProgramData.Category int serviceCategory);
+
+    /**
+     * Handle a CDMA cell broadcast SMS message forwarded from the system.
+     *
+     * @param slotIndex          the index of the slot which received the message
+     * @param smsCbProgramData   the SMS CB program data of the message
+     * @param originatingAddress the originating address of the message, as a non-separated dial
+     *                           string
+     * @param callback           a callback to run after each cell broadcast receiver has handled
+     *                           the SCP message. The bundle will contain a non-separated
+     *                           dial string as and an ArrayList of {@link CdmaSmsCbProgramResults}.
+     */
+    public abstract void onCdmaScpMessage(int slotIndex,
+            @NonNull List<CdmaSmsCbProgramData> smsCbProgramData,
+            @NonNull String originatingAddress, @NonNull Consumer<Bundle> callback);
+
+    /**
+     * Get broadcasted area information.
+     *
+     * @param slotIndex the index of the slot which received the area information.
+     *
+     * @return The area information string sent from the network. This is usually the human readable
+     * string shown in Setting app's SIM status page.
+     */
+    @WorkerThread
+    public abstract @NonNull CharSequence getCellBroadcastAreaInfo(int slotIndex);
+
+    /**
+     * If overriding this method, call through to the super method for any unknown actions.
+     * {@inheritDoc}
+     */
+    @Override
+    @CallSuper
+    public IBinder onBind(@Nullable Intent intent) {
+        return mStubWrapper;
+    }
+
+    /**
+     * A wrapper around ICellBroadcastService that forwards calls to implementations of
+     * {@link CellBroadcastService}.
+     *
+     * @hide
+     */
+    public class ICellBroadcastServiceWrapper extends ICellBroadcastService.Stub {
+        /**
+         * Handle a GSM cell broadcast SMS.
+         *
+         * @param slotIndex the index of the slot which received the broadcast
+         * @param message   the SMS message PDU
+         */
+        @Override
+        public void handleGsmCellBroadcastSms(int slotIndex, byte[] message) {
+            CellBroadcastService.this.onGsmCellBroadcastSms(slotIndex, message);
+        }
+
+        /**
+         * Handle a CDMA cell broadcast SMS.
+         *
+         * @param slotIndex       the index of the slot which received the broadcast
+         * @param bearerData      the CDMA SMS bearer data
+         * @param serviceCategory the CDMA SCPT service category
+         */
+        @Override
+        public void handleCdmaCellBroadcastSms(int slotIndex, byte[] bearerData,
+                int serviceCategory) {
+            CellBroadcastService.this.onCdmaCellBroadcastSms(slotIndex, bearerData,
+                    serviceCategory);
+        }
+
+        /**
+         * Handle a CDMA Service Category Program message.
+         *
+         * @param slotIndex          the index of the slot which received the message
+         * @param smsCbProgramData   the SMS CB program data of the message
+         * @param originatingAddress the originating address of the message
+         * @param callback           a callback to run after each cell broadcast receiver has
+         *                           handled the SCP message
+         */
+        @Override
+        public void handleCdmaScpMessage(int slotIndex,
+                List<CdmaSmsCbProgramData> smsCbProgramData, String originatingAddress,
+                RemoteCallback callback) {
+            Consumer<Bundle> consumer = bundle -> {
+                callback.sendResult(bundle);
+            };
+            CellBroadcastService.this.onCdmaScpMessage(slotIndex, smsCbProgramData,
+                    originatingAddress, consumer);
+        }
+
+        /**
+         * Get broadcasted area information
+         *
+         * @param slotIndex         the index of the slot which received the message
+         *
+         * @return The area information
+         */
+        @Override
+        public @NonNull CharSequence getCellBroadcastAreaInfo(int slotIndex) {
+            return CellBroadcastService.this.getCellBroadcastAreaInfo(slotIndex);
+        }
+
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+            CellBroadcastService.this.dump(fd, fout, args);
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, String[] args) {
+            PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd));
+            CellBroadcastService.this.dump(fd, pw, args);
+        }
+    }
+}
diff --git a/android-35/android/telephony/CellConfigLte.java b/android-35/android/telephony/CellConfigLte.java
new file mode 100644
index 0000000..3e4e244
--- /dev/null
+++ b/android-35/android/telephony/CellConfigLte.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * The container of LTE cell related configs.
+ * @hide
+ */
+public class CellConfigLte implements Parcelable {
+    private final boolean mIsEndcAvailable;
+
+    /** @hide */
+    public CellConfigLte() {
+        mIsEndcAvailable = false;
+    }
+
+    /** @hide */
+    public CellConfigLte(boolean isEndcAvailable) {
+        mIsEndcAvailable = isEndcAvailable;
+    }
+
+    /** @hide */
+    public CellConfigLte(CellConfigLte config) {
+        mIsEndcAvailable = config.mIsEndcAvailable;
+    }
+
+    /**
+     * Indicates that if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the LTE cell.
+     *
+     * Reference: 3GPP TS 36.331 v15.2.2 6.3.1 System information blocks.
+     *
+     * @return {@code true} if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the LTE cell.
+     *
+     */
+    boolean isEndcAvailable() {
+        return mIsEndcAvailable;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIsEndcAvailable);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CellConfigLte)) return false;
+
+        CellConfigLte o = (CellConfigLte) other;
+        return mIsEndcAvailable == o.mIsEndcAvailable;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mIsEndcAvailable);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder().append(this.getClass().getName())
+                .append(" :{")
+                .append(" isEndcAvailable = " + mIsEndcAvailable)
+                .append(" }")
+                .toString();
+    }
+
+    private CellConfigLte(Parcel in) {
+        mIsEndcAvailable = in.readBoolean();
+    }
+
+    public static final @android.annotation.NonNull Creator<CellConfigLte> CREATOR = new Creator<CellConfigLte>() {
+        @Override
+        public CellConfigLte createFromParcel(Parcel in) {
+            return new CellConfigLte(in);
+        }
+
+        @Override
+        public CellConfigLte[] newArray(int size) {
+            return new CellConfigLte[0];
+        }
+    };
+}
diff --git a/android-35/android/telephony/CellIdentity.java b/android-35/android/telephony/CellIdentity.java
new file mode 100644
index 0000000..6e3cfac
--- /dev/null
+++ b/android-35/android/telephony/CellIdentity.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2017 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;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.telephony.Rlog;
+
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * CellIdentity represents the identity of a unique cell. This is the base class for
+ * CellIdentityXxx which represents cell identity for specific network access technology.
+ */
+public abstract class CellIdentity implements Parcelable {
+
+    /** @hide */
+    public static final int INVALID_CHANNEL_NUMBER = Integer.MAX_VALUE;
+
+    /**
+     * parameters for validation
+     * @hide
+     */
+    public static final int MCC_LENGTH = 3;
+
+    /** @hide */
+    public static final int MNC_MIN_LENGTH = 2;
+    /** @hide */
+    public static final int MNC_MAX_LENGTH = 3;
+
+    // Log tag
+    /** @hide */
+    protected final String mTag;
+    // Cell identity type
+    /** @hide */
+    protected final int mType;
+    // 3-digit Mobile Country Code in string format. Null for CDMA cell identity.
+    /** @hide */
+    protected final String mMccStr;
+    // 2 or 3-digit Mobile Network Code in string format. Null for CDMA cell identity.
+    /** @hide */
+    protected final String mMncStr;
+
+    // long alpha Operator Name String or Enhanced Operator Name String
+    /** @hide */
+    protected String mAlphaLong;
+    // short alpha Operator Name String or Enhanced Operator Name String
+    /** @hide */
+    protected String mAlphaShort;
+
+    // Cell Global, 3GPP TS 23.003
+    /** @hide */
+    protected String mGlobalCellId;
+
+
+    /** @hide */
+    protected CellIdentity(@Nullable String tag, int type, @Nullable String mcc,
+            @Nullable String mnc, @Nullable String alphal, @Nullable String alphas) {
+        mTag = tag;
+        mType = type;
+
+        // Only allow INT_MAX if unknown string mcc/mnc
+        if (mcc == null || isMcc(mcc)) {
+            mMccStr = mcc;
+        } else if (mcc.isEmpty() || mcc.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mccStr is empty or unknown, set it as null.
+            mMccStr = null;
+        } else {
+            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MCC format
+            // after the bug got fixed.
+            mMccStr = null;
+            log("invalid MCC format: " + mcc);
+        }
+
+        if (mnc == null || isMnc(mnc)) {
+            mMncStr = mnc;
+        } else if (mnc.isEmpty() || mnc.equals(String.valueOf(Integer.MAX_VALUE))) {
+            // If the mncStr is empty or unknown, set it as null.
+            mMncStr = null;
+        } else {
+            // TODO: b/69384059 Should throw IllegalArgumentException for the invalid MNC format
+            // after the bug got fixed.
+            mMncStr = null;
+            log("invalid MNC format: " + mnc);
+        }
+
+        if ((mMccStr != null && mMncStr == null) || (mMccStr == null && mMncStr != null)) {
+            AnomalyReporter.reportAnomaly(
+                    UUID.fromString("e257ae06-ac0a-44c0-ba63-823b9f07b3e4"),
+                    "CellIdentity Missing Half of PLMN ID");
+        }
+
+        mAlphaLong = alphal;
+        mAlphaShort = alphas;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * @hide
+     * @return The type of the cell identity
+     */
+    public @CellInfo.Type int getType() {
+        return mType;
+    }
+
+    /**
+     * @return MCC or null for CDMA
+     * @hide
+     */
+    public String getMccString() {
+        return mMccStr;
+    }
+
+    /**
+     * @return MNC or null for CDMA
+     * @hide
+     */
+    public String getMncString() {
+        return mMncStr;
+    }
+
+    /**
+     * Returns the channel number of the cell identity.
+     *
+     * @hide
+     * @return The channel number, or {@link #INVALID_CHANNEL_NUMBER} if not implemented
+     */
+    public int getChannelNumber() {
+        return INVALID_CHANNEL_NUMBER;
+    }
+
+    /**
+     * @return The long alpha tag associated with the current scan result (may be the operator
+     * name string or extended operator name string). May be null if unknown.
+     */
+    @Nullable
+    public CharSequence getOperatorAlphaLong() {
+        return mAlphaLong;
+    }
+
+    /**
+     * @hide
+     */
+    public void setOperatorAlphaLong(String alphaLong) {
+        mAlphaLong = alphaLong;
+    }
+
+    /**
+     * @return The short alpha tag associated with the current scan result (may be the operator
+     * name string or extended operator name string).  May be null if unknown.
+     */
+    @Nullable
+    public CharSequence getOperatorAlphaShort() {
+        return mAlphaShort;
+    }
+
+    /**
+     * @hide
+     */
+    public void setOperatorAlphaShort(String alphaShort) {
+        mAlphaShort = alphaShort;
+    }
+
+    /**
+     * @return Global Cell ID
+     * @hide
+     */
+    @Nullable
+    public String getGlobalCellId() {
+        return mGlobalCellId;
+    }
+
+    /**
+     * @param ci a CellIdentity to compare to the current CellIdentity.
+     * @return true if ci has the same technology and Global Cell ID; false, otherwise.
+     * @hide
+     */
+    public boolean isSameCell(@Nullable CellIdentity ci) {
+        if (ci == null) return false;
+        if (this.getClass() != ci.getClass()) return false;
+        return TextUtils.equals(this.getGlobalCellId(), ci.getGlobalCellId());
+    }
+
+    /** @hide */
+    public @Nullable String getPlmn() {
+        if (mMccStr == null || mMncStr == null) return null;
+        return mMccStr + mMncStr;
+    }
+
+    /** @hide */
+    protected abstract void updateGlobalCellId();
+
+    /**
+     * @return a CellLocation object for this CellIdentity
+     * @hide
+     */
+    @SystemApi
+    public abstract @NonNull CellLocation asCellLocation();
+
+    /**
+     * Create and a return a new instance of CellIdentity with location-identifying information
+     * removed.
+     *
+     * @hide
+     */
+    @SystemApi
+    public abstract @NonNull CellIdentity sanitizeLocationInfo();
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CellIdentity)) {
+            return false;
+        }
+
+        CellIdentity o = (CellIdentity) other;
+        return mType == o.mType
+                && TextUtils.equals(mMccStr, o.mMccStr)
+                && TextUtils.equals(mMncStr, o.mMncStr)
+                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
+                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAlphaLong, mAlphaShort, mMccStr, mMncStr, mType);
+    }
+
+    /**
+     * Used by child classes for parceling.
+     *
+     * @hide
+     */
+    @CallSuper
+    public void writeToParcel(Parcel dest, int type) {
+        dest.writeInt(type);
+        dest.writeString(mMccStr);
+        dest.writeString(mMncStr);
+        dest.writeString(mAlphaLong);
+        dest.writeString(mAlphaShort);
+    }
+
+    /** Used by phone interface manager to verify if a given string is valid MccMnc
+     * @hide
+     */
+    public static boolean isValidPlmn(@NonNull String plmn) {
+        if (plmn.length() < MCC_LENGTH + MNC_MIN_LENGTH
+                || plmn.length() > MCC_LENGTH + MNC_MAX_LENGTH) {
+            return false;
+        }
+        return (isMcc(plmn.substring(0, MCC_LENGTH)) && isMnc(plmn.substring(MCC_LENGTH)));
+    }
+
+    /**
+     * Construct from Parcel
+     * @hide
+     */
+    protected CellIdentity(String tag, int type, Parcel source) {
+        this(tag, type, source.readString(), source.readString(),
+                source.readString(), source.readString());
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @android.annotation.NonNull Creator<CellIdentity> CREATOR =
+            new Creator<CellIdentity>() {
+                @Override
+                public CellIdentity createFromParcel(Parcel in) {
+                    int type = in.readInt();
+                    switch (type) {
+                        case CellInfo.TYPE_GSM: return CellIdentityGsm.createFromParcelBody(in);
+                        case CellInfo.TYPE_WCDMA: return CellIdentityWcdma.createFromParcelBody(in);
+                        case CellInfo.TYPE_CDMA: return CellIdentityCdma.createFromParcelBody(in);
+                        case CellInfo.TYPE_LTE: return CellIdentityLte.createFromParcelBody(in);
+                        case CellInfo.TYPE_TDSCDMA:
+                            return CellIdentityTdscdma.createFromParcelBody(in);
+                        case CellInfo.TYPE_NR: return CellIdentityNr.createFromParcelBody(in);
+                        default: throw new IllegalArgumentException("Bad Cell identity Parcel");
+                    }
+                }
+
+                @Override
+                public CellIdentity[] newArray(int size) {
+                    return new CellIdentity[size];
+                }
+            };
+
+    /** @hide */
+    protected void log(String s) {
+        Rlog.w(mTag, s);
+    }
+
+    /** @hide */
+    protected static final int inRangeOrUnavailable(int value, int rangeMin, int rangeMax) {
+        if (value < rangeMin || value > rangeMax) return CellInfo.UNAVAILABLE;
+        return value;
+    }
+
+    /** @hide */
+    protected static final long inRangeOrUnavailable(long value, long rangeMin, long rangeMax) {
+        if (value < rangeMin || value > rangeMax) return CellInfo.UNAVAILABLE_LONG;
+        return value;
+    }
+
+    /** @hide */
+    protected static final int inRangeOrUnavailable(
+            int value, int rangeMin, int rangeMax, int special) {
+        if ((value < rangeMin || value > rangeMax) && value != special) return CellInfo.UNAVAILABLE;
+        return value;
+    }
+
+    /** @hide */
+    private static boolean isMcc(@NonNull String mcc) {
+        // ensure no out of bounds indexing
+        if (mcc.length() != MCC_LENGTH) return false;
+
+        // Character.isDigit allows all unicode digits, not just [0-9]
+        for (int i = 0; i < MCC_LENGTH; i++) {
+            if (mcc.charAt(i) < '0' || mcc.charAt(i) > '9') return false;
+        }
+
+        return true;
+    }
+
+    /** @hide */
+    private static boolean isMnc(@NonNull String mnc) {
+        // ensure no out of bounds indexing
+        if (mnc.length() < MNC_MIN_LENGTH || mnc.length() > MNC_MAX_LENGTH) return false;
+
+        // Character.isDigit allows all unicode digits, not just [0-9]
+        for (int i = 0; i < mnc.length(); i++) {
+            if (mnc.charAt(i) < '0' || mnc.charAt(i) > '9') return false;
+        }
+
+        return true;
+    }
+}
diff --git a/android-35/android/telephony/CellIdentityCdma.java b/android-35/android/telephony/CellIdentityCdma.java
new file mode 100644
index 0000000..5eace54
--- /dev/null
+++ b/android-35/android/telephony/CellIdentityCdma.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2012 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;
+
+import static android.text.TextUtils.formatSimple;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.telephony.cdma.CdmaCellLocation;
+
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.telephony.Rlog;
+
+import java.util.Objects;
+
+/**
+ * CellIdentity is to represent a unique CDMA cell
+ */
+public final class CellIdentityCdma extends CellIdentity {
+    private static final String TAG = CellIdentityCdma.class.getSimpleName();
+    private static final boolean DBG = false;
+
+    private static final int NETWORK_ID_MAX = 65535;
+    private static final int SYSTEM_ID_MAX = 32767;
+    private static final int BASESTATION_ID_MAX = 65535;
+
+    private static final int LONGITUDE_MIN = -2592000;
+    private static final int LONGITUDE_MAX = 2592000;
+
+    private static final int LATITUDE_MIN = -1296000;
+    private static final int LATITUDE_MAX = 1296000;
+
+    // Network Id 0..65535
+    private final int mNetworkId;
+
+    // CDMA System Id 0..32767
+    private final int mSystemId;
+
+    // Base Station Id 0..65535
+    private final int mBasestationId;
+
+    /**
+     * Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+     * It is represented in units of 0.25 seconds and ranges from -2592000
+     * to 2592000, both values inclusive (corresponding to a range of -180
+     * to +180 degrees).
+     */
+    private final int mLongitude;
+
+    /**
+     * Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+     * It is represented in units of 0.25 seconds and ranges from -1296000
+     * to 1296000, both values inclusive (corresponding to a range of -90
+     * to +90 degrees).
+     */
+    private final int mLatitude;
+
+    /**
+     * @hide
+     */
+    public CellIdentityCdma() {
+        super(TAG, CellInfo.TYPE_CDMA, null, null, null, null);
+        mNetworkId = CellInfo.UNAVAILABLE;
+        mSystemId = CellInfo.UNAVAILABLE;
+        mBasestationId = CellInfo.UNAVAILABLE;
+        mLongitude = CellInfo.UNAVAILABLE;
+        mLatitude = CellInfo.UNAVAILABLE;
+        mGlobalCellId = null;
+    }
+
+    /**
+     * public constructor
+     * @param nid Network Id 0..65535
+     * @param sid CDMA System Id 0..32767
+     * @param bid Base Station Id 0..65535
+     * @param lon Longitude is a decimal number ranges from -2592000
+     *        to 2592000
+     * @param lat Latitude is a decimal number ranges from -1296000
+     *        to 1296000
+     * @param alphal long alpha Operator Name String or Enhanced Operator Name String
+     * @param alphas short alpha Operator Name String or Enhanced Operator Name String
+     *
+     * @hide
+     */
+    public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat,
+            @Nullable String alphal, @Nullable String alphas) {
+        super(TAG, CellInfo.TYPE_CDMA, null, null, alphal, alphas);
+        mNetworkId = inRangeOrUnavailable(nid, 0, NETWORK_ID_MAX);
+        mSystemId = inRangeOrUnavailable(sid, 0, SYSTEM_ID_MAX);
+        mBasestationId = inRangeOrUnavailable(bid, 0, BASESTATION_ID_MAX);
+        lat = inRangeOrUnavailable(lat, LATITUDE_MIN, LATITUDE_MAX);
+        lon = inRangeOrUnavailable(lon, LONGITUDE_MIN, LONGITUDE_MAX);
+
+        if (!isNullIsland(lat, lon)) {
+            mLongitude = lon;
+            mLatitude = lat;
+        } else {
+            mLongitude = mLatitude = CellInfo.UNAVAILABLE;
+        }
+        updateGlobalCellId();
+    }
+
+    private CellIdentityCdma(@NonNull CellIdentityCdma cid) {
+        this(cid.mNetworkId, cid.mSystemId, cid.mBasestationId, cid.mLongitude, cid.mLatitude,
+                cid.mAlphaLong, cid.mAlphaShort);
+    }
+
+    @NonNull CellIdentityCdma copy() {
+        return new CellIdentityCdma(this);
+    }
+
+    /** @hide */
+    @Override
+    public @NonNull CellIdentityCdma sanitizeLocationInfo() {
+        return new CellIdentityCdma(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
+                CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
+                mAlphaLong, mAlphaShort);
+    }
+
+    /** @hide */
+    @Override
+    protected void updateGlobalCellId() {
+        mGlobalCellId = null;
+        if (mNetworkId == CellInfo.UNAVAILABLE || mSystemId == CellInfo.UNAVAILABLE
+                || mBasestationId == CellInfo.UNAVAILABLE) return;
+
+        mGlobalCellId = formatSimple("%04x%04x%04x", mSystemId, mNetworkId,  mBasestationId);
+    }
+
+    /**
+     * Take the latitude and longitude in 1/4 seconds and see if
+     * the reported location is on Null Island.
+     *
+     * @return whether the reported Lat/Long are for Null Island
+     *
+     * @hide
+     */
+    private boolean isNullIsland(int lat, int lon) {
+        return Math.abs(lat) <= 1 && Math.abs(lon) <= 1;
+    }
+
+    /**
+     * @return Network Id 0..65535, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+     *         if unavailable.
+     */
+    public int getNetworkId() {
+        return mNetworkId;
+    }
+
+    /**
+     * @return System Id 0..32767, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+     *         if unavailable.
+     */
+    public int getSystemId() {
+        return mSystemId;
+    }
+
+    /**
+     * @return Base Station Id 0..65535, {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+     *         if unavailable.
+     */
+    public int getBasestationId() {
+        return mBasestationId;
+    }
+
+    /**
+     * @return Base station longitude, which is a decimal number as
+     * specified in 3GPP2 C.S0005-A v6.0. It is represented in units
+     * of 0.25 seconds and ranges from -2592000 to 2592000, both
+     * values inclusive (corresponding to a range of -180
+     * to +180 degrees). {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getLongitude() {
+        return mLongitude;
+    }
+
+    /**
+     * @return Base station latitude, which is a decimal number as
+     * specified in 3GPP2 C.S0005-A v6.0. It is represented in units
+     * of 0.25 seconds and ranges from -1296000 to 1296000, both
+     * values inclusive (corresponding to a range of -90
+     * to +90 degrees). {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getLatitude() {
+        return mLatitude;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mNetworkId, mSystemId, mBasestationId, mLatitude, mLongitude,
+                super.hashCode());
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public CdmaCellLocation asCellLocation() {
+        CdmaCellLocation cl = new CdmaCellLocation();
+        int bsid = mBasestationId != CellInfo.UNAVAILABLE ? mBasestationId : -1;
+        int sid = mSystemId != CellInfo.UNAVAILABLE ? mSystemId : -1;
+        int nid = mNetworkId != CellInfo.UNAVAILABLE ? mNetworkId : -1;
+        // lat and long already use CellInfo.UNAVAILABLE for invalid/unknown
+        cl.setCellLocationData(bsid, mLatitude, mLongitude, sid, nid);
+        return cl;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (!(other instanceof CellIdentityCdma)) {
+            return false;
+        }
+
+        CellIdentityCdma o = (CellIdentityCdma) other;
+
+        return mNetworkId == o.mNetworkId
+                && mSystemId == o.mSystemId
+                && mBasestationId == o.mBasestationId
+                && mLatitude == o.mLatitude
+                && mLongitude == o.mLongitude
+                && super.equals(other);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder(TAG)
+        .append(":{ mNetworkId=").append(mNetworkId)
+        .append(" mSystemId=").append(mSystemId)
+        .append(" mBasestationId=").append(mBasestationId)
+        .append(" mLongitude=").append(Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mLongitude))
+        .append(" mLatitude=").append(Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mLatitude))
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
+        .append("}").toString();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        super.writeToParcel(dest, CellInfo.TYPE_CDMA);
+        dest.writeInt(mNetworkId);
+        dest.writeInt(mSystemId);
+        dest.writeInt(mBasestationId);
+        dest.writeInt(mLongitude);
+        dest.writeInt(mLatitude);
+    }
+
+    /** Construct from Parcel, type has already been processed */
+    private CellIdentityCdma(Parcel in) {
+        super(TAG, CellInfo.TYPE_CDMA, in);
+        mNetworkId = in.readInt();
+        mSystemId = in.readInt();
+        mBasestationId = in.readInt();
+        mLongitude = in.readInt();
+        mLatitude = in.readInt();
+
+        updateGlobalCellId();
+        if (DBG) log(toString());
+    }
+
+    /** Implement the Parcelable interface */
+    @SuppressWarnings("hiding")
+    public static final @android.annotation.NonNull Creator<CellIdentityCdma> CREATOR =
+            new Creator<CellIdentityCdma>() {
+        @Override
+        public CellIdentityCdma createFromParcel(Parcel in) {
+            in.readInt();   // skip
+            return createFromParcelBody(in);
+        }
+
+        @Override
+        public CellIdentityCdma[] newArray(int size) {
+            return new CellIdentityCdma[size];
+        }
+    };
+
+    /** @hide */
+    protected static CellIdentityCdma createFromParcelBody(Parcel in) {
+        return new CellIdentityCdma(in);
+    }
+}
diff --git a/android-35/android/telephony/CellIdentityGsm.java b/android-35/android/telephony/CellIdentityGsm.java
new file mode 100644
index 0000000..2516a79
--- /dev/null
+++ b/android-35/android/telephony/CellIdentityGsm.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2012 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;
+
+import static android.text.TextUtils.formatSimple;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.telephony.gsm.GsmCellLocation;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * CellIdentity to represent a unique GSM cell
+ */
+public final class CellIdentityGsm extends CellIdentity {
+    private static final String TAG = CellIdentityGsm.class.getSimpleName();
+    private static final boolean DBG = false;
+
+    private static final int MAX_LAC = 65535;
+    private static final int MAX_CID = 65535;
+    private static final int MAX_ARFCN = 65535;
+    private static final int MAX_BSIC = 63;
+
+    // 16-bit Location Area Code, 0..65535
+    private final int mLac;
+    // 16-bit GSM Cell Identity described in TS 27.007, 0..65535
+    private final int mCid;
+    // 16-bit GSM Absolute RF Channel Number
+    private final int mArfcn;
+    // 6-bit Base Station Identity Code
+    private final int mBsic;
+
+    // a list of additional PLMN-IDs reported for this cell
+    private final ArraySet<String> mAdditionalPlmns;
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public CellIdentityGsm() {
+        super(TAG, CellInfo.TYPE_GSM, null, null, null, null);
+        mLac = CellInfo.UNAVAILABLE;
+        mCid = CellInfo.UNAVAILABLE;
+        mArfcn = CellInfo.UNAVAILABLE;
+        mBsic = CellInfo.UNAVAILABLE;
+        mAdditionalPlmns = new ArraySet<>();
+        mGlobalCellId = null;
+    }
+
+    /**
+     * public constructor
+     * @param lac 16-bit Location Area Code, 0..65535
+     * @param cid 16-bit GSM Cell Identity or 28-bit UMTS Cell Identity
+     * @param arfcn 16-bit GSM Absolute RF Channel Number
+     * @param bsic 6-bit Base Station Identity Code
+     * @param mccStr 3-digit Mobile Country Code in string format
+     * @param mncStr 2 or 3-digit Mobile Network Code in string format
+     * @param alphal long alpha Operator Name String or Enhanced Operator Name String
+     * @param alphas short alpha Operator Name String or Enhanced Operator Name String
+     * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell
+     *
+     * @hide
+     */
+    public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, @Nullable String mccStr,
+            @Nullable String mncStr, @Nullable String alphal, @Nullable String alphas,
+            @NonNull Collection<String> additionalPlmns) {
+        super(TAG, CellInfo.TYPE_GSM, mccStr, mncStr, alphal, alphas);
+        mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
+        mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
+        mArfcn = inRangeOrUnavailable(arfcn, 0, MAX_ARFCN);
+        mBsic = inRangeOrUnavailable(bsic, 0, MAX_BSIC);
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
+        for (String plmn : additionalPlmns) {
+            if (isValidPlmn(plmn)) {
+                mAdditionalPlmns.add(plmn);
+            }
+        }
+        updateGlobalCellId();
+    }
+
+    private CellIdentityGsm(@NonNull CellIdentityGsm cid) {
+        this(cid.mLac, cid.mCid, cid.mArfcn, cid.mBsic, cid.mMccStr,
+                cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns);
+    }
+
+    @NonNull CellIdentityGsm copy() {
+        return new CellIdentityGsm(this);
+    }
+
+    /** @hide */
+    @Override
+    public @NonNull CellIdentityGsm sanitizeLocationInfo() {
+        return new CellIdentityGsm(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
+                CellInfo.UNAVAILABLE, mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns);
+    }
+
+    /** @hide */
+    @Override
+    protected void updateGlobalCellId() {
+        mGlobalCellId = null;
+        String plmn = getPlmn();
+        if (plmn == null) return;
+
+        if (mLac == CellInfo.UNAVAILABLE || mCid == CellInfo.UNAVAILABLE) return;
+
+        mGlobalCellId = plmn + formatSimple("%04x%04x", mLac, mCid);
+    }
+
+    /**
+     * @return 3-digit Mobile Country Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     * @deprecated Use {@link #getMccString} instead.
+     */
+    @Deprecated
+    public int getMcc() {
+        return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
+    }
+
+    /**
+     * @return 2 or 3-digit Mobile Network Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     * @deprecated Use {@link #getMncString} instead.
+     */
+    @Deprecated
+    public int getMnc() {
+        return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
+    }
+
+    /**
+     * @return 16-bit Location Area Code, 0..65535,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getLac() {
+        return mLac;
+    }
+
+    /**
+     * @return 16-bit GSM Cell Identity described in TS 27.007, 0..65535,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getCid() {
+        return mCid;
+    }
+
+    /**
+     * @return 16-bit GSM Absolute RF Channel Number,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getArfcn() {
+        return mArfcn;
+    }
+
+    /**
+     * @return 6-bit Base Station Identity Code,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getBsic() {
+        return mBsic;
+    }
+
+    /**
+     * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
+     */
+    @Nullable
+    public String getMobileNetworkOperator() {
+        return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
+    }
+
+    /**
+     * @return Mobile Country Code in string format, null if unavailable.
+     */
+    @Nullable
+    public String getMccString() {
+        return mMccStr;
+    }
+
+    /**
+     * @return Mobile Network Code in string format, null if unavailable.
+     */
+    @Nullable
+    public String getMncString() {
+        return mMncStr;
+    }
+
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mArfcn;
+    }
+
+    /**
+     * @return a list of additional PLMN IDs supported by this cell.
+     */
+    @NonNull
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
+    }
+
+    /**
+     * @deprecated Primary Scrambling Code is not applicable to GSM.
+     * @return {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} - undefined for GSM
+     */
+    @Deprecated
+    public int getPsc() {
+        return CellInfo.UNAVAILABLE;
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public GsmCellLocation asCellLocation() {
+        GsmCellLocation cl = new GsmCellLocation();
+        int lac = mLac != CellInfo.UNAVAILABLE ? mLac : -1;
+        int cid = mCid != CellInfo.UNAVAILABLE ? mCid : -1;
+        cl.setLacAndCid(lac, cid);
+        cl.setPsc(-1);
+        return cl;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLac, mCid, mAdditionalPlmns.hashCode(), super.hashCode());
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (!(other instanceof CellIdentityGsm)) {
+            return false;
+        }
+
+        CellIdentityGsm o = (CellIdentityGsm) other;
+        return mLac == o.mLac
+                && mCid == o.mCid
+                && mArfcn == o.mArfcn
+                && mBsic == o.mBsic
+                && TextUtils.equals(mMccStr, o.mMccStr)
+                && TextUtils.equals(mMncStr, o.mMncStr)
+                && mAdditionalPlmns.equals(o.mAdditionalPlmns)
+                && super.equals(other);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder(TAG)
+        .append(":{ mLac=").append(mLac)
+        .append(" mCid=").append(mCid)
+        .append(" mArfcn=").append(mArfcn)
+        .append(" mBsic=").append("0x").append(Integer.toHexString(mBsic))
+        .append(" mMcc=").append(mMccStr)
+        .append(" mMnc=").append(mMncStr)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
+        .append(" mAdditionalPlmns=").append(mAdditionalPlmns)
+        .append("}").toString();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        super.writeToParcel(dest, CellInfo.TYPE_GSM);
+        dest.writeInt(mLac);
+        dest.writeInt(mCid);
+        dest.writeInt(mArfcn);
+        dest.writeInt(mBsic);
+        dest.writeArraySet(mAdditionalPlmns);
+    }
+
+    /** Construct from Parcel, type has already been processed */
+    private CellIdentityGsm(Parcel in) {
+        super(TAG, CellInfo.TYPE_GSM, in);
+        mLac = in.readInt();
+        mCid = in.readInt();
+        mArfcn = in.readInt();
+        mBsic = in.readInt();
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
+
+        updateGlobalCellId();
+        if (DBG) log(toString());
+    }
+
+    /** Implement the Parcelable interface */
+    @SuppressWarnings("hiding")
+    public static final @android.annotation.NonNull Creator<CellIdentityGsm> CREATOR =
+            new Creator<CellIdentityGsm>() {
+                @Override
+                public CellIdentityGsm createFromParcel(Parcel in) {
+                    in.readInt();   // skip
+                    return createFromParcelBody(in);
+                }
+
+                @Override
+                public CellIdentityGsm[] newArray(int size) {
+                    return new CellIdentityGsm[size];
+                }
+            };
+
+    /** @hide */
+    protected static CellIdentityGsm createFromParcelBody(Parcel in) {
+        return new CellIdentityGsm(in);
+    }
+}
diff --git a/android-35/android/telephony/CellIdentityLte.java b/android-35/android/telephony/CellIdentityLte.java
new file mode 100644
index 0000000..b4b8aee
--- /dev/null
+++ b/android-35/android/telephony/CellIdentityLte.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2012 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;
+
+import static android.text.TextUtils.formatSimple;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.telephony.gsm.GsmCellLocation;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * CellIdentity is to represent a unique LTE cell
+ */
+public final class CellIdentityLte extends CellIdentity {
+    private static final String TAG = CellIdentityLte.class.getSimpleName();
+    private static final boolean DBG = false;
+
+    private static final int MAX_CI = 268435455;
+    private static final int MAX_PCI = 503;
+    private static final int MAX_TAC = 65535;
+    private static final int MAX_EARFCN = 262143;
+    private static final int MAX_BANDWIDTH = 20000;
+
+    // 28-bit cell identity
+    private final int mCi;
+    // physical cell id 0..503
+    private final int mPci;
+    // 16-bit tracking area code
+    private final int mTac;
+    // 18-bit Absolute RF Channel Number
+    private final int mEarfcn;
+    // cell bandwidth, in kHz
+    private final int mBandwidth;
+    // cell bands
+    private final int[] mBands;
+
+    // a list of additional PLMN-IDs reported for this cell
+    private final ArraySet<String> mAdditionalPlmns;
+
+    private ClosedSubscriberGroupInfo mCsgInfo;
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public CellIdentityLte() {
+        super(TAG, CellInfo.TYPE_LTE, null, null, null, null);
+        mCi = CellInfo.UNAVAILABLE;
+        mPci = CellInfo.UNAVAILABLE;
+        mTac = CellInfo.UNAVAILABLE;
+        mEarfcn = CellInfo.UNAVAILABLE;
+        mBands = new int[] {};
+        mBandwidth = CellInfo.UNAVAILABLE;
+        mAdditionalPlmns = new ArraySet<>();
+        mCsgInfo = null;
+        mGlobalCellId = null;
+    }
+
+    /**
+     *
+     * @param mcc 3-digit Mobile Country Code, 0..999
+     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+     * @param ci 28-bit Cell Identity
+     * @param pci Physical Cell Id 0..503
+     * @param tac 16-bit Tracking Area Code
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
+        this(ci, pci, tac, CellInfo.UNAVAILABLE, new int[] {}, CellInfo.UNAVAILABLE,
+                String.valueOf(mcc), String.valueOf(mnc), null, null, new ArraySet<>(),
+                null);
+    }
+
+    /**
+     *
+     * @param ci 28-bit Cell Identity
+     * @param pci Physical Cell Id 0..503
+     * @param tac 16-bit Tracking Area Code
+     * @param earfcn 18-bit LTE Absolute RF Channel Number
+     * @param bandwidth cell bandwidth in kHz
+     * @param mccStr 3-digit Mobile Country Code in string format
+     * @param mncStr 2 or 3-digit Mobile Network Code in string format
+     * @param alphal long alpha Operator Name String or Enhanced Operator Name String
+     * @param alphas short alpha Operator Name String or Enhanced Operator Name String
+     * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell
+     * @param csgInfo info about the closed subscriber group broadcast by the cell
+     *
+     * @hide
+     */
+    public CellIdentityLte(int ci, int pci, int tac, int earfcn, @NonNull int[] bands,
+            int bandwidth, @Nullable String mccStr, @Nullable String mncStr,
+            @Nullable String alphal, @Nullable String alphas,
+            @NonNull Collection<String> additionalPlmns,
+            @Nullable ClosedSubscriberGroupInfo csgInfo) {
+        super(TAG, CellInfo.TYPE_LTE, mccStr, mncStr, alphal, alphas);
+        mCi = inRangeOrUnavailable(ci, 0, MAX_CI);
+        mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
+        mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
+        mEarfcn = inRangeOrUnavailable(earfcn, 0, MAX_EARFCN);
+        mBands = bands;
+        mBandwidth = inRangeOrUnavailable(bandwidth, 0, MAX_BANDWIDTH);
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
+        for (String plmn : additionalPlmns) {
+            if (isValidPlmn(plmn)) {
+                mAdditionalPlmns.add(plmn);
+            }
+        }
+        mCsgInfo = csgInfo;
+        updateGlobalCellId();
+    }
+
+    private CellIdentityLte(@NonNull CellIdentityLte cid) {
+        this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBands, cid.mBandwidth, cid.mMccStr,
+                cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo);
+    }
+
+    /** @hide */
+    @Override
+    public @NonNull CellIdentityLte sanitizeLocationInfo() {
+        return new CellIdentityLte(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
+                CellInfo.UNAVAILABLE, mBands, CellInfo.UNAVAILABLE,
+                mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns, null);
+    }
+
+    @NonNull CellIdentityLte copy() {
+        return new CellIdentityLte(this);
+    }
+
+    /** @hide */
+    @Override
+    protected void updateGlobalCellId() {
+        mGlobalCellId = null;
+        String plmn = getPlmn();
+        if (plmn == null) return;
+
+        if (mCi == CellInfo.UNAVAILABLE) return;
+
+        mGlobalCellId = plmn + formatSimple("%07x", mCi);
+    }
+
+    /**
+     * @return 3-digit Mobile Country Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     * @deprecated Use {@link #getMccString} instead.
+     */
+    @Deprecated
+    public int getMcc() {
+        return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
+    }
+
+    /**
+     * @return 2 or 3-digit Mobile Network Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     * @deprecated Use {@link #getMncString} instead.
+     */
+    @Deprecated
+    public int getMnc() {
+        return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
+    }
+
+    /**
+     * @return 28-bit Cell Identity,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getCi() {
+        return mCi;
+    }
+
+    /**
+     * @return Physical Cell Id 0..503,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getPci() {
+        return mPci;
+    }
+
+    /**
+     * @return 16-bit Tracking Area Code,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getTac() {
+        return mTac;
+    }
+
+    /**
+     * @return 18-bit Absolute RF Channel Number,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getEarfcn() {
+        return mEarfcn;
+    }
+
+    /**
+     * Get bands of the cell
+     *
+     * Reference: 3GPP TS 36.101 section 5.5
+     *
+     * @return Array of band number or empty array if not available.
+     */
+    @NonNull
+    public int[] getBands() {
+        return Arrays.copyOf(mBands, mBands.length);
+    }
+
+    /**
+     * @return Cell bandwidth in kHz,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getBandwidth() {
+        return mBandwidth;
+    }
+
+    /**
+     * @return Mobile Country Code in string format, null if unavailable.
+     */
+    @Nullable
+    public String getMccString() {
+        return mMccStr;
+    }
+
+    /**
+     * @return Mobile Network Code in string format, null if unavailable.
+     */
+    @Nullable
+    public String getMncString() {
+        return mMncStr;
+    }
+
+    /**
+     * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown.
+     */
+    @Nullable
+    public String getMobileNetworkOperator() {
+        return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
+    }
+
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mEarfcn;
+    }
+
+    /**
+     * @return a list of additional PLMN IDs supported by this cell.
+     */
+    @NonNull
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
+    }
+
+    /**
+     * @return closed subscriber group information about the cell if available, otherwise null.
+     */
+    @Nullable
+    public ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo() {
+        return mCsgInfo;
+    }
+
+    /**
+     * A hack to allow tunneling of LTE information via GsmCellLocation
+     * so that older Network Location Providers can return some information
+     * on LTE only networks, see bug 9228974.
+     *
+     * The tunnel'd LTE information is returned as follows:
+     *   LAC = TAC field
+     *   CID = CI field
+     *   PSC = 0.
+     *
+     * @hide
+     */
+    @NonNull
+    @Override
+    public GsmCellLocation asCellLocation() {
+        GsmCellLocation cl = new GsmCellLocation();
+        int tac = mTac != CellInfo.UNAVAILABLE ? mTac : -1;
+        int cid = mCi != CellInfo.UNAVAILABLE ? mCi : -1;
+        cl.setLacAndCid(tac, cid);
+        cl.setPsc(0);
+        return cl;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCi, mPci, mTac, mEarfcn, Arrays.hashCode(mBands),
+                mBandwidth, mAdditionalPlmns.hashCode(), mCsgInfo, super.hashCode());
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (!(other instanceof CellIdentityLte)) {
+            return false;
+        }
+
+        CellIdentityLte o = (CellIdentityLte) other;
+        return mCi == o.mCi
+                && mPci == o.mPci
+                && mTac == o.mTac
+                && mEarfcn == o.mEarfcn
+                && Arrays.equals(mBands, o.mBands)
+                && mBandwidth == o.mBandwidth
+                && TextUtils.equals(mMccStr, o.mMccStr)
+                && TextUtils.equals(mMncStr, o.mMncStr)
+                && mAdditionalPlmns.equals(o.mAdditionalPlmns)
+                && Objects.equals(mCsgInfo, o.mCsgInfo)
+                && super.equals(other);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder(TAG)
+        .append(":{ mCi=").append(mCi)
+        .append(" mPci=").append(mPci)
+        .append(" mTac=").append(mTac)
+        .append(" mEarfcn=").append(mEarfcn)
+        .append(" mBands=").append(Arrays.toString(mBands))
+        .append(" mBandwidth=").append(mBandwidth)
+        .append(" mMcc=").append(mMccStr)
+        .append(" mMnc=").append(mMncStr)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
+        .append(" mAdditionalPlmns=").append(mAdditionalPlmns)
+        .append(" mCsgInfo=").append(mCsgInfo)
+        .append("}").toString();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        super.writeToParcel(dest, CellInfo.TYPE_LTE);
+        dest.writeInt(mCi);
+        dest.writeInt(mPci);
+        dest.writeInt(mTac);
+        dest.writeInt(mEarfcn);
+        dest.writeIntArray(mBands);
+        dest.writeInt(mBandwidth);
+        dest.writeArraySet(mAdditionalPlmns);
+        dest.writeParcelable(mCsgInfo, flags);
+    }
+
+    /** Construct from Parcel, type has already been processed */
+    private CellIdentityLte(Parcel in) {
+        super(TAG, CellInfo.TYPE_LTE, in);
+        mCi = in.readInt();
+        mPci = in.readInt();
+        mTac = in.readInt();
+        mEarfcn = in.readInt();
+        mBands = in.createIntArray();
+        mBandwidth = in.readInt();
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
+        mCsgInfo = in.readParcelable(null, android.telephony.ClosedSubscriberGroupInfo.class);
+
+        updateGlobalCellId();
+        if (DBG) log(toString());
+    }
+
+    /** Implement the Parcelable interface */
+    @SuppressWarnings("hiding")
+    public static final @android.annotation.NonNull Creator<CellIdentityLte> CREATOR =
+            new Creator<CellIdentityLte>() {
+                @Override
+                public CellIdentityLte createFromParcel(Parcel in) {
+                    in.readInt();   // skip;
+                    return createFromParcelBody(in);
+                }
+
+                @Override
+                public CellIdentityLte[] newArray(int size) {
+                    return new CellIdentityLte[size];
+                }
+            };
+
+    /** @hide */
+    protected static CellIdentityLte createFromParcelBody(Parcel in) {
+        return new CellIdentityLte(in);
+    }
+}
diff --git a/android-35/android/telephony/CellIdentityNr.java b/android-35/android/telephony/CellIdentityNr.java
new file mode 100644
index 0000000..6aeb482
--- /dev/null
+++ b/android-35/android/telephony/CellIdentityNr.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2018 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;
+
+import static android.text.TextUtils.formatSimple;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.telephony.AccessNetworkConstants.NgranBands.NgranBand;
+import android.telephony.gsm.GsmCellLocation;
+import android.util.ArraySet;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Information to represent a unique NR(New Radio 5G) cell.
+ */
+public final class CellIdentityNr extends CellIdentity {
+    private static final String TAG = "CellIdentityNr";
+
+    private static final int MAX_PCI = 1007;
+    private static final int MAX_TAC = 16777215; // 0xffffff
+    private static final int MAX_NRARFCN = 3279165;
+    private static final long MAX_NCI = 68719476735L;
+
+    private final int mNrArfcn;
+    private final int mPci;
+    private final int mTac;
+    private final long mNci;
+    private final int[] mBands;
+
+    // a list of additional PLMN-IDs reported for this cell
+    private final ArraySet<String> mAdditionalPlmns;
+
+    /** @hide */
+    public CellIdentityNr() {
+        super(TAG, CellInfo.TYPE_NR, null, null, null, null);
+        mNrArfcn = CellInfo.UNAVAILABLE;
+        mPci = CellInfo.UNAVAILABLE;
+        mTac = CellInfo.UNAVAILABLE;
+        mNci = CellInfo.UNAVAILABLE;
+        mBands = new int[] {};
+        mAdditionalPlmns = new ArraySet();
+        mGlobalCellId = null;
+    }
+
+    /**
+     * @param pci Physical Cell Id in range [0, 1007].
+     * @param tac 24-bit Tracking Area Code.
+     * @param nrArfcn NR Absolute Radio Frequency Channel Number, in range [0, 3279165].
+     * @param bands Bands used by the cell. Band number defined in 3GPP TS 38.101-1 and TS 38.101-2.
+     * @param mccStr 3-digit Mobile Country Code in string format.
+     * @param mncStr 2 or 3-digit Mobile Network Code in string format.
+     * @param nci The 36-bit NR Cell Identity in range [0, 68719476735].
+     * @param alphal long alpha Operator Name String or Enhanced Operator Name String.
+     * @param alphas short alpha Operator Name String or Enhanced Operator Name String.
+     * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell
+     *
+     * @hide
+     */
+    public CellIdentityNr(int pci, int tac, int nrArfcn, @NonNull @NgranBand int[] bands,
+                          @Nullable String mccStr, @Nullable String mncStr, long nci,
+                          @Nullable String alphal, @Nullable String alphas,
+                          @NonNull Collection<String> additionalPlmns) {
+        super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas);
+        mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
+        mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
+        mNrArfcn = inRangeOrUnavailable(nrArfcn, 0, MAX_NRARFCN);
+        // TODO: input validation for bands
+        mBands = bands;
+        mNci = inRangeOrUnavailable(nci, 0, MAX_NCI);
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
+        for (String plmn : additionalPlmns) {
+            if (isValidPlmn(plmn)) {
+                mAdditionalPlmns.add(plmn);
+            }
+        }
+        updateGlobalCellId();
+    }
+
+    /** @hide */
+    @Override
+    public @NonNull CellIdentityNr sanitizeLocationInfo() {
+        return new CellIdentityNr(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mNrArfcn,
+                mBands, mMccStr, mMncStr, CellInfo.UNAVAILABLE_LONG, mAlphaLong, mAlphaShort,
+                mAdditionalPlmns);
+    }
+
+    /** @hide */
+    protected void updateGlobalCellId() {
+        mGlobalCellId = null;
+        String plmn = getPlmn();
+        if (plmn == null) return;
+
+        if (mNci == CellInfo.UNAVAILABLE_LONG) return;
+
+        mGlobalCellId = plmn + formatSimple("%09x", mNci);
+    }
+
+    /**
+     * @return a CellLocation object for this CellIdentity.
+     * @hide
+     */
+    @NonNull
+    @Override
+    public CellLocation asCellLocation() {
+        GsmCellLocation cl = new GsmCellLocation();
+        int tac = mTac != CellInfo.UNAVAILABLE ? mTac : -1;
+        cl.setLacAndCid(tac, -1);
+        cl.setPsc(0);
+        return cl;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mPci, mTac,
+                mNrArfcn, Arrays.hashCode(mBands), mNci, mAdditionalPlmns.hashCode());
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (!(other instanceof CellIdentityNr)) {
+            return false;
+        }
+
+        CellIdentityNr o = (CellIdentityNr) other;
+        return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn
+                && Arrays.equals(mBands, o.mBands) && mNci == o.mNci
+                && mAdditionalPlmns.equals(o.mAdditionalPlmns);
+    }
+
+    /**
+     * Get the NR(New Radio 5G) Cell Identity.
+     *
+     * @return The 36-bit NR Cell Identity in range [0, 68719476735] or
+     *         {@link CellInfo#UNAVAILABLE_LONG} if unknown.
+     */
+    public long getNci() {
+        return mNci;
+    }
+
+    /**
+     * Get the New Radio Absolute Radio Frequency Channel Number.
+     *
+     * Reference: 3GPP TS 38.101-1 section 5.4.2.1 NR-ARFCN and channel raster.
+     * Reference: 3GPP TS 38.101-2 section 5.4.2.1 NR-ARFCN and channel raster.
+     *
+     * @return Integer value in range [0, 3279165] or {@link CellInfo#UNAVAILABLE} if unknown.
+     */
+    @IntRange(from = 0, to = 3279165)
+    public int getNrarfcn() {
+        return mNrArfcn;
+    }
+
+    /**
+     * Get bands of the cell
+     *
+     * Reference: TS 38.101-1 table 5.2-1
+     * Reference: TS 38.101-2 table 5.2-1
+     *
+     * @return Array of band number or empty array if not available.
+     */
+    @NgranBand
+    @NonNull
+    public int[] getBands() {
+        return Arrays.copyOf(mBands, mBands.length);
+    }
+
+    /**
+     * Get the physical cell id.
+     * @return Integer value in range [0, 1007] or {@link CellInfo#UNAVAILABLE} if unknown.
+     */
+    @IntRange(from = 0, to = 1007)
+    public int getPci() {
+        return mPci;
+    }
+
+    /**
+     * Get the tracking area code.
+     * @return a 24 bit integer or {@link CellInfo#UNAVAILABLE} if unknown.
+     */
+    @IntRange(from = 0, to = 16777215)
+    public int getTac() {
+        return mTac;
+    }
+
+    /**
+     * @return Mobile Country Code in string format, or {@code null} if unknown.
+     */
+    @Nullable
+    public String getMccString() {
+        return mMccStr;
+    }
+
+    /**
+     * @return Mobile Network Code in string format, or {@code null} if unknown.
+     */
+    @Nullable
+    public String getMncString() {
+        return mMncStr;
+    }
+
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mNrArfcn;
+    }
+
+    /**
+     * @return a list of additional PLMN IDs supported by this cell.
+     */
+    @NonNull
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder(TAG + ":{")
+                .append(" mPci = ").append(mPci)
+                .append(" mTac = ").append(mTac)
+                .append(" mNrArfcn = ").append(mNrArfcn)
+                .append(" mBands = ").append(Arrays.toString(mBands))
+                .append(" mMcc = ").append(mMccStr)
+                .append(" mMnc = ").append(mMncStr)
+                .append(" mNci = ").append(mNci)
+                .append(" mAlphaLong = ").append(mAlphaLong)
+                .append(" mAlphaShort = ").append(mAlphaShort)
+                .append(" mAdditionalPlmns = ").append(mAdditionalPlmns)
+                .append(" }")
+                .toString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int type) {
+        super.writeToParcel(dest, CellInfo.TYPE_NR);
+        dest.writeInt(mPci);
+        dest.writeInt(mTac);
+        dest.writeInt(mNrArfcn);
+        dest.writeIntArray(mBands);
+        dest.writeLong(mNci);
+        dest.writeArraySet(mAdditionalPlmns);
+    }
+
+    /** Construct from Parcel, type has already been processed */
+    private CellIdentityNr(Parcel in) {
+        super(TAG, CellInfo.TYPE_NR, in);
+        mPci = in.readInt();
+        mTac = in.readInt();
+        mNrArfcn = in.readInt();
+        mBands = in.createIntArray();
+        mNci = in.readLong();
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
+
+        updateGlobalCellId();
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @android.annotation.NonNull Creator<CellIdentityNr> CREATOR =
+            new Creator<CellIdentityNr>() {
+                @Override
+                public CellIdentityNr createFromParcel(Parcel in) {
+                    // Skip the type info.
+                    in.readInt();
+                    return createFromParcelBody(in);
+                }
+
+                @Override
+                public CellIdentityNr[] newArray(int size) {
+                    return new CellIdentityNr[size];
+                }
+            };
+
+    /** @hide */
+    protected static CellIdentityNr createFromParcelBody(Parcel in) {
+        return new CellIdentityNr(in);
+    }
+}
diff --git a/android-35/android/telephony/CellIdentityTdscdma.java b/android-35/android/telephony/CellIdentityTdscdma.java
new file mode 100644
index 0000000..90e6295
--- /dev/null
+++ b/android-35/android/telephony/CellIdentityTdscdma.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2017 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;
+
+import static android.text.TextUtils.formatSimple;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.telephony.gsm.GsmCellLocation;
+import android.util.ArraySet;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * CellIdentity is to represent a unique TD-SCDMA cell
+ */
+public final class CellIdentityTdscdma extends CellIdentity {
+    private static final String TAG = CellIdentityTdscdma.class.getSimpleName();
+    private static final boolean DBG = false;
+
+    private static final int MAX_LAC = 65535;
+    private static final int MAX_CID = 268435455;
+    private static final int MAX_CPID = 127;
+    private static final int MAX_UARFCN = 65535;
+
+    // 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown.
+    private final int mLac;
+    // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, CellInfo.UNAVAILABLE
+    // if unknown.
+    private final int mCid;
+    // 8-bit Cell Parameters ID described in TS 25.331 sec 10.3.6.9,
+    // 0..127, CellInfo.UNAVAILABLE if unknown.
+    private final int mCpid;
+    // 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
+    private final int mUarfcn;
+
+    // a list of additional PLMN-IDs reported for this cell
+    private final ArraySet<String> mAdditionalPlmns;
+
+    private ClosedSubscriberGroupInfo mCsgInfo;
+
+    /**
+     * @hide
+     */
+    public CellIdentityTdscdma() {
+        super(TAG, CellInfo.TYPE_TDSCDMA, null, null, null, null);
+        mLac = CellInfo.UNAVAILABLE;
+        mCid = CellInfo.UNAVAILABLE;
+        mCpid = CellInfo.UNAVAILABLE;
+        mUarfcn = CellInfo.UNAVAILABLE;
+        mAdditionalPlmns = new ArraySet<>();
+        mCsgInfo = null;
+        mGlobalCellId = null;
+    }
+
+    /**
+     * @param mcc 3-digit Mobile Country Code in string format
+     * @param mnc 2 or 3-digit Mobile Network Code in string format
+     * @param lac 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown
+     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+     *        CellInfo.UNAVAILABLE if unknown
+     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127,
+     *        CellInfo.UNAVAILABLE if unknown
+     * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
+     * @param alphal long alpha Operator Name String or Enhanced Operator Name String
+     * @param alphas short alpha Operator Name String or Enhanced Operator Name String
+     * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell
+     * @param csgInfo info about the closed subscriber group broadcast by the cell
+     *
+     * @hide
+     */
+    public CellIdentityTdscdma(@Nullable String mcc, @Nullable String mnc, int lac, int cid,
+            int cpid, int uarfcn, @Nullable String alphal, @Nullable String alphas,
+            @NonNull Collection<String> additionalPlmns,
+            @Nullable ClosedSubscriberGroupInfo csgInfo) {
+        super(TAG, CellInfo.TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
+        mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
+        mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
+        mCpid = inRangeOrUnavailable(cpid, 0, MAX_CPID);
+        mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
+        for (String plmn : additionalPlmns) {
+            if (isValidPlmn(plmn)) {
+                mAdditionalPlmns.add(plmn);
+            }
+        }
+        mCsgInfo = csgInfo;
+        updateGlobalCellId();
+    }
+
+    private CellIdentityTdscdma(@NonNull CellIdentityTdscdma cid) {
+        this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid,
+                cid.mCpid, cid.mUarfcn, cid.mAlphaLong,
+                cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo);
+    }
+
+    /** @hide */
+    @Override
+    public @NonNull CellIdentityTdscdma sanitizeLocationInfo() {
+        return new CellIdentityTdscdma(mMccStr, mMncStr, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
+                CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort,
+                mAdditionalPlmns, null);
+    }
+
+    @NonNull CellIdentityTdscdma copy() {
+        return new CellIdentityTdscdma(this);
+    }
+
+    /** @hide */
+    @Override
+    protected void updateGlobalCellId() {
+        mGlobalCellId = null;
+        String plmn = getPlmn();
+        if (plmn == null) return;
+
+        if (mLac == CellInfo.UNAVAILABLE || mCid == CellInfo.UNAVAILABLE) return;
+
+        mGlobalCellId = plmn + formatSimple("%04x%04x", mLac, mCid);
+    }
+
+    /**
+     * Get Mobile Country Code in string format
+     * @return Mobile Country Code in string format, null if unknown
+     */
+    @Nullable
+    public String getMccString() {
+        return mMccStr;
+    }
+
+    /**
+     * Get Mobile Network Code in string format
+     * @return Mobile Network Code in string format, null if unknown
+     */
+    @Nullable
+    public String getMncString() {
+        return mMncStr;
+    }
+
+    /**
+     * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
+     */
+    @Nullable
+    public String getMobileNetworkOperator() {
+        return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
+    }
+
+    /**
+     * @return 16-bit Location Area Code, 0..65535,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getLac() {
+        return mLac;
+    }
+
+    /**
+     * @return 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getCid() {
+        return mCid;
+    }
+
+    /**
+     * @return 8-bit Cell Parameters ID described in TS 25.331, 0..127,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getCpid() {
+        return mCpid;
+    }
+
+    /**
+     * @return 16-bit UMTS Absolute RF Channel Number,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getUarfcn() {
+        return mUarfcn;
+    }
+
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mUarfcn;
+    }
+
+    /**
+     * @return a list of additional PLMN IDs supported by this cell.
+     */
+    @NonNull
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
+    }
+
+    /**
+     * @return closed subscriber group information about the cell if available, otherwise null.
+     */
+    @Nullable
+    public ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo() {
+        return mCsgInfo;
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public GsmCellLocation asCellLocation() {
+        GsmCellLocation cl = new GsmCellLocation();
+        int lac = mLac != CellInfo.UNAVAILABLE ? mLac : -1;
+        int cid = mCid != CellInfo.UNAVAILABLE ? mCid : -1;
+        cl.setLacAndCid(lac, cid);
+        cl.setPsc(-1); // There is no PSC for TD-SCDMA; not using this for CPI to stem shenanigans
+        return cl;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (!(other instanceof CellIdentityTdscdma)) {
+            return false;
+        }
+
+        CellIdentityTdscdma o = (CellIdentityTdscdma) other;
+        return  mLac == o.mLac
+                && mCid == o.mCid
+                && mCpid == o.mCpid
+                && mUarfcn == o.mUarfcn
+                && mAdditionalPlmns.equals(o.mAdditionalPlmns)
+                && Objects.equals(mCsgInfo, o.mCsgInfo)
+                && super.equals(other);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLac, mCid, mCpid, mUarfcn,
+                mAdditionalPlmns.hashCode(), mCsgInfo, super.hashCode());
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder(TAG)
+        .append(":{ mMcc=").append(mMccStr)
+        .append(" mMnc=").append(mMncStr)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
+        .append(" mLac=").append(mLac)
+        .append(" mCid=").append(mCid)
+        .append(" mCpid=").append(mCpid)
+        .append(" mUarfcn=").append(mUarfcn)
+        .append(" mAdditionalPlmns=").append(mAdditionalPlmns)
+        .append(" mCsgInfo=").append(mCsgInfo)
+        .append("}").toString();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        super.writeToParcel(dest, CellInfo.TYPE_TDSCDMA);
+        dest.writeInt(mLac);
+        dest.writeInt(mCid);
+        dest.writeInt(mCpid);
+        dest.writeInt(mUarfcn);
+        dest.writeArraySet(mAdditionalPlmns);
+        dest.writeParcelable(mCsgInfo, flags);
+    }
+
+    /** Construct from Parcel, type has already been processed */
+    private CellIdentityTdscdma(Parcel in) {
+        super(TAG, CellInfo.TYPE_TDSCDMA, in);
+        mLac = in.readInt();
+        mCid = in.readInt();
+        mCpid = in.readInt();
+        mUarfcn = in.readInt();
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
+        mCsgInfo = in.readParcelable(null, android.telephony.ClosedSubscriberGroupInfo.class);
+
+        updateGlobalCellId();
+        if (DBG) log(toString());
+    }
+
+    /** Implement the Parcelable interface */
+    @SuppressWarnings("hiding")
+    @NonNull
+    public static final Creator<CellIdentityTdscdma> CREATOR =
+            new Creator<CellIdentityTdscdma>() {
+                @Override
+                public @NonNull CellIdentityTdscdma createFromParcel(Parcel in) {
+                    in.readInt();   // skip
+                    return createFromParcelBody(in);
+                }
+
+                @Override
+                public @NonNull CellIdentityTdscdma[] newArray(int size) {
+                    return new CellIdentityTdscdma[size];
+                }
+            };
+
+    /** @hide */
+    protected static CellIdentityTdscdma createFromParcelBody(Parcel in) {
+        return new CellIdentityTdscdma(in);
+    }
+}
diff --git a/android-35/android/telephony/CellIdentityWcdma.java b/android-35/android/telephony/CellIdentityWcdma.java
new file mode 100644
index 0000000..72282cd
--- /dev/null
+++ b/android-35/android/telephony/CellIdentityWcdma.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2013 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;
+
+import static android.text.TextUtils.formatSimple;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.telephony.gsm.GsmCellLocation;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * CellIdentity to represent a unique UMTS cell
+ */
+public final class CellIdentityWcdma extends CellIdentity {
+    private static final String TAG = CellIdentityWcdma.class.getSimpleName();
+    private static final boolean DBG = false;
+
+    private static final int MAX_LAC = 65535;
+    private static final int MAX_CID = 268435455;
+    private static final int MAX_PSC = 511;
+    private static final int MAX_UARFCN = 16383; // a 14 bit number; TS 25.331 ex sec 10.3.8.15
+
+    // 16-bit Location Area Code, 0..65535
+    private final int mLac;
+    // 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455
+    private final int mCid;
+    // 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511
+    private final int mPsc;
+    // 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.4
+    @UnsupportedAppUsage
+    private final int mUarfcn;
+
+    // a list of additional PLMN-IDs reported for this cell
+    private final ArraySet<String> mAdditionalPlmns;
+
+    @Nullable
+    private final ClosedSubscriberGroupInfo mCsgInfo;
+
+    /**
+     * @hide
+     */
+    public CellIdentityWcdma() {
+        super(TAG, CellInfo.TYPE_WCDMA, null, null, null, null);
+        mLac = CellInfo.UNAVAILABLE;
+        mCid = CellInfo.UNAVAILABLE;
+        mPsc = CellInfo.UNAVAILABLE;
+        mUarfcn = CellInfo.UNAVAILABLE;
+        mAdditionalPlmns = new ArraySet<>();
+        mCsgInfo = null;
+        mGlobalCellId = null;
+    }
+
+    /**
+     * public constructor
+     * @param lac 16-bit Location Area Code, 0..65535
+     * @param cid 28-bit UMTS Cell Identity
+     * @param psc 9-bit UMTS Primary Scrambling Code
+     * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
+     * @param mccStr 3-digit Mobile Country Code in string format
+     * @param mncStr 2 or 3-digit Mobile Network Code in string format
+     * @param alphal long alpha Operator Name String or Enhanced Operator Name String
+     * @param alphas short alpha Operator Name String or Enhanced Operator Name String
+     * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell
+     * @param csgInfo info about the closed subscriber group broadcast by the cell
+     *
+     * @hide
+     */
+    public CellIdentityWcdma(int lac, int cid, int psc, int uarfcn, @Nullable String mccStr,
+            @Nullable String mncStr, @Nullable String alphal, @Nullable String alphas,
+            @NonNull Collection<String> additionalPlmns,
+            @Nullable ClosedSubscriberGroupInfo csgInfo) {
+        super(TAG, CellInfo.TYPE_WCDMA, mccStr, mncStr, alphal, alphas);
+        mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
+        mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
+        mPsc = inRangeOrUnavailable(psc, 0, MAX_PSC);
+        mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
+        for (String plmn : additionalPlmns) {
+            if (isValidPlmn(plmn)) {
+                mAdditionalPlmns.add(plmn);
+            }
+        }
+        mCsgInfo = csgInfo;
+        updateGlobalCellId();
+    }
+
+    private CellIdentityWcdma(@NonNull CellIdentityWcdma cid) {
+        this(cid.mLac, cid.mCid, cid.mPsc, cid.mUarfcn, cid.mMccStr,
+                cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo);
+    }
+
+    /** @hide */
+    @Override
+    public @NonNull CellIdentityWcdma sanitizeLocationInfo() {
+        return new CellIdentityWcdma(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
+                CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mMccStr, mMncStr,
+                mAlphaLong, mAlphaShort, mAdditionalPlmns, null);
+    }
+
+    @NonNull CellIdentityWcdma copy() {
+        return new CellIdentityWcdma(this);
+    }
+
+    /** @hide */
+    @Override
+    protected void updateGlobalCellId() {
+        mGlobalCellId = null;
+        String plmn = getPlmn();
+        if (plmn == null) return;
+
+        if (mLac == CellInfo.UNAVAILABLE || mCid == CellInfo.UNAVAILABLE) return;
+
+        mGlobalCellId = plmn + formatSimple("%04x%04x", mLac, mCid);
+    }
+
+    /**
+     * @return 3-digit Mobile Country Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     * @deprecated Use {@link #getMccString} instead.
+     */
+    @Deprecated
+    public int getMcc() {
+        return (mMccStr != null) ? Integer.valueOf(mMccStr) : CellInfo.UNAVAILABLE;
+    }
+
+    /**
+     * @return 2 or 3-digit Mobile Network Code, 0..999,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     * @deprecated Use {@link #getMncString} instead.
+     */
+    @Deprecated
+    public int getMnc() {
+        return (mMncStr != null) ? Integer.valueOf(mMncStr) : CellInfo.UNAVAILABLE;
+    }
+
+    /**
+     * @return 16-bit Location Area Code, 0..65535,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getLac() {
+        return mLac;
+    }
+
+    /**
+     * @return CID
+     * 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getCid() {
+        return mCid;
+    }
+
+    /**
+     * @return 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getPsc() {
+        return mPsc;
+    }
+
+    /**
+     * @return Mobile Country Code in string version, null if unavailable.
+     */
+    @Nullable
+    public String getMccString() {
+        return mMccStr;
+    }
+
+    /**
+     * @return Mobile Network Code in string version, null if unavailable.
+     */
+    @Nullable
+    public String getMncString() {
+        return mMncStr;
+    }
+
+    /**
+     * @return a 5 or 6 character string (MCC+MNC), null if any field is unknown
+     */
+    @Nullable
+    public String getMobileNetworkOperator() {
+        return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLac, mCid, mPsc, mAdditionalPlmns.hashCode(), super.hashCode());
+    }
+
+    /**
+     * @return 16-bit UMTS Absolute RF Channel Number,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getUarfcn() {
+        return mUarfcn;
+    }
+
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mUarfcn;
+    }
+
+    /**
+     * @return a list of additional PLMN IDs supported by this cell.
+     */
+    @NonNull
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
+    }
+
+    /**
+     * @return closed subscriber group information about the cell if available, otherwise null.
+     */
+    @Nullable
+    public ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo() {
+        return mCsgInfo;
+    }
+
+    /** @hide */
+    @NonNull
+    @Override
+    public GsmCellLocation asCellLocation() {
+        GsmCellLocation cl = new GsmCellLocation();
+        int lac = mLac != CellInfo.UNAVAILABLE ? mLac : -1;
+        int cid = mCid != CellInfo.UNAVAILABLE ? mCid : -1;
+        int psc = mPsc != CellInfo.UNAVAILABLE ? mPsc : -1;
+        cl.setLacAndCid(lac, cid);
+        cl.setPsc(psc);
+
+        return cl;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
+        if (!(other instanceof CellIdentityWcdma)) {
+            return false;
+        }
+
+        CellIdentityWcdma o = (CellIdentityWcdma) other;
+        return mLac == o.mLac
+                && mCid == o.mCid
+                && mPsc == o.mPsc
+                && mUarfcn == o.mUarfcn
+                && TextUtils.equals(mMccStr, o.mMccStr)
+                && TextUtils.equals(mMncStr, o.mMncStr)
+                && mAdditionalPlmns.equals(o.mAdditionalPlmns)
+                && Objects.equals(mCsgInfo, o.mCsgInfo)
+                && super.equals(other);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder(TAG)
+        .append(":{ mLac=").append(mLac)
+        .append(" mCid=").append(mCid)
+        .append(" mPsc=").append(mPsc)
+        .append(" mUarfcn=").append(mUarfcn)
+        .append(" mMcc=").append(mMccStr)
+        .append(" mMnc=").append(mMncStr)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
+        .append(" mAdditionalPlmns=").append(mAdditionalPlmns)
+        .append(" mCsgInfo=").append(mCsgInfo)
+        .append("}").toString();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        super.writeToParcel(dest, CellInfo.TYPE_WCDMA);
+        dest.writeInt(mLac);
+        dest.writeInt(mCid);
+        dest.writeInt(mPsc);
+        dest.writeInt(mUarfcn);
+        dest.writeArraySet(mAdditionalPlmns);
+        dest.writeParcelable(mCsgInfo, flags);
+    }
+
+    /** Construct from Parcel, type has already been processed */
+    private CellIdentityWcdma(Parcel in) {
+        super(TAG, CellInfo.TYPE_WCDMA, in);
+        mLac = in.readInt();
+        mCid = in.readInt();
+        mPsc = in.readInt();
+        mUarfcn = in.readInt();
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
+        mCsgInfo = in.readParcelable(null, android.telephony.ClosedSubscriberGroupInfo.class);
+
+        updateGlobalCellId();
+        if (DBG) log(toString());
+    }
+
+    /** Implement the Parcelable interface */
+    @SuppressWarnings("hiding")
+    public static final @android.annotation.NonNull Creator<CellIdentityWcdma> CREATOR =
+            new Creator<CellIdentityWcdma>() {
+                @Override
+                public CellIdentityWcdma createFromParcel(Parcel in) {
+                    in.readInt();   // skip
+                    return createFromParcelBody(in);
+                }
+
+                @Override
+                public CellIdentityWcdma[] newArray(int size) {
+                    return new CellIdentityWcdma[size];
+                }
+            };
+
+    /** @hide */
+    protected static CellIdentityWcdma createFromParcelBody(Parcel in) {
+        return new CellIdentityWcdma(in);
+    }
+}
diff --git a/android-35/android/telephony/CellInfo.java b/android-35/android/telephony/CellInfo.java
new file mode 100644
index 0000000..2b2df24
--- /dev/null
+++ b/android-35/android/telephony/CellInfo.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Immutable cell information from a point in time.
+ */
+public abstract class CellInfo implements Parcelable {
+
+    /**
+     * This value indicates that the integer field is unreported.
+     */
+    public static final int UNAVAILABLE = Integer.MAX_VALUE;
+
+    /**
+     * This value indicates that the long field is unreported.
+     */
+    public static final long UNAVAILABLE_LONG = Long.MAX_VALUE;
+
+    /**
+     * Cell identity type
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "TYPE_",
+            value = {TYPE_GSM, TYPE_CDMA, TYPE_LTE, TYPE_WCDMA, TYPE_TDSCDMA, TYPE_NR})
+    public @interface Type {}
+
+    /**
+     * Unknown cell identity type
+     * @hide
+     */
+    public static final int TYPE_UNKNOWN = 0;
+
+    /**
+     * GSM cell identity type
+     * @hide
+     */
+    public static final int TYPE_GSM = 1;
+
+    /**
+     * CDMA cell identity type
+     * @hide
+     */
+    public static final int TYPE_CDMA = 2;
+
+    /**
+     * LTE cell identity type
+     * @hide
+     */
+    public static final int TYPE_LTE = 3;
+
+    /**
+     * WCDMA cell identity type
+     * @hide
+     */
+    public static final int TYPE_WCDMA = 4;
+
+    /**
+     * TD-SCDMA cell identity type
+     * @hide
+     */
+    public static final int TYPE_TDSCDMA = 5;
+
+    /**
+     * 5G cell identity type
+     * @hide
+     */
+    public static final int TYPE_NR = 6;
+
+    // Type to distinguish where time stamp gets recorded.
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final int TIMESTAMP_TYPE_UNKNOWN = 0;
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final int TIMESTAMP_TYPE_ANTENNA = 1;
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final int TIMESTAMP_TYPE_MODEM = 2;
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final int TIMESTAMP_TYPE_OEM_RIL = 3;
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final int TIMESTAMP_TYPE_JAVA_RIL = 4;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        CONNECTION_NONE,
+        CONNECTION_PRIMARY_SERVING,
+        CONNECTION_SECONDARY_SERVING,
+        CONNECTION_UNKNOWN
+    })
+    public @interface CellConnectionStatus {}
+
+    /**
+     * Cell is not a serving cell.
+     *
+     * <p>The cell has been measured but is neither a camped nor serving cell (3GPP 36.304).
+     */
+    public static final int CONNECTION_NONE = 0;
+
+    /** UE is connected to cell for signalling and possibly data (3GPP 36.331, 25.331). */
+    public static final int CONNECTION_PRIMARY_SERVING = 1;
+
+    /** UE is connected to cell for data (3GPP 36.331, 25.331). */
+    public static final int CONNECTION_SECONDARY_SERVING = 2;
+
+    /** Connection status is unknown. */
+    public static final int CONNECTION_UNKNOWN = Integer.MAX_VALUE;
+
+    /** A cell connection status */
+    private int mCellConnectionStatus;
+
+    // True if device is mRegistered to the mobile network
+    private boolean mRegistered;
+
+    // Observation time stamped as type in nanoseconds since boot
+    private long mTimeStamp;
+
+    /** @hide */
+    protected CellInfo(int cellConnectionStatus, boolean registered, long timestamp) {
+        mCellConnectionStatus = cellConnectionStatus;
+        mRegistered = registered;
+        mTimeStamp = timestamp;
+    }
+
+    /** @hide */
+    protected CellInfo() {
+        this.mRegistered = false;
+        this.mTimeStamp = Long.MAX_VALUE;
+        this.mCellConnectionStatus = CONNECTION_NONE;
+    }
+
+    /** @hide */
+    protected CellInfo(CellInfo ci) {
+        this.mRegistered = ci.mRegistered;
+        this.mTimeStamp = ci.mTimeStamp;
+        this.mCellConnectionStatus = ci.mCellConnectionStatus;
+    }
+
+    /**
+     * True if the phone is registered to a mobile network that provides service on this cell
+     * and this cell is being used or would be used for network signaling.
+     */
+    public boolean isRegistered() {
+        return mRegistered;
+    }
+
+    /** @hide */
+    public void setRegistered(boolean registered) {
+        mRegistered = registered;
+    }
+
+    /**
+     * Approximate time this cell information was received from the modem.
+     *
+     * @return a time stamp in millis since boot.
+     */
+    @ElapsedRealtimeLong
+    public long getTimestampMillis() {
+        return mTimeStamp / 1000000;
+    }
+
+    /**
+     * Approximate time this cell information was received from the modem.
+     *
+     * @return a time stamp in nanos since boot.
+     * @deprecated Use {@link #getTimestampMillis} instead.
+     */
+    @Deprecated
+    public long getTimeStamp() {
+        return mTimeStamp;
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public void setTimeStamp(long ts) {
+        mTimeStamp = ts;
+    }
+
+    /**
+     * @return a {@link CellIdentity} instance.
+     */
+    @NonNull
+    public abstract CellIdentity getCellIdentity();
+
+    /**
+     * @return a {@link CellSignalStrength} instance.
+     */
+    @NonNull
+    public abstract CellSignalStrength getCellSignalStrength();
+
+    /** @hide */
+    public CellInfo sanitizeLocationInfo() {
+        return null;
+    }
+
+    /**
+     * Gets the connection status of this cell.
+     *
+     * @see #CONNECTION_NONE
+     * @see #CONNECTION_PRIMARY_SERVING
+     * @see #CONNECTION_SECONDARY_SERVING
+     * @see #CONNECTION_UNKNOWN
+     *
+     * @return The connection status of the cell.
+     */
+    @CellConnectionStatus
+    public int getCellConnectionStatus() {
+        return mCellConnectionStatus;
+    }
+    /** @hide */
+    public void setCellConnectionStatus(@CellConnectionStatus int cellConnectionStatus) {
+        mCellConnectionStatus = cellConnectionStatus;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCellConnectionStatus, mRegistered, mTimeStamp);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CellInfo)) return false;
+        CellInfo cellInfo = (CellInfo) o;
+        return mCellConnectionStatus == cellInfo.mCellConnectionStatus
+                && mRegistered == cellInfo.mRegistered
+                && mTimeStamp == cellInfo.mTimeStamp;
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("mRegistered=").append(mRegistered ? "YES" : "NO");
+        sb.append(" mTimeStamp=").append(mTimeStamp).append("ns");
+        sb.append(" mCellConnectionStatus=").append(mCellConnectionStatus);
+
+        return sb.toString();
+    }
+
+    /**
+     * Implement the Parcelable interface
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public abstract void writeToParcel(Parcel dest, int flags);
+
+    /**
+     * Used by child classes for parceling.
+     *
+     * @hide
+     */
+    protected void writeToParcel(Parcel dest, int flags, int type) {
+        dest.writeInt(type);
+        dest.writeInt(mRegistered ? 1 : 0);
+        dest.writeLong(mTimeStamp);
+        dest.writeInt(mCellConnectionStatus);
+    }
+
+    /**
+     * Used by child classes for parceling
+     *
+     * @hide
+     */
+    protected CellInfo(Parcel in) {
+        mRegistered = (in.readInt() == 1) ? true : false;
+        mTimeStamp = in.readLong();
+        mCellConnectionStatus = in.readInt();
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @android.annotation.NonNull Creator<CellInfo> CREATOR = new Creator<CellInfo>() {
+        @Override
+        public CellInfo createFromParcel(Parcel in) {
+                int type = in.readInt();
+                switch (type) {
+                    case TYPE_GSM: return CellInfoGsm.createFromParcelBody(in);
+                    case TYPE_CDMA: return CellInfoCdma.createFromParcelBody(in);
+                    case TYPE_LTE: return CellInfoLte.createFromParcelBody(in);
+                    case TYPE_WCDMA: return CellInfoWcdma.createFromParcelBody(in);
+                    case TYPE_TDSCDMA: return CellInfoTdscdma.createFromParcelBody(in);
+                    case TYPE_NR: return CellInfoNr.createFromParcelBody(in);
+                    default: throw new RuntimeException("Bad CellInfo Parcel");
+                }
+        }
+
+        @Override
+        public CellInfo[] newArray(int size) {
+            return new CellInfo[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/CellInfoCdma.java b/android-35/android/telephony/CellInfoCdma.java
new file mode 100644
index 0000000..aa8cff5
--- /dev/null
+++ b/android-35/android/telephony/CellInfoCdma.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.telephony.Rlog;
+
+/**
+ * A {@link CellInfo} representing a CDMA cell that provides identity and measurement info.
+ */
+public final class CellInfoCdma extends CellInfo implements Parcelable {
+
+    private static final String LOG_TAG = "CellInfoCdma";
+    private static final boolean DBG = false;
+
+    private CellIdentityCdma mCellIdentityCdma;
+    private CellSignalStrengthCdma mCellSignalStrengthCdma;
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public CellInfoCdma() {
+        super();
+        mCellIdentityCdma = new CellIdentityCdma();
+        mCellSignalStrengthCdma = new CellSignalStrengthCdma();
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public CellInfoCdma(CellInfoCdma ci) {
+        super(ci);
+        this.mCellIdentityCdma = ci.mCellIdentityCdma.copy();
+        this.mCellSignalStrengthCdma = ci.mCellSignalStrengthCdma.copy();
+    }
+
+    /** @hide */
+    public CellInfoCdma(int connectionStatus, boolean registered, long timeStamp,
+            CellIdentityCdma cellIdentityCdma, CellSignalStrengthCdma cellSignalStrengthCdma) {
+        super(connectionStatus, registered, timeStamp);
+        mCellIdentityCdma = cellIdentityCdma;
+        mCellSignalStrengthCdma = cellSignalStrengthCdma;
+    }
+
+    /**
+     * @return a {@link CellIdentityCdma} instance.
+     */
+    @Override
+    public @NonNull CellIdentityCdma getCellIdentity() {
+        return mCellIdentityCdma;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public void setCellIdentity(CellIdentityCdma cid) {
+        mCellIdentityCdma = cid;
+    }
+
+    /**
+     * @return a {@link CellSignalStrengthCdma} instance.
+     */
+    @Override
+    public @NonNull CellSignalStrengthCdma getCellSignalStrength() {
+        return mCellSignalStrengthCdma;
+    }
+
+    /** @hide */
+    @Override
+    public CellInfo sanitizeLocationInfo() {
+        CellInfoCdma result = new CellInfoCdma(this);
+        result.mCellIdentityCdma = mCellIdentityCdma.sanitizeLocationInfo();
+        return result;
+    }
+
+    /** @hide */
+    public void setCellSignalStrength(CellSignalStrengthCdma css) {
+        mCellSignalStrengthCdma = css;
+    }
+
+    /**
+     * @return hash code
+     */
+    @Override
+    public int hashCode() {
+        return super.hashCode() + mCellIdentityCdma.hashCode() + mCellSignalStrengthCdma.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!super.equals(other)) {
+            return false;
+        }
+        try {
+            CellInfoCdma o = (CellInfoCdma) other;
+            return mCellIdentityCdma.equals(o.mCellIdentityCdma)
+                    && mCellSignalStrengthCdma.equals(o.mCellSignalStrengthCdma);
+        } catch (ClassCastException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("CellInfoCdma:{");
+        sb.append(super.toString());
+        sb.append(" ").append(mCellIdentityCdma);
+        sb.append(" ").append(mCellSignalStrengthCdma);
+        sb.append("}");
+
+        return sb.toString();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags, TYPE_CDMA);
+        mCellIdentityCdma.writeToParcel(dest, flags);
+        mCellSignalStrengthCdma.writeToParcel(dest, flags);
+    }
+
+    /**
+     * Construct a CellInfoCdma object from the given parcel
+     * where the token is already been processed.
+     */
+    private CellInfoCdma(Parcel in) {
+        super(in);
+        mCellIdentityCdma = CellIdentityCdma.CREATOR.createFromParcel(in);
+        mCellSignalStrengthCdma = CellSignalStrengthCdma.CREATOR.createFromParcel(in);
+        if (DBG) log("CellInfoCdma(Parcel): " + toString());
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @android.annotation.NonNull Creator<CellInfoCdma> CREATOR = new Creator<CellInfoCdma>() {
+        @Override
+        public CellInfoCdma createFromParcel(Parcel in) {
+            in.readInt(); // Skip past token, we know what it is
+            return createFromParcelBody(in);
+        }
+
+        @Override
+        public CellInfoCdma[] newArray(int size) {
+            return new CellInfoCdma[size];
+        }
+    };
+
+    /** @hide */
+    protected static CellInfoCdma createFromParcelBody(Parcel in) {
+        return new CellInfoCdma(in);
+    }
+
+    /**
+     * log
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/CellInfoGsm.java b/android-35/android/telephony/CellInfoGsm.java
new file mode 100644
index 0000000..76e825b
--- /dev/null
+++ b/android-35/android/telephony/CellInfoGsm.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.telephony.Rlog;
+
+/**
+ * A {@link CellInfo} representing a GSM cell that provides identity and measurement info.
+ */
+public final class CellInfoGsm extends CellInfo implements Parcelable {
+
+    private static final String LOG_TAG = "CellInfoGsm";
+    private static final boolean DBG = false;
+
+    private CellIdentityGsm mCellIdentityGsm;
+    private CellSignalStrengthGsm mCellSignalStrengthGsm;
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public CellInfoGsm() {
+        super();
+        mCellIdentityGsm = new CellIdentityGsm();
+        mCellSignalStrengthGsm = new CellSignalStrengthGsm();
+    }
+
+    /** @hide */
+    public CellInfoGsm(CellInfoGsm ci) {
+        super(ci);
+        mCellIdentityGsm = ci.mCellIdentityGsm.copy();
+        mCellSignalStrengthGsm = ci.mCellSignalStrengthGsm.copy();
+    }
+
+    /** @hide */
+    public CellInfoGsm(int cellConnectionStatus, boolean registered, long timeStamp,
+            CellIdentityGsm cellIdentityGsm, CellSignalStrengthGsm cellSignalStrengthGsm) {
+        super(cellConnectionStatus, registered, timeStamp);
+        mCellIdentityGsm = cellIdentityGsm;
+        mCellSignalStrengthGsm = cellSignalStrengthGsm;
+    }
+
+    /**
+     * @return a {@link CellIdentityGsm} instance.
+     */
+    @Override
+    public @NonNull CellIdentityGsm getCellIdentity() {
+        return mCellIdentityGsm;
+    }
+
+    /** @hide */
+    public void setCellIdentity(CellIdentityGsm cid) {
+        mCellIdentityGsm = cid;
+    }
+
+    /**
+     * @return a {@link CellSignalStrengthGsm} instance.
+     */
+    @Override
+    public @NonNull CellSignalStrengthGsm getCellSignalStrength() {
+        return mCellSignalStrengthGsm;
+    }
+
+    /** @hide */
+    @Override
+    public CellInfo sanitizeLocationInfo() {
+        CellInfoGsm result = new CellInfoGsm(this);
+        result.mCellIdentityGsm = mCellIdentityGsm.sanitizeLocationInfo();
+        return result;
+    }
+
+    /** @hide */
+    public void setCellSignalStrength(CellSignalStrengthGsm css) {
+        mCellSignalStrengthGsm = css;
+    }
+
+    /**
+     * @return hash code
+     */
+    @Override
+    public int hashCode() {
+        return super.hashCode() + mCellIdentityGsm.hashCode() + mCellSignalStrengthGsm.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!super.equals(other)) {
+            return false;
+        }
+        try {
+            CellInfoGsm o = (CellInfoGsm) other;
+            return mCellIdentityGsm.equals(o.mCellIdentityGsm)
+                    && mCellSignalStrengthGsm.equals(o.mCellSignalStrengthGsm);
+        } catch (ClassCastException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("CellInfoGsm:{");
+        sb.append(super.toString());
+        sb.append(" ").append(mCellIdentityGsm);
+        sb.append(" ").append(mCellSignalStrengthGsm);
+        sb.append("}");
+
+        return sb.toString();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags, TYPE_GSM);
+        mCellIdentityGsm.writeToParcel(dest, flags);
+        mCellSignalStrengthGsm.writeToParcel(dest, flags);
+    }
+
+    /**
+     * Construct a CellInfoGsm object from the given parcel
+     * where the token is already been processed.
+     */
+    private CellInfoGsm(Parcel in) {
+        super(in);
+        mCellIdentityGsm = CellIdentityGsm.CREATOR.createFromParcel(in);
+        mCellSignalStrengthGsm = CellSignalStrengthGsm.CREATOR.createFromParcel(in);
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @android.annotation.NonNull Creator<CellInfoGsm> CREATOR = new Creator<CellInfoGsm>() {
+        @Override
+        public CellInfoGsm createFromParcel(Parcel in) {
+            in.readInt(); // Skip past token, we know what it is
+            return createFromParcelBody(in);
+        }
+
+        @Override
+        public CellInfoGsm[] newArray(int size) {
+            return new CellInfoGsm[size];
+        }
+    };
+
+    /** @hide */
+    protected static CellInfoGsm createFromParcelBody(Parcel in) {
+        return new CellInfoGsm(in);
+    }
+
+    /**
+     * log
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/CellInfoLte.java b/android-35/android/telephony/CellInfoLte.java
new file mode 100644
index 0000000..2d176d5
--- /dev/null
+++ b/android-35/android/telephony/CellInfoLte.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.telephony.Rlog;
+
+import java.util.Objects;
+
+/**
+ * A {@link CellInfo} representing an LTE cell that provides identity and measurement info.
+ */
+public final class CellInfoLte extends CellInfo implements Parcelable {
+
+    private static final String LOG_TAG = "CellInfoLte";
+    private static final boolean DBG = false;
+
+    private CellIdentityLte mCellIdentityLte;
+    private CellSignalStrengthLte mCellSignalStrengthLte;
+    private CellConfigLte mCellConfig;
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public CellInfoLte() {
+        super();
+        mCellIdentityLte = new CellIdentityLte();
+        mCellSignalStrengthLte = new CellSignalStrengthLte();
+        mCellConfig = new CellConfigLte();
+    }
+
+    /** @hide */
+    public CellInfoLte(CellInfoLte ci) {
+        super(ci);
+        this.mCellIdentityLte = ci.mCellIdentityLte.copy();
+        this.mCellSignalStrengthLte = ci.mCellSignalStrengthLte.copy();
+        this.mCellConfig = new CellConfigLte(ci.mCellConfig);
+    }
+
+    /** @hide */
+    public CellInfoLte(int connectionStatus, boolean registered, long timeStamp,
+            CellIdentityLte cellIdentityLte, CellSignalStrengthLte cellSignalStrengthLte,
+            CellConfigLte cellConfig) {
+        super(connectionStatus, registered, timeStamp);
+        mCellIdentityLte = cellIdentityLte;
+        mCellSignalStrengthLte = cellSignalStrengthLte;
+        mCellConfig = cellConfig;
+    }
+
+    /**
+     * @return a {@link CellIdentityLte} instance.
+     */
+    @Override
+    public @NonNull CellIdentityLte getCellIdentity() {
+        if (DBG) log("getCellIdentity: " + mCellIdentityLte);
+        return mCellIdentityLte;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setCellIdentity(CellIdentityLte cid) {
+        if (DBG) log("setCellIdentity: " + cid);
+        mCellIdentityLte = cid;
+    }
+
+    /**
+     * @return a {@link CellSignalStrengthLte} instance.
+     */
+    @Override
+    public @NonNull CellSignalStrengthLte getCellSignalStrength() {
+        if (DBG) log("getCellSignalStrength: " + mCellSignalStrengthLte);
+        return mCellSignalStrengthLte;
+    }
+
+    /** @hide */
+    @Override
+    public CellInfo sanitizeLocationInfo() {
+        CellInfoLte result = new CellInfoLte(this);
+        result.mCellIdentityLte = mCellIdentityLte.sanitizeLocationInfo();
+        return result;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setCellSignalStrength(CellSignalStrengthLte css) {
+        if (DBG) log("setCellSignalStrength: " + css);
+        mCellSignalStrengthLte = css;
+    }
+
+    /** @hide */
+    public void setCellConfig(CellConfigLte cellConfig) {
+        if (DBG) log("setCellConfig: " + cellConfig);
+        mCellConfig = cellConfig;
+    }
+
+    /** @hide */
+    public CellConfigLte getCellConfig() {
+        if (DBG) log("getCellConfig: " + mCellConfig);
+        return mCellConfig;
+    }
+
+    /**
+     * @return hash code
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                super.hashCode(),
+                mCellIdentityLte.hashCode(),
+                mCellSignalStrengthLte.hashCode(),
+                mCellConfig.hashCode());
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CellInfoLte)) return false;
+        CellInfoLte o = (CellInfoLte) other;
+        return super.equals(o) && mCellIdentityLte.equals(o.mCellIdentityLte)
+                && mCellSignalStrengthLte.equals(o.mCellSignalStrengthLte)
+                && mCellConfig.equals(o.mCellConfig);
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("CellInfoLte:{");
+        sb.append(super.toString());
+        sb.append(" ").append(mCellIdentityLte);
+        sb.append(" ").append(mCellSignalStrengthLte);
+        sb.append(" ").append(mCellConfig);
+        sb.append("}");
+
+        return sb.toString();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        super.writeToParcel(dest, flags, TYPE_LTE);
+        mCellIdentityLte.writeToParcel(dest, flags);
+        mCellSignalStrengthLte.writeToParcel(dest, flags);
+        mCellConfig.writeToParcel(dest, flags);
+    }
+
+    /**
+     * Construct a CellInfoLte object from the given parcel
+     * where the TYPE_LTE token is already been processed.
+     */
+    private CellInfoLte(Parcel in) {
+        super(in);
+        mCellIdentityLte = CellIdentityLte.CREATOR.createFromParcel(in);
+        mCellSignalStrengthLte = CellSignalStrengthLte.CREATOR.createFromParcel(in);
+        mCellConfig = CellConfigLte.CREATOR.createFromParcel(in);
+        if (DBG) log("CellInfoLte(Parcel): " + toString());
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @android.annotation.NonNull Creator<CellInfoLte> CREATOR = new Creator<CellInfoLte>() {
+        @Override
+        public CellInfoLte createFromParcel(Parcel in) {
+            in.readInt(); // Skip past token, we know what it is
+            return createFromParcelBody(in);
+        }
+
+        @Override
+        public CellInfoLte[] newArray(int size) {
+            return new CellInfoLte[size];
+        }
+    };
+
+    /** @hide */
+    protected static CellInfoLte createFromParcelBody(Parcel in) {
+        return new CellInfoLte(in);
+    }
+
+    /**
+     * log
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/CellInfoNr.java b/android-35/android/telephony/CellInfoNr.java
new file mode 100644
index 0000000..37fac24
--- /dev/null
+++ b/android-35/android/telephony/CellInfoNr.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+
+import dalvik.annotation.codegen.CovariantReturnType;
+
+import java.util.Objects;
+
+/**
+ * A {@link CellInfo} representing an 5G NR cell that provides identity and measurement info.
+ */
+public final class CellInfoNr extends CellInfo {
+    private static final String TAG = "CellInfoNr";
+
+    private CellIdentityNr mCellIdentity;
+    private final CellSignalStrengthNr mCellSignalStrength;
+
+    /** @hide */
+    public CellInfoNr() {
+        super();
+        mCellIdentity = new CellIdentityNr();
+        mCellSignalStrength = new CellSignalStrengthNr();
+    }
+
+    private CellInfoNr(Parcel in) {
+        super(in);
+        mCellIdentity = CellIdentityNr.CREATOR.createFromParcel(in);
+        mCellSignalStrength = CellSignalStrengthNr.CREATOR.createFromParcel(in);
+    }
+
+    private CellInfoNr(CellInfoNr other, boolean sanitizeLocationInfo) {
+        super(other);
+        mCellIdentity = sanitizeLocationInfo ? other.mCellIdentity.sanitizeLocationInfo()
+                : other.mCellIdentity;
+        mCellSignalStrength = other.mCellSignalStrength;
+    }
+
+    /** @hide */
+    public CellInfoNr(int connectionStatus, boolean registered, long timeStamp,
+            CellIdentityNr cellIdentityNr, CellSignalStrengthNr cellSignalStrengthNr) {
+        super(connectionStatus, registered, timeStamp);
+        mCellIdentity = cellIdentityNr;
+        mCellSignalStrength = cellSignalStrengthNr;
+    }
+
+    /**
+     * @return a {@link CellIdentityNr} instance.
+     */
+    @CovariantReturnType(returnType = CellIdentityNr.class, presentAfter = 29)
+    @Override
+    @NonNull
+    public CellIdentity getCellIdentity() {
+        return mCellIdentity;
+    }
+
+    /** @hide */
+    public void setCellIdentity(CellIdentityNr cid) {
+        mCellIdentity = cid;
+    }
+
+    /**
+     * @return a {@link CellSignalStrengthNr} instance.
+     */
+    @CovariantReturnType(returnType = CellSignalStrengthNr.class, presentAfter = 29)
+    @Override
+    @NonNull
+    public CellSignalStrength getCellSignalStrength() {
+        return mCellSignalStrength;
+    }
+
+    /** @hide */
+    @Override
+    public CellInfo sanitizeLocationInfo() {
+        return new CellInfoNr(this, true);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mCellIdentity, mCellSignalStrength);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CellInfoNr)) {
+            return false;
+        }
+
+        CellInfoNr o = (CellInfoNr) other;
+        return super.equals(o) && mCellIdentity.equals(o.mCellIdentity)
+                && mCellSignalStrength.equals(o.mCellSignalStrength);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+                .append(TAG + ":{")
+                .append(" " + super.toString())
+                .append(" " + mCellIdentity)
+                .append(" " + mCellSignalStrength)
+                .append(" }")
+                .toString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags, TYPE_NR);
+        mCellIdentity.writeToParcel(dest, flags);
+        mCellSignalStrength.writeToParcel(dest, flags);
+    }
+
+    public static final @android.annotation.NonNull Creator<CellInfoNr> CREATOR = new Creator<CellInfoNr>() {
+        @Override
+        public CellInfoNr createFromParcel(Parcel in) {
+            // Skip the type info.
+            in.readInt();
+            return new CellInfoNr(in);
+        }
+
+        @Override
+        public CellInfoNr[] newArray(int size) {
+            return new CellInfoNr[size];
+        }
+    };
+
+    /** @hide */
+    protected static CellInfoNr createFromParcelBody(Parcel in) {
+        return new CellInfoNr(in);
+    }
+}
diff --git a/android-35/android/telephony/CellInfoTdscdma.java b/android-35/android/telephony/CellInfoTdscdma.java
new file mode 100644
index 0000000..d8db429
--- /dev/null
+++ b/android-35/android/telephony/CellInfoTdscdma.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.telephony.Rlog;
+
+import java.util.Objects;
+
+/**
+ * A {@link CellInfo} representing a TD-SCDMA cell that provides identity and measurement info.
+ *
+ * @see android.telephony.CellInfo
+ * @see android.telephony.CellSignalStrengthTdscdma
+ * @see android.telephony.CellIdentityTdscdma
+ */
+public final class CellInfoTdscdma extends CellInfo implements Parcelable {
+
+    private static final String LOG_TAG = "CellInfoTdscdma";
+    private static final boolean DBG = false;
+
+    private CellIdentityTdscdma mCellIdentityTdscdma;
+    private CellSignalStrengthTdscdma mCellSignalStrengthTdscdma;
+
+    /** @hide */
+    public CellInfoTdscdma() {
+        super();
+        mCellIdentityTdscdma = new CellIdentityTdscdma();
+        mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma();
+    }
+
+    /** @hide */
+    public CellInfoTdscdma(CellInfoTdscdma ci) {
+        super(ci);
+        this.mCellIdentityTdscdma = ci.mCellIdentityTdscdma.copy();
+        this.mCellSignalStrengthTdscdma = ci.mCellSignalStrengthTdscdma.copy();
+    }
+
+    /** @hide */
+    public CellInfoTdscdma(int connectionStatus, boolean registered, long timeStamp,
+            CellIdentityTdscdma cellIdentityTdscdma,
+            CellSignalStrengthTdscdma cellSignalStrengthTdscdma) {
+        super(connectionStatus, registered, timeStamp);
+        mCellIdentityTdscdma = cellIdentityTdscdma;
+        mCellSignalStrengthTdscdma = cellSignalStrengthTdscdma;
+    }
+
+    /**
+     * @return a {@link CellIdentityTdscdma} instance.
+     */
+    @Override
+    public @NonNull CellIdentityTdscdma getCellIdentity() {
+        return mCellIdentityTdscdma;
+    }
+
+    /** @hide */
+    public void setCellIdentity(CellIdentityTdscdma cid) {
+        mCellIdentityTdscdma = cid;
+    }
+
+    /**
+     * @return a {@link CellSignalStrengthTdscdma} instance.
+     */
+    @Override
+    public @NonNull CellSignalStrengthTdscdma getCellSignalStrength() {
+        return mCellSignalStrengthTdscdma;
+    }
+
+    /** @hide */
+    @Override
+    public CellInfo sanitizeLocationInfo() {
+        CellInfoTdscdma result = new CellInfoTdscdma(this);
+        result.mCellIdentityTdscdma = mCellIdentityTdscdma.sanitizeLocationInfo();
+        return result;
+    }
+
+    /** @hide */
+    public void setCellSignalStrength(CellSignalStrengthTdscdma css) {
+        mCellSignalStrengthTdscdma = css;
+    }
+
+    /**
+     * @return hash code
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mCellIdentityTdscdma, mCellSignalStrengthTdscdma);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!super.equals(other)) {
+            return false;
+        }
+        try {
+            CellInfoTdscdma o = (CellInfoTdscdma) other;
+            return mCellIdentityTdscdma.equals(o.mCellIdentityTdscdma)
+                    && mCellSignalStrengthTdscdma.equals(o.mCellSignalStrengthTdscdma);
+        } catch (ClassCastException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("CellInfoTdscdma:{");
+        sb.append(super.toString());
+        sb.append(" ").append(mCellIdentityTdscdma);
+        sb.append(" ").append(mCellSignalStrengthTdscdma);
+        sb.append("}");
+
+        return sb.toString();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags, TYPE_TDSCDMA);
+        mCellIdentityTdscdma.writeToParcel(dest, flags);
+        mCellSignalStrengthTdscdma.writeToParcel(dest, flags);
+    }
+
+    /**
+     * Construct a CellInfoTdscdma object from the given parcel
+     * where the token is already been processed.
+     */
+    private CellInfoTdscdma(Parcel in) {
+        super(in);
+        mCellIdentityTdscdma = CellIdentityTdscdma.CREATOR.createFromParcel(in);
+        mCellSignalStrengthTdscdma = CellSignalStrengthTdscdma.CREATOR.createFromParcel(in);
+    }
+
+    /** Implement the Parcelable interface */
+    @NonNull
+    public static final Creator<CellInfoTdscdma> CREATOR = new Creator<CellInfoTdscdma>() {
+        @Override
+        public @NonNull CellInfoTdscdma createFromParcel(Parcel in) {
+            in.readInt(); // Skip past token, we know what it is
+            return createFromParcelBody(in);
+        }
+
+        @Override
+        public @NonNull CellInfoTdscdma[] newArray(int size) {
+            return new CellInfoTdscdma[size];
+        }
+    };
+
+    /** @hide */
+    protected static CellInfoTdscdma createFromParcelBody(Parcel in) {
+        return new CellInfoTdscdma(in);
+    }
+
+    /**
+     * log
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/CellInfoWcdma.java b/android-35/android/telephony/CellInfoWcdma.java
new file mode 100644
index 0000000..dc8e1fe
--- /dev/null
+++ b/android-35/android/telephony/CellInfoWcdma.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.telephony.Rlog;
+
+import java.util.Objects;
+
+/**
+ * A {@link CellInfo} representing a WCDMA cell that provides identity and measurement info.
+ */
+public final class CellInfoWcdma extends CellInfo implements Parcelable {
+
+    private static final String LOG_TAG = "CellInfoWcdma";
+    private static final boolean DBG = false;
+
+    private CellIdentityWcdma mCellIdentityWcdma;
+    private CellSignalStrengthWcdma mCellSignalStrengthWcdma;
+
+    /** @hide */
+    public CellInfoWcdma() {
+        super();
+        mCellIdentityWcdma = new CellIdentityWcdma();
+        mCellSignalStrengthWcdma = new CellSignalStrengthWcdma();
+    }
+
+    /** @hide */
+    public CellInfoWcdma(CellInfoWcdma ci) {
+        super(ci);
+        this.mCellIdentityWcdma = ci.mCellIdentityWcdma.copy();
+        this.mCellSignalStrengthWcdma = ci.mCellSignalStrengthWcdma.copy();
+    }
+
+    /** @hide */
+    public CellInfoWcdma(int connectionStatus, boolean registered, long timeStamp,
+            CellIdentityWcdma cellIdentityWcdma, CellSignalStrengthWcdma cellSignalStrengthWcdma) {
+        super(connectionStatus, registered, timeStamp);
+        mCellIdentityWcdma = cellIdentityWcdma;
+        mCellSignalStrengthWcdma = cellSignalStrengthWcdma;
+    }
+
+    /**
+     * @return a {@link CellIdentityWcdma} instance.
+     */
+    @Override
+    public CellIdentityWcdma getCellIdentity() {
+        return mCellIdentityWcdma;
+    }
+
+    /** @hide */
+    public void setCellIdentity(CellIdentityWcdma cid) {
+        mCellIdentityWcdma = cid;
+    }
+
+    /**
+     * @return a {@link CellSignalStrengthWcdma} instance.
+     */
+    @Override
+    public CellSignalStrengthWcdma getCellSignalStrength() {
+        return mCellSignalStrengthWcdma;
+    }
+
+    /** @hide */
+    @Override
+    public CellInfo sanitizeLocationInfo() {
+        CellInfoWcdma result = new CellInfoWcdma(this);
+        result.mCellIdentityWcdma = mCellIdentityWcdma.sanitizeLocationInfo();
+        return result;
+    }
+
+    /** @hide */
+    public void setCellSignalStrength(CellSignalStrengthWcdma css) {
+        mCellSignalStrengthWcdma = css;
+    }
+
+    /**
+     * @return hash code
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), mCellIdentityWcdma, mCellSignalStrengthWcdma);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!super.equals(other)) {
+            return false;
+        }
+        try {
+            CellInfoWcdma o = (CellInfoWcdma) other;
+            return mCellIdentityWcdma.equals(o.mCellIdentityWcdma)
+                    && mCellSignalStrengthWcdma.equals(o.mCellSignalStrengthWcdma);
+        } catch (ClassCastException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("CellInfoWcdma:{");
+        sb.append(super.toString());
+        sb.append(" ").append(mCellIdentityWcdma);
+        sb.append(" ").append(mCellSignalStrengthWcdma);
+        sb.append("}");
+
+        return sb.toString();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags, TYPE_WCDMA);
+        mCellIdentityWcdma.writeToParcel(dest, flags);
+        mCellSignalStrengthWcdma.writeToParcel(dest, flags);
+    }
+
+    /**
+     * Construct a CellInfoWcdma object from the given parcel
+     * where the token is already been processed.
+     */
+    private CellInfoWcdma(Parcel in) {
+        super(in);
+        mCellIdentityWcdma = CellIdentityWcdma.CREATOR.createFromParcel(in);
+        mCellSignalStrengthWcdma = CellSignalStrengthWcdma.CREATOR.createFromParcel(in);
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @android.annotation.NonNull Creator<CellInfoWcdma> CREATOR = new Creator<CellInfoWcdma>() {
+        @Override
+        public CellInfoWcdma createFromParcel(Parcel in) {
+            in.readInt(); // Skip past token, we know what it is
+            return createFromParcelBody(in);
+        }
+
+        @Override
+        public CellInfoWcdma[] newArray(int size) {
+            return new CellInfoWcdma[size];
+        }
+    };
+
+    /** @hide */
+    protected static CellInfoWcdma createFromParcelBody(Parcel in) {
+        return new CellInfoWcdma(in);
+    }
+
+    /**
+     * log
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/CellLocation.java b/android-35/android/telephony/CellLocation.java
new file mode 100644
index 0000000..e595002
--- /dev/null
+++ b/android-35/android/telephony/CellLocation.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2006 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;
+
+import android.app.ActivityThread;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.gsm.GsmCellLocation;
+
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.PhoneConstants;
+
+/**
+ * Abstract class that represents the location of the device.  {@more}
+ *
+ * @deprecated use {@link android.telephony.CellIdentity CellIdentity}.
+ */
+@Deprecated
+public abstract class CellLocation {
+
+    /**
+     * Request an updated CellLocation for callers targeting SDK 30 or older.
+     *
+     * Whenever Android is aware of location changes, a callback will automatically be sent to
+     * all registrants of {@link PhoneStateListener#LISTEN_CELL_LOCATION}. This API requests an
+     * additional location update for cases where power saving might cause location updates to be
+     * missed.
+     *
+     * <p>This method is a no-op for callers targeting SDK level 31 or greater.
+     * <p>This method is a no-op for callers that target SDK level 29 or 30 and lack
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
+     * <p>This method is a no-op for callers that target SDK level 28 or below and lack
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * @deprecated use {@link TelephonyManager#requestCellInfoUpdate}.
+     */
+    @Deprecated
+    public static void requestLocationUpdate() {
+        // Since this object doesn't have a context, this is the best we can do.
+        final Context appContext = ActivityThread.currentApplication();
+        if (appContext == null) return; // should never happen
+
+        try {
+            ITelephony phone = ITelephony.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getTelephonyServiceRegisterer()
+                            .get());
+            if (phone != null) {
+                phone.updateServiceLocationWithPackageName(appContext.getOpPackageName());
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Create a new CellLocation from a intent notifier Bundle
+     *
+     * This method maybe used by external applications.
+     *
+     * @param bundle Bundle from intent notifier
+     * @return newly created CellLocation
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static CellLocation newFromBundle(Bundle bundle) {
+        // TelephonyManager.getDefault().getCurrentPhoneType() handles the case when
+        // ITelephony interface is not up yet.
+        switch(TelephonyManager.getDefault().getCurrentPhoneType()) {
+        case PhoneConstants.PHONE_TYPE_CDMA:
+            return new CdmaCellLocation(bundle);
+        case PhoneConstants.PHONE_TYPE_GSM:
+            return new GsmCellLocation(bundle);
+        default:
+            return null;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @SuppressWarnings("HiddenAbstractMethod")
+    @UnsupportedAppUsage
+    public abstract void fillInNotifierBundle(Bundle bundle);
+
+    /**
+     * @hide
+     */
+    @SuppressWarnings("HiddenAbstractMethod")
+    @UnsupportedAppUsage
+    public abstract boolean isEmpty();
+
+    /**
+     * Invalidate this object.  The location area code and the cell id are set to -1.
+     * @hide
+     */
+    @SuppressWarnings("HiddenAbstractMethod")
+    public abstract void setStateInvalid();
+
+    /**
+     * Return a new CellLocation object representing an unknown
+     * location, or null for unknown/none phone radio types.
+     *
+     */
+    public static CellLocation getEmpty() {
+        // TelephonyManager.getDefault().getCurrentPhoneType() handles the case when
+        // ITelephony interface is not up yet.
+        switch(TelephonyManager.getDefault().getCurrentPhoneType()) {
+        case PhoneConstants.PHONE_TYPE_CDMA:
+            return new CdmaCellLocation();
+        case PhoneConstants.PHONE_TYPE_GSM:
+            return new GsmCellLocation();
+        default:
+            return null;
+        }
+    }
+}
diff --git a/android-35/android/telephony/CellSignalStrength.java b/android-35/android/telephony/CellSignalStrength.java
new file mode 100644
index 0000000..b9b9680
--- /dev/null
+++ b/android-35/android/telephony/CellSignalStrength.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.annotation.IntRange;
+import android.annotation.SystemApi;
+import android.os.PersistableBundle;
+
+/**
+ * Abstract base class for cell phone signal strength related information.
+ */
[email protected]
+public abstract class CellSignalStrength {
+
+    public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN =
+            TelephonyProtoEnums.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; // 0
+
+    public static final int SIGNAL_STRENGTH_POOR =
+            TelephonyProtoEnums.SIGNAL_STRENGTH_POOR; // 1
+
+    public static final int SIGNAL_STRENGTH_MODERATE =
+            TelephonyProtoEnums.SIGNAL_STRENGTH_MODERATE; // 2
+
+    public static final int SIGNAL_STRENGTH_GOOD =
+            TelephonyProtoEnums.SIGNAL_STRENGTH_GOOD; // 3
+
+    public static final int SIGNAL_STRENGTH_GREAT =
+            TelephonyProtoEnums.SIGNAL_STRENGTH_GREAT; // 4
+
+    /** @hide */
+    public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
+
+    /** @hide */
+    protected static final int NUM_SIGNAL_STRENGTH_THRESHOLDS = NUM_SIGNAL_STRENGTH_BINS - 1;
+
+    /** @hide */
+    protected CellSignalStrength() {
+    }
+
+    /** @hide */
+    public abstract void setDefaultValues();
+
+    /**
+     * Retrieve an abstract level value for the overall signal quality.
+     *
+     * @return a single integer from 0 to 4 representing the general signal quality.
+     *     0 represents very poor or unknown signal quality while 4 represents excellent
+     *     signal quality.
+     */
+    @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT)
+    public abstract int getLevel();
+
+    /**
+     * Get the technology-specific signal strength in Arbitrary Strength Units, calculated from the
+     * strength of the pilot signal or equivalent.
+     */
+    public abstract int getAsuLevel();
+
+    /**
+     * Get the technology-specific signal strength in dBm, which is the signal strength of the
+     * pilot signal or equivalent.
+     */
+    public abstract int getDbm();
+
+    /**
+     * Copies the CellSignalStrength.
+     *
+     * @return A deep copy of this class.
+     * @hide
+     */
+    public abstract CellSignalStrength copy();
+
+    /**
+     * Checks and returns whether there are any non-default values in this CellSignalStrength.
+     *
+     * Checks all the values in the subclass of CellSignalStrength and returns true if any of them
+     * have been set to a value other than their default.
+     *
+     * @hide
+     */
+    public abstract boolean isValid();
+
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    public abstract boolean equals (Object o);
+
+    /**
+     * Calculate and set the carrier-influenced values such as the signal "Level".
+     *
+     * @hide
+     */
+    public abstract void updateLevel(PersistableBundle cc, ServiceState ss);
+
+    // Range for RSSI in ASU (0-31, 99) as defined in TS 27.007 8.69
+    /** @hide */
+    public static final int getRssiDbmFromAsu(int asu) {
+        if (asu > 31 || asu < 0) return CellInfo.UNAVAILABLE;
+        return -113 + (2 * asu);
+    }
+
+    // Range for RSSI in ASU (0-31, 99) as defined in TS 27.007 8.69
+    /** @hide */
+    protected static final int getAsuFromRssiDbm(int dbm) {
+        if (dbm == CellInfo.UNAVAILABLE) return 99;
+        return (dbm + 113) / 2;
+    }
+
+    // Range for RSCP in ASU (0-96, 255) as defined in TS 27.007 8.69
+    /** @hide */
+    public static final int getRscpDbmFromAsu(int asu) {
+        if (asu > 96 || asu < 0) return CellInfo.UNAVAILABLE;
+        return asu - 120;
+    }
+
+    // Range for RSCP in ASU (0-96, 255) as defined in TS 27.007 8.69
+    /** @hide */
+    protected static final int getAsuFromRscpDbm(int dbm) {
+        if (dbm == CellInfo.UNAVAILABLE) return 255;
+        return dbm + 120;
+    }
+
+    // Range for SNR in ASU (0-49, 255) as defined in TS 27.007 8.69
+    /** @hide */
+    public static final int getEcNoDbFromAsu(int asu) {
+        if (asu > 49 || asu < 0) return CellInfo.UNAVAILABLE;
+        return -24 + (asu / 2);
+    }
+
+    /** @hide */
+    protected static final int inRangeOrUnavailable(int value, int rangeMin, int rangeMax) {
+        if (value < rangeMin || value > rangeMax) return CellInfo.UNAVAILABLE;
+        return value;
+    }
+
+    /** @hide */
+    protected static final int inRangeOrUnavailable(
+            int value, int rangeMin, int rangeMax, int special) {
+        if ((value < rangeMin || value > rangeMax) && value != special) return CellInfo.UNAVAILABLE;
+        return value;
+    }
+
+    /**
+     * Returns the number of signal strength levels.
+     * @return Number of signal strength levels, currently defined in the HAL as 5.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static int getNumSignalStrengthLevels() {
+        return NUM_SIGNAL_STRENGTH_BINS;
+    }
+}
diff --git a/android-35/android/telephony/CellSignalStrengthCdma.java b/android-35/android/telephony/CellSignalStrengthCdma.java
new file mode 100644
index 0000000..5298e67
--- /dev/null
+++ b/android-35/android/telephony/CellSignalStrengthCdma.java
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.annotation.IntRange;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import com.android.telephony.Rlog;
+
+import java.util.Objects;
+
+/**
+ * Signal strength related information.
+ */
+public final class CellSignalStrengthCdma extends CellSignalStrength implements Parcelable {
+
+    private static final String LOG_TAG = "CellSignalStrengthCdma";
+    private static final boolean DBG = false;
+
+    private int mCdmaDbm;   // This value is the RSSI value
+    private int mCdmaEcio;  // This value is the Ec/Io
+    private int mEvdoDbm;   // This value is the EVDO RSSI value
+    private int mEvdoEcio;  // This value is the EVDO Ec/Io
+    private int mEvdoSnr;   // Valid values are 0-8.  8 is the highest signal to noise ratio
+    private int mLevel;
+
+    /** @hide */
+    public CellSignalStrengthCdma() {
+        setDefaultValues();
+    }
+
+    /**
+     * SignalStrength constructor for input from the HAL.
+     *
+     * Note that values received from the HAL require coersion to be compatible here. All values
+     * reported through IRadio are the negative of the actual values (which results in a positive
+     * input to this method.
+     *
+     * <p>Note that this HAL is inconsistent with UMTS-based radio techs as the value indicating
+     * that a field is unreported is negative, rather than a large(r) positive number.
+     * <p>Also note that to keep the public-facing methods of this class consistent with others,
+     * unreported values are coerced to {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}
+     * rather than left as -1, which is a departure from SignalStrength, which is stuck with the
+     * values it currently reports.
+     *
+     * @param cdmaDbm CDMA signal strength value or CellInfo.UNAVAILABLE if invalid.
+     * @param cdmaEcio CDMA pilot/noise ratio or CellInfo.UNAVAILABLE  if invalid.
+     * @param evdoDbm negative of the EvDO signal strength value or CellInfo.UNAVAILABLE if invalid.
+     * @param evdoEcio negative of the EvDO pilot/noise ratio or CellInfo.UNAVAILABLE if invalid.
+     * @param evdoSnr an SNR value 0..8 or CellInfo.UNVAILABLE if invalid.
+     * @hide
+     */
+    public CellSignalStrengthCdma(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio,
+            int evdoSnr) {
+        mCdmaDbm = inRangeOrUnavailable(cdmaDbm, -120, 0);
+        mCdmaEcio = inRangeOrUnavailable(cdmaEcio, -160, 0);
+        mEvdoDbm = inRangeOrUnavailable(evdoDbm, -120, 0);
+        mEvdoEcio = inRangeOrUnavailable(evdoEcio, -160, 0);
+        mEvdoSnr = inRangeOrUnavailable(evdoSnr, 0, 8);
+
+        updateLevel(null, null);
+    }
+
+    /** @hide */
+    public CellSignalStrengthCdma(CellSignalStrengthCdma s) {
+        copyFrom(s);
+    }
+
+    /** @hide */
+    protected void copyFrom(CellSignalStrengthCdma s) {
+        mCdmaDbm = s.mCdmaDbm;
+        mCdmaEcio = s.mCdmaEcio;
+        mEvdoDbm = s.mEvdoDbm;
+        mEvdoEcio = s.mEvdoEcio;
+        mEvdoSnr = s.mEvdoSnr;
+        mLevel = s.mLevel;
+    }
+
+    /** @hide */
+    @Override
+    public CellSignalStrengthCdma copy() {
+        return new CellSignalStrengthCdma(this);
+    }
+
+    /** @hide */
+    @Override
+    public void setDefaultValues() {
+        mCdmaDbm = CellInfo.UNAVAILABLE;
+        mCdmaEcio = CellInfo.UNAVAILABLE;
+        mEvdoDbm = CellInfo.UNAVAILABLE;
+        mEvdoEcio = CellInfo.UNAVAILABLE;
+        mEvdoSnr = CellInfo.UNAVAILABLE;
+        mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT)
+    public int getLevel() {
+        return mLevel;
+    }
+
+    /** @hide */
+    @Override
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
+        int cdmaLevel = getCdmaLevel();
+        int evdoLevel = getEvdoLevel();
+        if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+            /* We don't know evdo, use cdma */
+            mLevel = getCdmaLevel();
+        } else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+            /* We don't know cdma, use evdo */
+            mLevel = getEvdoLevel();
+        } else {
+            /* We know both, use the lowest level */
+            mLevel = cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
+        }
+    }
+
+    /**
+     * Get the 1xRTT Level in (Android) ASU.
+     *
+     * There is no standard definition of ASU for CDMA; however, Android defines it as the
+     * the lesser of the following two results (for 1xRTT):
+     * <table>
+     *     <thead><tr><th>RSSI Range (dBm)</th><th>ASU Value</th></tr><thead>
+     *     <tbody>
+     *         <tr><td>-75..</td><td>16</td></tr>
+     *         <tr><td>-82..-76</td><td>8</td></tr>
+     *         <tr><td>-90..-83</td><td>4</td></tr>
+     *         <tr><td>-95..-91</td><td>2</td></tr>
+     *         <tr><td>-100..-96</td><td>1</td></tr>
+     *         <tr><td>..-101</td><td>99</td></tr>
+     *     </tbody>
+     * </table>
+     * <table>
+     *     <thead><tr><th>Ec/Io Range (dB)</th><th>ASU Value</th></tr><thead>
+     *     <tbody>
+     *         <tr><td>-90..</td><td>16</td></tr>
+     *         <tr><td>-100..-91</td><td>8</td></tr>
+     *         <tr><td>-115..-101</td><td>4</td></tr>
+     *         <tr><td>-130..-116</td><td>2</td></tr>
+     *         <tr><td>--150..-131</td><td>1</td></tr>
+     *         <tr><td>..-151</td><td>99</td></tr>
+     *     </tbody>
+     * </table>
+     * @return 1xRTT Level in Android ASU {1,2,4,8,16,99}
+     */
+    @Override
+    public int getAsuLevel() {
+        final int cdmaDbm = getCdmaDbm();
+        final int cdmaEcio = getCdmaEcio();
+        int cdmaAsuLevel;
+        int ecioAsuLevel;
+
+        if (cdmaDbm == CellInfo.UNAVAILABLE) cdmaAsuLevel = 99;
+        else if (cdmaDbm >= -75) cdmaAsuLevel = 16;
+        else if (cdmaDbm >= -82) cdmaAsuLevel = 8;
+        else if (cdmaDbm >= -90) cdmaAsuLevel = 4;
+        else if (cdmaDbm >= -95) cdmaAsuLevel = 2;
+        else if (cdmaDbm >= -100) cdmaAsuLevel = 1;
+        else cdmaAsuLevel = 99;
+
+        // Ec/Io are in dB*10
+        if (cdmaEcio == CellInfo.UNAVAILABLE) ecioAsuLevel = 99;
+        else if (cdmaEcio >= -90) ecioAsuLevel = 16;
+        else if (cdmaEcio >= -100) ecioAsuLevel = 8;
+        else if (cdmaEcio >= -115) ecioAsuLevel = 4;
+        else if (cdmaEcio >= -130) ecioAsuLevel = 2;
+        else if (cdmaEcio >= -150) ecioAsuLevel = 1;
+        else ecioAsuLevel = 99;
+
+        int level = (cdmaAsuLevel < ecioAsuLevel) ? cdmaAsuLevel : ecioAsuLevel;
+        if (DBG) log("getAsuLevel=" + level);
+        return level;
+    }
+
+    /**
+     * Get cdma as level 0..4
+     */
+    public int getCdmaLevel() {
+        final int cdmaDbm = getCdmaDbm();
+        final int cdmaEcio = getCdmaEcio();
+        int levelDbm;
+        int levelEcio;
+
+        if (cdmaDbm == CellInfo.UNAVAILABLE) levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        else if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT;
+        else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD;
+        else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE;
+        else if (cdmaDbm >= -100) levelDbm = SIGNAL_STRENGTH_POOR;
+        else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+        // Ec/Io are in dB*10
+        if (cdmaEcio == CellInfo.UNAVAILABLE) levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        else if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT;
+        else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD;
+        else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE;
+        else if (cdmaEcio >= -150) levelEcio = SIGNAL_STRENGTH_POOR;
+        else levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+        int level = (levelDbm < levelEcio) ? levelDbm : levelEcio;
+        if (DBG) log("getCdmaLevel=" + level);
+        return level;
+    }
+
+    /**
+     * Get Evdo as level 0..4
+     */
+    public int getEvdoLevel() {
+        int evdoDbm = getEvdoDbm();
+        int evdoSnr = getEvdoSnr();
+        int levelEvdoDbm;
+        int levelEvdoSnr;
+
+        if (evdoDbm == CellInfo.UNAVAILABLE) levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        else if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT;
+        else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD;
+        else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE;
+        else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR;
+        else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+        if (evdoSnr == CellInfo.UNAVAILABLE) levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        else if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT;
+        else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD;
+        else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE;
+        else if (evdoSnr >= 1) levelEvdoSnr = SIGNAL_STRENGTH_POOR;
+        else levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+        int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+        if (DBG) log("getEvdoLevel=" + level);
+        return level;
+    }
+
+    /**
+     * Get the EVDO Level in (Android) ASU.
+     *
+     * There is no standard definition of ASU for CDMA; however, Android defines it as the
+     * the lesser of the following two results (for EVDO):
+     * <table>
+     *     <thead><tr><th>RSSI Range (dBm)</th><th>ASU Value</th></tr><thead>
+     *     <tbody>
+     *         <tr><td>-65..</td><td>16</td></tr>
+     *         <tr><td>-75..-66</td><td>8</td></tr>
+     *         <tr><td>-85..-76</td><td>4</td></tr>
+     *         <tr><td>-95..-86</td><td>2</td></tr>
+     *         <tr><td>-105..-96</td><td>1</td></tr>
+     *         <tr><td>..-106</td><td>99</td></tr>
+     *     </tbody>
+     * </table>
+     * <table>
+     *     <thead><tr><th>SNR Range (unitless)</th><th>ASU Value</th></tr><thead>
+     *     <tbody>
+     *         <tr><td>7..</td><td>16</td></tr>
+     *         <tr><td>6</td><td>8</td></tr>
+     *         <tr><td>5</td><td>4</td></tr>
+     *         <tr><td>3..4</td><td>2</td></tr>
+     *         <tr><td>1..2</td><td>1</td></tr>
+     *         <tr><td>0</td><td>99</td></tr>
+     *     </tbody>
+     * </table>
+     *
+     * @return EVDO Level in Android ASU {1,2,4,8,16,99}
+     *
+     * @hide
+     */
+    public int getEvdoAsuLevel() {
+        int evdoDbm = getEvdoDbm();
+        int evdoSnr = getEvdoSnr();
+        int levelEvdoDbm;
+        int levelEvdoSnr;
+
+        if (evdoDbm >= -65) levelEvdoDbm = 16;
+        else if (evdoDbm >= -75) levelEvdoDbm = 8;
+        else if (evdoDbm >= -85) levelEvdoDbm = 4;
+        else if (evdoDbm >= -95) levelEvdoDbm = 2;
+        else if (evdoDbm >= -105) levelEvdoDbm = 1;
+        else levelEvdoDbm = 99;
+
+        if (evdoSnr >= 7) levelEvdoSnr = 16;
+        else if (evdoSnr >= 6) levelEvdoSnr = 8;
+        else if (evdoSnr >= 5) levelEvdoSnr = 4;
+        else if (evdoSnr >= 3) levelEvdoSnr = 2;
+        else if (evdoSnr >= 1) levelEvdoSnr = 1;
+        else levelEvdoSnr = 99;
+
+        int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+        if (DBG) log("getEvdoAsuLevel=" + level);
+        return level;
+    }
+
+    /**
+     * Get the signal strength as dBm
+     *
+     * @return min(CDMA RSSI, EVDO RSSI) of the measured cell.
+     */
+    @Override
+    public int getDbm() {
+        int cdmaDbm = getCdmaDbm();
+        int evdoDbm = getEvdoDbm();
+
+        // Use the lower value to be conservative
+        return (cdmaDbm < evdoDbm) ? cdmaDbm : evdoDbm;
+    }
+
+    /**
+     * Get the CDMA RSSI value in dBm
+     */
+    public int getCdmaDbm() {
+        return mCdmaDbm;
+    }
+
+    /** @hide */
+    public void setCdmaDbm(int cdmaDbm) {
+        mCdmaDbm = cdmaDbm;
+    }
+
+    /**
+     * Get the CDMA Ec/Io value in dB*10
+     */
+    public int getCdmaEcio() {
+        return mCdmaEcio;
+    }
+
+    /** @hide */
+    public void setCdmaEcio(int cdmaEcio) {
+        mCdmaEcio = cdmaEcio;
+    }
+
+    /**
+     * Get the EVDO RSSI value in dBm
+     */
+    public int getEvdoDbm() {
+        return mEvdoDbm;
+    }
+
+    /** @hide */
+    public void setEvdoDbm(int evdoDbm) {
+        mEvdoDbm = evdoDbm;
+    }
+
+    /**
+     * Get the EVDO Ec/Io value in dB*10
+     */
+    public int getEvdoEcio() {
+        return mEvdoEcio;
+    }
+
+    /** @hide */
+    public void setEvdoEcio(int evdoEcio) {
+        mEvdoEcio = evdoEcio;
+    }
+
+    /**
+     * Get the signal to noise ratio. Valid values are 0-8. 8 is the highest.
+     */
+    public int getEvdoSnr() {
+        return mEvdoSnr;
+    }
+
+    /** @hide */
+    public void setEvdoSnr(int evdoSnr) {
+        mEvdoSnr = evdoSnr;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCdmaDbm, mCdmaEcio, mEvdoDbm, mEvdoEcio, mEvdoSnr, mLevel);
+    }
+
+    private static final CellSignalStrengthCdma sInvalid = new CellSignalStrengthCdma();
+
+    /** @hide */
+    @Override
+    public boolean isValid() {
+        return !this.equals(sInvalid);
+    }
+
+    @Override
+    public boolean equals (Object o) {
+        CellSignalStrengthCdma s;
+        if (!(o instanceof CellSignalStrengthCdma)) return false;
+        s = (CellSignalStrengthCdma) o;
+
+        return mCdmaDbm == s.mCdmaDbm
+                && mCdmaEcio == s.mCdmaEcio
+                && mEvdoDbm == s.mEvdoDbm
+                && mEvdoEcio == s.mEvdoEcio
+                && mEvdoSnr == s.mEvdoSnr
+                && mLevel == s.mLevel;
+    }
+
+    /**
+     * @return string representation.
+     */
+    @Override
+    public String toString() {
+        return "CellSignalStrengthCdma:"
+                + " cdmaDbm=" + mCdmaDbm
+                + " cdmaEcio=" + mCdmaEcio
+                + " evdoDbm=" + mEvdoDbm
+                + " evdoEcio=" + mEvdoEcio
+                + " evdoSnr=" + mEvdoSnr
+                + " level=" + mLevel;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        dest.writeInt(mCdmaDbm);
+        dest.writeInt(mCdmaEcio);
+        dest.writeInt(mEvdoDbm);
+        dest.writeInt(mEvdoEcio);
+        dest.writeInt(mEvdoSnr);
+        dest.writeInt(mLevel);
+    }
+
+    /**
+     * Construct a SignalStrength object from the given parcel
+     * where the TYPE_CDMA token is already been processed.
+     */
+    private CellSignalStrengthCdma(Parcel in) {
+        // CdmaDbm, CdmaEcio, EvdoDbm and EvdoEcio are written into
+        // the parcel as positive values.
+        // Need to convert into negative values unless the value is invalid
+        mCdmaDbm = in.readInt();
+        mCdmaEcio = in.readInt();
+        mEvdoDbm = in.readInt();
+        mEvdoEcio = in.readInt();
+        mEvdoSnr = in.readInt();
+        mLevel = in.readInt();
+        if (DBG) log("CellSignalStrengthCdma(Parcel): " + toString());
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @SuppressWarnings("hiding")
+    public static final @android.annotation.NonNull Parcelable.Creator<CellSignalStrengthCdma> CREATOR =
+            new Parcelable.Creator<CellSignalStrengthCdma>() {
+        @Override
+        public CellSignalStrengthCdma createFromParcel(Parcel in) {
+            return new CellSignalStrengthCdma(in);
+        }
+
+        @Override
+        public CellSignalStrengthCdma[] newArray(int size) {
+            return new CellSignalStrengthCdma[size];
+        }
+    };
+
+    /**
+     * log
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/CellSignalStrengthGsm.java b/android-35/android/telephony/CellSignalStrengthGsm.java
new file mode 100644
index 0000000..7b78084
--- /dev/null
+++ b/android-35/android/telephony/CellSignalStrengthGsm.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.annotation.IntRange;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import com.android.telephony.Rlog;
+
+import java.util.Objects;
+
+/**
+ * GSM signal strength related information.
+ */
+public final class CellSignalStrengthGsm extends CellSignalStrength implements Parcelable {
+
+    private static final String LOG_TAG = "CellSignalStrengthGsm";
+    private static final boolean DBG = false;
+
+    private static final int GSM_RSSI_MAX = -51;
+    private static final int GSM_RSSI_GREAT = -89;
+    private static final int GSM_RSSI_GOOD = -97;
+    private static final int GSM_RSSI_MODERATE = -103;
+    private static final int GSM_RSSI_POOR = -107;
+    private static final int GSM_RSSI_MIN = -113;
+
+    private static final int[] sRssiThresholds = new int[] {
+            GSM_RSSI_POOR, GSM_RSSI_MODERATE, GSM_RSSI_GOOD, GSM_RSSI_GREAT};
+
+    private int mRssi; // in dBm [-113, -51] or UNAVAILABLE
+    @UnsupportedAppUsage
+    private int mBitErrorRate; // bit error rate (0-7, 99) TS 27.007 8.5 or UNAVAILABLE
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private int mTimingAdvance; // range from 0-219 or CellInfo.UNAVAILABLE if unknown
+    private int mLevel;
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public CellSignalStrengthGsm() {
+        setDefaultValues();
+    }
+
+    /** @hide */
+    public CellSignalStrengthGsm(int rssi, int ber, int ta) {
+        mRssi = inRangeOrUnavailable(rssi, GSM_RSSI_MIN, GSM_RSSI_MAX);
+        mBitErrorRate = inRangeOrUnavailable(ber, 0, 7, 99);
+        mTimingAdvance = inRangeOrUnavailable(ta, 0, 219);
+        updateLevel(null, null);
+    }
+
+    /** @hide */
+    public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
+        copyFrom(s);
+    }
+
+    /** @hide */
+    protected void copyFrom(CellSignalStrengthGsm s) {
+        mRssi = s.mRssi;
+        mBitErrorRate = s.mBitErrorRate;
+        mTimingAdvance = s.mTimingAdvance;
+        mLevel = s.mLevel;
+    }
+
+    /** @hide */
+    @Override
+    public CellSignalStrengthGsm copy() {
+        return new CellSignalStrengthGsm(this);
+    }
+
+    /** @hide */
+    @Override
+    public void setDefaultValues() {
+        mRssi = CellInfo.UNAVAILABLE;
+        mBitErrorRate = CellInfo.UNAVAILABLE;
+        mTimingAdvance = CellInfo.UNAVAILABLE;
+        mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT)
+    public int getLevel() {
+        return mLevel;
+    }
+
+    /** @hide */
+    @Override
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
+        int[] rssiThresholds;
+        if (cc == null) {
+            rssiThresholds = sRssiThresholds;
+        } else {
+            rssiThresholds = cc.getIntArray(CarrierConfigManager.KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY);
+            if (rssiThresholds == null || rssiThresholds.length != NUM_SIGNAL_STRENGTH_THRESHOLDS) {
+                rssiThresholds = sRssiThresholds;
+            }
+        }
+        int level = NUM_SIGNAL_STRENGTH_THRESHOLDS;
+        if (mRssi < GSM_RSSI_MIN || mRssi > GSM_RSSI_MAX) {
+            mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+            return;
+        }
+        while (level > 0 && mRssi < rssiThresholds[level - 1]) level--;
+        mLevel = level;
+    }
+
+    /**
+     * Get the GSM timing advance between 0..219 symbols (normally 0..63).
+     * <p>{@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} is reported when there is no RR
+     * connection. Refer to 3GPP 45.010 Sec 5.8.
+     *
+     * @return the current GSM timing advance, if available.
+     */
+    public int getTimingAdvance() {
+        return mTimingAdvance;
+    }
+
+    /**
+     * Get the signal strength as dBm.
+     *
+     * @return the RSSI of the measured cell.
+     */
+    @Override
+    public int getDbm() {
+        return mRssi;
+    }
+
+    /**
+     * Get the RSSI in ASU.
+     *
+     * Asu is calculated based on 3GPP RSSI. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+     *
+     * @return RSSI in ASU 0..31, 99, or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}.
+     */
+    @Override
+    public int getAsuLevel() {
+        return getAsuFromRssiDbm(mRssi);
+    }
+
+    /**
+     * Return the Received Signal Strength Indicator.
+     *
+     * @return the RSSI in dBm (-113, -51) or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}.
+     */
+    public int getRssi() {
+        return mRssi;
+    }
+
+    /**
+     * Return the Bit Error Rate.
+     *
+     * @return the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}.
+     */
+    public int getBitErrorRate() {
+        return mBitErrorRate;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRssi, mBitErrorRate, mTimingAdvance);
+    }
+
+    private static final CellSignalStrengthGsm sInvalid = new CellSignalStrengthGsm();
+
+    /** @hide */
+    @Override
+    public boolean isValid() {
+        return !this.equals(sInvalid);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof CellSignalStrengthGsm)) return false;
+        CellSignalStrengthGsm s = (CellSignalStrengthGsm) o;
+
+        return mRssi == s.mRssi
+                && mBitErrorRate == s.mBitErrorRate
+                && mTimingAdvance == s.mTimingAdvance
+                && mLevel == s.mLevel;
+    }
+
+    /**
+     * @return string representation.
+     */
+    @Override
+    public String toString() {
+        return "CellSignalStrengthGsm:"
+                + " rssi=" + mRssi
+                + " ber=" + mBitErrorRate
+                + " mTa=" + mTimingAdvance
+                + " mLevel=" + mLevel;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        dest.writeInt(mRssi);
+        dest.writeInt(mBitErrorRate);
+        dest.writeInt(mTimingAdvance);
+        dest.writeInt(mLevel);
+    }
+
+    /**
+     * Construct a SignalStrength object from the given parcel
+     * where the token is already been processed.
+     */
+    private CellSignalStrengthGsm(Parcel in) {
+        mRssi = in.readInt();
+        mBitErrorRate = in.readInt();
+        mTimingAdvance = in.readInt();
+        mLevel = in.readInt();
+        if (DBG) log("CellSignalStrengthGsm(Parcel): " + toString());
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @SuppressWarnings("hiding")
+    public static final @android.annotation.NonNull Parcelable.Creator<CellSignalStrengthGsm> CREATOR =
+            new Parcelable.Creator<CellSignalStrengthGsm>() {
+        @Override
+        public CellSignalStrengthGsm createFromParcel(Parcel in) {
+            return new CellSignalStrengthGsm(in);
+        }
+
+        @Override
+        public CellSignalStrengthGsm[] newArray(int size) {
+            return new CellSignalStrengthGsm[size];
+        }
+    };
+
+    /**
+     * log
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/CellSignalStrengthLte.java b/android-35/android/telephony/CellSignalStrengthLte.java
new file mode 100644
index 0000000..f528263
--- /dev/null
+++ b/android-35/android/telephony/CellSignalStrengthLte.java
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.annotation.IntRange;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import com.android.telephony.Rlog;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * LTE signal strength related information.
+ */
+public final class CellSignalStrengthLte extends CellSignalStrength implements Parcelable {
+
+    private static final String LOG_TAG = "CellSignalStrengthLte";
+    private static final boolean DBG = false;
+
+    /**
+     * Indicates the unknown or undetectable RSSI value in ASU.
+     *
+     * Reference: TS 27.007 8.5 - Signal quality +CSQ
+     */
+    private static final int SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN = 99;
+    /**
+     * Indicates the maximum valid RSSI value in ASU.
+     *
+     * Reference: TS 27.007 8.5 - Signal quality +CSQ
+     */
+    private static final int SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MAX_VALUE = 31;
+    /**
+     * Indicates the minimum valid RSSI value in ASU.
+     *
+     * Reference: TS 27.007 8.5 - Signal quality +CSQ
+     */
+    private static final int SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE = 0;
+
+    private static final int MAX_LTE_RSRP = -44;
+    private static final int MIN_LTE_RSRP = -140;
+
+    /**
+     * Indicates RSRP is considered for {@link #getLevel()} and reported from modem.
+     *
+     * @hide
+     */
+    public static final int USE_RSRP = 1 << 0;
+    /**
+     * Indicates RSRQ is considered for {@link #getLevel()} and reported from modem.
+     *
+     * @hide
+     */
+    public static final int USE_RSRQ = 1 << 1;
+    /**
+     * Indicates RSSNR is considered for {@link #getLevel()} and reported from modem.
+     *
+     * @hide
+     */
+    public static final int USE_RSSNR = 1 << 2;
+
+    @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
+    private int mSignalStrength; // To be removed
+    private int mRssi;
+    @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
+    private int mRsrp;
+    @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
+    private int mRsrq;
+    @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
+    private int mRssnr;
+    /**
+     * CSI channel quality indicator (CQI) table index. There are multiple CQI tables.
+     * The definition of CQI in each table is different.
+     *
+     * Reference: 3GPP TS 136.213 section 7.2.3.
+     *
+     * Range [1, 6].
+     */
+    private int mCqiTableIndex;
+    @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
+    private int mCqi;
+    @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
+    private int mTimingAdvance;
+    private int mLevel;
+
+    /**
+     * Bit-field integer to determine whether to use Reference Signal Received Power (RSRP),
+     * Reference Signal Received Quality (RSRQ), and/or Reference Signal Signal to Noise Ratio
+     * (RSSNR) for the number of LTE signal bars. If multiple measures are set, the parameter
+     * whose signal level value is smallest is used to indicate the signal level.
+     *
+     *  RSRP = 1 << 0,
+     *  RSRQ = 1 << 1,
+     *  RSSNR = 1 << 2,
+     *
+     * For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
+     * If the key is invalid or not configured, a default value (RSRP = 1 << 0) will apply.
+     */
+    private int mParametersUseForLevel;
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public CellSignalStrengthLte() {
+        setDefaultValues();
+    }
+
+    /**
+     * Construct a cell signal strength
+     *
+     * @param rssi in dBm [-113,-51], {@link CellInfo#UNAVAILABLE}
+     * @param rsrp in dBm [-140,-43], {@link CellInfo#UNAVAILABLE}
+     * @param rsrq in dB [-34, 3], {@link CellInfo#UNAVAILABLE}
+     * @param rssnr in dB [-20, +30], {@link CellInfo#UNAVAILABLE}
+     * @param cqiTableIndex [1, 6], {@link CellInfo#UNAVAILABLE}
+     * @param cqi [0, 15], {@link CellInfo#UNAVAILABLE}
+     * @param timingAdvance [0, 1282], {@link CellInfo#UNAVAILABLE}
+     *
+     */
+    /** @hide */
+    public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqiTableIndex,
+            int cqi, int timingAdvance) {
+        mRssi = inRangeOrUnavailable(rssi, -113, -51);
+        mSignalStrength = mRssi;
+        mRsrp = inRangeOrUnavailable(rsrp, -140, -43);
+        mRsrq = inRangeOrUnavailable(rsrq, -34, 3);
+        mRssnr = inRangeOrUnavailable(rssnr, -20, 30);
+        mCqiTableIndex = inRangeOrUnavailable(cqiTableIndex, 1, 6);
+        mCqi = inRangeOrUnavailable(cqi, 0, 15);
+        mTimingAdvance = inRangeOrUnavailable(timingAdvance, 0, 1282);
+        updateLevel(null, null);
+    }
+
+    /**
+     * Construct a cell signal strength
+     *
+     * @param rssi in dBm [-113,-51], {@link CellInfo#UNAVAILABLE}
+     * @param rsrp in dBm [-140,-43], {@link CellInfo#UNAVAILABLE}
+     * @param rsrq in dB [-34, 3], {@link CellInfo#UNAVAILABLE}
+     * @param rssnr in dB [-20, +30], {@link CellInfo#UNAVAILABLE}
+     * @param cqi [0, 15], {@link CellInfo#UNAVAILABLE}
+     * @param timingAdvance [0, 1282], {@link CellInfo#UNAVAILABLE}
+     *
+     */
+    /** @hide */
+    public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi,
+            int timingAdvance) {
+        this(rssi, rsrp, rsrq, rssnr, CellInfo.UNAVAILABLE, cqi, timingAdvance);
+    }
+
+    /** @hide */
+    public CellSignalStrengthLte(CellSignalStrengthLte s) {
+        copyFrom(s);
+    }
+
+    /** @hide */
+    protected void copyFrom(CellSignalStrengthLte s) {
+        mSignalStrength = s.mSignalStrength;
+        mRssi = s.mRssi;
+        mRsrp = s.mRsrp;
+        mRsrq = s.mRsrq;
+        mRssnr = s.mRssnr;
+        mCqiTableIndex = s.mCqiTableIndex;
+        mCqi = s.mCqi;
+        mTimingAdvance = s.mTimingAdvance;
+        mLevel = s.mLevel;
+        mParametersUseForLevel = s.mParametersUseForLevel;
+    }
+
+    /** @hide */
+    @Override
+    public CellSignalStrengthLte copy() {
+        return new CellSignalStrengthLte(this);
+    }
+
+    /** @hide */
+    @Override
+    public void setDefaultValues() {
+        mSignalStrength = CellInfo.UNAVAILABLE;
+        mRssi = CellInfo.UNAVAILABLE;
+        mRsrp = CellInfo.UNAVAILABLE;
+        mRsrq = CellInfo.UNAVAILABLE;
+        mRssnr = CellInfo.UNAVAILABLE;
+        mCqiTableIndex = CellInfo.UNAVAILABLE;
+        mCqi = CellInfo.UNAVAILABLE;
+        mTimingAdvance = CellInfo.UNAVAILABLE;
+        mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        mParametersUseForLevel = USE_RSRP;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT)
+    public int getLevel() {
+        return mLevel;
+    }
+
+    // Lifted from Default carrier configs and max range of RSRP
+    private static final int[] sRsrpThresholds = new int[] {
+            -115, /* SIGNAL_STRENGTH_POOR */
+            -105, /* SIGNAL_STRENGTH_MODERATE */
+            -95,  /* SIGNAL_STRENGTH_GOOD */
+            -85   /* SIGNAL_STRENGTH_GREAT */
+    };
+
+    // Lifted from Default carrier configs and max range of RSRQ
+    private static final int[] sRsrqThresholds = new int[] {
+            -19, /* SIGNAL_STRENGTH_POOR */
+            -17, /* SIGNAL_STRENGTH_MODERATE */
+            -14, /* SIGNAL_STRENGTH_GOOD */
+            -12  /* SIGNAL_STRENGTH_GREAT */
+    };
+    // Lifted from Default carrier configs and max range of RSSNR
+    private static final int[] sRssnrThresholds = new int[] {
+            -3, /* SIGNAL_STRENGTH_POOR */
+            1,  /* SIGNAL_STRENGTH_MODERATE */
+            5,  /* SIGNAL_STRENGTH_GOOD */
+            13  /* SIGNAL_STRENGTH_GREAT */
+    };
+    private static final int sRsrpBoost = 0;
+
+    /**
+     * Checks if the given parameter type is considered to use for {@link #getLevel()}.
+     *
+     * Note: if multiple parameter types are considered, the smaller level for one of the
+     * parameters would be returned by {@link #getLevel()}
+     *
+     * @param parameterType bitwise OR of {@link #USE_RSRP}, {@link #USE_RSRQ},
+     *         {@link #USE_RSSNR}
+     * @return {@code true} if the level is calculated based on the given parameter type;
+     *      {@code false} otherwise.
+     */
+    private boolean isLevelForParameter(int parameterType) {
+        return (parameterType & mParametersUseForLevel) == parameterType;
+    }
+
+    /** @hide */
+    @Override
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
+        int[] rsrpThresholds, rsrqThresholds, rssnrThresholds;
+        boolean rsrpOnly;
+        if (cc == null) {
+            mParametersUseForLevel = USE_RSRP;
+            rsrpThresholds = sRsrpThresholds;
+            rsrqThresholds = sRsrqThresholds;
+            rssnrThresholds = sRssnrThresholds;
+            rsrpOnly = false;
+        } else {
+            if (ss != null && ss.isUsingNonTerrestrialNetwork()) {
+                if (DBG) log("updateLevel: from NTN_LTE");
+                mParametersUseForLevel = cc.getInt(
+                        CarrierConfigManager.KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT);
+                rsrpThresholds = cc.getIntArray(
+                        CarrierConfigManager.KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY);
+                rsrqThresholds = cc.getIntArray(
+                        CarrierConfigManager.KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY);
+                rssnrThresholds = cc.getIntArray(
+                        CarrierConfigManager.KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY);
+            } else {
+                mParametersUseForLevel = cc.getInt(
+                        CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT);
+                rsrpThresholds = cc.getIntArray(
+                        CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
+                rsrqThresholds = cc.getIntArray(
+                        CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY);
+                rssnrThresholds = cc.getIntArray(
+                        CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY);
+            }
+            if (rsrpThresholds == null) rsrpThresholds = sRsrpThresholds;
+            if (rsrqThresholds == null) rsrqThresholds = sRsrqThresholds;
+            if (rssnrThresholds == null) rssnrThresholds = sRssnrThresholds;
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel);
+                Rlog.i(LOG_TAG, "Applying LTE RSRP Thresholds: "
+                        + Arrays.toString(rsrpThresholds));
+                Rlog.i(LOG_TAG, "Applying LTE RSRQ Thresholds: "
+                        + Arrays.toString(rsrqThresholds));
+                Rlog.i(LOG_TAG, "Applying LTE RSSNR Thresholds: "
+                        + Arrays.toString(rssnrThresholds));
+            }
+            rsrpOnly = cc.getBoolean(
+                    CarrierConfigManager.KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
+        }
+
+        int rsrpBoost = 0;
+        if (ss != null) {
+            rsrpBoost = ss.getArfcnRsrpBoost();
+        }
+
+        int rsrp = inRangeOrUnavailable(mRsrp + rsrpBoost, MIN_LTE_RSRP, MAX_LTE_RSRP);
+
+        if (rsrpOnly) {
+            int level = updateLevelWithMeasure(rsrp, rsrpThresholds);
+            if (DBG) log("updateLevel() - rsrp = " + level);
+            if (level != SignalStrength.INVALID) {
+                mLevel = level;
+                return;
+            }
+        }
+
+        int rsrpLevel = SignalStrength.INVALID;
+        int rsrqLevel = SignalStrength.INVALID;
+        int rssnrLevel = SignalStrength.INVALID;
+
+        if (isLevelForParameter(USE_RSRP)) {
+            rsrpLevel = updateLevelWithMeasure(rsrp, rsrpThresholds);
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Updated 4G LTE RSRP Level: " + rsrpLevel);
+            }
+        }
+        if (isLevelForParameter(USE_RSRQ)) {
+            rsrqLevel = updateLevelWithMeasure(mRsrq, rsrqThresholds);
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Updated 4G LTE RSRQ Level: " + rsrqLevel);
+            }
+        }
+        if (isLevelForParameter(USE_RSSNR)) {
+            rssnrLevel = updateLevelWithMeasure(mRssnr, rssnrThresholds);
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Updated 4G LTE RSSNR Level: " + rssnrLevel);
+            }
+        }
+        // Apply the smaller value among three levels of three measures.
+        mLevel = Math.min(Math.min(rsrpLevel, rsrqLevel), rssnrLevel);
+
+        if (mLevel == SignalStrength.INVALID) {
+            int rssiLevel;
+            if (mRssi > -51) {
+                rssiLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+            } else if (mRssi >= -89) {
+                rssiLevel = SIGNAL_STRENGTH_GREAT;
+            } else if (mRssi >= -97) {
+                rssiLevel = SIGNAL_STRENGTH_GOOD;
+            } else if (mRssi >= -103) {
+                rssiLevel = SIGNAL_STRENGTH_MODERATE;
+            } else if (mRssi >= -113) {
+                rssiLevel = SIGNAL_STRENGTH_POOR;
+            } else {
+                rssiLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+            }
+            if (DBG) log("getLteLevel - rssi:" + mRssi + " rssiIconLevel:" + rssiLevel);
+            mLevel = rssiLevel;
+        }
+    }
+
+    /**
+     * Update level with corresponding measure and thresholds.
+     *
+     * @param measure corresponding signal measure
+     * @param thresholds corresponding signal thresholds
+     * @return level of the signal strength
+     */
+    private int updateLevelWithMeasure(int measure, int[] thresholds) {
+        int level;
+        if (measure == CellInfo.UNAVAILABLE) {
+            level = SignalStrength.INVALID;
+        } else if (measure >= thresholds[3]) {
+            level = SIGNAL_STRENGTH_GREAT;
+        } else if (measure >= thresholds[2]) {
+            level = SIGNAL_STRENGTH_GOOD;
+        } else if (measure >= thresholds[1]) {
+            level = SIGNAL_STRENGTH_MODERATE;
+        } else if (measure >= thresholds[0]) {
+            level = SIGNAL_STRENGTH_POOR;
+        } else {
+            level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        }
+        return level;
+    }
+
+    /**
+     * Get reference signal received quality
+     *
+     * @return the RSRQ if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getRsrq() {
+        return mRsrq;
+    }
+
+    /**
+     * Get Received Signal Strength Indication (RSSI) in dBm
+     *
+     * The value range is [-113, -51] inclusively or {@link CellInfo#UNAVAILABLE} if unavailable.
+     *
+     * Reference: TS 27.007 8.5 Signal quality +CSQ
+     *
+     * @return the RSSI if available or {@link CellInfo#UNAVAILABLE} if unavailable.
+     */
+    public int getRssi() {
+        return mRssi;
+    }
+
+    /**
+     * Get reference signal signal-to-noise ratio in dB
+     * Range: -20 dB to +30 dB.
+     *
+     * @return the RSSNR if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE} if unavailable.
+     */
+    public int getRssnr() {
+        return mRssnr;
+    }
+
+    /**
+     * Get reference signal received power in dBm
+     * Range: -140 dBm to -43 dBm.
+     *
+     * @return the RSRP of the measured cell or {@link CellInfo#UNAVAILABLE} if
+     * unavailable.
+     */
+    public int getRsrp() {
+        return mRsrp;
+    }
+
+    /**
+     * Get table index for channel quality indicator
+     *
+     * Reference: 3GPP TS 136.213 section 7.2.3.
+     *
+     * @return the CQI table index if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    @IntRange(from = 1, to = 6)
+    public int getCqiTableIndex() {
+        return mCqiTableIndex;
+    }
+
+    /**
+     * Get channel quality indicator
+     *
+     * Reference: 3GPP TS 136.213 section 7.2.3.
+     *
+     * @return the CQI if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    @IntRange(from = 0, to = 15)
+    public int getCqi() {
+        return mCqi;
+    }
+
+    /**
+     * Get signal strength in dBm
+     *
+     * @return the RSRP of the measured cell.
+     */
+    @Override
+    public int getDbm() {
+        return mRsrp;
+    }
+
+    /**
+     * Get the RSRP in ASU.
+     *
+     * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+     *
+     * @return RSCP in ASU 0..97, 255, or UNAVAILABLE
+     */
+    @Override
+    public int getAsuLevel() {
+        int lteAsuLevel = 99;
+        int lteDbm = mRsrp;
+        if (lteDbm == CellInfo.UNAVAILABLE) lteAsuLevel = 99;
+        else if (lteDbm <= -140) lteAsuLevel = 0;
+        else if (lteDbm >= -43) lteAsuLevel = 97;
+        else lteAsuLevel = lteDbm + 140;
+        if (DBG) log("Lte Asu level: "+lteAsuLevel);
+        return lteAsuLevel;
+    }
+
+    /**
+     * Get the timing advance value for LTE, as a value in range of 0..1282.
+     * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} is reported when there is no
+     * active RRC connection. Refer to 3GPP 36.213 Sec 4.2.3
+     *
+     * @return the LTE timing advance if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getTimingAdvance() {
+        return mTimingAdvance;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqiTableIndex, mCqi, mTimingAdvance,
+                mLevel);
+    }
+
+    private static final CellSignalStrengthLte sInvalid = new CellSignalStrengthLte();
+
+    /** @hide */
+    @Override
+    public boolean isValid() {
+        return !this.equals(sInvalid);
+    }
+
+    @Override
+    public boolean equals (Object o) {
+        CellSignalStrengthLte s;
+
+        if (!(o instanceof CellSignalStrengthLte)) return false;
+        s = (CellSignalStrengthLte) o;
+
+        return mRssi == s.mRssi
+                && mRsrp == s.mRsrp
+                && mRsrq == s.mRsrq
+                && mRssnr == s.mRssnr
+                && mCqiTableIndex == s.mCqiTableIndex
+                && mCqi == s.mCqi
+                && mTimingAdvance == s.mTimingAdvance
+                && mLevel == s.mLevel;
+    }
+
+    /**
+     * @return string representation.
+     */
+    @Override
+    public String toString() {
+        return "CellSignalStrengthLte:"
+                + " rssi=" + mRssi
+                + " rsrp=" + mRsrp
+                + " rsrq=" + mRsrq
+                + " rssnr=" + mRssnr
+                + " cqiTableIndex=" + mCqiTableIndex
+                + " cqi=" + mCqi
+                + " ta=" + mTimingAdvance
+                + " level=" + mLevel
+                + " parametersUseForLevel=" + mParametersUseForLevel;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        dest.writeInt(mRssi);
+        // Need to multiply rsrp and rsrq by -1
+        // to ensure consistency when reading values written here
+        // unless the values are invalid
+        dest.writeInt(mRsrp);
+        dest.writeInt(mRsrq);
+        dest.writeInt(mRssnr);
+        dest.writeInt(mCqiTableIndex);
+        dest.writeInt(mCqi);
+        dest.writeInt(mTimingAdvance);
+        dest.writeInt(mLevel);
+    }
+
+    /**
+     * Construct a SignalStrength object from the given parcel
+     * where the token is already been processed.
+     */
+    private CellSignalStrengthLte(Parcel in) {
+        mRssi = in.readInt();
+        mSignalStrength = mRssi;
+        mRsrp = in.readInt();
+        mRsrq = in.readInt();
+        mRssnr = in.readInt();
+        mCqiTableIndex = in.readInt();
+        mCqi = in.readInt();
+        mTimingAdvance = in.readInt();
+        mLevel = in.readInt();
+        if (DBG) log("CellSignalStrengthLte(Parcel): " + toString());
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @SuppressWarnings("hiding")
+    public static final @android.annotation.NonNull Parcelable.Creator<CellSignalStrengthLte> CREATOR =
+            new Parcelable.Creator<CellSignalStrengthLte>() {
+        @Override
+        public CellSignalStrengthLte createFromParcel(Parcel in) {
+            return new CellSignalStrengthLte(in);
+        }
+
+        @Override
+        public CellSignalStrengthLte[] newArray(int size) {
+            return new CellSignalStrengthLte[size];
+        }
+    };
+
+    /**
+     * log
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+
+    /** @hide */
+    public static int convertRssnrUnitFromTenDbToDB(int rssnr) {
+        return (int) Math.floor((float) rssnr / 10);
+    }
+
+    /** @hide */
+    public static int convertRssiAsuToDBm(int rssiAsu) {
+        if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) {
+            return CellInfo.UNAVAILABLE;
+        }
+        if ((rssiAsu < SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE
+                || rssiAsu > SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MAX_VALUE)) {
+            Rlog.e(LOG_TAG, "convertRssiAsuToDBm: invalid RSSI in ASU=" + rssiAsu);
+            return CellInfo.UNAVAILABLE;
+        }
+        return -113 + (2 * rssiAsu);
+    }
+}
diff --git a/android-35/android/telephony/CellSignalStrengthNr.java b/android-35/android/telephony/CellSignalStrengthNr.java
new file mode 100644
index 0000000..03519a3
--- /dev/null
+++ b/android-35/android/telephony/CellSignalStrengthNr.java
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * 5G NR signal strength related information.
+ */
+public final class CellSignalStrengthNr extends CellSignalStrength implements Parcelable {
+    /**
+     * The value is used to indicate that the asu level is unknown.
+     * Reference: 3GPP TS 27.007 section 8.69.
+     * @hide
+     */
+    public static final int UNKNOWN_ASU_LEVEL = 99;
+
+    private static final boolean VDBG = false;
+
+    private static final String TAG = "CellSignalStrengthNr";
+
+    // Lifted from Default carrier configs and max range of SSRSRP
+    // Boundaries: [-156 dB, -31 dB]
+    private int[] mSsRsrpThresholds = new int[] {
+            -110, /* SIGNAL_STRENGTH_POOR */
+            -90, /* SIGNAL_STRENGTH_MODERATE */
+            -80, /* SIGNAL_STRENGTH_GOOD */
+            -65,  /* SIGNAL_STRENGTH_GREAT */
+    };
+
+    // Lifted from Default carrier configs and max range of SSRSRQ
+    // Boundaries: [-43 dB, 20 dB]
+    private int[] mSsRsrqThresholds = new int[] {
+            -31, /* SIGNAL_STRENGTH_POOR */
+            -19, /* SIGNAL_STRENGTH_MODERATE */
+            -7, /* SIGNAL_STRENGTH_GOOD */
+            6  /* SIGNAL_STRENGTH_GREAT */
+    };
+
+    // Lifted from Default carrier configs and max range of SSSINR
+    // Boundaries: [-23 dB, 40 dB]
+    private int[] mSsSinrThresholds = new int[] {
+            -5, /* SIGNAL_STRENGTH_POOR */
+            5, /* SIGNAL_STRENGTH_MODERATE */
+            15, /* SIGNAL_STRENGTH_GOOD */
+            30  /* SIGNAL_STRENGTH_GREAT */
+    };
+
+    /**
+     * Indicates SSRSRP is considered for {@link #getLevel()} and reporting from modem.
+     *
+     * @hide
+     */
+    public static final int USE_SSRSRP = 1 << 0;
+    /**
+     * Indicates SSRSRQ is considered for {@link #getLevel()} and reporting from modem.
+     *
+     * @hide
+     */
+    public static final int USE_SSRSRQ = 1 << 1;
+    /**
+     * Indicates SSSINR is considered for {@link #getLevel()} and reporting from modem.
+     *
+     * @hide
+     */
+    public static final int USE_SSSINR = 1 << 2;
+
+    /**
+     * Bit-field integer to determine whether to use SS reference signal received power (SSRSRP),
+     * SS reference signal received quality (SSRSRQ), or/and SS signal-to-noise and interference
+     * ratio (SSSINR) for the number of 5G NR signal bars. If multiple measures are set bit, the
+     * parameter whose value is smallest is used to indicate the signal bar.
+     *
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "USE_" }, value = {
+        USE_SSRSRP,
+        USE_SSRSRQ,
+        USE_SSSINR
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SignalLevelAndReportCriteriaSource {}
+
+    private int mCsiRsrp;
+    private int mCsiRsrq;
+    private int mCsiSinr;
+    /**
+     * CSI channel quality indicator (CQI) table index. There are multiple CQI tables.
+     * The definition of CQI in each table is different.
+     *
+     * Reference: 3GPP TS 138.214 section 5.2.2.1.
+     *
+     * Range [1, 3].
+     */
+    private int mCsiCqiTableIndex;
+    /**
+     * CSI channel quality indicators (CQI) for all subbands.
+     *
+     * If the CQI report is for the entire wideband, a single CQI index is provided.
+     * If the CQI report is for all subbands, one CQI index is provided for each subband,
+     * in ascending order of subband index.
+     * If CQI is not available, the CQI report is empty.
+     *
+     * Reference: 3GPP TS 138.214 section 5.2.2.1.
+     *
+     * Range [0, 15] for each CQI.
+     */
+    private List<Integer> mCsiCqiReport;
+    private int mSsRsrp;
+    private int mSsRsrq;
+    private int mSsSinr;
+    private int mLevel;
+
+    /**
+     * Bit-field integer to determine whether to use SS reference signal received power (SSRSRP),
+     * SS reference signal received quality (SSRSRQ), or/and SS signal-to-noise and interference
+     * ratio (SSSINR) for the number of 5G NR signal bars. If multiple measures are set bit, the
+     * parameter whose value is smallest is used to indicate the signal bar.
+     *
+     *  SSRSRP = 1 << 0,
+     *  SSRSRQ = 1 << 1,
+     *  SSSINR = 1 << 2,
+     *
+     * For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2).
+     * If the key is invalid or not configured, a default value (SSRSRP = 1 << 0) will apply.
+     */
+    private int mParametersUseForLevel;
+
+    /**
+     * Timing advance value for a one way trip from cell to device in microseconds.
+     * Approximate distance is calculated using 300m/us * timingAdvance.
+     *
+     * Reference: 3GPP TS 36.213 section 4.2.3.
+     *
+     * Range: [0, 1282]
+     */
+    private int mTimingAdvance;
+
+    /** @hide */
+    public CellSignalStrengthNr() {
+        setDefaultValues();
+    }
+
+    /**
+     * @param csiRsrp CSI reference signal received power.
+     * @param csiRsrq CSI reference signal received quality.
+     * @param csiSinr CSI signal-to-noise and interference ratio.
+     * @param csiCqiTableIndex CSI CSI channel quality indicator (CQI) table index.
+     * @param csiCqiReport CSI channel quality indicators (CQI) for all subbands.
+     * @param ssRsrp SS reference signal received power.
+     * @param ssRsrq SS reference signal received quality.
+     * @param ssSinr SS signal-to-noise and interference ratio.
+     * @param timingAdvance Timing advance.
+     * @hide
+     */
+    public CellSignalStrengthNr(int csiRsrp, int csiRsrq, int csiSinr, int csiCqiTableIndex,
+            List<Byte> csiCqiReport, int ssRsrp, int ssRsrq, int ssSinr, int timingAdvance) {
+        mCsiRsrp = inRangeOrUnavailable(csiRsrp, -156, -31);
+        mCsiRsrq = inRangeOrUnavailable(csiRsrq, -20, -3);
+        mCsiSinr = inRangeOrUnavailable(csiSinr, -23, 23);
+        mCsiCqiTableIndex = inRangeOrUnavailable(csiCqiTableIndex, 1, 3);
+        mCsiCqiReport = csiCqiReport.stream()
+                .map(cqi -> inRangeOrUnavailable(Byte.toUnsignedInt(cqi), 0, 15))
+                .collect(Collectors.toList());
+        mSsRsrp = inRangeOrUnavailable(ssRsrp, -156, -31);
+        mSsRsrq = inRangeOrUnavailable(ssRsrq, -43, 20);
+        mSsSinr = inRangeOrUnavailable(ssSinr, -23, 40);
+        mTimingAdvance = inRangeOrUnavailable(timingAdvance, 0, 1282);
+        updateLevel(null, null);
+    }
+
+    /**
+     * @param csiRsrp CSI reference signal received power.
+     * @param csiRsrq CSI reference signal received quality.
+     * @param csiSinr CSI signal-to-noise and interference ratio.
+     * @param ssRsrp SS reference signal received power.
+     * @param ssRsrq SS reference signal received quality.
+     * @param ssSinr SS signal-to-noise and interference ratio.
+     * @hide
+     */
+    public CellSignalStrengthNr(
+            int csiRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr) {
+        this(csiRsrp, csiRsrq, csiSinr, CellInfo.UNAVAILABLE, Collections.emptyList(),
+                ssRsrp, ssRsrq, ssSinr, CellInfo.UNAVAILABLE);
+    }
+
+    /**
+     * Flip sign cell strength value when taking in the value from hal
+     * @param val cell strength value
+     * @return flipped value
+     * @hide
+     */
+    public static int flip(int val) {
+        return val != CellInfo.UNAVAILABLE ? -val : val;
+    }
+
+    /**
+     * Reference: 3GPP TS 38.133 10.1.6.1.
+     * Range: -156 dBm to -31 dBm.
+     * @return SS reference signal received power, {@link CellInfo#UNAVAILABLE} means unreported
+     * value.
+     */
+    public int getSsRsrp() {
+        return mSsRsrp;
+    }
+
+    /**
+     * Reference: 3GPP TS 38.215; 3GPP TS 38.133 section 10
+     * Range: -43 dB to 20 dB.
+     * @return SS reference signal received quality, {@link CellInfo#UNAVAILABLE} means unreported
+     * value.
+     */
+    public int getSsRsrq() {
+        return mSsRsrq;
+    }
+
+    /**
+     * Reference: 3GPP TS 38.215 Sec 5.1.*, 3GPP TS 38.133 10.1.16.1
+     * Range: -23 dB to 40 dB
+     * @return SS signal-to-noise and interference ratio, {@link CellInfo#UNAVAILABLE} means
+     * unreported value.
+     */
+    public int getSsSinr() {
+        return mSsSinr;
+    }
+
+    /**
+     * Reference: 3GPP TS 38.133 10.1.6.1.
+     * Range: -156 dBm to -31 dBm.
+     * @return CSI reference signal received power, {@link CellInfo#UNAVAILABLE} means unreported
+     * value.
+     */
+    public int getCsiRsrp() {
+        return mCsiRsrp;
+    }
+
+    /**
+     * Reference: 3GPP TS 38.215.
+     * Range: -20 dB to -3 dB.
+     * @return CSI reference signal received quality, {@link CellInfo#UNAVAILABLE} means unreported
+     * value.
+     */
+    public int getCsiRsrq() {
+        return mCsiRsrq;
+    }
+
+    /**
+     * Reference: 3GPP TS 38.215 Sec 5.1.*, 3GPP TS 38.133 10.1.16.1
+     * Range: -23 dB to 23 dB
+     * @return CSI signal-to-noise and interference ratio, {@link CellInfo#UNAVAILABLE} means
+     * unreported value.
+     */
+    public int getCsiSinr() {
+        return mCsiSinr;
+    }
+
+    /**
+     * Return CSI channel quality indicator (CQI) table index. There are multiple CQI tables.
+     * The definition of CQI in each table is different.
+     *
+     * Reference: 3GPP TS 138.214 section 5.2.2.1.
+     *
+     * @return the CQI table index if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    @IntRange(from = 1, to = 3)
+    public int getCsiCqiTableIndex() {
+        return mCsiCqiTableIndex;
+    }
+    /**
+     * Return a list of CSI channel quality indicators (CQI) for all subbands.
+     *
+     * If the CQI report is for the entire wideband, a single CQI index is provided.
+     * If the CQI report is for all subbands, one CQI index is provided for each subband,
+     * in ascending order of subband index.
+     * If CQI is not available, the CQI report is empty.
+     *
+     * Reference: 3GPP TS 138.214 section 5.2.2.1.
+     *
+     * @return the CQIs for all subbands if available or empty list if unavailable.
+     */
+    @NonNull
+    @IntRange(from = 0, to = 15)
+    public List<Integer> getCsiCqiReport() {
+        return mCsiCqiReport;
+    }
+
+    /**
+     * Get the timing advance value for a one way trip from cell to device for NR in microseconds.
+     * {@link android.telephony.CellInfo#UNAVAILABLE} is reported when there is no
+     * active RRC connection.
+     *
+     * Reference: 3GPP TS 36.213 section 4.2.3.
+     * Range: 0 us to 1282 us.
+     *
+     * @return the NR timing advance if available or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE} if unavailable.
+     */
+    @IntRange(from = 0, to = 1282)
+    public int getTimingAdvanceMicros() {
+        return mTimingAdvance;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCsiRsrp);
+        dest.writeInt(mCsiRsrq);
+        dest.writeInt(mCsiSinr);
+        dest.writeInt(mCsiCqiTableIndex);
+        dest.writeList(mCsiCqiReport);
+        dest.writeInt(mSsRsrp);
+        dest.writeInt(mSsRsrq);
+        dest.writeInt(mSsSinr);
+        dest.writeInt(mLevel);
+        dest.writeInt(mTimingAdvance);
+    }
+
+    private CellSignalStrengthNr(Parcel in) {
+        mCsiRsrp = in.readInt();
+        mCsiRsrq = in.readInt();
+        mCsiSinr = in.readInt();
+        mCsiCqiTableIndex = in.readInt();
+        mCsiCqiReport = in.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class);
+        mSsRsrp = in.readInt();
+        mSsRsrq = in.readInt();
+        mSsSinr = in.readInt();
+        mLevel = in.readInt();
+        mTimingAdvance = in.readInt();
+    }
+
+    /** @hide */
+    @Override
+    public void setDefaultValues() {
+        mCsiRsrp = CellInfo.UNAVAILABLE;
+        mCsiRsrq = CellInfo.UNAVAILABLE;
+        mCsiSinr = CellInfo.UNAVAILABLE;
+        mCsiCqiTableIndex = CellInfo.UNAVAILABLE;
+        mCsiCqiReport = Collections.emptyList();
+        mSsRsrp = CellInfo.UNAVAILABLE;
+        mSsRsrq = CellInfo.UNAVAILABLE;
+        mSsSinr = CellInfo.UNAVAILABLE;
+        mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        mParametersUseForLevel = USE_SSRSRP;
+        mTimingAdvance = CellInfo.UNAVAILABLE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT)
+    public int getLevel() {
+        return mLevel;
+    }
+
+    /**
+     * Checks if the given parameter type is considered to use for {@link #getLevel()}.
+     *
+     * Note: if multiple parameter types are considered, the smaller level for one of the
+     * parameters would be returned by {@link #getLevel()}
+     *
+     * @param parameterType bitwise OR of {@link #USE_SSRSRP}, {@link #USE_SSRSRQ},
+     *         {@link #USE_SSSINR}
+     * @return {@code true} if the level is calculated based on the given parameter type;
+     *      {@code false} otherwise.
+     *
+     */
+    private boolean isLevelForParameter(@SignalLevelAndReportCriteriaSource int parameterType) {
+        return (parameterType & mParametersUseForLevel) == parameterType;
+    }
+
+    /** @hide */
+    @Override
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
+        if (cc == null) {
+            mParametersUseForLevel = USE_SSRSRP;
+        } else {
+            mParametersUseForLevel = cc.getInt(
+                    CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, USE_SSRSRP);
+            mSsRsrpThresholds = cc.getIntArray(
+                    CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY);
+            if (VDBG) {
+                Rlog.i(TAG, "Applying 5G NR SSRSRP Thresholds: "
+                        + Arrays.toString(mSsRsrpThresholds));
+            }
+            mSsRsrqThresholds = cc.getIntArray(
+                    CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY);
+            if (VDBG) {
+                Rlog.i(TAG, "Applying 5G NR SSRSRQ Thresholds: "
+                        + Arrays.toString(mSsRsrqThresholds));
+            }
+            mSsSinrThresholds = cc.getIntArray(
+                    CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY);
+            if (VDBG) {
+                Rlog.i(TAG, "Applying 5G NR SSSINR Thresholds: "
+                        + Arrays.toString(mSsSinrThresholds));
+            }
+        }
+        int ssRsrpLevel = SignalStrength.INVALID;
+        int ssRsrqLevel = SignalStrength.INVALID;
+        int ssSinrLevel = SignalStrength.INVALID;
+        if (isLevelForParameter(USE_SSRSRP)) {
+            int rsrpBoost = 0;
+            if (ss != null) {
+                rsrpBoost = ss.getArfcnRsrpBoost();
+            }
+            ssRsrpLevel = updateLevelWithMeasure(mSsRsrp + rsrpBoost, mSsRsrpThresholds);
+            if (VDBG) {
+                Rlog.i(TAG, "Updated 5G NR SSRSRP Level: " + ssRsrpLevel);
+            }
+        }
+        if (isLevelForParameter(USE_SSRSRQ)) {
+            ssRsrqLevel = updateLevelWithMeasure(mSsRsrq, mSsRsrqThresholds);
+            if (VDBG) {
+                Rlog.i(TAG, "Updated 5G NR SSRSRQ Level: " + ssRsrqLevel);
+            }
+        }
+        if (isLevelForParameter(USE_SSSINR)) {
+            ssSinrLevel = updateLevelWithMeasure(mSsSinr, mSsSinrThresholds);
+            if (VDBG) {
+                Rlog.i(TAG, "Updated 5G NR SSSINR Level: " + ssSinrLevel);
+            }
+        }
+        // Apply the smaller value among three levels of three measures.
+        mLevel = Math.min(Math.min(ssRsrpLevel, ssRsrqLevel), ssSinrLevel);
+    }
+
+    /**
+     * Update level with corresponding measure and thresholds.
+     *
+     * @param measure corresponding signal measure
+     * @param thresholds corresponding signal thresholds
+     * @return level of the signal strength
+     */
+    private int updateLevelWithMeasure(int measure, int[] thresholds) {
+        int level;
+        if (measure == CellInfo.UNAVAILABLE) {
+            level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        } else if (measure >= thresholds[3]) {
+            level = SIGNAL_STRENGTH_GREAT;
+        } else if (measure >= thresholds[2]) {
+            level = SIGNAL_STRENGTH_GOOD;
+        } else if (measure >= thresholds[1]) {
+            level = SIGNAL_STRENGTH_MODERATE;
+        }  else if (measure >= thresholds[0]) {
+            level = SIGNAL_STRENGTH_POOR;
+        } else {
+            level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        }
+        return level;
+    }
+
+    /**
+     * Get the RSRP in ASU.
+     *
+     * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+     *
+     * @return RSRP in ASU 0..97, 255, or UNAVAILABLE
+     */
+    @Override
+    public int getAsuLevel() {
+        int asuLevel;
+        int nrDbm = getDbm();
+        if (nrDbm == CellInfo.UNAVAILABLE) {
+            asuLevel = UNKNOWN_ASU_LEVEL;
+        } else if (nrDbm <= -140) {
+            asuLevel = 0;
+        } else if (nrDbm >= -43) {
+            asuLevel = 97;
+        } else {
+            asuLevel = nrDbm + 140;
+        }
+        return asuLevel;
+    }
+
+    /**
+     * Get the SS-RSRP as dBm value -140..-44dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}.
+     */
+    @Override
+    public int getDbm() {
+        return mSsRsrp;
+    }
+
+    /** @hide */
+    public CellSignalStrengthNr(CellSignalStrengthNr s) {
+        mCsiRsrp = s.mCsiRsrp;
+        mCsiRsrq = s.mCsiRsrq;
+        mCsiSinr = s.mCsiSinr;
+        mCsiCqiTableIndex = s.mCsiCqiTableIndex;
+        mCsiCqiReport = s.mCsiCqiReport;
+        mSsRsrp = s.mSsRsrp;
+        mSsRsrq = s.mSsRsrq;
+        mSsSinr = s.mSsSinr;
+        mLevel = s.mLevel;
+        mParametersUseForLevel = s.mParametersUseForLevel;
+        mTimingAdvance = s.mTimingAdvance;
+    }
+
+    /** @hide */
+    @Override
+    public CellSignalStrengthNr copy() {
+        return new CellSignalStrengthNr(this);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mCsiCqiTableIndex,
+                mCsiCqiReport, mSsRsrp, mSsRsrq, mSsSinr, mLevel, mTimingAdvance);
+    }
+
+    private static final CellSignalStrengthNr sInvalid = new CellSignalStrengthNr();
+
+    /** @hide */
+    @Override
+    public boolean isValid() {
+        return !this.equals(sInvalid);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof CellSignalStrengthNr) {
+            CellSignalStrengthNr o = (CellSignalStrengthNr) obj;
+            return mCsiRsrp == o.mCsiRsrp && mCsiRsrq == o.mCsiRsrq && mCsiSinr == o.mCsiSinr
+                    && mCsiCqiTableIndex == o.mCsiCqiTableIndex
+                    && mCsiCqiReport.equals(o.mCsiCqiReport)
+                    && mSsRsrp == o.mSsRsrp && mSsRsrq == o.mSsRsrq && mSsSinr == o.mSsSinr
+                    && mLevel == o.mLevel && mTimingAdvance == o.mTimingAdvance;
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+                .append(TAG + ":{")
+                .append(" csiRsrp = " + mCsiRsrp)
+                .append(" csiRsrq = " + mCsiRsrq)
+                .append(" csiCqiTableIndex = " + mCsiCqiTableIndex)
+                .append(" csiCqiReport = " + mCsiCqiReport)
+                .append(" ssRsrp = " + mSsRsrp)
+                .append(" ssRsrq = " + mSsRsrq)
+                .append(" ssSinr = " + mSsSinr)
+                .append(" level = " + mLevel)
+                .append(" parametersUseForLevel = " + mParametersUseForLevel)
+                .append(" timingAdvance = " + mTimingAdvance)
+                .append(" }")
+                .toString();
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @android.annotation.NonNull Parcelable.Creator<CellSignalStrengthNr> CREATOR =
+            new Parcelable.Creator<CellSignalStrengthNr>() {
+        @Override
+        public CellSignalStrengthNr createFromParcel(Parcel in) {
+            return new CellSignalStrengthNr(in);
+        }
+
+        @Override
+        public CellSignalStrengthNr[] newArray(int size) {
+            return new CellSignalStrengthNr[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/CellSignalStrengthTdscdma.java b/android-35/android/telephony/CellSignalStrengthTdscdma.java
new file mode 100644
index 0000000..8a7c70e
--- /dev/null
+++ b/android-35/android/telephony/CellSignalStrengthTdscdma.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import com.android.telephony.Rlog;
+
+import java.util.Objects;
+
+/**
+ * Tdscdma signal strength related information.
+ *
+ * This class provides signal strength and signal quality information for the TD-SCDMA air
+ * interface. For more information see 3gpp 25.225.
+ */
+public final class CellSignalStrengthTdscdma extends CellSignalStrength implements Parcelable {
+
+    private static final String LOG_TAG = "CellSignalStrengthTdscdma";
+    private static final boolean DBG = false;
+
+    // These levels are arbitrary but carried over from SignalStrength.java for consistency.
+    private static final int TDSCDMA_RSCP_MAX = -24;
+    private static final int TDSCDMA_RSCP_GREAT = -49;
+    private static final int TDSCDMA_RSCP_GOOD = -73;
+    private static final int TDSCDMA_RSCP_MODERATE = -97;
+    private static final int TDSCDMA_RSCP_POOR = -110;
+    private static final int TDSCDMA_RSCP_MIN = -120;
+
+
+    private int mRssi; // in dBm [-113, -51], CellInfo.UNAVAILABLE
+
+    private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
+                               // CellInfo.UNAVAILABLE if unknown
+    private int mRscp; // Pilot Power in dBm [-120, -24] or CellInfo.UNAVAILABLE
+                       // CellInfo.UNAVAILABLE if unknown
+
+    private int mLevel;
+
+    /** @hide */
+    public CellSignalStrengthTdscdma() {
+        setDefaultValues();
+    }
+
+    /**
+     * @param rssi in dBm [-113, -51] or UNAVAILABLE
+     * @param ber [0-7], 99 or UNAVAILABLE
+     * @param rscp in dBm [-120, -24] or UNAVAILABLE
+     *
+     * @hide
+     */
+    public CellSignalStrengthTdscdma(int rssi, int ber, int rscp) {
+        mRssi = inRangeOrUnavailable(rssi, -113, -51);
+        mBitErrorRate = inRangeOrUnavailable(ber, 0, 7, 99);
+        mRscp = inRangeOrUnavailable(rscp, -120, -24);
+        updateLevel(null, null);
+    }
+
+    /** @hide */
+    public CellSignalStrengthTdscdma(CellSignalStrengthTdscdma s) {
+        copyFrom(s);
+    }
+
+    /** @hide */
+    protected void copyFrom(CellSignalStrengthTdscdma s) {
+        mRssi = s.mRssi;
+        mBitErrorRate = s.mBitErrorRate;
+        mRscp = s.mRscp;
+        mLevel = s.mLevel;
+    }
+
+    /** @hide */
+    @Override
+    public CellSignalStrengthTdscdma copy() {
+        return new CellSignalStrengthTdscdma(this);
+    }
+
+    /** @hide */
+    @Override
+    public void setDefaultValues() {
+        mRssi = CellInfo.UNAVAILABLE;
+        mBitErrorRate = CellInfo.UNAVAILABLE;
+        mRscp = CellInfo.UNAVAILABLE;
+        mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+    }
+
+
+    /** {@inheritDoc} */
+    @Override
+    @IntRange(from = 0, to = 4)
+    public int getLevel() {
+        return mLevel;
+    }
+
+    /** @hide */
+    @Override
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
+        if (mRscp > TDSCDMA_RSCP_MAX) mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        else if (mRscp >= TDSCDMA_RSCP_GREAT) mLevel = SIGNAL_STRENGTH_GREAT;
+        else if (mRscp >= TDSCDMA_RSCP_GOOD)  mLevel = SIGNAL_STRENGTH_GOOD;
+        else if (mRscp >= TDSCDMA_RSCP_MODERATE)  mLevel = SIGNAL_STRENGTH_MODERATE;
+        else if (mRscp >= TDSCDMA_RSCP_POOR) mLevel = SIGNAL_STRENGTH_POOR;
+        else mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+    }
+
+    /**
+     * Get the RSCP as dBm value -120..-24dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}.
+     */
+    @Override
+    public int getDbm() {
+        return mRscp;
+    }
+
+    /**
+     * Get the RSCP as dBm value -120..-24dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}.
+     */
+    public int getRscp() {
+        return mRscp;
+    }
+
+    /**
+     * Get the RSSI as dBm value -113..-51dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}.
+     *
+     * @hide
+     */
+    public int getRssi() {
+        return mRssi;
+    }
+
+    /**
+     * Get the BER as an ASU value 0..7, 99, or {@link CellInfo#UNAVAILABLE UNAVAILABLE}.
+     * @hide
+     */
+    public int getBitErrorRate() {
+        return mBitErrorRate;
+    }
+
+    /**
+     * Get the RSCP in ASU.
+     *
+     * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+     *
+     * @return RSCP in ASU 0..96, 255, or {@link CellInfo#UNAVAILABLE UNAVAILABLE}.
+     */
+    @Override
+    public int getAsuLevel() {
+        if (mRscp != CellInfo.UNAVAILABLE) return getAsuFromRscpDbm(mRscp);
+        // For historical reasons, if RSCP is unavailable, this API will very incorrectly return
+        // RSSI. This hackery will be removed when most devices are using Radio HAL 1.2+
+        if (mRssi != CellInfo.UNAVAILABLE) return getAsuFromRssiDbm(mRssi);
+        return getAsuFromRscpDbm(CellInfo.UNAVAILABLE);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRssi, mBitErrorRate, mRscp, mLevel);
+    }
+
+    private static final CellSignalStrengthTdscdma sInvalid = new CellSignalStrengthTdscdma();
+
+    /** @hide */
+    @Override
+    public boolean isValid() {
+        return !this.equals(sInvalid);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof CellSignalStrengthTdscdma)) return false;
+        CellSignalStrengthTdscdma s = (CellSignalStrengthTdscdma) o;
+
+        return mRssi == s.mRssi
+                && mBitErrorRate == s.mBitErrorRate
+                && mRscp == s.mRscp
+                && mLevel == s.mLevel;
+    }
+
+    /**
+     * @return string representation.
+     */
+    @Override
+    public String toString() {
+        return "CellSignalStrengthTdscdma:"
+                + " rssi=" + mRssi
+                + " ber=" + mBitErrorRate
+                + " rscp=" + mRscp
+                + " level=" + mLevel;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        dest.writeInt(mRssi);
+        dest.writeInt(mBitErrorRate);
+        dest.writeInt(mRscp);
+        dest.writeInt(mLevel);
+    }
+
+    /**
+     * Construct a SignalStrength object from the given parcel
+     * where the token is already been processed.
+     */
+    private CellSignalStrengthTdscdma(Parcel in) {
+        mRssi = in.readInt();
+        mBitErrorRate = in.readInt();
+        mRscp = in.readInt();
+        mLevel = in.readInt();
+        if (DBG) log("CellSignalStrengthTdscdma(Parcel): " + toString());
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @SuppressWarnings("hiding")
+    @NonNull
+    public static final Parcelable.Creator<CellSignalStrengthTdscdma> CREATOR =
+            new Parcelable.Creator<CellSignalStrengthTdscdma>() {
+        @Override
+        public @NonNull CellSignalStrengthTdscdma createFromParcel(Parcel in) {
+            return new CellSignalStrengthTdscdma(in);
+        }
+
+        @Override
+        public @NonNull CellSignalStrengthTdscdma[] newArray(int size) {
+            return new CellSignalStrengthTdscdma[size];
+        }
+    };
+
+    /**
+     * log
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/CellSignalStrengthWcdma.java b/android-35/android/telephony/CellSignalStrengthWcdma.java
new file mode 100644
index 0000000..f30440d
--- /dev/null
+++ b/android-35/android/telephony/CellSignalStrengthWcdma.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.annotation.IntRange;
+import android.annotation.StringDef;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.text.TextUtils;
+
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Wcdma signal strength related information.
+ */
+public final class CellSignalStrengthWcdma extends CellSignalStrength implements Parcelable {
+
+    private static final String LOG_TAG = "CellSignalStrengthWcdma";
+    private static final boolean DBG = false;
+
+    private static final int WCDMA_RSSI_MAX = -51;
+    private static final int WCDMA_RSSI_GREAT = -77;
+    private static final int WCDMA_RSSI_GOOD = -87;
+    private static final int WCDMA_RSSI_MODERATE = -97;
+    private static final int WCDMA_RSSI_POOR = -107;
+    private static final int WCDMA_RSSI_MIN = -113;
+
+    private static final int[] sRssiThresholds = new int[]{
+            WCDMA_RSSI_POOR, WCDMA_RSSI_MODERATE, WCDMA_RSSI_GOOD, WCDMA_RSSI_GREAT};
+
+    private static final int WCDMA_RSCP_MAX = -24;
+    private static final int WCDMA_RSCP_GREAT = -85;
+    private static final int WCDMA_RSCP_GOOD = -95;
+    private static final int WCDMA_RSCP_MODERATE = -105;
+    private static final int WCDMA_RSCP_POOR = -115;
+    private static final int WCDMA_RSCP_MIN = -120;
+
+    private static final int[] sRscpThresholds = new int[] {
+            WCDMA_RSCP_POOR, WCDMA_RSCP_MODERATE, WCDMA_RSCP_GOOD, WCDMA_RSCP_GREAT};
+
+    // TODO: Because these are used as values in CarrierConfig, they should be exposed somehow.
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef({LEVEL_CALCULATION_METHOD_RSSI, LEVEL_CALCULATION_METHOD_RSCP})
+    public @interface LevelCalculationMethod {}
+    /** @hide */
+    public static final String LEVEL_CALCULATION_METHOD_RSSI = "rssi";
+    /** @hide */
+    public static final String LEVEL_CALCULATION_METHOD_RSCP = "rscp";
+
+    // Default to RSSI for backwards compatibility with older devices
+    private static final String DEFAULT_LEVEL_CALCULATION_METHOD = LEVEL_CALCULATION_METHOD_RSSI;
+
+    private int mRssi; // in dBm [-113, 51] or CellInfo.UNAVAILABLE if unknown
+
+    @UnsupportedAppUsage
+    private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
+                               // CellInfo.UNAVAILABLE if unknown
+    private int mRscp; // in dBm [-120, -24]
+    private int mEcNo; // range -24, 1, CellInfo.UNAVAILABLE if unknown
+    private int mLevel;
+
+    /** @hide */
+    public CellSignalStrengthWcdma() {
+        setDefaultValues();
+    }
+
+    /** @hide */
+    public CellSignalStrengthWcdma(int rssi, int ber, int rscp, int ecno) {
+        mRssi = inRangeOrUnavailable(rssi, WCDMA_RSSI_MIN, WCDMA_RSSI_MAX);
+        mBitErrorRate = inRangeOrUnavailable(ber, 0, 7, 99);
+        mRscp = inRangeOrUnavailable(rscp, -120, -24);
+        mEcNo = inRangeOrUnavailable(ecno, -24, 1);
+        updateLevel(null, null);
+    }
+
+    /** @hide */
+    public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
+        copyFrom(s);
+    }
+
+    /** @hide */
+    protected void copyFrom(CellSignalStrengthWcdma s) {
+        mRssi = s.mRssi;
+        mBitErrorRate = s.mBitErrorRate;
+        mRscp = s.mRscp;
+        mEcNo = s.mEcNo;
+        mLevel = s.mLevel;
+    }
+
+    /** @hide */
+    @Override
+    public CellSignalStrengthWcdma copy() {
+        return new CellSignalStrengthWcdma(this);
+    }
+
+    /** @hide */
+    @Override
+    public void setDefaultValues() {
+        mRssi = CellInfo.UNAVAILABLE;
+        mBitErrorRate = CellInfo.UNAVAILABLE;
+        mRscp = CellInfo.UNAVAILABLE;
+        mEcNo = CellInfo.UNAVAILABLE;
+        mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT)
+    public int getLevel() {
+        return mLevel;
+    }
+
+    /** @hide */
+    @Override
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
+        String calcMethod;
+        int[] rscpThresholds;
+
+        if (cc == null) {
+            calcMethod = DEFAULT_LEVEL_CALCULATION_METHOD;
+            rscpThresholds = sRscpThresholds;
+        } else {
+            // TODO: abstract this entire thing into a series of functions
+            calcMethod = cc.getString(
+                    CarrierConfigManager.KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING,
+                    DEFAULT_LEVEL_CALCULATION_METHOD);
+            if (TextUtils.isEmpty(calcMethod)) calcMethod = DEFAULT_LEVEL_CALCULATION_METHOD;
+            rscpThresholds = cc.getIntArray(
+                    CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY);
+            if (rscpThresholds == null || rscpThresholds.length != NUM_SIGNAL_STRENGTH_THRESHOLDS) {
+                rscpThresholds = sRscpThresholds;
+            }
+        }
+
+        int level = NUM_SIGNAL_STRENGTH_THRESHOLDS;
+        switch (calcMethod) {
+            case LEVEL_CALCULATION_METHOD_RSCP:
+                if (mRscp < WCDMA_RSCP_MIN || mRscp > WCDMA_RSCP_MAX) {
+                    mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+                    return;
+                }
+                while (level > 0 && mRscp < rscpThresholds[level - 1]) level--;
+                mLevel = level;
+                return;
+            default:
+                loge("Invalid Level Calculation Method for CellSignalStrengthWcdma = "
+                        + calcMethod);
+                /** fall through */
+            case LEVEL_CALCULATION_METHOD_RSSI:
+                if (mRssi < WCDMA_RSSI_MIN || mRssi > WCDMA_RSSI_MAX) {
+                    mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+                    return;
+                }
+                while (level > 0 && mRssi < sRssiThresholds[level - 1]) level--;
+                mLevel = level;
+                return;
+        }
+    }
+
+    /**
+     * Get the RSCP as dBm value -120..-24dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}.
+     */
+    @Override
+    public int getDbm() {
+        if (mRscp != CellInfo.UNAVAILABLE) return mRscp;
+        return mRssi;
+    }
+
+    /**
+     * Get the RSCP in ASU.
+     *
+     * Asu is calculated based on 3GPP RSCP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+     *
+     * @return RSCP in ASU 0..96, 255, or UNAVAILABLE
+     */
+    @Override
+    public int getAsuLevel() {
+        if (mRscp != CellInfo.UNAVAILABLE) return getAsuFromRscpDbm(mRscp);
+        // For historical reasons, if RSCP is unavailable, this API will very incorrectly return
+        // RSSI. This hackery will be removed when most devices are using Radio HAL 1.2+
+        if (mRssi != CellInfo.UNAVAILABLE) return getAsuFromRssiDbm(mRssi);
+        return getAsuFromRscpDbm(CellInfo.UNAVAILABLE);
+    }
+
+    /**
+     * Get the RSSI as dBm
+     *
+     * @hide
+     */
+    public int getRssi() {
+        return mRssi;
+    }
+
+    /**
+     * Get the RSCP as dBm
+     *
+     * @hide
+     */
+    public int getRscp() {
+        return mRscp;
+    }
+
+    /**
+     * Get the Ec/No (Energy per chip over the noise spectral density) as dB.
+     *
+     * Reference: TS 25.133 Section 9.1.2.3
+     *
+     * @return the Ec/No of the measured cell in the range [-24, 1] or
+     * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable
+     */
+    public int getEcNo() {
+        return mEcNo;
+    }
+
+    /**
+     * Return the Bit Error Rate
+     *
+     * @returns the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or UNAVAILABLE.
+     * @hide
+     */
+    public int getBitErrorRate() {
+        return mBitErrorRate;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRssi, mBitErrorRate, mRscp, mEcNo, mLevel);
+    }
+
+    private static final CellSignalStrengthWcdma sInvalid = new CellSignalStrengthWcdma();
+
+    /** @hide */
+    @Override
+    public boolean isValid() {
+        return !this.equals(sInvalid);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof CellSignalStrengthWcdma)) return false;
+        CellSignalStrengthWcdma s = (CellSignalStrengthWcdma) o;
+
+        return mRssi == s.mRssi
+                && mBitErrorRate == s.mBitErrorRate
+                && mRscp == s.mRscp
+                && mEcNo == s.mEcNo
+                && mLevel == s.mLevel;
+    }
+
+    /**
+     * @return string representation.
+     */
+    @Override
+    public String toString() {
+        return "CellSignalStrengthWcdma:"
+                + " ss=" + mRssi
+                + " ber=" + mBitErrorRate
+                + " rscp=" + mRscp
+                + " ecno=" + mEcNo
+                + " level=" + mLevel;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (DBG) log("writeToParcel(Parcel, int): " + toString());
+        dest.writeInt(mRssi);
+        dest.writeInt(mBitErrorRate);
+        dest.writeInt(mRscp);
+        dest.writeInt(mEcNo);
+        dest.writeInt(mLevel);
+    }
+
+    /**
+     * Construct a SignalStrength object from the given parcel
+     * where the token is already been processed.
+     */
+    private CellSignalStrengthWcdma(Parcel in) {
+        mRssi = in.readInt();
+        mBitErrorRate = in.readInt();
+        mRscp = in.readInt();
+        mEcNo = in.readInt();
+        mLevel = in.readInt();
+        if (DBG) log("CellSignalStrengthWcdma(Parcel): " + toString());
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @SuppressWarnings("hiding")
+    public static final @android.annotation.NonNull Parcelable.Creator<CellSignalStrengthWcdma> CREATOR =
+            new Parcelable.Creator<CellSignalStrengthWcdma>() {
+        @Override
+        public CellSignalStrengthWcdma createFromParcel(Parcel in) {
+            return new CellSignalStrengthWcdma(in);
+        }
+
+        @Override
+        public CellSignalStrengthWcdma[] newArray(int size) {
+            return new CellSignalStrengthWcdma[size];
+        }
+    };
+
+    /**
+     * log warning
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+
+    /**
+     * log error
+     */
+    private static void loge(String s) {
+        Rlog.e(LOG_TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/CellularIdentifierDisclosure.java b/android-35/android/telephony/CellularIdentifierDisclosure.java
new file mode 100644
index 0000000..7b2db6d
--- /dev/null
+++ b/android-35/android/telephony/CellularIdentifierDisclosure.java
@@ -0,0 +1,161 @@
+/*
+ * 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.telephony;
+
+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;
+
+/**
+ * A single occurrence of a cellular identifier being disclosed in the clear before a security
+ * context is established.
+ *
+ * @hide
+ */
+public final class CellularIdentifierDisclosure implements Parcelable {
+    private static final String TAG = "CellularIdentifierDisclosure";
+
+    private @NasProtocolMessage int mNasProtocolMessage;
+    private @CellularIdentifier int mCellularIdentifier;
+    private String mPlmn;
+    private boolean mIsEmergency;
+
+    public CellularIdentifierDisclosure(@NasProtocolMessage int nasProtocolMessage,
+            @CellularIdentifier int cellularIdentifier, String plmn, boolean isEmergency) {
+        mNasProtocolMessage = nasProtocolMessage;
+        mCellularIdentifier = cellularIdentifier;
+        mPlmn = plmn;
+        mIsEmergency = isEmergency;
+    }
+
+    private CellularIdentifierDisclosure(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public @NasProtocolMessage int getNasProtocolMessage() {
+        return mNasProtocolMessage;
+    }
+
+    public @CellularIdentifier int getCellularIdentifier() {
+        return mCellularIdentifier;
+    }
+
+    public String getPlmn() {
+        return mPlmn;
+    }
+
+    public boolean isEmergency() {
+        return mIsEmergency;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mNasProtocolMessage);
+        out.writeInt(mCellularIdentifier);
+        out.writeBoolean(mIsEmergency);
+        out.writeString8(mPlmn);
+    }
+
+    public static final Parcelable.Creator<CellularIdentifierDisclosure> CREATOR =
+            new Parcelable.Creator<CellularIdentifierDisclosure>() {
+                public CellularIdentifierDisclosure createFromParcel(Parcel in) {
+                    return new CellularIdentifierDisclosure(in);
+                }
+
+                public CellularIdentifierDisclosure[] newArray(int size) {
+                    return new CellularIdentifierDisclosure[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return TAG + ":{ mNasProtocolMessage = " + mNasProtocolMessage
+                + " mCellularIdentifier = " + mCellularIdentifier + " mIsEmergency = "
+                + mIsEmergency + " mPlmn = " + mPlmn;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CellularIdentifierDisclosure)) return false;
+        CellularIdentifierDisclosure that = (CellularIdentifierDisclosure) o;
+        return mNasProtocolMessage == that.mNasProtocolMessage
+                && mCellularIdentifier == that.mCellularIdentifier
+                && mIsEmergency == that.mIsEmergency && mPlmn.equals(that.mPlmn);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mNasProtocolMessage, mCellularIdentifier, mIsEmergency,
+                mPlmn);
+    }
+
+    private void readFromParcel(@NonNull Parcel in) {
+        mNasProtocolMessage = in.readInt();
+        mCellularIdentifier = in.readInt();
+        mIsEmergency = in.readBoolean();
+        mPlmn = in.readString8();
+    }
+
+    public static final int NAS_PROTOCOL_MESSAGE_UNKNOWN = 0;
+    public static final int NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST = 1;
+    public static final int NAS_PROTOCOL_MESSAGE_IDENTITY_RESPONSE = 2;
+    public static final int NAS_PROTOCOL_MESSAGE_DETACH_REQUEST = 3;
+    public static final int NAS_PROTOCOL_MESSAGE_TRACKING_AREA_UPDATE_REQUEST = 4;
+    public static final int NAS_PROTOCOL_MESSAGE_LOCATION_UPDATE_REQUEST = 5;
+    public static final int NAS_PROTOCOL_MESSAGE_AUTHENTICATION_AND_CIPHERING_RESPONSE = 6;
+    public static final int NAS_PROTOCOL_MESSAGE_REGISTRATION_REQUEST = 7;
+    public static final int NAS_PROTOCOL_MESSAGE_DEREGISTRATION_REQUEST = 8;
+    public static final int NAS_PROTOCOL_MESSAGE_CM_REESTABLISHMENT_REQUEST = 9;
+    public static final int NAS_PROTOCOL_MESSAGE_CM_SERVICE_REQUEST = 10;
+    public static final int NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION = 11;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"NAS_PROTOCOL_MESSAGE_"}, value = {NAS_PROTOCOL_MESSAGE_UNKNOWN,
+            NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, NAS_PROTOCOL_MESSAGE_IDENTITY_RESPONSE,
+            NAS_PROTOCOL_MESSAGE_DETACH_REQUEST, NAS_PROTOCOL_MESSAGE_TRACKING_AREA_UPDATE_REQUEST,
+            NAS_PROTOCOL_MESSAGE_LOCATION_UPDATE_REQUEST,
+            NAS_PROTOCOL_MESSAGE_AUTHENTICATION_AND_CIPHERING_RESPONSE,
+            NAS_PROTOCOL_MESSAGE_REGISTRATION_REQUEST, NAS_PROTOCOL_MESSAGE_DEREGISTRATION_REQUEST,
+            NAS_PROTOCOL_MESSAGE_CM_REESTABLISHMENT_REQUEST,
+            NAS_PROTOCOL_MESSAGE_CM_SERVICE_REQUEST, NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION})
+    public @interface NasProtocolMessage {
+    }
+
+    public static final int CELLULAR_IDENTIFIER_UNKNOWN = 0;
+    public static final int CELLULAR_IDENTIFIER_IMSI = 1;
+    public static final int CELLULAR_IDENTIFIER_IMEI = 2;
+    public static final int CELLULAR_IDENTIFIER_SUCI = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CELLULAR_IDENTIFIER_"}, value = {CELLULAR_IDENTIFIER_UNKNOWN,
+            CELLULAR_IDENTIFIER_IMSI, CELLULAR_IDENTIFIER_IMEI, CELLULAR_IDENTIFIER_SUCI})
+    public @interface CellularIdentifier {
+    }
+}
diff --git a/android-35/android/telephony/ClientRequestStats.java b/android-35/android/telephony/ClientRequestStats.java
new file mode 100644
index 0000000..c787893
--- /dev/null
+++ b/android-35/android/telephony/ClientRequestStats.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2016 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.TelephonyHistogram;
+import android.util.SparseArray;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parcelable class to store Client request statistics information.
+ *
+ * @hide
+ */
+public final class ClientRequestStats implements Parcelable {
+    public static final @android.annotation.NonNull Parcelable.Creator<ClientRequestStats> CREATOR =
+            new Parcelable.Creator<ClientRequestStats>() {
+
+                public ClientRequestStats createFromParcel(Parcel in) {
+                    return new ClientRequestStats(in);
+                }
+
+                public ClientRequestStats[] newArray(int size) {
+                    return new ClientRequestStats[size];
+                }
+            };
+    private static final int REQUEST_HISTOGRAM_BUCKET_COUNT = 5;
+    private String mCallingPackage;
+    /* completed requests wake lock time in milli seconds */
+    private long mCompletedRequestsWakelockTime = 0;
+    private long mCompletedRequestsCount = 0;
+    private long mPendingRequestsWakelockTime = 0;
+    private long mPendingRequestsCount = 0;
+    private SparseArray<TelephonyHistogram> mRequestHistograms =
+            new SparseArray<TelephonyHistogram>();
+
+    public ClientRequestStats(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public ClientRequestStats() {
+    }
+
+    public ClientRequestStats(ClientRequestStats clientRequestStats) {
+        mCallingPackage = clientRequestStats.getCallingPackage();
+        mCompletedRequestsCount = clientRequestStats.getCompletedRequestsCount();
+        mCompletedRequestsWakelockTime = clientRequestStats.getCompletedRequestsWakelockTime();
+        mPendingRequestsCount = clientRequestStats.getPendingRequestsCount();
+        mPendingRequestsWakelockTime = clientRequestStats.getPendingRequestsWakelockTime();
+
+        List<TelephonyHistogram> list = clientRequestStats.getRequestHistograms();
+        for (TelephonyHistogram entry : list) {
+            mRequestHistograms.put(entry.getId(), entry);
+        }
+    }
+
+    public String getCallingPackage() {
+        return mCallingPackage;
+    }
+
+    public void setCallingPackage(String mCallingPackage) {
+        this.mCallingPackage = mCallingPackage;
+    }
+
+    public long getCompletedRequestsWakelockTime() {
+        return mCompletedRequestsWakelockTime;
+    }
+
+    public void addCompletedWakelockTime(long completedRequestsWakelockTime) {
+        this.mCompletedRequestsWakelockTime += completedRequestsWakelockTime;
+    }
+
+    public long getPendingRequestsWakelockTime() {
+        return mPendingRequestsWakelockTime;
+    }
+
+    public void setPendingRequestsWakelockTime(long pendingRequestsWakelockTime) {
+        this.mPendingRequestsWakelockTime = pendingRequestsWakelockTime;
+    }
+
+    public long getCompletedRequestsCount() {
+        return mCompletedRequestsCount;
+    }
+
+    public void incrementCompletedRequestsCount() {
+        this.mCompletedRequestsCount++;
+    }
+
+    public long getPendingRequestsCount() {
+        return mPendingRequestsCount;
+    }
+
+    public void setPendingRequestsCount(long pendingRequestsCount) {
+        this.mPendingRequestsCount = pendingRequestsCount;
+    }
+
+    public List<TelephonyHistogram> getRequestHistograms() {
+        List<TelephonyHistogram> list;
+        synchronized (mRequestHistograms) {
+            list = new ArrayList<>(mRequestHistograms.size());
+            for (int i = 0; i < mRequestHistograms.size(); i++) {
+                TelephonyHistogram entry = new TelephonyHistogram(mRequestHistograms.valueAt(i));
+                list.add(entry);
+            }
+        }
+        return list;
+    }
+
+    public void updateRequestHistograms(int requestId, int time) {
+        synchronized (mRequestHistograms) {
+            TelephonyHistogram entry = mRequestHistograms.get(requestId);
+            if (entry == null) {
+                entry = new TelephonyHistogram(TelephonyHistogram.TELEPHONY_CATEGORY_RIL,
+                        requestId, REQUEST_HISTOGRAM_BUCKET_COUNT);
+                mRequestHistograms.put(requestId, entry);
+            }
+            entry.addTimeTaken(time);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "ClientRequestStats{" +
+                "mCallingPackage='" + mCallingPackage + '\'' +
+                ", mCompletedRequestsWakelockTime=" + mCompletedRequestsWakelockTime +
+                ", mCompletedRequestsCount=" + mCompletedRequestsCount +
+                ", mPendingRequestsWakelockTime=" + mPendingRequestsWakelockTime +
+                ", mPendingRequestsCount=" + mPendingRequestsCount +
+                '}';
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public void readFromParcel(Parcel in) {
+        mCallingPackage = in.readString();
+        mCompletedRequestsWakelockTime = in.readLong();
+        mCompletedRequestsCount = in.readLong();
+        mPendingRequestsWakelockTime = in.readLong();
+        mPendingRequestsCount = in.readLong();
+        ArrayList<TelephonyHistogram> requestHistograms = new ArrayList<TelephonyHistogram>();
+        in.readTypedList(requestHistograms, TelephonyHistogram.CREATOR);
+        for (TelephonyHistogram h : requestHistograms) {
+            mRequestHistograms.put(h.getId(), h);
+        }
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mCallingPackage);
+        dest.writeLong(mCompletedRequestsWakelockTime);
+        dest.writeLong(mCompletedRequestsCount);
+        dest.writeLong(mPendingRequestsWakelockTime);
+        dest.writeLong(mPendingRequestsCount);
+        dest.writeTypedList(getRequestHistograms());
+    }
+}
diff --git a/android-35/android/telephony/ClosedSubscriberGroupInfo.java b/android-35/android/telephony/ClosedSubscriberGroupInfo.java
new file mode 100644
index 0000000..bf418ab
--- /dev/null
+++ b/android-35/android/telephony/ClosedSubscriberGroupInfo.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Information to represent a closed subscriber group.
+ */
+public final class ClosedSubscriberGroupInfo implements Parcelable {
+    private static final String TAG = "ClosedSubscriberGroupInfo";
+
+    private final boolean mCsgIndicator;
+
+    private final String mHomeNodebName;
+
+    private final int mCsgIdentity;
+
+    /** @hide */
+    public ClosedSubscriberGroupInfo(boolean csgIndicator, @Nullable String homeNodebName,
+            int csgIdentity) {
+        mCsgIndicator = csgIndicator;
+        mHomeNodebName = (homeNodebName == null) ? "" : homeNodebName;
+        mCsgIdentity = csgIdentity;
+    }
+
+    /**
+     * Indicates whether the cell is restricted to only CSG members.
+     *
+     * A cell not broadcasting the CSG Indication but reporting CSG information is considered a
+     * Hybrid Cell.
+     * Refer to the "csg-Indication" field in 3GPP TS 36.331 section 6.2.2
+     * SystemInformationBlockType1.
+     * Also refer to "CSG Indicator" in 3GPP TS 25.331 section 10.2.48.8.1 and TS 25.304.
+     *
+     * @return true if the cell is restricted to group members only.
+     */
+    public boolean getCsgIndicator() {
+        return mCsgIndicator;
+    }
+
+    /**
+     * Returns human-readable name of the closed subscriber group operating this cell (Node-B).
+     *
+     * Refer to "hnb-Name" in TS 36.331 section 6.2.2 SystemInformationBlockType9.
+     * Also refer to "HNB Name" in 3GPP TS25.331 section 10.2.48.8.23 and TS 23.003 section 4.8.
+     *
+     * @return the home Node-B name if available.
+     */
+    public @NonNull String getHomeNodebName() {
+        return mHomeNodebName;
+    }
+
+    /**
+     * The identity of the closed subscriber group that the cell belongs to.
+     *
+     * Refer to "CSG-Identity" in TS 36.336 section 6.3.4.
+     * Also refer to "CSG Identity" in 3GPP TS 25.331 section 10.3.2.8 and TS 23.003 section 4.7.
+     *
+     * @return the unique 27-bit CSG Identity.
+     */
+    @IntRange(from = 0, to = 0x7FFFFFF)
+    public int getCsgIdentity() {
+        return mCsgIdentity;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCsgIndicator, mHomeNodebName, mCsgIdentity);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof ClosedSubscriberGroupInfo)) {
+            return false;
+        }
+
+        ClosedSubscriberGroupInfo o = (ClosedSubscriberGroupInfo) other;
+        return mCsgIndicator == o.mCsgIndicator && o.mHomeNodebName.equals(mHomeNodebName)
+                && mCsgIdentity == o.mCsgIdentity;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder(TAG + ":{")
+                .append(" mCsgIndicator = ").append(mCsgIndicator)
+                .append(" mHomeNodebName = ").append(mHomeNodebName)
+                .append(" mCsgIdentity = ").append(mCsgIdentity)
+                .toString();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int type) {
+        dest.writeBoolean(mCsgIndicator);
+        dest.writeString(mHomeNodebName);
+        dest.writeInt(mCsgIdentity);
+    }
+
+    /** Construct from Parcel, type has already been processed */
+    private ClosedSubscriberGroupInfo(Parcel in) {
+        this(in.readBoolean(), in.readString(), in.readInt());
+    }
+
+    /**
+     * Implement the Parcelable interface
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @android.annotation.NonNull Creator<ClosedSubscriberGroupInfo> CREATOR =
+            new Creator<ClosedSubscriberGroupInfo>() {
+                @Override
+                public ClosedSubscriberGroupInfo createFromParcel(Parcel in) {
+                    return createFromParcelBody(in);
+                }
+
+                @Override
+                public ClosedSubscriberGroupInfo[] newArray(int size) {
+                    return new ClosedSubscriberGroupInfo[size];
+                }
+            };
+
+    /** @hide */
+    protected static ClosedSubscriberGroupInfo createFromParcelBody(Parcel in) {
+        return new ClosedSubscriberGroupInfo(in);
+    }
+}
diff --git a/android-35/android/telephony/DataConnectionRealTimeInfo.java b/android-35/android/telephony/DataConnectionRealTimeInfo.java
new file mode 100644
index 0000000..8767133
--- /dev/null
+++ b/android-35/android/telephony/DataConnectionRealTimeInfo.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data connection real time information
+ *
+ * TODO: How to handle multiple subscriptions?
+ * @hide
+ */
+public class DataConnectionRealTimeInfo implements Parcelable {
+    private long mTime;             // Time the info was collected since boot in nanos;
+
+    public static final int DC_POWER_STATE_LOW
+            = TelephonyProtoEnums.DATA_CONNECTION_POWER_STATE_LOW ; // = 1
+    public static final int DC_POWER_STATE_MEDIUM
+            = TelephonyProtoEnums.DATA_CONNECTION_POWER_STATE_MEDIUM; // = 2
+    public static final int DC_POWER_STATE_HIGH
+            = TelephonyProtoEnums.DATA_CONNECTION_POWER_STATE_HIGH; // = 3
+    public static final int DC_POWER_STATE_UNKNOWN
+            = TelephonyProtoEnums.DATA_CONNECTION_POWER_STATE_UNKNOWN; // = Integer.MAX_VALUE
+
+    private int mDcPowerState;      // DC_POWER_STATE_[LOW | MEDIUM | HIGH | UNKNOWN]
+
+    /**
+     * Constructor
+     *
+     * @hide
+     */
+    public DataConnectionRealTimeInfo(long time, int dcPowerState) {
+        mTime = time;
+        mDcPowerState = dcPowerState;
+    }
+
+    /**
+     * Constructor
+     *
+     * @hide
+     */
+    public DataConnectionRealTimeInfo() {
+        mTime = Long.MAX_VALUE;
+        mDcPowerState = DC_POWER_STATE_UNKNOWN;
+    }
+
+    /**
+     * Construct a PreciseCallState object from the given parcel.
+     */
+    private DataConnectionRealTimeInfo(Parcel in) {
+        mTime = in.readLong();
+        mDcPowerState = in.readInt();
+    }
+
+    /**
+     * @return time the information was collected or Long.MAX_VALUE if unknown
+     */
+    public long getTime() {
+        return mTime;
+    }
+
+    /**
+     * @return DC_POWER_STATE_[LOW | MEDIUM | HIGH | UNKNOWN]
+     */
+    public int getDcPowerState() {
+        return mDcPowerState;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeLong(mTime);
+        out.writeInt(mDcPowerState);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<DataConnectionRealTimeInfo> CREATOR
+            = new Parcelable.Creator<DataConnectionRealTimeInfo>() {
+
+        @Override
+        public DataConnectionRealTimeInfo createFromParcel(Parcel in) {
+            return new DataConnectionRealTimeInfo(in);
+        }
+
+        @Override
+        public DataConnectionRealTimeInfo[] newArray(int size) {
+            return new DataConnectionRealTimeInfo[size];
+        }
+    };
+
+    @Override
+    public int hashCode() {
+        final long prime = 17;
+        long result = 1;
+        result = (prime * result) + mTime;
+        result += (prime * result) + mDcPowerState;
+        return (int)result;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        DataConnectionRealTimeInfo other = (DataConnectionRealTimeInfo) obj;
+        return (mTime == other.mTime)
+                && (mDcPowerState == other.mDcPowerState);
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("mTime=").append(mTime);
+        sb.append(" mDcPowerState=").append(mDcPowerState);
+
+        return sb.toString();
+    }
+}
diff --git a/android-35/android/telephony/DataFailCause.java b/android-35/android/telephony/DataFailCause.java
new file mode 100644
index 0000000..dd7e2d7
--- /dev/null
+++ b/android-35/android/telephony/DataFailCause.java
@@ -0,0 +1,1757 @@
+/*
+ * Copyright (C) 2006 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;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.telephony.Annotation.DataFailureCause;
+
+import com.android.internal.telephony.util.ArrayUtils;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * DataFailCause collects data connection failure causes code from different sources.
+ */
+public final class DataFailCause {
+    /** There is no failure */
+    public static final int NONE = 0;
+
+    // This series of errors as specified by the standards
+    // specified in ril.h
+    /** Operator determined barring. (no retry) */
+    public static final int OPERATOR_BARRED = 0x08;
+    /** NAS signalling. */
+    public static final int NAS_SIGNALLING = 0x0E;
+    /** Logical Link Control (LLC) Sub Network Dependent Convergence Protocol (SNDCP). */
+    public static final int LLC_SNDCP = 0x19;
+    /** Insufficient resources. */
+    public static final int INSUFFICIENT_RESOURCES = 0x1A;
+    /** Missing or unknown APN. */
+    public static final int MISSING_UNKNOWN_APN = 0x1B;              /* no retry */
+    /** Unknown Packet Data Protocol (PDP) address type. */
+    public static final int UNKNOWN_PDP_ADDRESS_TYPE = 0x1C;         /* no retry */
+    /** User authentication. */
+    public static final int USER_AUTHENTICATION = 0x1D;              /* no retry */
+    /** Activation rejected by Gateway GPRS Support Node (GGSN), Serving Gateway or PDN Gateway. */
+    public static final int ACTIVATION_REJECT_GGSN = 0x1E;           /* no retry */
+    /** Activation rejected, unspecified. */
+    public static final int ACTIVATION_REJECT_UNSPECIFIED = 0x1F;
+    /** Service option not supported. */
+    public static final int SERVICE_OPTION_NOT_SUPPORTED = 0x20;     /* no retry */
+    /** Requested service option not subscribed. */
+    public static final int SERVICE_OPTION_NOT_SUBSCRIBED = 0x21;    /* no retry */
+    /** Service option temporarily out of order. */
+    public static final int SERVICE_OPTION_OUT_OF_ORDER = 0x22;
+    /** The Network Service Access Point Identifier (NSAPI) is in use. */
+    public static final int NSAPI_IN_USE = 0x23;                     /* no retry */
+    /* possibly restart radio, based on config */
+    /** Regular deactivation. */
+    public static final int REGULAR_DEACTIVATION = 0x24;
+    /** Quality of service (QoS) is not accepted. */
+    public static final int QOS_NOT_ACCEPTED = 0x25;
+    /** Network Failure. */
+    public static final int NETWORK_FAILURE = 0x26;
+    /** Universal Mobile Telecommunications System (UMTS) reactivation request. */
+    public static final int UMTS_REACTIVATION_REQ = 0x27;
+    /** Feature not supported. */
+    public static final int FEATURE_NOT_SUPP = 0x28;
+    /** Semantic error in the Traffic flow templates (TFT) operation. */
+    public static final int TFT_SEMANTIC_ERROR = 0x29;
+    /** Syntactical error in the Traffic flow templates (TFT) operation. */
+    public static final int TFT_SYTAX_ERROR = 0x2A;
+    /** Unknown Packet Data Protocol (PDP) context. */
+    public static final int UNKNOWN_PDP_CONTEXT = 0x2B;
+    /** Semantic errors in packet filter. */
+    public static final int FILTER_SEMANTIC_ERROR = 0x2C;
+    /** Syntactical errors in packet filter(s). */
+    public static final int FILTER_SYTAX_ERROR = 0x2D;
+    /** Packet Data Protocol (PDP) without active traffic flow template (TFT). */
+    public static final int PDP_WITHOUT_ACTIVE_TFT = 0x2E;
+    /**
+     * UE requested to modify QoS parameters or the bearer control mode, which is not compatible
+     * with the selected bearer control mode.
+     */
+    public static final int ACTIVATION_REJECTED_BCM_VIOLATION = 0x30;
+    /** Packet Data Protocol (PDP) type IPv4 only allowed. */
+    public static final int ONLY_IPV4_ALLOWED = 0x32;                /* no retry */
+    /** Packet Data Protocol (PDP) type IPv6 only allowed. */
+    public static final int ONLY_IPV6_ALLOWED = 0x33;                /* no retry */
+    /** Single address bearers only allowed. */
+    public static final int ONLY_SINGLE_BEARER_ALLOWED = 0x34;
+    /** EPS Session Management (ESM) information is not received. */
+    public static final int ESM_INFO_NOT_RECEIVED = 0x35;
+    /** PDN connection does not exist. */
+    public static final int PDN_CONN_DOES_NOT_EXIST = 0x36;
+    /** Multiple connections to a same PDN is not allowed. */
+    public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 0x37;
+    /**
+     * Network has already initiated the activation, modification, or deactivation of bearer
+     * resources that was requested by the UE.
+     */
+    public static final int COLLISION_WITH_NETWORK_INITIATED_REQUEST = 0x38;
+    /**
+     * Network supports IPv4v6 PDP type only. Non-IP type is not allowed. In LTE mode of operation,
+     * this is a PDN throttling cause code, meaning the UE may throttle further requests to the
+     * same APN.
+     */
+    public static final int ONLY_IPV4V6_ALLOWED = 0x39;
+    /**
+     * Network supports non-IP PDP type only. IPv4, IPv6 and IPv4v6 is not allowed. In LTE mode of
+     * operation, this is a PDN throttling cause code, meaning the UE can throttle further requests
+     * to the same APN.
+     */
+    public static final int ONLY_NON_IP_ALLOWED = 0x3A;
+    /** QCI (QoS Class Identifier) indicated in the UE request cannot be supported. */
+    public static final int UNSUPPORTED_QCI_VALUE = 0x3B;
+    /** Procedure requested by the UE was rejected because the bearer handling is not supported. */
+    public static final int BEARER_HANDLING_NOT_SUPPORTED = 0x3C;
+    /**
+     * This cause is used to report a service or option not available event only when no other
+     * cause in the service or option not available class applies.
+     * @hide // TODO: Unhide in U.
+     */
+    public static final int SERVICE_OR_OPTION_NOT_AVAILABLE = 0x3F;
+    /** Max number of Packet Data Protocol (PDP) context reached. */
+    public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 0x41;
+    /** Unsupported APN in current public land mobile network (PLMN). */
+    public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 0x42;
+    /** Invalid transaction id. */
+    public static final int INVALID_TRANSACTION_ID = 0x51;
+    /** Incorrect message semantic. */
+    public static final int MESSAGE_INCORRECT_SEMANTIC = 0x5F;
+    /** Invalid mandatory information. */
+    public static final int INVALID_MANDATORY_INFO = 0x60;
+    /** Unsupported message type. */
+    public static final int MESSAGE_TYPE_UNSUPPORTED = 0x61;
+    /** Message type uncompatible. */
+    public static final int MSG_TYPE_NONCOMPATIBLE_STATE = 0x62;
+    /** Unknown info element. */
+    public static final int UNKNOWN_INFO_ELEMENT = 0x63;
+    /** Conditional Information Element (IE) error. */
+    public static final int CONDITIONAL_IE_ERROR = 0x64;
+    /** Message and protocol state uncompatible. */
+    public static final int MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 0x65;
+    /** Protocol errors. */
+    public static final int PROTOCOL_ERRORS = 0x6F;                  /* no retry */
+    /** APN type conflict. */
+    public static final int APN_TYPE_CONFLICT = 0x70;
+    /** Invalid Proxy-Call Session Control Function (P-CSCF) address. */
+    public static final int INVALID_PCSCF_ADDR = 0x71;
+    /** Internal data call preempt by high priority APN. */
+    public static final int INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 0x72;
+    /** EPS (Evolved Packet System) Mobility Management (EMM) access barred. */
+    public static final int EMM_ACCESS_BARRED = 0x73;
+    /** Emergency interface only. */
+    public static final int EMERGENCY_IFACE_ONLY = 0x74;
+    /** Interface mismatch. */
+    public static final int IFACE_MISMATCH = 0x75;
+    /** Companion interface in use. */
+    public static final int COMPANION_IFACE_IN_USE = 0x76;
+    /** IP address mismatch. */
+    public static final int IP_ADDRESS_MISMATCH = 0x77;
+    public static final int IFACE_AND_POL_FAMILY_MISMATCH = 0x78;
+    /** EPS (Evolved Packet System) Mobility Management (EMM) access barred infinity retry. **/
+    public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 0x79;
+    /** Authentication failure on emergency call. */
+    public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 0x7A;
+    /** Not receiving a DNS address that was mandatory. */
+    public static final int INVALID_DNS_ADDR = 0x7B;
+    /** Not receiving either a PCSCF or a DNS address, one of them being mandatory. */
+    public static final int INVALID_PCSCF_OR_DNS_ADDRESS = 0x7C;
+    /** Emergency call bring up on a different ePDG. */
+    public static final int CALL_PREEMPT_BY_EMERGENCY_APN = 0x7F;
+    /** UE performs a detach or disconnect PDN action based on TE requirements. */
+    public static final int UE_INITIATED_DETACH_OR_DISCONNECT = 0x80;
+
+    /** Reason unspecified for foreign agent rejected MIP (Mobile IP) registration. */
+    public static final int MIP_FA_REASON_UNSPECIFIED = 0x7D0;
+    /** Foreign agent administratively prohibited MIP (Mobile IP) registration. */
+    public static final int MIP_FA_ADMIN_PROHIBITED = 0x7D1;
+    /** Foreign agent rejected MIP (Mobile IP) registration because of insufficient resources. */
+    public static final int MIP_FA_INSUFFICIENT_RESOURCES = 0x7D2;
+    /**
+     * Foreign agent rejected MIP (Mobile IP) registration because of MN-AAA authenticator was
+     * wrong.
+     */
+    public static final int MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE = 0x7D3;
+    /**
+     * Foreign agent rejected MIP (Mobile IP) registration because of home agent authentication
+     * failure.
+     */
+    public static final int MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE = 0x7D4;
+    /**
+     * Foreign agent rejected MIP (Mobile IP) registration because of requested lifetime was too
+     * long.
+     */
+    public static final int MIP_FA_REQUESTED_LIFETIME_TOO_LONG = 0x7D5;
+    /** Foreign agent rejected MIP (Mobile IP) registration because of malformed request. */
+    public static final int MIP_FA_MALFORMED_REQUEST = 0x7D6;
+    /** Foreign agent rejected MIP (Mobile IP) registration because of malformed reply. */
+    public static final int MIP_FA_MALFORMED_REPLY = 0x7D7;
+    /**
+     * Foreign agent rejected MIP (Mobile IP) registration because of requested encapsulation was
+     * unavailable.
+     */
+    public static final int MIP_FA_ENCAPSULATION_UNAVAILABLE = 0x7D8;
+    /**
+     * Foreign agent rejected MIP (Mobile IP) registration of VJ Header Compression was
+     * unavailable.
+     */
+    public static final int MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE = 0x7D9;
+    /**
+     * Foreign agent rejected MIP (Mobile IP) registration because of reverse tunnel was
+     * unavailable.
+     */
+    public static final int MIP_FA_REVERSE_TUNNEL_UNAVAILABLE = 0x7DA;
+    /**
+     * Foreign agent rejected MIP (Mobile IP) registration because of reverse tunnel was mandatory
+     * but not requested by device.
+     */
+    public static final int MIP_FA_REVERSE_TUNNEL_IS_MANDATORY = 0x7DB;
+    /**
+     * Foreign agent rejected MIP (Mobile IP) registration because of delivery style was not
+     * supported.
+     */
+    public static final int MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED = 0x7DC;
+    /**
+     * Foreign agent rejected MIP (Mobile IP) registration because of missing NAI (Network Access
+     * Identifier).
+     */
+    public static final int MIP_FA_MISSING_NAI = 0x7DD;
+    /** Foreign agent rejected MIP (Mobile IP) registration because of missing Home Agent. */
+    public static final int MIP_FA_MISSING_HOME_AGENT = 0x7DE;
+    /** Foreign agent rejected MIP (Mobile IP) registration because of missing Home Address. */
+    public static final int MIP_FA_MISSING_HOME_ADDRESS = 0x7DF;
+    /** Foreign agent rejected MIP (Mobile IP) registration because of unknown challenge. */
+    public static final int MIP_FA_UNKNOWN_CHALLENGE = 0x7E0;
+    /** Foreign agent rejected MIP (Mobile IP) registration because of missing challenge. */
+    public static final int MIP_FA_MISSING_CHALLENGE = 0x7E1;
+    /** Foreign agent rejected MIP (Mobile IP) registration because of stale challenge. */
+    public static final int MIP_FA_STALE_CHALLENGE = 0x7E2;
+    /** Reason unspecified for home agent rejected MIP (Mobile IP) registration. */
+    public static final int MIP_HA_REASON_UNSPECIFIED = 0x7E3;
+    /** Home agent administratively prohibited MIP (Mobile IP) registration. */
+    public static final int MIP_HA_ADMIN_PROHIBITED = 0x7E4;
+    /** Home agent rejected MIP (Mobile IP) registration because of insufficient resources. */
+    public static final int MIP_HA_INSUFFICIENT_RESOURCES = 0x7E5;
+    /**
+     * Home agent rejected MIP (Mobile IP) registration because of MN-HA authenticator was
+     * wrong.
+     */
+    public static final int MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE = 0x7E6;
+    /**
+     * Home agent rejected MIP (Mobile IP) registration because of foreign agent authentication
+     * failure.
+     */
+    public static final int MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE = 0x7E7;
+    /** Home agent rejected MIP (Mobile IP) registration because of registration id mismatch. */
+    public static final int MIP_HA_REGISTRATION_ID_MISMATCH = 0x7E8;
+    /** Home agent rejected MIP (Mobile IP) registration because of malformed request. */
+    public static final int MIP_HA_MALFORMED_REQUEST = 0x7E9;
+    /** Home agent rejected MIP (Mobile IP) registration because of unknown home agent address. */
+    public static final int MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS = 0x7EA;
+    /**
+     * Home agent rejected MIP (Mobile IP) registration because of reverse tunnel was
+     * unavailable.
+     */
+    public static final int MIP_HA_REVERSE_TUNNEL_UNAVAILABLE = 0x7EB;
+    /**
+     * Home agent rejected MIP (Mobile IP) registration because of reverse tunnel is mandatory but
+     * not requested by device.
+     */
+    public static final int MIP_HA_REVERSE_TUNNEL_IS_MANDATORY = 0x7EC;
+    /** Home agent rejected MIP (Mobile IP) registration because of encapsulation unavailable. */
+    public static final int MIP_HA_ENCAPSULATION_UNAVAILABLE = 0x7ED;
+    /** Tearing down is in progress. */
+    public static final int CLOSE_IN_PROGRESS = 0x7EE;
+    /** Brought down by the network. */
+    public static final int NETWORK_INITIATED_TERMINATION = 0x7EF;
+    /** Another application in modem preempts the data call. */
+    public static final int MODEM_APP_PREEMPTED = 0x7F0;
+    /**
+     * IPV4 PDN is in throttled state due to network providing only IPV6 address during the
+     * previous VSNCP bringup (subs_limited_to_v6).
+     */
+    public static final int PDN_IPV4_CALL_DISALLOWED = 0x7F1;
+    /** IPV4 PDN is in throttled state due to previous VSNCP bringup failure(s). */
+    public static final int PDN_IPV4_CALL_THROTTLED = 0x7F2;
+    /**
+     * IPV6 PDN is in throttled state due to network providing only IPV4 address during the
+     * previous VSNCP bringup (subs_limited_to_v4).
+     */
+    public static final int PDN_IPV6_CALL_DISALLOWED = 0x7F3;
+    /** IPV6 PDN is in throttled state due to previous VSNCP bringup failure(s). */
+    public static final int PDN_IPV6_CALL_THROTTLED = 0x7F4;
+    /** Modem restart. */
+    public static final int MODEM_RESTART = 0x7F5;
+    /** PDP PPP calls are not supported. */
+    public static final int PDP_PPP_NOT_SUPPORTED = 0x7F6;
+    /** RAT on which the data call is attempted/connected is no longer the preferred RAT. */
+    public static final int UNPREFERRED_RAT = 0x7F7;
+    /** Physical link is in the process of cleanup. */
+    public static final int PHYSICAL_LINK_CLOSE_IN_PROGRESS = 0x7F8;
+    /** Interface bring up is attempted for an APN that is yet to be handed over to target RAT. */
+    public static final int APN_PENDING_HANDOVER = 0x7F9;
+    /** APN bearer type in the profile does not match preferred network mode. */
+    public static final int PROFILE_BEARER_INCOMPATIBLE = 0x7FA;
+    /** Card was refreshed or removed. */
+    public static final int SIM_CARD_CHANGED = 0x7FB;
+    /** Device is going into lower power mode or powering down. */
+    public static final int LOW_POWER_MODE_OR_POWERING_DOWN = 0x7FC;
+    /** APN has been disabled. */
+    public static final int APN_DISABLED = 0x7FD;
+    /** Maximum PPP inactivity timer expired. */
+    public static final int MAX_PPP_INACTIVITY_TIMER_EXPIRED = 0x7FE;
+    /** IPv6 address transfer failed. */
+    public static final int IPV6_ADDRESS_TRANSFER_FAILED = 0x7FF;
+    /** Target RAT swap failed. */
+    public static final int TRAT_SWAP_FAILED = 0x800;
+    /** Device falls back from eHRPD to HRPD. */
+    public static final int EHRPD_TO_HRPD_FALLBACK = 0x801;
+    /**
+     * UE is in MIP-only configuration but the MIP configuration fails on call bring up due to
+     * incorrect provisioning.
+     */
+    public static final int MIP_CONFIG_FAILURE = 0x802;
+    /**
+     * PDN inactivity timer expired due to no data transmission in a configurable duration of time.
+     */
+    public static final int PDN_INACTIVITY_TIMER_EXPIRED = 0x803;
+    /**
+     * IPv4 data call bring up is rejected because the UE already maintains the allotted maximum
+     * number of IPv4 data connections.
+     */
+    public static final int MAX_IPV4_CONNECTIONS = 0x804;
+    /**
+     * IPv6 data call bring up is rejected because the UE already maintains the allotted maximum
+     * number of IPv6 data connections.
+     */
+    public static final int MAX_IPV6_CONNECTIONS = 0x805;
+    /**
+     * New PDN bring up is rejected during interface selection because the UE has already allotted
+     * the available interfaces for other PDNs.
+     */
+    public static final int APN_MISMATCH = 0x806;
+    /**
+     * New call bring up is rejected since the existing data call IP type doesn't match the
+     * requested IP.
+     */
+    public static final int IP_VERSION_MISMATCH = 0x807;
+    /** Dial up networking (DUN) call bring up is rejected since UE is in eHRPD RAT. */
+    public static final int DUN_CALL_DISALLOWED = 0x808;
+    /*** Rejected/Brought down since UE is transition between EPC and NONEPC RAT. */
+    public static final int INTERNAL_EPC_NONEPC_TRANSITION = 0x809;
+    /** The current interface is being in use. */
+    public static final int INTERFACE_IN_USE = 0x80A;
+    /** PDN connection to the APN is disallowed on the roaming network. */
+    public static final int APN_DISALLOWED_ON_ROAMING = 0x80B;
+    /** APN-related parameters are changed. */
+    public static final int APN_PARAMETERS_CHANGED = 0x80C;
+    /** PDN is attempted to be brought up with NULL APN but NULL APN is not supported. */
+    public static final int NULL_APN_DISALLOWED = 0x80D;
+    /**
+     * Thermal level increases and causes calls to be torn down when normal mode of operation is
+     * not allowed.
+     */
+    public static final int THERMAL_MITIGATION = 0x80E;
+    /**
+     * PDN Connection to a given APN is disallowed because data is disabled from the device user
+     * interface settings.
+     */
+    public static final int DATA_SETTINGS_DISABLED = 0x80F;
+    /**
+     * PDN Connection to a given APN is disallowed because data roaming is disabled from the device
+     * user interface settings and the UE is roaming.
+     */
+    public static final int DATA_ROAMING_SETTINGS_DISABLED = 0x810;
+    /** DDS (Default data subscription) switch occurs. */
+    public static final int DDS_SWITCHED = 0x811;
+    /** PDN being brought up with an APN that is part of forbidden APN Name list. */
+    public static final int FORBIDDEN_APN_NAME = 0x812;
+    /** Default data subscription switch is in progress. */
+    public static final int DDS_SWITCH_IN_PROGRESS = 0x813;
+    /** Roaming is disallowed during call bring up. */
+    public static final int CALL_DISALLOWED_IN_ROAMING = 0x814;
+    /**
+     * UE is unable to bring up a non-IP data call because the device is not camped on a NB1 cell.
+     */
+    public static final int NON_IP_NOT_SUPPORTED = 0x815;
+    /** Non-IP PDN is in throttled state due to previous VSNCP bringup failure(s). */
+    public static final int PDN_NON_IP_CALL_THROTTLED = 0x816;
+    /** Non-IP PDN is in disallowed state due to the network providing only an IP address. */
+    public static final int PDN_NON_IP_CALL_DISALLOWED = 0x817;
+    /** Device in CDMA locked state. */
+    public static final int CDMA_LOCK = 0x818;
+    /** Received an intercept order from the base station. */
+    public static final int CDMA_INTERCEPT = 0x819;
+    /** Receiving a reorder from the base station. */
+    public static final int CDMA_REORDER = 0x81A;
+    /** Receiving a release from the base station with a SO (Service Option) Reject reason. */
+    public static final int CDMA_RELEASE_DUE_TO_SO_REJECTION = 0x81B;
+    /** Receiving an incoming call from the base station. */
+    public static final int CDMA_INCOMING_CALL = 0x81C;
+    /** Received an alert stop from the base station due to incoming only. */
+    public static final int CDMA_ALERT_STOP = 0x81D;
+    /**
+     * Channel acquisition failures. This indicates that device has failed acquiring all the
+     * channels in the PRL.
+     */
+    public static final int CHANNEL_ACQUISITION_FAILURE = 0x81E;
+    /** Maximum access probes transmitted. */
+    public static final int MAX_ACCESS_PROBE = 0x81F;
+    /** Concurrent service is not supported by base station. */
+    public static final int CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION = 0x820;
+    /** There was no response received from the base station. */
+    public static final int NO_RESPONSE_FROM_BASE_STATION = 0x821;
+    /** The base station rejecting the call. */
+    public static final int REJECTED_BY_BASE_STATION = 0x822;
+    /** The concurrent services requested were not compatible. */
+    public static final int CONCURRENT_SERVICES_INCOMPATIBLE = 0x823;
+    /** Device does not have CDMA service. */
+    public static final int NO_CDMA_SERVICE = 0x824;
+    /** RUIM not being present. */
+    public static final int RUIM_NOT_PRESENT = 0x825;
+    /** Receiving a retry order from the base station. */
+    public static final int CDMA_RETRY_ORDER = 0x826;
+    /** Access blocked by the base station. */
+    public static final int ACCESS_BLOCK = 0x827;
+    /** Access blocked by the base station for all mobile devices. */
+    public static final int ACCESS_BLOCK_ALL = 0x828;
+    /** Maximum access probes for the IS-707B call. */
+    public static final int IS707B_MAX_ACCESS_PROBES = 0x829;
+    /** Put device in thermal emergency. */
+    public static final int THERMAL_EMERGENCY = 0x82A;
+    /** In favor of a voice call or SMS when concurrent voice and data are not supported. */
+    public static final int CONCURRENT_SERVICES_NOT_ALLOWED = 0x82B;
+    /** The other clients rejected incoming call. */
+    public static final int INCOMING_CALL_REJECTED = 0x82C;
+    /** No service on the gateway. */
+    public static final int NO_SERVICE_ON_GATEWAY = 0x82D;
+    /** GPRS context is not available. */
+    public static final int NO_GPRS_CONTEXT = 0x82E;
+    /**
+     * Network refuses service to the MS because either an identity of the MS is not acceptable to
+     * the network or the MS does not pass the authentication check.
+     */
+    public static final int ILLEGAL_MS = 0x82F;
+    /** ME could not be authenticated and the ME used is not acceptable to the network. */
+    public static final int ILLEGAL_ME = 0x830;
+    /** Not allowed to operate either GPRS or non-GPRS services. */
+    public static final int GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED = 0x831;
+    /** MS is not allowed to operate GPRS services. */
+    public static final int GPRS_SERVICES_NOT_ALLOWED = 0x832;
+    /** No matching identity or context could be found in the network. */
+    public static final int MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK = 0x833;
+    /**
+     * Mobile reachable timer has expired, or the GMM context data related to the subscription does
+     * not exist in the SGSN.
+     */
+    public static final int IMPLICITLY_DETACHED = 0x834;
+    /**
+     * UE requests GPRS service, or the network initiates a detach request in a PLMN which does not
+     * offer roaming for GPRS services to that MS.
+     */
+    public static final int PLMN_NOT_ALLOWED = 0x835;
+    /**
+     * MS requests service, or the network initiates a detach request, in a location area where the
+     * HPLMN determines that the MS, by subscription, is not allowed to operate.
+     */
+    public static final int LOCATION_AREA_NOT_ALLOWED = 0x836;
+    /**
+     * UE requests GPRS service or the network initiates a detach request in a PLMN that does not
+     * offer roaming for GPRS services.
+     */
+    public static final int GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN = 0x837;
+    /** PDP context already exists. */
+    public static final int PDP_DUPLICATE = 0x838;
+    /** RAT change on the UE. */
+    public static final int UE_RAT_CHANGE = 0x839;
+    /** Network cannot serve a request from the MS due to congestion. */
+    public static final int CONGESTION = 0x83A;
+    /**
+     * MS requests an establishment of the radio access bearers for all active PDP contexts by
+     * sending a service request message indicating data to the network, but the SGSN does not have
+     * any active PDP context.
+     */
+    public static final int NO_PDP_CONTEXT_ACTIVATED = 0x83B;
+    /** Access class blocking restrictions for the current camped cell. */
+    public static final int ACCESS_CLASS_DSAC_REJECTION = 0x83C;
+    /** SM attempts PDP activation for a maximum of four attempts. */
+    public static final int PDP_ACTIVATE_MAX_RETRY_FAILED = 0x83D;
+    /** Radio access bearer failure. */
+    public static final int RADIO_ACCESS_BEARER_FAILURE = 0x83E;
+    /** Invalid EPS bearer identity in the request. */
+    public static final int ESM_UNKNOWN_EPS_BEARER_CONTEXT = 0x83F;
+    /** Data radio bearer is released by the RRC. */
+    public static final int DRB_RELEASED_BY_RRC = 0x840;
+    /** Indicate the connection was released. */
+    public static final int CONNECTION_RELEASED = 0x841;
+    /** UE is detached. */
+    public static final int EMM_DETACHED = 0x842;
+    /** Attach procedure is rejected by the network. */
+    public static final int EMM_ATTACH_FAILED = 0x843;
+    /** Attach procedure is started for EMC purposes. */
+    public static final int EMM_ATTACH_STARTED = 0x844;
+    /** Service request procedure failure. */
+    public static final int LTE_NAS_SERVICE_REQUEST_FAILED = 0x845;
+    /** Active dedicated bearer was requested using the same default bearer ID. */
+    public static final int DUPLICATE_BEARER_ID = 0x846;
+    /** Collision scenarios for the UE and network-initiated procedures. */
+    public static final int ESM_COLLISION_SCENARIOS = 0x847;
+    /** Bearer must be deactivated to synchronize with the network. */
+    public static final int ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK = 0x848;
+    /** Active dedicated bearer was requested for an existing default bearer. */
+    public static final int ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER = 0x849;
+    /** Bad OTA message is received from the network. */
+    public static final int ESM_BAD_OTA_MESSAGE = 0x84A;
+    /** Download server rejected the call. */
+    public static final int ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL = 0x84B;
+    /** PDN was disconnected by the downlaod server due to IRAT. */
+    public static final int ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT = 0x84C;
+    /** Dedicated bearer will be deactivated regardless of the network response. */
+    public static final int DS_EXPLICIT_DEACTIVATION = 0x84D;
+    /** No specific local cause is mentioned, usually a valid OTA cause. */
+    public static final int ESM_LOCAL_CAUSE_NONE = 0x84E;
+    /** Throttling is not needed for this service request failure. */
+    public static final int LTE_THROTTLING_NOT_REQUIRED = 0x84F;
+    /** Access control list check failure at the lower layer. */
+    public static final int ACCESS_CONTROL_LIST_CHECK_FAILURE = 0x850;
+    /** Service is not allowed on the requested PLMN. */
+    public static final int SERVICE_NOT_ALLOWED_ON_PLMN = 0x851;
+    /** T3417 timer expiration of the service request procedure. */
+    public static final int EMM_T3417_EXPIRED = 0x852;
+    /** Extended service request fails due to expiration of the T3417 EXT timer. */
+    public static final int EMM_T3417_EXT_EXPIRED = 0x853;
+    /** Transmission failure of radio resource control (RRC) uplink data. */
+    public static final int RRC_UPLINK_DATA_TRANSMISSION_FAILURE = 0x854;
+    /** Radio resource control (RRC) uplink data delivery failed due to a handover. */
+    public static final int RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER = 0x855;
+    /** Radio resource control (RRC) uplink data delivery failed due to a connection release. */
+    public static final int RRC_UPLINK_CONNECTION_RELEASE = 0x856;
+    /** Radio resource control (RRC) uplink data delivery failed due to a radio link failure. */
+    public static final int RRC_UPLINK_RADIO_LINK_FAILURE = 0x857;
+    /**
+     * Radio resource control (RRC) is not connected but the non-access stratum (NAS) sends an
+     * uplink data request.
+     */
+    public static final int RRC_UPLINK_ERROR_REQUEST_FROM_NAS = 0x858;
+    /** Radio resource control (RRC) connection failure at access stratum. */
+    public static final int RRC_CONNECTION_ACCESS_STRATUM_FAILURE = 0x859;
+    /**
+     * Radio resource control (RRC) connection establishment is aborted due to another procedure.
+     */
+    public static final int RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS = 0x85A;
+    /** Radio resource control (RRC) connection establishment failed due to access barrred. */
+    public static final int RRC_CONNECTION_ACCESS_BARRED = 0x85B;
+    /**
+     * Radio resource control (RRC) connection establishment failed due to cell reselection at
+     * access stratum.
+     */
+    public static final int RRC_CONNECTION_CELL_RESELECTION = 0x85C;
+    /**
+     * Connection establishment failed due to configuration failure at the radio resource control
+     * (RRC).
+     */
+    public static final int RRC_CONNECTION_CONFIG_FAILURE = 0x85D;
+    /** Radio resource control (RRC) connection could not be established in the time limit. */
+    public static final int RRC_CONNECTION_TIMER_EXPIRED = 0x85E;
+    /**
+     * Connection establishment failed due to a link failure at the radio resource control (RRC).
+     */
+    public static final int RRC_CONNECTION_LINK_FAILURE = 0x85F;
+    /**
+     * Connection establishment failed as the radio resource control (RRC) is not camped on any
+     * cell.
+     */
+    public static final int RRC_CONNECTION_CELL_NOT_CAMPED = 0x860;
+    /**
+     * Connection establishment failed due to a service interval failure at the radio resource
+     * control (RRC).
+     */
+    public static final int RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE = 0x861;
+    /**
+     * Radio resource control (RRC) connection establishment failed due to the network rejecting
+     * the UE connection request.
+     */
+    public static final int RRC_CONNECTION_REJECT_BY_NETWORK = 0x862;
+    /** Normal radio resource control (RRC) connection release. */
+    public static final int RRC_CONNECTION_NORMAL_RELEASE = 0x863;
+    /**
+     * Radio resource control (RRC) connection release failed due to radio link failure conditions.
+     */
+    public static final int RRC_CONNECTION_RADIO_LINK_FAILURE = 0x864;
+    /** Radio resource control (RRC) connection re-establishment failure. */
+    public static final int RRC_CONNECTION_REESTABLISHMENT_FAILURE = 0x865;
+    /** UE is out of service during the call register. */
+    public static final int RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER = 0x866;
+    /**
+     * Connection has been released by the radio resource control (RRC) due to an abort request.
+     */
+    public static final int RRC_CONNECTION_ABORT_REQUEST = 0x867;
+    /**
+     * Radio resource control (RRC) connection released due to a system information block read
+     * error.
+     */
+    public static final int RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR = 0x868;
+    /** Network-initiated detach with reattach. */
+    public static final int NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH = 0x869;
+    /** Network-initiated detach without reattach. */
+    public static final int NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH = 0x86A;
+    /** ESM procedure maximum attempt timeout failure. */
+    public static final int ESM_PROCEDURE_TIME_OUT = 0x86B;
+    /**
+     * No PDP exists with the given connection ID while modifying or deactivating or activation for
+     * an already active PDP.
+     */
+    public static final int INVALID_CONNECTION_ID = 0x86C;
+    /** Maximum NSAPIs have been exceeded during PDP activation. */
+    public static final int MAXIMIUM_NSAPIS_EXCEEDED = 0x86D;
+    /** Primary context for NSAPI does not exist. */
+    public static final int INVALID_PRIMARY_NSAPI = 0x86E;
+    /** Unable to encode the OTA message for MT PDP or deactivate PDP. */
+    public static final int CANNOT_ENCODE_OTA_MESSAGE = 0x86F;
+    /**
+     * Radio access bearer is not established by the lower layers during activation, modification,
+     * or deactivation.
+     */
+    public static final int RADIO_ACCESS_BEARER_SETUP_FAILURE = 0x870;
+    /** Expiration of the PDP establish timer with a maximum of five retries. */
+    public static final int PDP_ESTABLISH_TIMEOUT_EXPIRED = 0x871;
+    /** Expiration of the PDP modify timer with a maximum of four retries. */
+    public static final int PDP_MODIFY_TIMEOUT_EXPIRED = 0x872;
+    /** Expiration of the PDP deactivate timer with a maximum of four retries. */
+    public static final int PDP_INACTIVE_TIMEOUT_EXPIRED = 0x873;
+    /** PDP activation failed due to RRC_ABORT or a forbidden PLMN. */
+    public static final int PDP_LOWERLAYER_ERROR = 0x874;
+    /** MO PDP modify collision when the MT PDP is already in progress. */
+    public static final int PDP_MODIFY_COLLISION = 0x875;
+    /** Maximum size of the L2 message was exceeded. */
+    public static final int MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED = 0x876;
+    /** Non-access stratum (NAS) request was rejected by the network. */
+    public static final int NAS_REQUEST_REJECTED_BY_NETWORK = 0x877;
+    /**
+     * Radio resource control (RRC) connection establishment failure due to an error in the request
+     * message.
+     */
+    public static final int RRC_CONNECTION_INVALID_REQUEST = 0x878;
+    /**
+     * Radio resource control (RRC) connection establishment failure due to a change in the
+     * tracking area ID.
+     */
+    public static final int RRC_CONNECTION_TRACKING_AREA_ID_CHANGED = 0x879;
+    /**
+     * Radio resource control (RRC) connection establishment failure due to the RF was unavailable.
+     */
+    public static final int RRC_CONNECTION_RF_UNAVAILABLE = 0x87A;
+    /**
+     * Radio resource control (RRC) connection was aborted before deactivating the LTE stack due to
+     * a successful LTE to WCDMA/GSM/TD-SCDMA IRAT change.
+     */
+    public static final int RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE = 0x87B;
+    /**
+     * If the UE has an LTE radio link failure before security is established, the radio resource
+     * control (RRC) connection must be released and the UE must return to idle.
+     */
+    public static final int RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE = 0x87C;
+    /**
+     * Radio resource control (RRC) connection was aborted by the non-access stratum (NAS) after an
+     * IRAT to LTE IRAT handover.
+     */
+    public static final int RRC_CONNECTION_ABORTED_AFTER_HANDOVER = 0x87D;
+    /**
+     * Radio resource control (RRC) connection was aborted before deactivating the LTE stack after
+     * a successful LTE to GSM/EDGE IRAT cell change order procedure.
+     */
+    public static final int RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE = 0x87E;
+    /**
+     * Radio resource control (RRC) connection was aborted in the middle of a LTE to GSM IRAT cell
+     * change order procedure.
+     */
+    public static final int RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE = 0x87F;
+    /** IMSI present in the UE is unknown in the home subscriber server. */
+    public static final int IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER = 0x880;
+    /** IMEI of the UE is not accepted by the network. */
+    public static final int IMEI_NOT_ACCEPTED = 0x881;
+    /** EPS and non-EPS services are not allowed by the network. */
+    public static final int EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED = 0x882;
+    /** EPS services are not allowed in the PLMN. */
+    public static final int EPS_SERVICES_NOT_ALLOWED_IN_PLMN = 0x883;
+    /** Mobile switching center is temporarily unreachable. */
+    public static final int MSC_TEMPORARILY_NOT_REACHABLE = 0x884;
+    /** CS domain is not available. */
+    public static final int CS_DOMAIN_NOT_AVAILABLE = 0x885;
+    /** ESM level failure. */
+    public static final int ESM_FAILURE = 0x886;
+    /** MAC level failure. */
+    public static final int MAC_FAILURE = 0x887;
+    /** Synchronization failure. */
+    public static final int SYNCHRONIZATION_FAILURE = 0x888;
+    /** UE security capabilities mismatch. */
+    public static final int UE_SECURITY_CAPABILITIES_MISMATCH = 0x889;
+    /** Unspecified security mode reject. */
+    public static final int SECURITY_MODE_REJECTED = 0x88A;
+    /** Unacceptable non-EPS authentication. */
+    public static final int UNACCEPTABLE_NON_EPS_AUTHENTICATION = 0x88B;
+    /** CS fallback call establishment is not allowed. */
+    public static final int CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED = 0x88C;
+    /** No EPS bearer context was activated. */
+    public static final int NO_EPS_BEARER_CONTEXT_ACTIVATED = 0x88D;
+    /** Invalid EMM state. */
+    public static final int INVALID_EMM_STATE = 0x88E;
+    /** Non-Access Spectrum layer failure. */
+    public static final int NAS_LAYER_FAILURE = 0x88F;
+    /** Multiple PDP call feature is disabled. */
+    public static final int MULTIPLE_PDP_CALL_NOT_ALLOWED = 0x890;
+    /** Data call has been brought down because EMBMS is not enabled at the RRC layer. */
+    public static final int EMBMS_NOT_ENABLED = 0x891;
+    /** Data call was unsuccessfully transferred during the IRAT handover. */
+    public static final int IRAT_HANDOVER_FAILED = 0x892;
+    /** EMBMS data call has been successfully brought down. */
+    public static final int EMBMS_REGULAR_DEACTIVATION = 0x893;
+    /** Test loop-back data call has been successfully brought down. */
+    public static final int TEST_LOOPBACK_REGULAR_DEACTIVATION = 0x894;
+    /** Lower layer registration failure. */
+    public static final int LOWER_LAYER_REGISTRATION_FAILURE = 0x895;
+    /**
+     * Network initiates a detach on LTE with error cause ""data plan has been replenished or has
+     * expired.
+     */
+    public static final int DATA_PLAN_EXPIRED = 0x896;
+    /** UMTS interface is brought down due to handover from UMTS to iWLAN. */
+    public static final int UMTS_HANDOVER_TO_IWLAN = 0x897;
+    /** Received a connection deny due to general or network busy on EVDO network. */
+    public static final int EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY = 0x898;
+    /** Received a connection deny due to billing or authentication failure on EVDO network. */
+    public static final int EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE = 0x899;
+    /** HDR system has been changed due to redirection or the PRL was not preferred. */
+    public static final int EVDO_HDR_CHANGED = 0x89A;
+    /** Device exited HDR due to redirection or the PRL was not preferred. */
+    public static final int EVDO_HDR_EXITED = 0x89B;
+    /** Device does not have an HDR session. */
+    public static final int EVDO_HDR_NO_SESSION = 0x89C;
+    /** It is ending an HDR call origination in favor of a GPS fix. */
+    public static final int EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL = 0x89D;
+    /** Connection setup on the HDR system was time out. */
+    public static final int EVDO_HDR_CONNECTION_SETUP_TIMEOUT = 0x89E;
+    /** Device failed to acquire a co-located HDR for origination. */
+    public static final int FAILED_TO_ACQUIRE_COLOCATED_HDR = 0x89F;
+    /** OTASP commit is in progress. */
+    public static final int OTASP_COMMIT_IN_PROGRESS = 0x8A0;
+    /** Device has no hybrid HDR service. */
+    public static final int NO_HYBRID_HDR_SERVICE = 0x8A1;
+    /** HDR module could not be obtained because of the RF locked. */
+    public static final int HDR_NO_LOCK_GRANTED = 0x8A2;
+    /** DBM or SMS is in progress. */
+    public static final int DBM_OR_SMS_IN_PROGRESS = 0x8A3;
+    /** HDR module released the call due to fade. */
+    public static final int HDR_FADE = 0x8A4;
+    /** HDR system access failure. */
+    public static final int HDR_ACCESS_FAILURE = 0x8A5;
+    /**
+     * P_rev supported by 1 base station is less than 6, which is not supported for a 1X data call.
+     * The UE must be in the footprint of BS which has p_rev >= 6 to support this SO33 call.
+     */
+    public static final int UNSUPPORTED_1X_PREV = 0x8A6;
+    /** Client ended the data call. */
+    public static final int LOCAL_END = 0x8A7;
+    /** Device has no service. */
+    public static final int NO_SERVICE = 0x8A8;
+    /** Device lost the system due to fade. */
+    public static final int FADE = 0x8A9;
+    /** Receiving a release from the base station with no reason. */
+    public static final int NORMAL_RELEASE = 0x8AA;
+    /** Access attempt is already in progress. */
+    public static final int ACCESS_ATTEMPT_ALREADY_IN_PROGRESS = 0x8AB;
+    /** Device is in the process of redirecting or handing off to a different target system. */
+    public static final int REDIRECTION_OR_HANDOFF_IN_PROGRESS = 0x8AC;
+    /** Device is operating in Emergency mode. */
+    public static final int EMERGENCY_MODE = 0x8AD;
+    /** Device is in use (e.g., voice call). */
+    public static final int PHONE_IN_USE = 0x8AE;
+    /**
+     * Device operational mode is different from the mode requested in the traffic channel bring up.
+     */
+    public static final int INVALID_MODE = 0x8AF;
+    /** SIM was marked by the network as invalid for the circuit and/or packet service domain. */
+    public static final int INVALID_SIM_STATE = 0x8B0;
+    /** There is no co-located HDR. */
+    public static final int NO_COLLOCATED_HDR = 0x8B1;
+    /** UE is entering power save mode. */
+    public static final int UE_IS_ENTERING_POWERSAVE_MODE = 0x8B2;
+    /** Dual switch from single standby to dual standby is in progress. */
+    public static final int DUAL_SWITCH = 0x8B3;
+    /**
+     * Data call bring up fails in the PPP setup due to a timeout.
+     * (e.g., an LCP conf ack was not received from the network)
+     */
+    public static final int PPP_TIMEOUT = 0x8B4;
+    /**
+     * Data call bring up fails in the PPP setup due to an authorization failure.
+     * (e.g., authorization is required, but not negotiated with the network during an LCP phase)
+     */
+    public static final int PPP_AUTH_FAILURE = 0x8B5;
+    /** Data call bring up fails in the PPP setup due to an option mismatch. */
+    public static final int PPP_OPTION_MISMATCH = 0x8B6;
+    /** Data call bring up fails in the PPP setup due to a PAP failure. */
+    public static final int PPP_PAP_FAILURE = 0x8B7;
+    /** Data call bring up fails in the PPP setup due to a CHAP failure. */
+    public static final int PPP_CHAP_FAILURE = 0x8B8;
+    /**
+     * Data call bring up fails in the PPP setup because the PPP is in the process of cleaning the
+     * previous PPP session.
+     */
+    public static final int PPP_CLOSE_IN_PROGRESS = 0x8B9;
+    /**
+     * IPv6 interface bring up fails because the network provided only the IPv4 address for the
+     * upcoming PDN permanent client can reattempt a IPv6 call bring up after the IPv4 interface is
+     * also brought down. However, there is no guarantee that the network will provide a IPv6
+     * address.
+     */
+    public static final int LIMITED_TO_IPV4 = 0x8BA;
+    /**
+     * IPv4 interface bring up fails because the network provided only the IPv6 address for the
+     * upcoming PDN permanent client can reattempt a IPv4 call bring up after the IPv6 interface is
+     * also brought down. However there is no guarantee that the network will provide a IPv4
+     * address.
+     */
+    public static final int LIMITED_TO_IPV6 = 0x8BB;
+    /** Data call bring up fails in the VSNCP phase due to a VSNCP timeout error. */
+    public static final int VSNCP_TIMEOUT = 0x8BC;
+    /**
+     * Data call bring up fails in the VSNCP phase due to a general error. It's used when there is
+     * no other specific error code available to report the failure.
+     */
+    public static final int VSNCP_GEN_ERROR = 0x8BD;
+    /**
+     * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+     * configuration request because the requested APN is unauthorized.
+     *
+     * @deprecated Use {@link #VSNCP_APN_UNAUTHORIZED} instead.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    public static final int VSNCP_APN_UNATHORIZED = 0x8BE; // NOTYPO
+    /**
+     * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+     * configuration request because the requested APN is unauthorized.
+     */
+    public static final int VSNCP_APN_UNAUTHORIZED = 0x8BE;
+    /**
+     * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+     * configuration request because the PDN limit has been exceeded.
+     */
+    public static final int VSNCP_PDN_LIMIT_EXCEEDED = 0x8BF;
+    /**
+     * Data call bring up fails in the VSNCP phase due to the network rejected the VSNCP
+     * configuration request due to no PDN gateway address.
+     */
+    public static final int VSNCP_NO_PDN_GATEWAY_ADDRESS = 0x8C0;
+    /**
+     * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+     * configuration request because the PDN gateway is unreachable.
+     */
+    public static final int VSNCP_PDN_GATEWAY_UNREACHABLE = 0x8C1;
+    /**
+     * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+     * configuration request due to a PDN gateway reject.
+     */
+    public static final int VSNCP_PDN_GATEWAY_REJECT = 0x8C2;
+    /**
+     * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+     * configuration request with the reason of insufficient parameter.
+     */
+    public static final int VSNCP_INSUFFICIENT_PARAMETERS = 0x8C3;
+    /**
+     * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+     * configuration request with the reason of resource unavailable.
+     */
+    public static final int VSNCP_RESOURCE_UNAVAILABLE = 0x8C4;
+    /**
+     * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+     * configuration request with the reason of administratively prohibited at the HSGW.
+     */
+    public static final int VSNCP_ADMINISTRATIVELY_PROHIBITED = 0x8C5;
+    /**
+     * Data call bring up fails in the VSNCP phase due to a network rejection of PDN ID in use, or
+     * all existing PDNs are brought down with this end reason because one of the PDN bring up was
+     * rejected by the network with the reason of PDN ID in use.
+     */
+    public static final int VSNCP_PDN_ID_IN_USE = 0x8C6;
+    /**
+     * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+     * configuration request for the reason of subscriber limitation.
+     */
+    public static final int VSNCP_SUBSCRIBER_LIMITATION = 0x8C7;
+    /**
+     * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+     * configuration request because the PDN exists for this APN.
+     */
+    public static final int VSNCP_PDN_EXISTS_FOR_THIS_APN = 0x8C8;
+    /**
+     * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+     * configuration request with reconnect to this PDN not allowed, or an active data call is
+     * terminated by the network because reconnection to this PDN is not allowed. Upon receiving
+     * this error code from the network, the modem infinitely throttles the PDN until the next
+     * power cycle.
+     */
+    public static final int VSNCP_RECONNECT_NOT_ALLOWED = 0x8C9;
+    /** Device failure to obtain the prefix from the network. */
+    public static final int IPV6_PREFIX_UNAVAILABLE = 0x8CA;
+    /** System preference change back to SRAT during handoff */
+    public static final int HANDOFF_PREFERENCE_CHANGED = 0x8CB;
+    /** Data call fail due to the slice not being allowed for the data call. */
+    public static final int SLICE_REJECTED = 0x8CC;
+    /** No matching rule available for the request, and match-all rule is not allowed for it. */
+    public static final int MATCH_ALL_RULE_NOT_ALLOWED = 0x8CD;
+    /** If connection failed for all matching URSP rules. */
+    public static final int ALL_MATCHING_RULES_FAILED = 0x8CE;
+
+    //IKE error notifications message as specified in 3GPP TS 24.302 (Section 8.1.2.2).
+
+    /** The PDN connection corresponding to the requested APN has been rejected. */
+    public static final int IWLAN_PDN_CONNECTION_REJECTION = 0x2000;
+    /**
+     * The PDN connection has been rejected. No additional PDN connections can be established
+     * for the UE due to the network policies or capabilities.
+     */
+    public static final int IWLAN_MAX_CONNECTION_REACHED = 0x2001;
+    /**
+     * The PDN connection has been rejected due to a semantic error in TFT operation.
+     */
+    public static final int IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION = 0x2031;
+    /**
+     * The PDN connection has been rejected due to a syntactic error in TFT operation.
+     */
+    public static final int IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION = 0x2032;
+    /**
+     * The PDN connection has been rejected due to sematic errors in the packet filter.
+     */
+    public static final int IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS = 0x2034;
+    /**
+     * The PDN connection has been rejected due to syntactic errors in the packet filter.
+     */
+    public static final int IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS = 0x2035;
+    /**
+     * No non-3GPP subscription is associated with the IMSI.
+     * The UE is not allowed to use non-3GPP access to EPC.
+     */
+    public static final int IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED = 0x2328;
+    /** The user identified by the IMSI is unknown. */
+    public static final int IWLAN_USER_UNKNOWN = 0x2329;
+    /**
+     * The requested APN is not included in the user's profile,
+     * and therefore is not authorized for that user.
+     */
+    public static final int IWLAN_NO_APN_SUBSCRIPTION = 0x232A;
+    /** The user is barred from using the non-3GPP access or the subscribed APN. */
+    public static final int IWLAN_AUTHORIZATION_REJECTED = 0x232B;
+    /** The Mobile Equipment used is not acceptable to the network */
+    public static final int IWLAN_ILLEGAL_ME = 0x232E;
+    /**
+     * The network has determined that the requested procedure cannot be completed successfully
+     * due to network failure.
+     */
+    public static final int IWLAN_NETWORK_FAILURE = 0x2904;
+    /** The access type is restricted to the user. */
+    public static final int IWLAN_RAT_TYPE_NOT_ALLOWED = 0x2AF9;
+    /** The network does not accept emergency PDN bringup request using an IMEI */
+    public static final int IWLAN_IMEI_NOT_ACCEPTED = 0x2AFD;
+    /**
+     * The ePDG performs PLMN filtering (based on roaming agreements) and rejects
+     * the request from the UE.
+     * The UE requests service in a PLMN where the UE is not allowed to operate.
+     */
+    public static final int IWLAN_PLMN_NOT_ALLOWED = 0x2B03;
+    /** The ePDG does not support un-authenticated IMSI based emergency PDN bringup **/
+    public static final int IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED = 0x2B2F;
+
+    // The below error causes are relevant when the device is unable to establish an IPSec tunnel
+    // with the ePDG for any reason, e.g. authentication fail or certificate validation or DNS
+    // Resolution and timeout failure.
+
+    /**
+     * The requested service was rejected because of congestion in the network while accessing the
+     * IWLAN ePDG. Defined in 3GPP TS 24.502, Section 9.2.4.2.
+     */
+    public static final int IWLAN_CONGESTION = 0x3C8C;
+
+    // Below IWLAN error codes are defined by the UE and do not relate to any 3GPP spec value
+    /** IKE configuration error resulting in failure */
+    public static final int IWLAN_IKEV2_CONFIG_FAILURE = 0x4000;
+    /**
+     * Sent in the response to an IKE_AUTH message when, for some reason,
+     * the authentication failed.
+     */
+    public static final int IWLAN_IKEV2_AUTH_FAILURE = 0x4001;
+    /** IKE message timeout, tunnel setup failed due to no response from EPDG */
+    public static final int IWLAN_IKEV2_MSG_TIMEOUT = 0x4002;
+    /** IKE Certification validation failure  */
+    public static final int IWLAN_IKEV2_CERT_INVALID = 0x4003;
+    /** Unable to resolve FQDN for the ePDG to an IP address */
+    public static final int IWLAN_DNS_RESOLUTION_NAME_FAILURE = 0x4004;
+    /** No response received from the DNS Server due to a timeout*/
+    public static final int IWLAN_DNS_RESOLUTION_TIMEOUT = 0x4005;
+    /** Expected to update or bring down an ePDG tunnel, but no tunnel found*/
+    public static final int IWLAN_TUNNEL_NOT_FOUND = 0x4006;
+    /**
+     * Failed to apply tunnel transform
+     *
+     * @hide
+     */
+    public static final int IWLAN_TUNNEL_TRANSFORM_FAILED = 0x4007;
+    /**
+     * IWLAN PDN setup failed due to Wi-Fi lost during IKE tunnel setup,
+     * match exception reported by IKE module
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_NETWORK_LOST_EXCEPTION = 0x4008;
+    /**
+     * Carrier-specific error codes during IKEv2 SA setup
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_PRIVATE_PROTOCOL_ERROR = 0x4009;
+    /**
+     * IKE Session closed before child session opened
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED = 0x400A;
+    /**
+     * IKE Init timeout, no response from EPDG
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_INIT_TIMEOUT = 0x400B;
+    /**
+     * DPD message does not get an ack after the re-tx attempts and duration, i.e., times out.
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_DPD_TIMEOUT = 0x400C;
+    /**
+     * The Wi-Fi to Wi-Fi handover of the IMS PDN fails because the network does not respond to the
+     * MOBIKE/rekey mobility message in the expected manner
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_MOBILITY_TIMEOUT = 0x400D;
+    /**
+     * IKE client sent "IKE AUTH request 3" to the network but got "Internal address failure" from
+     * the network since no internal addresses can be assigned.
+     *
+     * @hide
+     */
+    public static final int IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE = 0x400E;
+
+    // OEM sepecific error codes. To be used by OEMs when they don't
+    // want to reveal error code which would be replaced by ERROR_UNSPECIFIED
+    public static final int OEM_DCFAILCAUSE_1 = 0x1001;
+    public static final int OEM_DCFAILCAUSE_2 = 0x1002;
+    public static final int OEM_DCFAILCAUSE_3 = 0x1003;
+    public static final int OEM_DCFAILCAUSE_4 = 0x1004;
+    public static final int OEM_DCFAILCAUSE_5 = 0x1005;
+    public static final int OEM_DCFAILCAUSE_6 = 0x1006;
+    public static final int OEM_DCFAILCAUSE_7 = 0x1007;
+    public static final int OEM_DCFAILCAUSE_8 = 0x1008;
+    public static final int OEM_DCFAILCAUSE_9 = 0x1009;
+    public static final int OEM_DCFAILCAUSE_10 = 0x100A;
+    public static final int OEM_DCFAILCAUSE_11 = 0x100B;
+    public static final int OEM_DCFAILCAUSE_12 = 0x100C;
+    public static final int OEM_DCFAILCAUSE_13 = 0x100D;
+    public static final int OEM_DCFAILCAUSE_14 = 0x100E;
+    public static final int OEM_DCFAILCAUSE_15 = 0x100F;
+
+    // Local errors generated by Vendor RIL
+    // specified in ril.h
+    /** Data fail due to registration failure. */
+    public static final int REGISTRATION_FAIL = -1;
+    /** Data fail due to GPRS registration failure. */
+    public static final int GPRS_REGISTRATION_FAIL = -2;
+    /** Data call drop due to network/modem disconnect. */
+    public static final int SIGNAL_LOST = -3;                        /* no retry */
+    /**
+     * Preferred technology has changed, must retry with parameters appropriate for new technology.
+     */
+    public static final int PREF_RADIO_TECH_CHANGED = -4;
+    /** data call was disconnected because radio was resetting, powered off. */
+    public static final int RADIO_POWER_OFF = -5;                    /* no retry */
+    /** Data call was disconnected by modem because tethered. */
+    public static final int TETHERED_CALL_ACTIVE = -6;               /* no retry */
+    /** Data call fail due to unspecific errors. */
+    public static final int ERROR_UNSPECIFIED = 0xFFFF;
+
+    // Errors generated by the Framework
+    // specified here
+    /** Unknown data failure cause. */
+    public static final int UNKNOWN = 0x10000;
+    /** Data fail due to radio not unavailable. */
+    public static final int RADIO_NOT_AVAILABLE = 0x10001;                   /* no retry */
+    /** Data fail due to unacceptable network parameter. */
+    public static final int UNACCEPTABLE_NETWORK_PARAMETER = 0x10002;        /* no retry */
+    /** Data connection was lost. */
+    public static final int LOST_CONNECTION = 0x10004;
+
+    /**
+     * Data handover failed.
+     *
+     * @hide
+     */
+    public static final int HANDOVER_FAILED = 0x10006;
+
+    /**
+     * Enterprise setup failure: duplicate CID in DataCallResponse.
+     *
+     * @hide
+     */
+    public static final int DUPLICATE_CID = 0x10007;
+
+    /**
+     * Enterprise setup failure: no default data connection set up yet.
+     *
+     * @hide
+     */
+    public static final int NO_DEFAULT_DATA = 0x10008;
+
+    /**
+     * Data service is temporarily unavailable.
+     *
+     * @hide
+     */
+    public static final int SERVICE_TEMPORARILY_UNAVAILABLE = 0x10009;
+
+    /**
+     * The request is not supported by the vendor.
+     *
+     * @hide
+     */
+    public static final int REQUEST_NOT_SUPPORTED = 0x1000A;
+
+    /**
+     * An internal setup data error initiated by telephony that no retry should be performed.
+     *
+     * @hide
+     */
+    public static final int NO_RETRY_FAILURE = 0x1000B;
+
+    private static final Map<Integer, String> sFailCauseMap;
+    static {
+        sFailCauseMap = new HashMap<>();
+        sFailCauseMap.put(NONE, "NONE");
+        sFailCauseMap.put(OPERATOR_BARRED, "OPERATOR_BARRED");
+        sFailCauseMap.put(NAS_SIGNALLING, "NAS_SIGNALLING");
+        sFailCauseMap.put(LLC_SNDCP, "LLC_SNDCP");
+        sFailCauseMap.put(INSUFFICIENT_RESOURCES, "INSUFFICIENT_RESOURCES");
+        sFailCauseMap.put(MISSING_UNKNOWN_APN, "MISSING_UNKNOWN_APN");
+        sFailCauseMap.put(UNKNOWN_PDP_ADDRESS_TYPE, "UNKNOWN_PDP_ADDRESS_TYPE");
+        sFailCauseMap.put(USER_AUTHENTICATION, "USER_AUTHENTICATION");
+        sFailCauseMap.put(ACTIVATION_REJECT_GGSN, "ACTIVATION_REJECT_GGSN");
+        sFailCauseMap.put(ACTIVATION_REJECT_UNSPECIFIED,
+                "ACTIVATION_REJECT_UNSPECIFIED");
+        sFailCauseMap.put(SERVICE_OPTION_NOT_SUPPORTED,
+                "SERVICE_OPTION_NOT_SUPPORTED");
+        sFailCauseMap.put(SERVICE_OPTION_NOT_SUBSCRIBED,
+                "SERVICE_OPTION_NOT_SUBSCRIBED");
+        sFailCauseMap.put(SERVICE_OPTION_OUT_OF_ORDER, "SERVICE_OPTION_OUT_OF_ORDER");
+        sFailCauseMap.put(NSAPI_IN_USE, "NSAPI_IN_USE");
+        sFailCauseMap.put(REGULAR_DEACTIVATION, "REGULAR_DEACTIVATION");
+        sFailCauseMap.put(QOS_NOT_ACCEPTED, "QOS_NOT_ACCEPTED");
+        sFailCauseMap.put(NETWORK_FAILURE, "NETWORK_FAILURE");
+        sFailCauseMap.put(UMTS_REACTIVATION_REQ, "UMTS_REACTIVATION_REQ");
+        sFailCauseMap.put(FEATURE_NOT_SUPP, "FEATURE_NOT_SUPP");
+        sFailCauseMap.put(TFT_SEMANTIC_ERROR, "TFT_SEMANTIC_ERROR");
+        sFailCauseMap.put(TFT_SYTAX_ERROR, "TFT_SYTAX_ERROR");
+        sFailCauseMap.put(UNKNOWN_PDP_CONTEXT, "UNKNOWN_PDP_CONTEXT");
+        sFailCauseMap.put(FILTER_SEMANTIC_ERROR, "FILTER_SEMANTIC_ERROR");
+        sFailCauseMap.put(FILTER_SYTAX_ERROR, "FILTER_SYTAX_ERROR");
+        sFailCauseMap.put(PDP_WITHOUT_ACTIVE_TFT, "PDP_WITHOUT_ACTIVE_TFT");
+        sFailCauseMap.put(ACTIVATION_REJECTED_BCM_VIOLATION, "ACTIVATION_REJECTED_BCM_VIOLATION");
+        sFailCauseMap.put(ONLY_IPV4_ALLOWED, "ONLY_IPV4_ALLOWED");
+        sFailCauseMap.put(ONLY_IPV6_ALLOWED, "ONLY_IPV6_ALLOWED");
+        sFailCauseMap.put(ONLY_SINGLE_BEARER_ALLOWED, "ONLY_SINGLE_BEARER_ALLOWED");
+        sFailCauseMap.put(ESM_INFO_NOT_RECEIVED, "ESM_INFO_NOT_RECEIVED");
+        sFailCauseMap.put(PDN_CONN_DOES_NOT_EXIST, "PDN_CONN_DOES_NOT_EXIST");
+        sFailCauseMap.put(MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
+                "MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED");
+        sFailCauseMap.put(COLLISION_WITH_NETWORK_INITIATED_REQUEST,
+                "COLLISION_WITH_NETWORK_INITIATED_REQUEST");
+        sFailCauseMap.put(ONLY_IPV4V6_ALLOWED, "ONLY_IPV4V6_ALLOWED");
+        sFailCauseMap.put(ONLY_NON_IP_ALLOWED, "ONLY_NON_IP_ALLOWED");
+        sFailCauseMap.put(UNSUPPORTED_QCI_VALUE, "UNSUPPORTED_QCI_VALUE");
+        sFailCauseMap.put(BEARER_HANDLING_NOT_SUPPORTED, "BEARER_HANDLING_NOT_SUPPORTED");
+        sFailCauseMap.put(SERVICE_OR_OPTION_NOT_AVAILABLE, "SERVICE_OR_OPTION_NOT_AVAILABLE");
+        sFailCauseMap.put(ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED,
+                "ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED");
+        sFailCauseMap.put(UNSUPPORTED_APN_IN_CURRENT_PLMN,
+                "UNSUPPORTED_APN_IN_CURRENT_PLMN");
+        sFailCauseMap.put(INVALID_TRANSACTION_ID, "INVALID_TRANSACTION_ID");
+        sFailCauseMap.put(MESSAGE_INCORRECT_SEMANTIC, "MESSAGE_INCORRECT_SEMANTIC");
+        sFailCauseMap.put(INVALID_MANDATORY_INFO, "INVALID_MANDATORY_INFO");
+        sFailCauseMap.put(MESSAGE_TYPE_UNSUPPORTED, "MESSAGE_TYPE_UNSUPPORTED");
+        sFailCauseMap.put(MSG_TYPE_NONCOMPATIBLE_STATE, "MSG_TYPE_NONCOMPATIBLE_STATE");
+        sFailCauseMap.put(UNKNOWN_INFO_ELEMENT, "UNKNOWN_INFO_ELEMENT");
+        sFailCauseMap.put(CONDITIONAL_IE_ERROR, "CONDITIONAL_IE_ERROR");
+        sFailCauseMap.put(MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE,
+                "MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE");
+        sFailCauseMap.put(PROTOCOL_ERRORS, "PROTOCOL_ERRORS");
+        sFailCauseMap.put(APN_TYPE_CONFLICT, "APN_TYPE_CONFLICT");
+        sFailCauseMap.put(INVALID_PCSCF_ADDR, "INVALID_PCSCF_ADDR");
+        sFailCauseMap.put(INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN,
+                "INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN");
+        sFailCauseMap.put(EMM_ACCESS_BARRED, "EMM_ACCESS_BARRED");
+        sFailCauseMap.put(EMERGENCY_IFACE_ONLY, "EMERGENCY_IFACE_ONLY");
+        sFailCauseMap.put(IFACE_MISMATCH, "IFACE_MISMATCH");
+        sFailCauseMap.put(COMPANION_IFACE_IN_USE, "COMPANION_IFACE_IN_USE");
+        sFailCauseMap.put(IP_ADDRESS_MISMATCH, "IP_ADDRESS_MISMATCH");
+        sFailCauseMap.put(IFACE_AND_POL_FAMILY_MISMATCH,
+                "IFACE_AND_POL_FAMILY_MISMATCH");
+        sFailCauseMap.put(EMM_ACCESS_BARRED_INFINITE_RETRY,
+                "EMM_ACCESS_BARRED_INFINITE_RETRY");
+        sFailCauseMap.put(AUTH_FAILURE_ON_EMERGENCY_CALL,
+                "AUTH_FAILURE_ON_EMERGENCY_CALL");
+        sFailCauseMap.put(INVALID_DNS_ADDR, "INVALID_DNS_ADDR");
+        sFailCauseMap.put(INVALID_PCSCF_OR_DNS_ADDRESS, "INVALID_PCSCF_OR_DNS_ADDRESS");
+        sFailCauseMap.put(CALL_PREEMPT_BY_EMERGENCY_APN, "CALL_PREEMPT_BY_EMERGENCY_APN");
+        sFailCauseMap.put(UE_INITIATED_DETACH_OR_DISCONNECT, "UE_INITIATED_DETACH_OR_DISCONNECT");
+        sFailCauseMap.put(MIP_FA_REASON_UNSPECIFIED, "MIP_FA_REASON_UNSPECIFIED");
+        sFailCauseMap.put(MIP_FA_ADMIN_PROHIBITED, "MIP_FA_ADMIN_PROHIBITED");
+        sFailCauseMap.put(MIP_FA_INSUFFICIENT_RESOURCES, "MIP_FA_INSUFFICIENT_RESOURCES");
+        sFailCauseMap.put(MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE,
+                "MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE");
+        sFailCauseMap.put(MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE,
+                "MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE");
+        sFailCauseMap.put(MIP_FA_REQUESTED_LIFETIME_TOO_LONG, "MIP_FA_REQUESTED_LIFETIME_TOO_LONG");
+        sFailCauseMap.put(MIP_FA_MALFORMED_REQUEST, "MIP_FA_MALFORMED_REQUEST");
+        sFailCauseMap.put(MIP_FA_MALFORMED_REPLY, "MIP_FA_MALFORMED_REPLY");
+        sFailCauseMap.put(MIP_FA_ENCAPSULATION_UNAVAILABLE, "MIP_FA_ENCAPSULATION_UNAVAILABLE");
+        sFailCauseMap.put(MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE,
+                "MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE");
+        sFailCauseMap.put(MIP_FA_REVERSE_TUNNEL_UNAVAILABLE, "MIP_FA_REVERSE_TUNNEL_UNAVAILABLE");
+        sFailCauseMap.put(MIP_FA_REVERSE_TUNNEL_IS_MANDATORY, "MIP_FA_REVERSE_TUNNEL_IS_MANDATORY");
+        sFailCauseMap.put(MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED,
+                "MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED");
+        sFailCauseMap.put(MIP_FA_MISSING_NAI, "MIP_FA_MISSING_NAI");
+        sFailCauseMap.put(MIP_FA_MISSING_HOME_AGENT, "MIP_FA_MISSING_HOME_AGENT");
+        sFailCauseMap.put(MIP_FA_MISSING_HOME_ADDRESS, "MIP_FA_MISSING_HOME_ADDRESS");
+        sFailCauseMap.put(MIP_FA_UNKNOWN_CHALLENGE, "MIP_FA_UNKNOWN_CHALLENGE");
+        sFailCauseMap.put(MIP_FA_MISSING_CHALLENGE, "MIP_FA_MISSING_CHALLENGE");
+        sFailCauseMap.put(MIP_FA_STALE_CHALLENGE, "MIP_FA_STALE_CHALLENGE");
+        sFailCauseMap.put(MIP_HA_REASON_UNSPECIFIED, "MIP_HA_REASON_UNSPECIFIED");
+        sFailCauseMap.put(MIP_HA_ADMIN_PROHIBITED, "MIP_HA_ADMIN_PROHIBITED");
+        sFailCauseMap.put(MIP_HA_INSUFFICIENT_RESOURCES, "MIP_HA_INSUFFICIENT_RESOURCES");
+        sFailCauseMap.put(MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE,
+                "MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE");
+        sFailCauseMap.put(MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE,
+                "MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE");
+        sFailCauseMap.put(MIP_HA_REGISTRATION_ID_MISMATCH, "MIP_HA_REGISTRATION_ID_MISMATCH");
+        sFailCauseMap.put(MIP_HA_MALFORMED_REQUEST, "MIP_HA_MALFORMED_REQUEST");
+        sFailCauseMap.put(MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS, "MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS");
+        sFailCauseMap.put(MIP_HA_REVERSE_TUNNEL_UNAVAILABLE, "MIP_HA_REVERSE_TUNNEL_UNAVAILABLE");
+        sFailCauseMap.put(MIP_HA_REVERSE_TUNNEL_IS_MANDATORY, "MIP_HA_REVERSE_TUNNEL_IS_MANDATORY");
+        sFailCauseMap.put(MIP_HA_ENCAPSULATION_UNAVAILABLE, "MIP_HA_ENCAPSULATION_UNAVAILABLE");
+        sFailCauseMap.put(CLOSE_IN_PROGRESS, "CLOSE_IN_PROGRESS");
+        sFailCauseMap.put(NETWORK_INITIATED_TERMINATION, "NETWORK_INITIATED_TERMINATION");
+        sFailCauseMap.put(MODEM_APP_PREEMPTED, "MODEM_APP_PREEMPTED");
+        sFailCauseMap.put(PDN_IPV4_CALL_DISALLOWED, "PDN_IPV4_CALL_DISALLOWED");
+        sFailCauseMap.put(PDN_IPV4_CALL_THROTTLED, "PDN_IPV4_CALL_THROTTLED");
+        sFailCauseMap.put(PDN_IPV6_CALL_DISALLOWED, "PDN_IPV6_CALL_DISALLOWED");
+        sFailCauseMap.put(PDN_IPV6_CALL_THROTTLED, "PDN_IPV6_CALL_THROTTLED");
+        sFailCauseMap.put(MODEM_RESTART, "MODEM_RESTART");
+        sFailCauseMap.put(PDP_PPP_NOT_SUPPORTED, "PDP_PPP_NOT_SUPPORTED");
+        sFailCauseMap.put(UNPREFERRED_RAT, "UNPREFERRED_RAT");
+        sFailCauseMap.put(PHYSICAL_LINK_CLOSE_IN_PROGRESS, "PHYSICAL_LINK_CLOSE_IN_PROGRESS");
+        sFailCauseMap.put(APN_PENDING_HANDOVER, "APN_PENDING_HANDOVER");
+        sFailCauseMap.put(PROFILE_BEARER_INCOMPATIBLE, "PROFILE_BEARER_INCOMPATIBLE");
+        sFailCauseMap.put(SIM_CARD_CHANGED, "SIM_CARD_CHANGED");
+        sFailCauseMap.put(LOW_POWER_MODE_OR_POWERING_DOWN, "LOW_POWER_MODE_OR_POWERING_DOWN");
+        sFailCauseMap.put(APN_DISABLED, "APN_DISABLED");
+        sFailCauseMap.put(MAX_PPP_INACTIVITY_TIMER_EXPIRED, "MAX_PPP_INACTIVITY_TIMER_EXPIRED");
+        sFailCauseMap.put(IPV6_ADDRESS_TRANSFER_FAILED, "IPV6_ADDRESS_TRANSFER_FAILED");
+        sFailCauseMap.put(TRAT_SWAP_FAILED, "TRAT_SWAP_FAILED");
+        sFailCauseMap.put(EHRPD_TO_HRPD_FALLBACK, "EHRPD_TO_HRPD_FALLBACK");
+        sFailCauseMap.put(MIP_CONFIG_FAILURE, "MIP_CONFIG_FAILURE");
+        sFailCauseMap.put(PDN_INACTIVITY_TIMER_EXPIRED, "PDN_INACTIVITY_TIMER_EXPIRED");
+        sFailCauseMap.put(MAX_IPV4_CONNECTIONS, "MAX_IPV4_CONNECTIONS");
+        sFailCauseMap.put(MAX_IPV6_CONNECTIONS, "MAX_IPV6_CONNECTIONS");
+        sFailCauseMap.put(APN_MISMATCH, "APN_MISMATCH");
+        sFailCauseMap.put(IP_VERSION_MISMATCH, "IP_VERSION_MISMATCH");
+        sFailCauseMap.put(DUN_CALL_DISALLOWED, "DUN_CALL_DISALLOWED");
+        sFailCauseMap.put(INTERNAL_EPC_NONEPC_TRANSITION, "INTERNAL_EPC_NONEPC_TRANSITION");
+        sFailCauseMap.put(INTERFACE_IN_USE, "INTERFACE_IN_USE");
+        sFailCauseMap.put(APN_DISALLOWED_ON_ROAMING, "APN_DISALLOWED_ON_ROAMING");
+        sFailCauseMap.put(APN_PARAMETERS_CHANGED, "APN_PARAMETERS_CHANGED");
+        sFailCauseMap.put(NULL_APN_DISALLOWED, "NULL_APN_DISALLOWED");
+        sFailCauseMap.put(THERMAL_MITIGATION, "THERMAL_MITIGATION");
+        sFailCauseMap.put(DATA_SETTINGS_DISABLED, "DATA_SETTINGS_DISABLED");
+        sFailCauseMap.put(DATA_ROAMING_SETTINGS_DISABLED, "DATA_ROAMING_SETTINGS_DISABLED");
+        sFailCauseMap.put(DDS_SWITCHED, "DDS_SWITCHED");
+        sFailCauseMap.put(FORBIDDEN_APN_NAME, "FORBIDDEN_APN_NAME");
+        sFailCauseMap.put(DDS_SWITCH_IN_PROGRESS, "DDS_SWITCH_IN_PROGRESS");
+        sFailCauseMap.put(CALL_DISALLOWED_IN_ROAMING, "CALL_DISALLOWED_IN_ROAMING");
+        sFailCauseMap.put(NON_IP_NOT_SUPPORTED, "NON_IP_NOT_SUPPORTED");
+        sFailCauseMap.put(PDN_NON_IP_CALL_THROTTLED, "PDN_NON_IP_CALL_THROTTLED");
+        sFailCauseMap.put(PDN_NON_IP_CALL_DISALLOWED, "PDN_NON_IP_CALL_DISALLOWED");
+        sFailCauseMap.put(CDMA_LOCK, "CDMA_LOCK");
+        sFailCauseMap.put(CDMA_INTERCEPT, "CDMA_INTERCEPT");
+        sFailCauseMap.put(CDMA_REORDER, "CDMA_REORDER");
+        sFailCauseMap.put(CDMA_RELEASE_DUE_TO_SO_REJECTION, "CDMA_RELEASE_DUE_TO_SO_REJECTION");
+        sFailCauseMap.put(CDMA_INCOMING_CALL, "CDMA_INCOMING_CALL");
+        sFailCauseMap.put(CDMA_ALERT_STOP, "CDMA_ALERT_STOP");
+        sFailCauseMap.put(CHANNEL_ACQUISITION_FAILURE, "CHANNEL_ACQUISITION_FAILURE");
+        sFailCauseMap.put(MAX_ACCESS_PROBE, "MAX_ACCESS_PROBE");
+        sFailCauseMap.put(CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION,
+                "CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION");
+        sFailCauseMap.put(NO_RESPONSE_FROM_BASE_STATION, "NO_RESPONSE_FROM_BASE_STATION");
+        sFailCauseMap.put(REJECTED_BY_BASE_STATION, "REJECTED_BY_BASE_STATION");
+        sFailCauseMap.put(CONCURRENT_SERVICES_INCOMPATIBLE, "CONCURRENT_SERVICES_INCOMPATIBLE");
+        sFailCauseMap.put(NO_CDMA_SERVICE, "NO_CDMA_SERVICE");
+        sFailCauseMap.put(RUIM_NOT_PRESENT, "RUIM_NOT_PRESENT");
+        sFailCauseMap.put(CDMA_RETRY_ORDER, "CDMA_RETRY_ORDER");
+        sFailCauseMap.put(ACCESS_BLOCK, "ACCESS_BLOCK");
+        sFailCauseMap.put(ACCESS_BLOCK_ALL, "ACCESS_BLOCK_ALL");
+        sFailCauseMap.put(IS707B_MAX_ACCESS_PROBES, "IS707B_MAX_ACCESS_PROBES");
+        sFailCauseMap.put(THERMAL_EMERGENCY, "THERMAL_EMERGENCY");
+        sFailCauseMap.put(CONCURRENT_SERVICES_NOT_ALLOWED, "CONCURRENT_SERVICES_NOT_ALLOWED");
+        sFailCauseMap.put(INCOMING_CALL_REJECTED, "INCOMING_CALL_REJECTED");
+        sFailCauseMap.put(NO_SERVICE_ON_GATEWAY, "NO_SERVICE_ON_GATEWAY");
+        sFailCauseMap.put(NO_GPRS_CONTEXT, "NO_GPRS_CONTEXT");
+        sFailCauseMap.put(ILLEGAL_MS, "ILLEGAL_MS");
+        sFailCauseMap.put(ILLEGAL_ME, "ILLEGAL_ME");
+        sFailCauseMap.put(GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED,
+                "GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED");
+        sFailCauseMap.put(GPRS_SERVICES_NOT_ALLOWED, "GPRS_SERVICES_NOT_ALLOWED");
+        sFailCauseMap.put(MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK,
+                "MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK");
+        sFailCauseMap.put(IMPLICITLY_DETACHED, "IMPLICITLY_DETACHED");
+        sFailCauseMap.put(PLMN_NOT_ALLOWED, "PLMN_NOT_ALLOWED");
+        sFailCauseMap.put(LOCATION_AREA_NOT_ALLOWED, "LOCATION_AREA_NOT_ALLOWED");
+        sFailCauseMap.put(GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN,
+                "GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN");
+        sFailCauseMap.put(PDP_DUPLICATE, "PDP_DUPLICATE");
+        sFailCauseMap.put(UE_RAT_CHANGE, "UE_RAT_CHANGE");
+        sFailCauseMap.put(CONGESTION, "CONGESTION");
+        sFailCauseMap.put(NO_PDP_CONTEXT_ACTIVATED, "NO_PDP_CONTEXT_ACTIVATED");
+        sFailCauseMap.put(ACCESS_CLASS_DSAC_REJECTION, "ACCESS_CLASS_DSAC_REJECTION");
+        sFailCauseMap.put(PDP_ACTIVATE_MAX_RETRY_FAILED, "PDP_ACTIVATE_MAX_RETRY_FAILED");
+        sFailCauseMap.put(RADIO_ACCESS_BEARER_FAILURE, "RADIO_ACCESS_BEARER_FAILURE");
+        sFailCauseMap.put(ESM_UNKNOWN_EPS_BEARER_CONTEXT, "ESM_UNKNOWN_EPS_BEARER_CONTEXT");
+        sFailCauseMap.put(DRB_RELEASED_BY_RRC, "DRB_RELEASED_BY_RRC");
+        sFailCauseMap.put(CONNECTION_RELEASED, "CONNECTION_RELEASED");
+        sFailCauseMap.put(EMM_DETACHED, "EMM_DETACHED");
+        sFailCauseMap.put(EMM_ATTACH_FAILED, "EMM_ATTACH_FAILED");
+        sFailCauseMap.put(EMM_ATTACH_STARTED, "EMM_ATTACH_STARTED");
+        sFailCauseMap.put(LTE_NAS_SERVICE_REQUEST_FAILED, "LTE_NAS_SERVICE_REQUEST_FAILED");
+        sFailCauseMap.put(DUPLICATE_BEARER_ID, "DUPLICATE_BEARER_ID");
+        sFailCauseMap.put(ESM_COLLISION_SCENARIOS, "ESM_COLLISION_SCENARIOS");
+        sFailCauseMap.put(ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK,
+                "ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK");
+        sFailCauseMap.put(ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER,
+                "ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER");
+        sFailCauseMap.put(ESM_BAD_OTA_MESSAGE, "ESM_BAD_OTA_MESSAGE");
+        sFailCauseMap.put(ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL,
+                "ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL");
+        sFailCauseMap.put(ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT,
+                "ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT");
+        sFailCauseMap.put(DS_EXPLICIT_DEACTIVATION, "DS_EXPLICIT_DEACTIVATION");
+        sFailCauseMap.put(ESM_LOCAL_CAUSE_NONE, "ESM_LOCAL_CAUSE_NONE");
+        sFailCauseMap.put(LTE_THROTTLING_NOT_REQUIRED, "LTE_THROTTLING_NOT_REQUIRED");
+        sFailCauseMap.put(ACCESS_CONTROL_LIST_CHECK_FAILURE,
+                "ACCESS_CONTROL_LIST_CHECK_FAILURE");
+        sFailCauseMap.put(SERVICE_NOT_ALLOWED_ON_PLMN, "SERVICE_NOT_ALLOWED_ON_PLMN");
+        sFailCauseMap.put(EMM_T3417_EXPIRED, "EMM_T3417_EXPIRED");
+        sFailCauseMap.put(EMM_T3417_EXT_EXPIRED, "EMM_T3417_EXT_EXPIRED");
+        sFailCauseMap.put(RRC_UPLINK_DATA_TRANSMISSION_FAILURE,
+                "RRC_UPLINK_DATA_TRANSMISSION_FAILURE");
+        sFailCauseMap.put(RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER,
+                "RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER");
+        sFailCauseMap.put(RRC_UPLINK_CONNECTION_RELEASE, "RRC_UPLINK_CONNECTION_RELEASE");
+        sFailCauseMap.put(RRC_UPLINK_RADIO_LINK_FAILURE, "RRC_UPLINK_RADIO_LINK_FAILURE");
+        sFailCauseMap.put(RRC_UPLINK_ERROR_REQUEST_FROM_NAS, "RRC_UPLINK_ERROR_REQUEST_FROM_NAS");
+        sFailCauseMap.put(RRC_CONNECTION_ACCESS_STRATUM_FAILURE,
+                "RRC_CONNECTION_ACCESS_STRATUM_FAILURE");
+        sFailCauseMap.put(RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS,
+                "RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS");
+        sFailCauseMap.put(RRC_CONNECTION_ACCESS_BARRED, "RRC_CONNECTION_ACCESS_BARRED");
+        sFailCauseMap.put(RRC_CONNECTION_CELL_RESELECTION, "RRC_CONNECTION_CELL_RESELECTION");
+        sFailCauseMap.put(RRC_CONNECTION_CONFIG_FAILURE, "RRC_CONNECTION_CONFIG_FAILURE");
+        sFailCauseMap.put(RRC_CONNECTION_TIMER_EXPIRED, "RRC_CONNECTION_TIMER_EXPIRED");
+        sFailCauseMap.put(RRC_CONNECTION_LINK_FAILURE, "RRC_CONNECTION_LINK_FAILURE");
+        sFailCauseMap.put(RRC_CONNECTION_CELL_NOT_CAMPED, "RRC_CONNECTION_CELL_NOT_CAMPED");
+        sFailCauseMap.put(RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE,
+                "RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE");
+        sFailCauseMap.put(RRC_CONNECTION_REJECT_BY_NETWORK, "RRC_CONNECTION_REJECT_BY_NETWORK");
+        sFailCauseMap.put(RRC_CONNECTION_NORMAL_RELEASE, "RRC_CONNECTION_NORMAL_RELEASE");
+        sFailCauseMap.put(RRC_CONNECTION_RADIO_LINK_FAILURE, "RRC_CONNECTION_RADIO_LINK_FAILURE");
+        sFailCauseMap.put(RRC_CONNECTION_REESTABLISHMENT_FAILURE,
+                "RRC_CONNECTION_REESTABLISHMENT_FAILURE");
+        sFailCauseMap.put(RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER,
+                "RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER");
+        sFailCauseMap.put(RRC_CONNECTION_ABORT_REQUEST, "RRC_CONNECTION_ABORT_REQUEST");
+        sFailCauseMap.put(RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR,
+                "RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR");
+        sFailCauseMap.put(NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH,
+                "NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH");
+        sFailCauseMap.put(NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH,
+                "NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH");
+        sFailCauseMap.put(ESM_PROCEDURE_TIME_OUT, "ESM_PROCEDURE_TIME_OUT");
+        sFailCauseMap.put(INVALID_CONNECTION_ID, "INVALID_CONNECTION_ID");
+        sFailCauseMap.put(MAXIMIUM_NSAPIS_EXCEEDED, "MAXIMIUM_NSAPIS_EXCEEDED");
+        sFailCauseMap.put(INVALID_PRIMARY_NSAPI, "INVALID_PRIMARY_NSAPI");
+        sFailCauseMap.put(CANNOT_ENCODE_OTA_MESSAGE, "CANNOT_ENCODE_OTA_MESSAGE");
+        sFailCauseMap.put(RADIO_ACCESS_BEARER_SETUP_FAILURE, "RADIO_ACCESS_BEARER_SETUP_FAILURE");
+        sFailCauseMap.put(PDP_ESTABLISH_TIMEOUT_EXPIRED, "PDP_ESTABLISH_TIMEOUT_EXPIRED");
+        sFailCauseMap.put(PDP_MODIFY_TIMEOUT_EXPIRED, "PDP_MODIFY_TIMEOUT_EXPIRED");
+        sFailCauseMap.put(PDP_INACTIVE_TIMEOUT_EXPIRED, "PDP_INACTIVE_TIMEOUT_EXPIRED");
+        sFailCauseMap.put(PDP_LOWERLAYER_ERROR, "PDP_LOWERLAYER_ERROR");
+        sFailCauseMap.put(PDP_MODIFY_COLLISION, "PDP_MODIFY_COLLISION");
+        sFailCauseMap.put(MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED,
+                "MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED");
+        sFailCauseMap.put(NAS_REQUEST_REJECTED_BY_NETWORK, "NAS_REQUEST_REJECTED_BY_NETWORK");
+        sFailCauseMap.put(RRC_CONNECTION_INVALID_REQUEST, "RRC_CONNECTION_INVALID_REQUEST");
+        sFailCauseMap.put(RRC_CONNECTION_TRACKING_AREA_ID_CHANGED,
+                "RRC_CONNECTION_TRACKING_AREA_ID_CHANGED");
+        sFailCauseMap.put(RRC_CONNECTION_RF_UNAVAILABLE, "RRC_CONNECTION_RF_UNAVAILABLE");
+        sFailCauseMap.put(RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE,
+                "RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE");
+        sFailCauseMap.put(RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE,
+                "RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE");
+        sFailCauseMap.put(RRC_CONNECTION_ABORTED_AFTER_HANDOVER,
+                "RRC_CONNECTION_ABORTED_AFTER_HANDOVER");
+        sFailCauseMap.put(RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE,
+                "RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE");
+        sFailCauseMap.put(RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE,
+                "RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE");
+        sFailCauseMap.put(IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER,
+                "IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER");
+        sFailCauseMap.put(IMEI_NOT_ACCEPTED, "IMEI_NOT_ACCEPTED");
+        sFailCauseMap.put(EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED,
+                "EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED");
+        sFailCauseMap.put(EPS_SERVICES_NOT_ALLOWED_IN_PLMN, "EPS_SERVICES_NOT_ALLOWED_IN_PLMN");
+        sFailCauseMap.put(MSC_TEMPORARILY_NOT_REACHABLE, "MSC_TEMPORARILY_NOT_REACHABLE");
+        sFailCauseMap.put(CS_DOMAIN_NOT_AVAILABLE, "CS_DOMAIN_NOT_AVAILABLE");
+        sFailCauseMap.put(ESM_FAILURE, "ESM_FAILURE");
+        sFailCauseMap.put(MAC_FAILURE, "MAC_FAILURE");
+        sFailCauseMap.put(SYNCHRONIZATION_FAILURE, "SYNCHRONIZATION_FAILURE");
+        sFailCauseMap.put(UE_SECURITY_CAPABILITIES_MISMATCH, "UE_SECURITY_CAPABILITIES_MISMATCH");
+        sFailCauseMap.put(SECURITY_MODE_REJECTED, "SECURITY_MODE_REJECTED");
+        sFailCauseMap.put(UNACCEPTABLE_NON_EPS_AUTHENTICATION,
+                "UNACCEPTABLE_NON_EPS_AUTHENTICATION");
+        sFailCauseMap.put(CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED,
+                "CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED");
+        sFailCauseMap.put(NO_EPS_BEARER_CONTEXT_ACTIVATED, "NO_EPS_BEARER_CONTEXT_ACTIVATED");
+        sFailCauseMap.put(INVALID_EMM_STATE, "INVALID_EMM_STATE");
+        sFailCauseMap.put(NAS_LAYER_FAILURE, "NAS_LAYER_FAILURE");
+        sFailCauseMap.put(MULTIPLE_PDP_CALL_NOT_ALLOWED, "MULTIPLE_PDP_CALL_NOT_ALLOWED");
+        sFailCauseMap.put(EMBMS_NOT_ENABLED, "EMBMS_NOT_ENABLED");
+        sFailCauseMap.put(IRAT_HANDOVER_FAILED, "IRAT_HANDOVER_FAILED");
+        sFailCauseMap.put(EMBMS_REGULAR_DEACTIVATION, "EMBMS_REGULAR_DEACTIVATION");
+        sFailCauseMap.put(TEST_LOOPBACK_REGULAR_DEACTIVATION, "TEST_LOOPBACK_REGULAR_DEACTIVATION");
+        sFailCauseMap.put(LOWER_LAYER_REGISTRATION_FAILURE, "LOWER_LAYER_REGISTRATION_FAILURE");
+        sFailCauseMap.put(DATA_PLAN_EXPIRED, "DATA_PLAN_EXPIRED");
+        sFailCauseMap.put(UMTS_HANDOVER_TO_IWLAN, "UMTS_HANDOVER_TO_IWLAN");
+        sFailCauseMap.put(EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY,
+                "EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY");
+        sFailCauseMap.put(EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE,
+                "EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE");
+        sFailCauseMap.put(EVDO_HDR_CHANGED, "EVDO_HDR_CHANGED");
+        sFailCauseMap.put(EVDO_HDR_EXITED, "EVDO_HDR_EXITED");
+        sFailCauseMap.put(EVDO_HDR_NO_SESSION, "EVDO_HDR_NO_SESSION");
+        sFailCauseMap.put(EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL,
+                "EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL");
+        sFailCauseMap.put(EVDO_HDR_CONNECTION_SETUP_TIMEOUT, "EVDO_HDR_CONNECTION_SETUP_TIMEOUT");
+        sFailCauseMap.put(FAILED_TO_ACQUIRE_COLOCATED_HDR, "FAILED_TO_ACQUIRE_COLOCATED_HDR");
+        sFailCauseMap.put(OTASP_COMMIT_IN_PROGRESS, "OTASP_COMMIT_IN_PROGRESS");
+        sFailCauseMap.put(NO_HYBRID_HDR_SERVICE, "NO_HYBRID_HDR_SERVICE");
+        sFailCauseMap.put(HDR_NO_LOCK_GRANTED, "HDR_NO_LOCK_GRANTED");
+        sFailCauseMap.put(DBM_OR_SMS_IN_PROGRESS, "DBM_OR_SMS_IN_PROGRESS");
+        sFailCauseMap.put(HDR_FADE, "HDR_FADE");
+        sFailCauseMap.put(HDR_ACCESS_FAILURE, "HDR_ACCESS_FAILURE");
+        sFailCauseMap.put(UNSUPPORTED_1X_PREV, "UNSUPPORTED_1X_PREV");
+        sFailCauseMap.put(LOCAL_END, "LOCAL_END");
+        sFailCauseMap.put(NO_SERVICE, "NO_SERVICE");
+        sFailCauseMap.put(FADE, "FADE");
+        sFailCauseMap.put(NORMAL_RELEASE, "NORMAL_RELEASE");
+        sFailCauseMap.put(ACCESS_ATTEMPT_ALREADY_IN_PROGRESS, "ACCESS_ATTEMPT_ALREADY_IN_PROGRESS");
+        sFailCauseMap.put(REDIRECTION_OR_HANDOFF_IN_PROGRESS, "REDIRECTION_OR_HANDOFF_IN_PROGRESS");
+        sFailCauseMap.put(EMERGENCY_MODE, "EMERGENCY_MODE");
+        sFailCauseMap.put(PHONE_IN_USE, "PHONE_IN_USE");
+        sFailCauseMap.put(INVALID_MODE, "INVALID_MODE");
+        sFailCauseMap.put(INVALID_SIM_STATE, "INVALID_SIM_STATE");
+        sFailCauseMap.put(NO_COLLOCATED_HDR, "NO_COLLOCATED_HDR");
+        sFailCauseMap.put(UE_IS_ENTERING_POWERSAVE_MODE, "UE_IS_ENTERING_POWERSAVE_MODE");
+        sFailCauseMap.put(DUAL_SWITCH, "DUAL_SWITCH");
+        sFailCauseMap.put(PPP_TIMEOUT, "PPP_TIMEOUT");
+        sFailCauseMap.put(PPP_AUTH_FAILURE, "PPP_AUTH_FAILURE");
+        sFailCauseMap.put(PPP_OPTION_MISMATCH, "PPP_OPTION_MISMATCH");
+        sFailCauseMap.put(PPP_PAP_FAILURE, "PPP_PAP_FAILURE");
+        sFailCauseMap.put(PPP_CHAP_FAILURE, "PPP_CHAP_FAILURE");
+        sFailCauseMap.put(PPP_CLOSE_IN_PROGRESS, "PPP_CLOSE_IN_PROGRESS");
+        sFailCauseMap.put(LIMITED_TO_IPV4, "LIMITED_TO_IPV4");
+        sFailCauseMap.put(LIMITED_TO_IPV6, "LIMITED_TO_IPV6");
+        sFailCauseMap.put(VSNCP_TIMEOUT, "VSNCP_TIMEOUT");
+        sFailCauseMap.put(VSNCP_GEN_ERROR, "VSNCP_GEN_ERROR");
+        sFailCauseMap.put(VSNCP_APN_UNATHORIZED, "VSNCP_APN_UNATHORIZED");
+        sFailCauseMap.put(VSNCP_APN_UNAUTHORIZED, "VSNCP_APN_UNAUTHORIZED");
+        sFailCauseMap.put(VSNCP_PDN_LIMIT_EXCEEDED, "VSNCP_PDN_LIMIT_EXCEEDED");
+        sFailCauseMap.put(VSNCP_NO_PDN_GATEWAY_ADDRESS, "VSNCP_NO_PDN_GATEWAY_ADDRESS");
+        sFailCauseMap.put(VSNCP_PDN_GATEWAY_UNREACHABLE, "VSNCP_PDN_GATEWAY_UNREACHABLE");
+        sFailCauseMap.put(VSNCP_PDN_GATEWAY_REJECT, "VSNCP_PDN_GATEWAY_REJECT");
+        sFailCauseMap.put(VSNCP_INSUFFICIENT_PARAMETERS, "VSNCP_INSUFFICIENT_PARAMETERS");
+        sFailCauseMap.put(VSNCP_RESOURCE_UNAVAILABLE, "VSNCP_RESOURCE_UNAVAILABLE");
+        sFailCauseMap.put(VSNCP_ADMINISTRATIVELY_PROHIBITED, "VSNCP_ADMINISTRATIVELY_PROHIBITED");
+        sFailCauseMap.put(VSNCP_PDN_ID_IN_USE, "VSNCP_PDN_ID_IN_USE");
+        sFailCauseMap.put(VSNCP_SUBSCRIBER_LIMITATION, "VSNCP_SUBSCRIBER_LIMITATION");
+        sFailCauseMap.put(VSNCP_PDN_EXISTS_FOR_THIS_APN, "VSNCP_PDN_EXISTS_FOR_THIS_APN");
+        sFailCauseMap.put(VSNCP_RECONNECT_NOT_ALLOWED, "VSNCP_RECONNECT_NOT_ALLOWED");
+        sFailCauseMap.put(IPV6_PREFIX_UNAVAILABLE, "IPV6_PREFIX_UNAVAILABLE");
+        sFailCauseMap.put(HANDOFF_PREFERENCE_CHANGED, "HANDOFF_PREFERENCE_CHANGED");
+        sFailCauseMap.put(SLICE_REJECTED, "SLICE_REJECTED");
+        sFailCauseMap.put(MATCH_ALL_RULE_NOT_ALLOWED, "MATCH_ALL_RULE_NOT_ALLOWED");
+        sFailCauseMap.put(ALL_MATCHING_RULES_FAILED, "ALL_MATCHING_RULES_FAILED");
+        sFailCauseMap.put(IWLAN_PDN_CONNECTION_REJECTION, "IWLAN_PDN_CONNECTION_REJECTION");
+        sFailCauseMap.put(IWLAN_MAX_CONNECTION_REACHED, "IWLAN_MAX_CONNECTION_REACHED");
+        sFailCauseMap.put(IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION,
+                "IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION");
+        sFailCauseMap.put(IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION,
+                "IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION");
+        sFailCauseMap.put(IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS,
+                "IWLAN_SEMANTIC_ERRORS_IN_PACKET_FILTERS");
+        sFailCauseMap.put(IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS,
+                "IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS");
+        sFailCauseMap.put(IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED,
+                "IWLAN_NON_3GPP_ACCESS_TO_EPC_NOT_ALLOWED");
+        sFailCauseMap.put(IWLAN_USER_UNKNOWN, "IWLAN_USER_UNKNOWN");
+        sFailCauseMap.put(IWLAN_NO_APN_SUBSCRIPTION, "IWLAN_NO_APN_SUBSCRIPTION");
+        sFailCauseMap.put(IWLAN_AUTHORIZATION_REJECTED, "IWLAN_AUTHORIZATION_REJECTED");
+        sFailCauseMap.put(IWLAN_ILLEGAL_ME, "IWLAN_ILLEGAL_ME");
+        sFailCauseMap.put(IWLAN_NETWORK_FAILURE, "IWLAN_NETWORK_FAILURE");
+        sFailCauseMap.put(IWLAN_RAT_TYPE_NOT_ALLOWED, "IWLAN_RAT_TYPE_NOT_ALLOWED");
+        sFailCauseMap.put(IWLAN_IMEI_NOT_ACCEPTED, "IWLAN_IMEI_NOT_ACCEPTED");
+        sFailCauseMap.put(IWLAN_PLMN_NOT_ALLOWED, "IWLAN_PLMN_NOT_ALLOWED");
+        sFailCauseMap.put(IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED,
+                "IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED");
+        sFailCauseMap.put(IWLAN_IKEV2_CONFIG_FAILURE, "IWLAN_IKEV2_CONFIG_FAILURE");
+        sFailCauseMap.put(IWLAN_IKEV2_AUTH_FAILURE, "IWLAN_IKEV2_AUTH_FAILURE");
+        sFailCauseMap.put(IWLAN_IKEV2_MSG_TIMEOUT, "IWLAN_IKEV2_MSG_TIMEOUT");
+        sFailCauseMap.put(IWLAN_IKEV2_CERT_INVALID, "IWLAN_IKEV2_CERT_INVALID");
+        sFailCauseMap.put(IWLAN_DNS_RESOLUTION_NAME_FAILURE, "IWLAN_DNS_RESOLUTION_NAME_FAILURE");
+        sFailCauseMap.put(IWLAN_DNS_RESOLUTION_TIMEOUT, "IWLAN_DNS_RESOLUTION_TIMEOUT");
+        sFailCauseMap.put(IWLAN_TUNNEL_NOT_FOUND, "IWLAN_TUNNEL_NOT_FOUND");
+        sFailCauseMap.put(IWLAN_TUNNEL_TRANSFORM_FAILED, "IWLAN_TUNNEL_TRANSFORM_FAILED");
+        sFailCauseMap.put(IWLAN_IKE_INIT_TIMEOUT, "IWLAN_IKE_INIT_TIMEOUT");
+        sFailCauseMap.put(IWLAN_IKE_NETWORK_LOST_EXCEPTION, "IWLAN_IKE_NETWORK_LOST_EXCEPTION");
+        sFailCauseMap.put(IWLAN_IKE_PRIVATE_PROTOCOL_ERROR, "IWLAN_IKE_PRIVATE_PROTOCOL_ERROR");
+        sFailCauseMap.put(IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED,
+                "IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED");
+        sFailCauseMap.put(IWLAN_IKE_DPD_TIMEOUT, "IWLAN_IKE_DPD_TIMEOUT");
+        sFailCauseMap.put(IWLAN_IKE_MOBILITY_TIMEOUT, "IWLAN_IKE_MOBILITY_TIMEOUT");
+        sFailCauseMap.put(IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE,
+                "IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_4, "OEM_DCFAILCAUSE_4");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_5, "OEM_DCFAILCAUSE_5");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_6, "OEM_DCFAILCAUSE_6");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_7, "OEM_DCFAILCAUSE_7");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_8, "OEM_DCFAILCAUSE_8");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_9, "OEM_DCFAILCAUSE_9");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_10, "OEM_DCFAILCAUSE_10");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_11, "OEM_DCFAILCAUSE_11");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_12, "OEM_DCFAILCAUSE_12");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_13, "OEM_DCFAILCAUSE_13");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_14, "OEM_DCFAILCAUSE_14");
+        sFailCauseMap.put(OEM_DCFAILCAUSE_15, "OEM_DCFAILCAUSE_15");
+        sFailCauseMap.put(REGISTRATION_FAIL, "REGISTRATION_FAIL");
+        sFailCauseMap.put(GPRS_REGISTRATION_FAIL, "GPRS_REGISTRATION_FAIL");
+        sFailCauseMap.put(SIGNAL_LOST, "SIGNAL_LOST");
+        sFailCauseMap.put(PREF_RADIO_TECH_CHANGED, "PREF_RADIO_TECH_CHANGED");
+        sFailCauseMap.put(RADIO_POWER_OFF, "RADIO_POWER_OFF");
+        sFailCauseMap.put(TETHERED_CALL_ACTIVE, "TETHERED_CALL_ACTIVE");
+        sFailCauseMap.put(ERROR_UNSPECIFIED, "ERROR_UNSPECIFIED");
+        sFailCauseMap.put(UNKNOWN, "UNKNOWN");
+        sFailCauseMap.put(RADIO_NOT_AVAILABLE, "RADIO_NOT_AVAILABLE");
+        sFailCauseMap.put(UNACCEPTABLE_NETWORK_PARAMETER,
+                "UNACCEPTABLE_NETWORK_PARAMETER");
+        sFailCauseMap.put(LOST_CONNECTION, "LOST_CONNECTION");
+        sFailCauseMap.put(HANDOVER_FAILED, "HANDOVER_FAILED");
+        sFailCauseMap.put(DUPLICATE_CID, "DUPLICATE_CID");
+        sFailCauseMap.put(NO_DEFAULT_DATA, "NO_DEFAULT_DATA");
+        sFailCauseMap.put(SERVICE_TEMPORARILY_UNAVAILABLE, "SERVICE_TEMPORARILY_UNAVAILABLE");
+        sFailCauseMap.put(REQUEST_NOT_SUPPORTED, "REQUEST_NOT_SUPPORTED");
+        sFailCauseMap.put(NO_RETRY_FAILURE, "NO_RETRY_FAILURE");
+    }
+
+    private DataFailCause() {
+    }
+
+    /**
+     * Map of subId -> set of data call setup permanent failure for the carrier.
+     */
+    private static final HashMap<Integer, Set<Integer>> sPermanentFailureCache =
+            new HashMap<>();
+
+    /**
+     * Returns whether or not the fail cause is a failure that requires a modem restart
+     *
+     * @param context device context
+     * @param cause data disconnect cause
+     * @param subId subscription index
+     * @return true if the fail cause code needs platform to trigger a modem restart.
+     *
+     * @hide
+     */
+    public static boolean isRadioRestartFailure(@NonNull Context context,
+                                                @DataFailureCause int cause,
+                                                int subId) {
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager != null) {
+            PersistableBundle b = configManager.getConfigForSubId(subId);
+
+            if (b != null) {
+                if (cause == REGULAR_DEACTIVATION
+                        && b.getBoolean(CarrierConfigManager
+                        .KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL)) {
+                    // This is for backward compatibility support. We need to continue support this
+                    // old configuration until it gets removed in the future.
+                    return true;
+                }
+                // Check the current configurations.
+                int[] causeCodes = b.getIntArray(CarrierConfigManager
+                        .KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY);
+                if (causeCodes != null) {
+                    return Arrays.stream(causeCodes).anyMatch(i -> i == cause);
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /** @hide */
+    // TODO: Migrated to DataConfigManager
+    public static boolean isPermanentFailure(@NonNull Context context,
+                                             @DataFailureCause int failCause,
+                                             int subId) {
+        synchronized (sPermanentFailureCache) {
+
+            Set<Integer> permanentFailureSet = sPermanentFailureCache.get(subId);
+
+            // In case of cache miss, we need to look up the settings from carrier config.
+            if (permanentFailureSet == null) {
+                // Retrieve the permanent failure from carrier config
+                CarrierConfigManager configManager = (CarrierConfigManager)
+                        context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+                if (configManager != null) {
+                    PersistableBundle b = configManager.getConfigForSubId(subId);
+                    if (b != null) {
+                        String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager
+                                .KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS);
+                        if (permanentFailureStrings != null) {
+                            permanentFailureSet = new HashSet<>();
+                            for (Map.Entry<Integer, String> e : sFailCauseMap.entrySet()) {
+                                if (ArrayUtils.contains(permanentFailureStrings, e.getValue())) {
+                                    permanentFailureSet.add(e.getKey());
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // If we are not able to find the configuration from carrier config, use the default
+                // ones.
+                if (permanentFailureSet == null) {
+                    permanentFailureSet = new HashSet<Integer>();
+                    permanentFailureSet.add(OPERATOR_BARRED);
+                    permanentFailureSet.add(MISSING_UNKNOWN_APN);
+                    permanentFailureSet.add(UNKNOWN_PDP_ADDRESS_TYPE);
+                    permanentFailureSet.add(USER_AUTHENTICATION);
+                    permanentFailureSet.add(ACTIVATION_REJECT_GGSN);
+                    permanentFailureSet.add(SERVICE_OPTION_NOT_SUPPORTED);
+                    permanentFailureSet.add(SERVICE_OPTION_NOT_SUBSCRIBED);
+                    permanentFailureSet.add(NSAPI_IN_USE);
+                    permanentFailureSet.add(ONLY_IPV4_ALLOWED);
+                    permanentFailureSet.add(ONLY_IPV6_ALLOWED);
+                    permanentFailureSet.add(PROTOCOL_ERRORS);
+                    permanentFailureSet.add(RADIO_POWER_OFF);
+                    permanentFailureSet.add(TETHERED_CALL_ACTIVE);
+                    permanentFailureSet.add(RADIO_NOT_AVAILABLE);
+                    permanentFailureSet.add(UNACCEPTABLE_NETWORK_PARAMETER);
+                    permanentFailureSet.add(SIGNAL_LOST);
+                    permanentFailureSet.add(DUPLICATE_CID);
+                    permanentFailureSet.add(MATCH_ALL_RULE_NOT_ALLOWED);
+                    permanentFailureSet.add(ALL_MATCHING_RULES_FAILED);
+                }
+
+                permanentFailureSet.add(NO_RETRY_FAILURE);
+                sPermanentFailureCache.put(subId, permanentFailureSet);
+            }
+
+            return permanentFailureSet.contains(failCause);
+        }
+    }
+
+    /** @hide */
+    public static boolean isEventLoggable(@DataFailureCause int dataFailCause) {
+        return (dataFailCause == OPERATOR_BARRED) || (dataFailCause == INSUFFICIENT_RESOURCES)
+                || (dataFailCause == UNKNOWN_PDP_ADDRESS_TYPE)
+                || (dataFailCause == USER_AUTHENTICATION)
+                || (dataFailCause == ACTIVATION_REJECT_GGSN)
+                || (dataFailCause == ACTIVATION_REJECT_UNSPECIFIED)
+                || (dataFailCause == SERVICE_OPTION_NOT_SUBSCRIBED)
+                || (dataFailCause == SERVICE_OPTION_NOT_SUPPORTED)
+                || (dataFailCause == SERVICE_OPTION_OUT_OF_ORDER)
+                || (dataFailCause == NSAPI_IN_USE)
+                || (dataFailCause == ONLY_IPV4_ALLOWED)
+                || (dataFailCause == ONLY_IPV6_ALLOWED)
+                || (dataFailCause == PROTOCOL_ERRORS)
+                || (dataFailCause == SIGNAL_LOST)
+                || (dataFailCause == RADIO_POWER_OFF)
+                || (dataFailCause == TETHERED_CALL_ACTIVE)
+                || (dataFailCause == UNACCEPTABLE_NETWORK_PARAMETER);
+    }
+
+    /** @hide */
+    public static String toString(@DataFailureCause int dataFailCause) {
+        return sFailCauseMap.getOrDefault(dataFailCause, "UNKNOWN") + "(0x"
+                + Integer.toHexString(dataFailCause) + ")";
+    }
+
+    /** @hide */
+    public static int getFailCause(@DataFailureCause int failCause) {
+        if (sFailCauseMap.containsKey(failCause)) {
+            return failCause;
+        } else {
+            return UNKNOWN;
+        }
+    }
+
+    /** @hide */
+    public static boolean isFailCauseExisting(@DataFailureCause int failCause) {
+        return sFailCauseMap.containsKey(failCause);
+    }
+}
diff --git a/android-35/android/telephony/DataSpecificRegistrationInfo.java b/android-35/android/telephony/DataSpecificRegistrationInfo.java
new file mode 100644
index 0000000..5e5e028
--- /dev/null
+++ b/android-35/android/telephony/DataSpecificRegistrationInfo.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2018 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to data network registration.
+ * @hide
+ */
+@SystemApi
+public final class DataSpecificRegistrationInfo implements Parcelable {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = "LTE_ATTACH_TYPE_",
+        value = {
+            LTE_ATTACH_TYPE_UNKNOWN,
+            LTE_ATTACH_TYPE_EPS_ONLY,
+            LTE_ATTACH_TYPE_COMBINED,
+        })
+    public @interface LteAttachResultType {}
+
+    /**
+     * Default value.
+     * Attach type is unknown.
+     */
+    public static final int LTE_ATTACH_TYPE_UNKNOWN = 0;
+
+    /**
+     * LTE is attached with EPS only.
+     *
+     * Reference: 3GPP TS 24.301 9.9.3 EMM information elements.
+     */
+    public static final int LTE_ATTACH_TYPE_EPS_ONLY = 1;
+
+    /**
+     * LTE combined EPS and IMSI attach.
+     *
+     * Reference: 3GPP TS 24.301 9.9.3 EMM information elements.
+     */
+    public static final int LTE_ATTACH_TYPE_COMBINED = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = {"LTE_ATTACH_EXTRA_INFO_"},
+            value = {
+                    LTE_ATTACH_EXTRA_INFO_NONE,
+                    LTE_ATTACH_EXTRA_INFO_CSFB_NOT_PREFERRED,
+                    LTE_ATTACH_EXTRA_INFO_SMS_ONLY
+            })
+    public @interface LteAttachExtraInfo {}
+
+    /**
+     * Default value.
+     */
+    public static final int LTE_ATTACH_EXTRA_INFO_NONE = 0;
+
+    /**
+     * CSFB is not preferred.
+     * Applicable for LTE only.
+     *
+     * Reference: 3GPP TS 24.301 9.9.3 EMM information elements.
+     */
+    public static final int LTE_ATTACH_EXTRA_INFO_CSFB_NOT_PREFERRED = 1 << 0;
+
+    /**
+     * Attached for SMS only.
+     * Applicable for LTE only.
+     *
+     * Reference: 3GPP TS 24.301 9.9.3 EMM information elements.
+     */
+    public static final int LTE_ATTACH_EXTRA_INFO_SMS_ONLY = 1 << 1;
+
+    /**
+     * @hide
+     * The maximum number of simultaneous Data Calls that
+     * must be established using setupDataCall().
+     */
+    public final int maxDataCalls;
+
+    /**
+     * @hide
+     * Indicates if the use of dual connectivity with NR is restricted.
+     * Reference: 3GPP TS 24.301 v15.03 section 9.3.3.12A.
+     */
+    public final boolean isDcNrRestricted;
+
+    /**
+     * Indicates if NR is supported by the selected PLMN.
+     * @hide
+     * {@code true} if the bit N is in the PLMN-InfoList-r15 is true and the selected PLMN is
+     * present in plmn-IdentityList at position N.
+     * Reference: 3GPP TS 36.331 v15.2.2 section 6.3.1 PLMN-InfoList-r15.
+     *            3GPP TS 36.331 v15.2.2 section 6.2.2 SystemInformationBlockType1 message.
+     */
+    public final boolean isNrAvailable;
+
+    /**
+     * @hide
+     * Indicates that if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the primary serving
+     * cell.
+     *
+     * True the primary serving cell is LTE cell and the plmn-InfoList-r15 is present in SIB2 and
+     * at least one bit in this list is true, otherwise this value should be false.
+     *
+     * Reference: 3GPP TS 36.331 v15.2.2 6.3.1 System information blocks.
+     */
+    public final boolean isEnDcAvailable;
+
+    /**
+     * Provides network support info for VoPS and Emergency bearer support
+     */
+    @Nullable
+    private final VopsSupportInfo mVopsSupportInfo;
+
+    /** The type of network attachment */
+    private final @LteAttachResultType int mLteAttachResultType;
+
+    /** LTE attach extra info */
+    private final @LteAttachExtraInfo int mLteAttachExtraInfo;
+
+    private DataSpecificRegistrationInfo(Builder builder) {
+        this.maxDataCalls = builder.mMaxDataCalls;
+        this.isDcNrRestricted = builder.mIsDcNrRestricted;
+        this.isNrAvailable = builder.mIsNrAvailable;
+        this.isEnDcAvailable = builder.mIsEnDcAvailable;
+        this.mVopsSupportInfo = builder.mVopsSupportInfo;
+        this.mLteAttachResultType = builder.mLteAttachResultType;
+        this.mLteAttachExtraInfo = builder.mLteAttachExtraInfo;
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public DataSpecificRegistrationInfo(
+            int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable,
+            boolean isEnDcAvailable, @Nullable VopsSupportInfo vops) {
+        this.maxDataCalls = maxDataCalls;
+        this.isDcNrRestricted = isDcNrRestricted;
+        this.isNrAvailable = isNrAvailable;
+        this.isEnDcAvailable = isEnDcAvailable;
+        this.mVopsSupportInfo = vops;
+        this.mLteAttachResultType = LTE_ATTACH_TYPE_UNKNOWN;
+        this.mLteAttachExtraInfo = LTE_ATTACH_EXTRA_INFO_NONE;
+    }
+
+    /**
+     * Constructor from another data specific registration info
+     *
+     * @param dsri another data specific registration info
+     * @hide
+     */
+    DataSpecificRegistrationInfo(@NonNull DataSpecificRegistrationInfo dsri) {
+        maxDataCalls = dsri.maxDataCalls;
+        isDcNrRestricted = dsri.isDcNrRestricted;
+        isNrAvailable = dsri.isNrAvailable;
+        isEnDcAvailable = dsri.isEnDcAvailable;
+        mVopsSupportInfo = dsri.mVopsSupportInfo;
+        mLteAttachResultType = dsri.mLteAttachResultType;
+        mLteAttachExtraInfo = dsri.mLteAttachExtraInfo;
+    }
+
+    private DataSpecificRegistrationInfo(/* @NonNull */ Parcel source) {
+        maxDataCalls = source.readInt();
+        isDcNrRestricted = source.readBoolean();
+        isNrAvailable = source.readBoolean();
+        isEnDcAvailable = source.readBoolean();
+        mVopsSupportInfo = source.readParcelable(VopsSupportInfo.class.getClassLoader(), android.telephony.VopsSupportInfo.class);
+        mLteAttachResultType = source.readInt();
+        mLteAttachExtraInfo = source.readInt();
+    }
+
+    @Override
+    public void writeToParcel(/* @NonNull */ Parcel dest, int flags) {
+        dest.writeInt(maxDataCalls);
+        dest.writeBoolean(isDcNrRestricted);
+        dest.writeBoolean(isNrAvailable);
+        dest.writeBoolean(isEnDcAvailable);
+        dest.writeParcelable(mVopsSupportInfo, flags);
+        dest.writeInt(mLteAttachResultType);
+        dest.writeInt(mLteAttachExtraInfo);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return new StringBuilder().append(this.getClass().getName())
+                .append(" :{")
+                .append(" maxDataCalls = " + maxDataCalls)
+                .append(" isDcNrRestricted = " + isDcNrRestricted)
+                .append(" isNrAvailable = " + isNrAvailable)
+                .append(" isEnDcAvailable = " + isEnDcAvailable)
+                .append(" mLteAttachResultType = " + mLteAttachResultType)
+                .append(" mLteAttachExtraInfo = " + mLteAttachExtraInfo)
+                .append(" " + mVopsSupportInfo)
+                .append(" }")
+                .toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable,
+                isEnDcAvailable, mVopsSupportInfo,
+                mLteAttachResultType, mLteAttachExtraInfo);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+
+        if (!(o instanceof DataSpecificRegistrationInfo)) return false;
+
+        DataSpecificRegistrationInfo other = (DataSpecificRegistrationInfo) o;
+        return this.maxDataCalls == other.maxDataCalls
+                && this.isDcNrRestricted == other.isDcNrRestricted
+                && this.isNrAvailable == other.isNrAvailable
+                && this.isEnDcAvailable == other.isEnDcAvailable
+                && Objects.equals(mVopsSupportInfo, other.mVopsSupportInfo)
+                && this.mLteAttachResultType == other.mLteAttachResultType
+                && this.mLteAttachExtraInfo == other.mLteAttachExtraInfo;
+    }
+
+    public static final @NonNull Parcelable.Creator<DataSpecificRegistrationInfo> CREATOR =
+            new Parcelable.Creator<DataSpecificRegistrationInfo>() {
+                @Override
+                public DataSpecificRegistrationInfo createFromParcel(Parcel source) {
+                    return new DataSpecificRegistrationInfo(source);
+                }
+
+                @Override
+                public DataSpecificRegistrationInfo[] newArray(int size) {
+                    return new DataSpecificRegistrationInfo[size];
+                }
+            };
+
+    /**
+     * @return The LTE VOPS (Voice over Packet Switched) support information
+     *
+     * @deprecated use {@link #getVopsSupportInfo()}
+     */
+    @Deprecated
+    @NonNull
+    public LteVopsSupportInfo getLteVopsSupportInfo() {
+        return mVopsSupportInfo instanceof LteVopsSupportInfo
+                ? (LteVopsSupportInfo) mVopsSupportInfo
+                : new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
+                LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
+    }
+
+    /**
+     * @return The VOPS (Voice over Packet Switched) support information.
+     *
+     * The instance of {@link LteVopsSupportInfo}, or {@link NrVopsSupportInfo},
+     * null if there is there is no VOPS support information available.
+     */
+    @Nullable
+    public VopsSupportInfo getVopsSupportInfo() {
+        return mVopsSupportInfo;
+    }
+
+    /**
+     * Provides the LTE attach type.
+     */
+    public @LteAttachResultType int getLteAttachResultType() {
+        return mLteAttachResultType;
+    }
+
+    /**
+     * Provides the extra information of LTE attachment.
+     *
+     * @return the bitwise OR of {@link LteAttachExtraInfo}.
+     */
+    public @LteAttachExtraInfo int getLteAttachExtraInfo() {
+        return mLteAttachExtraInfo;
+    }
+
+    /**
+     * Builds {@link DataSpecificRegistrationInfo} instances, which may include optional parameters.
+     * @hide
+     */
+    public static final class Builder {
+        private final int mMaxDataCalls;
+
+        private boolean mIsDcNrRestricted;
+        private boolean mIsNrAvailable;
+        private boolean mIsEnDcAvailable;
+        private @Nullable VopsSupportInfo mVopsSupportInfo;
+        private @LteAttachResultType int mLteAttachResultType = LTE_ATTACH_TYPE_UNKNOWN;
+        private @LteAttachExtraInfo int mLteAttachExtraInfo = LTE_ATTACH_EXTRA_INFO_NONE;
+
+        public Builder(int maxDataCalls) {
+            mMaxDataCalls = maxDataCalls;
+        }
+
+        /**
+         * Ses whether the use of dual connectivity with NR is restricted.
+         * @param isDcNrRestricted {@code true} if the use of dual connectivity with NR is
+         *        restricted.
+         */
+        public @NonNull Builder setDcNrRestricted(boolean isDcNrRestricted) {
+            mIsDcNrRestricted = isDcNrRestricted;
+            return this;
+        }
+
+        /**
+         * Sets whether NR is supported by the selected PLMN.
+         * @param isNrAvailable {@code true} if NR is supported.
+         */
+        public @NonNull Builder setNrAvailable(boolean isNrAvailable) {
+            mIsNrAvailable = isNrAvailable;
+            return this;
+        }
+
+        /**
+         * Sets whether E-UTRA-NR Dual Connectivity (EN-DC) is supported by the primary serving
+         * cell.
+         * @param isEnDcAvailable {@code true} if EN_DC is supported.
+         */
+        public @NonNull Builder setEnDcAvailable(boolean isEnDcAvailable) {
+            mIsEnDcAvailable = isEnDcAvailable;
+            return this;
+        }
+
+        /**
+         * Sets the network support info for VoPS and Emergency bearer support.
+         * @param vops The network support info for VoPS and Emergency bearer support.
+         */
+        @Nullable
+        public @NonNull Builder setVopsSupportInfo(VopsSupportInfo vops) {
+            mVopsSupportInfo = vops;
+            return this;
+        }
+
+        /**
+         * Sets the LTE attach type.
+         * @param lteAttachResultType the Lte attach type
+         */
+        public @NonNull Builder setLteAttachResultType(
+                @LteAttachResultType int lteAttachResultType) {
+            mLteAttachResultType = lteAttachResultType;
+            return this;
+        }
+
+        /**
+         * Sets the extra information of LTE attachment.
+         * @param lteAttachExtraInfo the extra information of LTE attachment.
+         */
+        public @NonNull Builder setLteAttachExtraInfo(
+                @LteAttachExtraInfo int lteAttachExtraInfo) {
+            mLteAttachExtraInfo = lteAttachExtraInfo;
+            return this;
+        }
+
+        /**
+         * @return a built {@link DataSpecificRegistrationInfo} instance.
+         */
+        public @NonNull DataSpecificRegistrationInfo build() {
+            return new DataSpecificRegistrationInfo(this);
+        }
+    }
+}
diff --git a/android-35/android/telephony/DataThrottlingRequest.java b/android-35/android/telephony/DataThrottlingRequest.java
new file mode 100644
index 0000000..2827e8d
--- /dev/null
+++ b/android-35/android/telephony/DataThrottlingRequest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresFeature;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+/**
+ * Class stores information related to the type of data throttling request. Must be populated as
+ * field in {@link ThermalMitigationRequest} for sending of thermal mitigation request at {@link
+ * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)}.
+ * @hide
+ */
+@SystemApi
+public final class DataThrottlingRequest implements Parcelable {
+    /**
+     * Clear all existing data throttling, enable data, and attempt to enable radio for thermal
+     * mitigation all within the requested completion window. Note that attempting to enable radio
+     * will not guarantee that radio will actually be enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int DATA_THROTTLING_ACTION_NO_DATA_THROTTLING = 0;
+
+    /**
+     * Enact secondary carrier data throttling within specified completion window. This also
+     * attempts to enables radio if currently disabled for thermal mitigation, enables data, and
+     * removes any existing data throttling on primary carrier. Note that attempting to enable radio
+     * will not guarantee that radio will actually be enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING)
+    public static final int DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER = 1;
+
+    /**
+     * Enact primary carrier data throttling within specified completion window. This also attempts
+     * to enable radio if currently disabled for thermal mitigation and disables data on secondary
+     * carrier if currently enabled. Note that attempting to enable radio will not guarantee that
+     * radio will actually be enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING)
+    public static final int DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER = 2;
+
+    /**
+     * Immediately hold on to the current level of data throttling indicating that the current level
+     * of data throttling has alleviated the thermal concerns which caused the original data
+     * throttling request. A thermal module should remain actively monitoring the temperature levels
+     * and request an appropriate thermal mitigation action. {@link
+     * #THERMAL_MITIGATION_RESULT_INVALID_PARAMETERS} will be returned if completion window is not
+     * 0.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING)
+    public static final int DATA_THROTTLING_ACTION_HOLD = 3;
+
+    /**
+     * Type of data throttling action to carry out.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "DATA_THROTTLING_ACTION_" }, value = {
+        DATA_THROTTLING_ACTION_NO_DATA_THROTTLING,
+        DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER,
+        DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER,
+        DATA_THROTTLING_ACTION_HOLD})
+    public @interface DataThrottlingAction {}
+
+    /**
+     * Represents the data throttling action that will be requested. See {@link
+     * DATA_THROTTLING_ACTION_NO_DATA_THROTTLING}, {@link
+     * #DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}, {@link
+     * #DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER}, and {@link
+     * #DATA_THROTTLING_ACTION_HOLD} for more details.
+     **/
+    private @DataThrottlingAction int mDataThrottlingAction;
+    /**
+     * Represents the time over which modem should gradually execute the data thorttling request.
+     */
+    private long mCompletionDurationMillis;
+
+    private DataThrottlingRequest(@NonNull int dataThrottlingAction,
+            long completionDurationMillis) {
+        mDataThrottlingAction = dataThrottlingAction;
+        mCompletionDurationMillis = completionDurationMillis;
+    }
+
+    private DataThrottlingRequest(Parcel in) {
+        mDataThrottlingAction = in.readInt();
+        mCompletionDurationMillis = in.readLong();
+    }
+
+    /**
+     * Implement the Parcelable interface
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mDataThrottlingAction);
+        dest.writeLong(mCompletionDurationMillis);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "[DataThrottlingRequest "
+            + ", DataThrottlingAction=" + mDataThrottlingAction
+            + ", completionDurationMillis=" + mCompletionDurationMillis
+            + "]";
+    }
+
+    /**
+    * @return the dataThrottlingAction.
+    */
+    public @DataThrottlingAction int getDataThrottlingAction() {
+        return mDataThrottlingAction;
+    }
+
+    /**
+     * @return the completionDurationMillis which represents the time over which modem should
+     * gradually execute the data thorttling request.
+     */
+    public long getCompletionDurationMillis() {
+        return mCompletionDurationMillis;
+    }
+
+    public static final @NonNull Parcelable.Creator<DataThrottlingRequest> CREATOR =
+            new Parcelable.Creator<DataThrottlingRequest>() {
+
+        @Override
+        public DataThrottlingRequest createFromParcel(Parcel in) {
+            return new DataThrottlingRequest(in);
+        }
+
+        @Override
+        public DataThrottlingRequest[] newArray(int size) {
+            return new DataThrottlingRequest[size];
+        }
+    };
+
+    /**
+     * Provides a convenient way to set the fields of a {@link DataThrottlingRequest} when creating
+     * a new instance.
+     *
+     * <p>The example below shows how you might create a new {@code DataThrottlingRequest}:
+     *
+     * <pre><code>
+     *
+     * DataThrottlingRequest dp = new DataThrottlingRequest.Builder()
+     *     .setDataThrottlingAction(
+     *          DataThrottlingRequest.DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER)
+     *     .setCompletionDurationMillis(10000L)
+     *     .build();
+     * </code></pre>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        private @DataThrottlingAction int mDataThrottlingAction;
+        private long mCompletionDurationMillis;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {}
+
+        /**
+         * Set the data throttling action.
+         *
+         * @param dataThrottlingAction data throttling action.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setDataThrottlingAction(
+                @DataThrottlingAction int dataThrottlingAction) {
+            mDataThrottlingAction = dataThrottlingAction;
+            return this;
+        }
+
+        /**
+         * Set the completion duration.
+         *
+         * @param completionDurationMillis completion duration in millis which represents the time
+         *      over which modem should gradually execute the data thorttling request. This can
+         *      never be a negative number and must be 0 for {@link #DATA_THROTTLING_ACTION_HOLD}.
+         *      Otherwise, an IllegalArgumentException will be thrown.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setCompletionDurationMillis(long completionDurationMillis) {
+            mCompletionDurationMillis = completionDurationMillis;
+            return this;
+        }
+
+        /**
+         * Build the DataThrottlingRequest.
+         *
+         * @return the DataThrottlingRequest object.
+         */
+        public @NonNull DataThrottlingRequest build() {
+            if (mCompletionDurationMillis < 0) {
+                throw new IllegalArgumentException("completionDurationMillis cannot be a negative "
+                        + "number");
+            }
+
+            if (mDataThrottlingAction == DataThrottlingRequest.DATA_THROTTLING_ACTION_HOLD
+                    && mCompletionDurationMillis != 0) {
+                throw new IllegalArgumentException("completionDurationMillis must be 0 for "
+                    + "DataThrottlingRequest.DATA_THROTTLING_ACTION_HOLD");
+            }
+
+            return new DataThrottlingRequest(mDataThrottlingAction, mCompletionDurationMillis);
+        }
+    }
+
+}
diff --git a/android-35/android/telephony/DisconnectCause.java b/android-35/android/telephony/DisconnectCause.java
new file mode 100644
index 0000000..a8c077d
--- /dev/null
+++ b/android-35/android/telephony/DisconnectCause.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * Describes the cause of a disconnected call. Those disconnect causes can be converted into a more
+ * generic {@link android.telecom.DisconnectCause} object.
+ *
+ * Used in {@link PhoneStateListener#onCallDisconnectCauseChanged}.
+ */
+public final class DisconnectCause {
+
+    /** The disconnect cause is not valid (Not received a disconnect cause) */
+    public static final int NOT_VALID                      = -1;
+    /** Has not yet disconnected */
+    public static final int NOT_DISCONNECTED               = 0;
+    /** An incoming call that was missed and never answered */
+    public static final int INCOMING_MISSED                = 1;
+    /** Normal; Remote hangup*/
+    public static final int NORMAL                         = 2;
+    /** Normal; Local hangup */
+    public static final int LOCAL                          = 3;
+    /** Outgoing call to busy line */
+    public static final int BUSY                           = 4;
+    /** Outgoing call to congested network */
+    public static final int CONGESTION                     = 5;
+    /** Not presently used */
+    public static final int MMI                            = 6;
+    /** Invalid dial string */
+    public static final int INVALID_NUMBER                 = 7;
+    /** Cannot reach the peer */
+    public static final int NUMBER_UNREACHABLE             = 8;
+    /** Cannot reach the server */
+    public static final int SERVER_UNREACHABLE             = 9;
+    /** Invalid credentials */
+    public static final int INVALID_CREDENTIALS            = 10;
+    /** Calling from out of network is not allowed */
+    public static final int OUT_OF_NETWORK                 = 11;
+    /** Server error */
+    public static final int SERVER_ERROR                   = 12;
+    /** Client timed out */
+    public static final int TIMED_OUT                      = 13;
+    /** Client went out of network range */
+    public static final int LOST_SIGNAL                    = 14;
+    /** GSM or CDMA ACM limit exceeded */
+    public static final int LIMIT_EXCEEDED                 = 15;
+    /** An incoming call that was rejected */
+    public static final int INCOMING_REJECTED              = 16;
+    /** Radio is turned off explicitly */
+    public static final int POWER_OFF                      = 17;
+    /** Out of service */
+    public static final int OUT_OF_SERVICE                 = 18;
+    /** No ICC, ICC locked, or other ICC error */
+    public static final int ICC_ERROR                      = 19;
+    /** Call was blocked by call barring */
+    public static final int CALL_BARRED                    = 20;
+    /** Call was blocked by fixed dial number */
+    public static final int FDN_BLOCKED                    = 21;
+    /** Call was blocked by restricted all voice access */
+    public static final int CS_RESTRICTED                  = 22;
+    /** Call was blocked by restricted normal voice access */
+    public static final int CS_RESTRICTED_NORMAL           = 23;
+    /** Call was blocked by restricted emergency voice access */
+    public static final int CS_RESTRICTED_EMERGENCY        = 24;
+    /** Unassigned number */
+    public static final int UNOBTAINABLE_NUMBER            = 25;
+    /** MS is locked until next power cycle */
+    public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE  = 26;
+    /** Drop call*/
+    public static final int CDMA_DROP                      = 27;
+    /** INTERCEPT order received, MS state idle entered */
+    public static final int CDMA_INTERCEPT                 = 28;
+    /** MS has been redirected, call is cancelled */
+    public static final int CDMA_REORDER                   = 29;
+    /** Service option rejection */
+    public static final int CDMA_SO_REJECT                 = 30;
+    /** Requested service is rejected, retry delay is set */
+    public static final int CDMA_RETRY_ORDER               = 31;
+    /** Unable to obtain access to the CDMA system */
+    public static final int CDMA_ACCESS_FAILURE            = 32;
+    /** Not a preempted call */
+    public static final int CDMA_PREEMPTED                 = 33;
+    /** Not an emergency call */
+    public static final int CDMA_NOT_EMERGENCY             = 34;
+    /** Access Blocked by CDMA network */
+    public static final int CDMA_ACCESS_BLOCKED            = 35;
+    /** Unknown error or not specified */
+    public static final int ERROR_UNSPECIFIED              = 36;
+    /**
+     * Only emergency numbers are allowed, but we tried to dial a non-emergency number.
+     * @hide
+     */
+    // TODO: This should be the same as NOT_EMERGENCY
+    public static final int EMERGENCY_ONLY                 = 37;
+    /**
+     * The supplied CALL Intent didn't contain a valid phone number.
+     */
+    public static final int NO_PHONE_NUMBER_SUPPLIED       = 38;
+    /**
+     * Our initial phone number was actually an MMI sequence.
+     */
+    public static final int DIALED_MMI                     = 39;
+    /**
+     * We tried to call a voicemail: URI but the device has no voicemail number configured.
+     */
+    public static final int VOICEMAIL_NUMBER_MISSING       = 40;
+    /**
+     * This status indicates that InCallScreen should display the
+     * CDMA-specific "call lost" dialog.  (If an outgoing call fails,
+     * and the CDMA "auto-retry" feature is enabled, *and* the retried
+     * call fails too, we display this specific dialog.)
+     *
+     * TODO: this is currently unused, since the "call lost" dialog
+     * needs to be triggered by a *disconnect* event, rather than when
+     * the InCallScreen first comes to the foreground.  For now we use
+     * the needToShowCallLostDialog field for this (see below.)
+     *
+     * @hide
+     */
+    public static final int CDMA_CALL_LOST                 = 41;
+    /**
+     * This status indicates that the call was placed successfully,
+     * but additionally, the InCallScreen needs to display the
+     * "Exiting ECM" dialog.
+     *
+     * (Details: "Emergency callback mode" is a CDMA-specific concept
+     * where the phone disallows data connections over the cell
+     * network for some period of time after you make an emergency
+     * call.  If the phone is in ECM and you dial a non-emergency
+     * number, that automatically *cancels* ECM, but we additionally
+     * need to warn the user that ECM has been canceled (see bug
+     * 4207607.))
+     *
+     * TODO: Rethink where the best place to put this is. It is not a notification
+     * of a failure of the connection -- it is an additional message that accompanies
+     * a successful connection giving the user important information about what happened.
+     *
+     * {@hide}
+     */
+    public static final int EXITED_ECM                     = 42;
+
+    /**
+     * The outgoing call failed with an unknown cause.
+     */
+    public static final int OUTGOING_FAILURE               = 43;
+
+    /**
+     * The outgoing call was canceled by the {@link android.telecom.ConnectionService}.
+     */
+    public static final int OUTGOING_CANCELED              = 44;
+
+    /**
+     * The call, which was an IMS call, disconnected because it merged with another call.
+     */
+    public static final int IMS_MERGED_SUCCESSFULLY        = 45;
+
+    /**
+     * Stk Call Control modified DIAL request to USSD request.
+     */
+    public static final int DIAL_MODIFIED_TO_USSD          = 46;
+    /**
+     * Stk Call Control modified DIAL request to SS request.
+     */
+    public static final int DIAL_MODIFIED_TO_SS            = 47;
+    /**
+     * Stk Call Control modified DIAL request to DIAL with modified data.
+     */
+    public static final int DIAL_MODIFIED_TO_DIAL          = 48;
+
+    /**
+     * The call was terminated because CDMA phone service and roaming have already been activated.
+     */
+    public static final int CDMA_ALREADY_ACTIVATED         = 49;
+
+    /**
+     * The call was terminated because it is not possible to place a video call while TTY is
+     * enabled.
+     */
+    public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50;
+
+    /**
+     * The call was terminated because it was pulled to another device.
+     */
+    public static final int CALL_PULLED = 51;
+
+    /**
+     * The call was terminated because it was answered on another device.
+     */
+    public static final int ANSWERED_ELSEWHERE = 52;
+
+    /**
+     * The call was terminated because the maximum allowable number of calls has been reached.
+     */
+    public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53;
+
+    /**
+     * The call was terminated because cellular data has been disabled.
+     * Used when in a video call and the user disables cellular data via the settings.
+     */
+    public static final int DATA_DISABLED = 54;
+
+    /**
+     * The call was terminated because the data policy has disabled cellular data.
+     * Used when in a video call and the user has exceeded the device data limit.
+     */
+    public static final int DATA_LIMIT_REACHED = 55;
+
+    /**
+     * The call being placed was detected as a call forwarding number and was being dialed while
+     * roaming on a carrier that does not allow this.
+     */
+    public static final int DIALED_CALL_FORWARDING_WHILE_ROAMING = 57;
+
+    /**
+     * The network does not accept the emergency call request because IMEI was used as
+     * identification and this cability is not supported by the network.
+     */
+    public static final int IMEI_NOT_ACCEPTED = 58;
+
+    /**
+     * A call over WIFI was disconnected because the WIFI signal was lost or became too degraded to
+     * continue the call.
+     */
+    public static final int WIFI_LOST = 59;
+
+    /**
+     * The call has failed because of access class barring.
+     */
+    public static final int IMS_ACCESS_BLOCKED = 60;
+
+    /**
+     * The call has ended (mid-call) because the device's battery is too low.
+     */
+    public static final int LOW_BATTERY = 61;
+
+    /**
+     * A call was not dialed because the device's battery is too low.
+     */
+    public static final int DIAL_LOW_BATTERY = 62;
+
+    /**
+     * Emergency call failed with a temporary fail cause and can be redialed on this slot.
+     */
+    public static final int EMERGENCY_TEMP_FAILURE = 63;
+
+    /**
+     * Emergency call failed with a permanent fail cause and should not be redialed on this
+     * slot.
+     */
+    public static final int EMERGENCY_PERM_FAILURE = 64;
+
+    /**
+     * This cause is used to report a normal event only when no other cause in the normal class
+     * applies.
+     */
+    public static final int NORMAL_UNSPECIFIED = 65;
+
+    /**
+     * Stk Call Control modified DIAL request to video DIAL request.
+     */
+    public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66;
+
+    /**
+     * Stk Call Control modified Video DIAL request to SS request.
+     */
+    public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67;
+
+    /**
+     * Stk Call Control modified Video DIAL request to USSD request.
+     */
+    public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68;
+
+    /**
+     * Stk Call Control modified Video DIAL request to DIAL request.
+     */
+    public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69;
+
+    /**
+     * Stk Call Control modified Video DIAL request to Video DIAL request.
+     */
+    public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70;
+
+    /**
+     * The network has reported that an alternative emergency number has been dialed, but the user
+     * must exit airplane mode to place the call.
+     */
+    public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71;
+
+    /**
+     * Indicates that a new outgoing call cannot be placed because there is already an outgoing
+     * call dialing out.
+     */
+    public static final int ALREADY_DIALING = 72;
+
+    /**
+     * Indicates that a new outgoing call cannot be placed while there is a ringing call.
+     */
+    public static final int CANT_CALL_WHILE_RINGING = 73;
+
+    /**
+     * Indicates that a new outgoing call cannot be placed because calling has been disabled using
+     * the ro.telephony.disable-call system property.
+     */
+    public static final int CALLING_DISABLED = 74;
+
+    /**
+     * Indicates that a new outgoing call cannot be placed because there is currently an ongoing
+     * foreground and background call.
+     */
+    public static final int TOO_MANY_ONGOING_CALLS = 75;
+
+    /**
+     * Indicates that a new outgoing call cannot be placed because OTASP provisioning is currently
+     * in process.
+     */
+    public static final int OTASP_PROVISIONING_IN_PROCESS = 76;
+
+    /**
+     * Indicates that the call is dropped due to RTCP inactivity, primarily due to media path
+     * disruption.
+     */
+    public static final int MEDIA_TIMEOUT = 77;
+
+    /**
+     * Indicates that an emergency call cannot be placed over WFC because the service is not
+     * available in the current location.
+     */
+    public static final int EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE = 78;
+
+    /**
+     * Indicates that WiFi calling service is not available in the current location.
+     */
+    public static final int WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 79;
+
+    /**
+     * Indicates that an emergency call was placed, which caused the existing connection to be
+     * hung up.
+     */
+    public static final int OUTGOING_EMERGENCY_CALL_PLACED = 80;
+
+    /**
+     * Indicates that incoming call was rejected by the modem before the call went in ringing
+     */
+    public static final int INCOMING_AUTO_REJECTED = 81;
+
+    /**
+     * Indicates that the call was unable to be made because the satellite modem is enabled.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_ENABLED = 82;
+
+    //*********************************************************************************************
+    // When adding a disconnect type:
+    // 1) Update toString() with the newly added disconnect type.
+    // 2) Update android.telecom.DisconnectCauseUtil with any mappings to a telecom.DisconnectCause.
+    //*********************************************************************************************
+
+    /** Private constructor to avoid class instantiation. */
+    private DisconnectCause() {
+        // Do nothing.
+    }
+
+    /**
+     * Returns descriptive string for the specified disconnect cause.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static @NonNull String toString(int cause) {
+        switch (cause) {
+            case NOT_DISCONNECTED:
+                return "NOT_DISCONNECTED";
+            case INCOMING_MISSED:
+                return "INCOMING_MISSED";
+            case NORMAL:
+                return "NORMAL";
+            case LOCAL:
+                return "LOCAL";
+            case BUSY:
+                return "BUSY";
+            case CONGESTION:
+                return "CONGESTION";
+            case INVALID_NUMBER:
+                return "INVALID_NUMBER";
+            case NUMBER_UNREACHABLE:
+                return "NUMBER_UNREACHABLE";
+            case SERVER_UNREACHABLE:
+                return "SERVER_UNREACHABLE";
+            case INVALID_CREDENTIALS:
+                return "INVALID_CREDENTIALS";
+            case OUT_OF_NETWORK:
+                return "OUT_OF_NETWORK";
+            case SERVER_ERROR:
+                return "SERVER_ERROR";
+            case TIMED_OUT:
+                return "TIMED_OUT";
+            case LOST_SIGNAL:
+                return "LOST_SIGNAL";
+            case LIMIT_EXCEEDED:
+                return "LIMIT_EXCEEDED";
+            case INCOMING_REJECTED:
+                return "INCOMING_REJECTED";
+            case POWER_OFF:
+                return "POWER_OFF";
+            case OUT_OF_SERVICE:
+                return "OUT_OF_SERVICE";
+            case ICC_ERROR:
+                return "ICC_ERROR";
+            case CALL_BARRED:
+                return "CALL_BARRED";
+            case FDN_BLOCKED:
+                return "FDN_BLOCKED";
+            case CS_RESTRICTED:
+                return "CS_RESTRICTED";
+            case CS_RESTRICTED_NORMAL:
+                return "CS_RESTRICTED_NORMAL";
+            case CS_RESTRICTED_EMERGENCY:
+                return "CS_RESTRICTED_EMERGENCY";
+            case UNOBTAINABLE_NUMBER:
+                return "UNOBTAINABLE_NUMBER";
+            case CDMA_LOCKED_UNTIL_POWER_CYCLE:
+                return "CDMA_LOCKED_UNTIL_POWER_CYCLE";
+            case CDMA_DROP:
+                return "CDMA_DROP";
+            case CDMA_INTERCEPT:
+                return "CDMA_INTERCEPT";
+            case CDMA_REORDER:
+                return "CDMA_REORDER";
+            case CDMA_SO_REJECT:
+                return "CDMA_SO_REJECT";
+            case CDMA_RETRY_ORDER:
+                return "CDMA_RETRY_ORDER";
+            case CDMA_ACCESS_FAILURE:
+                return "CDMA_ACCESS_FAILURE";
+            case CDMA_PREEMPTED:
+                return "CDMA_PREEMPTED";
+            case CDMA_NOT_EMERGENCY:
+                return "CDMA_NOT_EMERGENCY";
+            case CDMA_ACCESS_BLOCKED:
+                return "CDMA_ACCESS_BLOCKED";
+            case EMERGENCY_ONLY:
+                return "EMERGENCY_ONLY";
+            case NO_PHONE_NUMBER_SUPPLIED:
+                return "NO_PHONE_NUMBER_SUPPLIED";
+            case DIALED_MMI:
+                return "DIALED_MMI";
+            case VOICEMAIL_NUMBER_MISSING:
+                return "VOICEMAIL_NUMBER_MISSING";
+            case CDMA_CALL_LOST:
+                return "CDMA_CALL_LOST";
+            case EXITED_ECM:
+                return "EXITED_ECM";
+            case DIAL_MODIFIED_TO_USSD:
+                return "DIAL_MODIFIED_TO_USSD";
+            case DIAL_MODIFIED_TO_SS:
+                return "DIAL_MODIFIED_TO_SS";
+            case DIAL_MODIFIED_TO_DIAL:
+                return "DIAL_MODIFIED_TO_DIAL";
+            case DIAL_MODIFIED_TO_DIAL_VIDEO:
+                return "DIAL_MODIFIED_TO_DIAL_VIDEO";
+            case DIAL_VIDEO_MODIFIED_TO_SS:
+                return "DIAL_VIDEO_MODIFIED_TO_SS";
+            case DIAL_VIDEO_MODIFIED_TO_USSD:
+                return "DIAL_VIDEO_MODIFIED_TO_USSD";
+            case DIAL_VIDEO_MODIFIED_TO_DIAL:
+                return "DIAL_VIDEO_MODIFIED_TO_DIAL";
+            case DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO:
+                return "DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO";
+            case ERROR_UNSPECIFIED:
+                return "ERROR_UNSPECIFIED";
+            case OUTGOING_FAILURE:
+                return "OUTGOING_FAILURE";
+            case OUTGOING_CANCELED:
+                return "OUTGOING_CANCELED";
+            case IMS_MERGED_SUCCESSFULLY:
+                return "IMS_MERGED_SUCCESSFULLY";
+            case CDMA_ALREADY_ACTIVATED:
+                return "CDMA_ALREADY_ACTIVATED";
+            case VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED:
+                return "VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED";
+            case CALL_PULLED:
+                return "CALL_PULLED";
+            case ANSWERED_ELSEWHERE:
+                return "ANSWERED_ELSEWHERE";
+            case MAXIMUM_NUMBER_OF_CALLS_REACHED:
+                return "MAXIMUM_NUMER_OF_CALLS_REACHED";
+            case DATA_DISABLED:
+                return "DATA_DISABLED";
+            case DATA_LIMIT_REACHED:
+                return "DATA_LIMIT_REACHED";
+            case DIALED_CALL_FORWARDING_WHILE_ROAMING:
+                return "DIALED_CALL_FORWARDING_WHILE_ROAMING";
+            case IMEI_NOT_ACCEPTED:
+                return "IMEI_NOT_ACCEPTED";
+            case WIFI_LOST:
+                return "WIFI_LOST";
+            case IMS_ACCESS_BLOCKED:
+                return "IMS_ACCESS_BLOCKED";
+            case LOW_BATTERY:
+                return "LOW_BATTERY";
+            case DIAL_LOW_BATTERY:
+                return "DIAL_LOW_BATTERY";
+            case EMERGENCY_TEMP_FAILURE:
+                return "EMERGENCY_TEMP_FAILURE";
+            case EMERGENCY_PERM_FAILURE:
+                return "EMERGENCY_PERM_FAILURE";
+            case NORMAL_UNSPECIFIED:
+                return "NORMAL_UNSPECIFIED";
+            case IMS_SIP_ALTERNATE_EMERGENCY_CALL:
+                return "IMS_SIP_ALTERNATE_EMERGENCY_CALL";
+            case ALREADY_DIALING:
+                return "ALREADY_DIALING";
+            case CANT_CALL_WHILE_RINGING:
+                return "CANT_CALL_WHILE_RINGING";
+            case CALLING_DISABLED:
+                return "CALLING_DISABLED";
+            case TOO_MANY_ONGOING_CALLS:
+                return "TOO_MANY_ONGOING_CALLS";
+            case OTASP_PROVISIONING_IN_PROCESS:
+                return "OTASP_PROVISIONING_IN_PROCESS";
+            case MEDIA_TIMEOUT:
+                return "MEDIA_TIMEOUT";
+            case EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE:
+                return "EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE";
+            case WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
+                return "WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION";
+            case OUTGOING_EMERGENCY_CALL_PLACED:
+                return "OUTGOING_EMERGENCY_CALL_PLACED";
+            case INCOMING_AUTO_REJECTED:
+                return "INCOMING_AUTO_REJECTED";
+            case SATELLITE_ENABLED:
+                return "SATELLITE_ENABLED";
+            default:
+                return "INVALID: " + cause;
+        }
+    }
+}
diff --git a/android-35/android/telephony/DomainSelectionService.java b/android-35/android/telephony/DomainSelectionService.java
new file mode 100644
index 0000000..633694a
--- /dev/null
+++ b/android-35/android/telephony/DomainSelectionService.java
@@ -0,0 +1,890 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.telephony.Annotation.DisconnectCauses;
+import android.telephony.Annotation.PreciseDisconnectCauses;
+import android.telephony.ims.ImsReasonInfo;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.IDomainSelectionServiceController;
+import com.android.internal.telephony.IDomainSelector;
+import com.android.internal.telephony.ITransportSelectorCallback;
+import com.android.internal.telephony.ITransportSelectorResultCallback;
+import com.android.internal.telephony.IWwanSelectorCallback;
+import com.android.internal.telephony.IWwanSelectorResultCallback;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Base domain selection implementation.
+ * <p>
+ * Services that extend {@link DomainSelectionService} must register the service in their
+ * AndroidManifest.xml to be detected by the framework.
+ * <p>
+ * 1) The application must declare that they use the
+ * android.permission.BIND_DOMAIN_SELECTION_SERVICE permission.
+ * <p>
+ * 2) The DomainSelectionService definition in the manifest must follow this format:
+ * <pre>
+ * {@code
+ * ...
+ * <service android:name=".EgDomainSelectionService"
+ *    android:permission="android.permission.BIND_DOMAIN_SELECTION_SERVICE" >
+ *    <intent-filter>
+ *       <action android:name="android.telephony.DomainSelectionService" />
+ *    </intent-filter>
+ * </service>
+ * ...
+ * }
+ * </pre>
+ * <p>
+ * The ComponentName corresponding to this DomainSelectionService component MUST also be set
+ * as the system domain selection implementation in order to be bound.
+ * The system domain selection implementation is set in the device overlay for
+ * {@code config_domain_selection_service_component_name}
+ * in {@code packages/services/Telephony/res/values/config.xml}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+public abstract class DomainSelectionService extends Service {
+
+    private static final String LOG_TAG = "DomainSelectionService";
+
+    /**
+     * The intent that must be defined as an intent-filter in the AndroidManifest of the
+     * {@link DomainSelectionService}.
+     *
+     * @hide
+     */
+    public static final String SERVICE_INTERFACE = "android.telephony.DomainSelectionService";
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "SELECTOR_TYPE_",
+            value = {
+                    SELECTOR_TYPE_CALLING,
+                    SELECTOR_TYPE_SMS})
+    public @interface SelectorType {}
+
+    /** Indicates the domain selector type for calling. */
+    public static final int SELECTOR_TYPE_CALLING = 1;
+    /** Indicates the domain selector type for sms. */
+    public static final int SELECTOR_TYPE_SMS = 2;
+
+    /** Indicates that the modem can scan for emergency service as per modem’s implementation. */
+    public static final int SCAN_TYPE_NO_PREFERENCE = 0;
+
+    /** Indicates that the modem will scan for emergency service in limited service mode. */
+    public static final int SCAN_TYPE_LIMITED_SERVICE = 1;
+
+    /** Indicates that the modem will scan for emergency service in full service mode. */
+    public static final int SCAN_TYPE_FULL_SERVICE = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "SCAN_TYPE_",
+            value = {
+                    SCAN_TYPE_NO_PREFERENCE,
+                    SCAN_TYPE_LIMITED_SERVICE,
+                    SCAN_TYPE_FULL_SERVICE})
+    public @interface EmergencyScanType {}
+
+    /**
+     * Contains attributes required to determine the domain for a telephony service.
+     */
+    @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+    public static final class SelectionAttributes implements Parcelable {
+
+        private static final String TAG = "SelectionAttributes";
+
+        private int mSlotIndex;
+        private int mSubId;
+        private @Nullable String mCallId;
+        private @Nullable Uri mAddress;
+        private @SelectorType int mSelectorType;
+        private boolean mIsVideoCall;
+        private boolean mIsEmergency;
+        private boolean mIsTestEmergencyNumber;
+        private boolean mIsExitedFromAirplaneMode;
+        private @Nullable ImsReasonInfo mImsReasonInfo;
+        private @PreciseDisconnectCauses int mCause;
+        private @Nullable EmergencyRegistrationResult mEmergencyRegistrationResult;
+
+        /**
+         * @param slotIndex The logical slot index.
+         * @param subscriptionId The subscription identifier.
+         * @param callId The call identifier.
+         * @param address The dialed address.
+         * @param selectorType Indicates the requested domain selector type.
+         * @param video Indicates it's a video call.
+         * @param emergency Indicates it's emergency service.
+         * @param isTest Indicates it's a test emergency number.
+         * @param exited {@code true} if the request caused the device to move out of airplane mode.
+         * @param imsReasonInfo The reason why the last PS attempt failed.
+         * @param cause The reason why the last CS attempt failed.
+         * @param regResult The current registration result for emergency services.
+         */
+        private SelectionAttributes(int slotIndex, int subscriptionId, @Nullable String callId,
+                @Nullable Uri address, @SelectorType int selectorType,
+                boolean video, boolean emergency, boolean isTest, boolean exited,
+                @Nullable ImsReasonInfo imsReasonInfo, @PreciseDisconnectCauses int cause,
+                @Nullable EmergencyRegistrationResult regResult) {
+            mSlotIndex = slotIndex;
+            mSubId = subscriptionId;
+            mCallId = callId;
+            mAddress = address;
+            mSelectorType = selectorType;
+            mIsVideoCall = video;
+            mIsEmergency = emergency;
+            mIsTestEmergencyNumber = isTest;
+            mIsExitedFromAirplaneMode = exited;
+            mImsReasonInfo = imsReasonInfo;
+            mCause = cause;
+            mEmergencyRegistrationResult = regResult;
+        }
+
+        /**
+         * Copy constructor.
+         *
+         * @param s Source selection attributes.
+         * @hide
+         */
+        public SelectionAttributes(@NonNull SelectionAttributes s) {
+            mSlotIndex = s.mSlotIndex;
+            mSubId = s.mSubId;
+            mCallId = s.mCallId;
+            mAddress = s.mAddress;
+            mSelectorType = s.mSelectorType;
+            mIsEmergency = s.mIsEmergency;
+            mIsTestEmergencyNumber = s.mIsTestEmergencyNumber;
+            mIsExitedFromAirplaneMode = s.mIsExitedFromAirplaneMode;
+            mImsReasonInfo = s.mImsReasonInfo;
+            mCause = s.mCause;
+            mEmergencyRegistrationResult = s.mEmergencyRegistrationResult;
+        }
+
+        /**
+         * Constructs a SelectionAttributes object from the given parcel.
+         */
+        private SelectionAttributes(@NonNull Parcel in) {
+            readFromParcel(in);
+        }
+
+        /**
+         * @return The logical slot index.
+         */
+        public int getSlotIndex() {
+            return mSlotIndex;
+        }
+
+        /**
+         * @return The subscription identifier.
+         */
+        public int getSubscriptionId() {
+            return mSubId;
+        }
+
+        /**
+         * @return The call identifier.
+         */
+        public @Nullable String getCallId() {
+            return mCallId;
+        }
+
+        /**
+         * @return The dialed address.
+         */
+        public @Nullable Uri getAddress() {
+            return mAddress;
+        }
+
+        /**
+         * @return The domain selector type.
+         */
+        public @SelectorType int getSelectorType() {
+            return mSelectorType;
+        }
+
+        /**
+         * @return {@code true} if the request is for a video call.
+         */
+        public boolean isVideoCall() {
+            return mIsVideoCall;
+        }
+
+        /**
+         * @return {@code true} if the request is for emergency services.
+         */
+        public boolean isEmergency() {
+            return mIsEmergency;
+        }
+
+        /**
+         * @return {@code true} if the dialed number is a test emergency number.
+         */
+        public boolean isTestEmergencyNumber() {
+            return mIsTestEmergencyNumber;
+        }
+
+        /**
+         * @return {@code true} if the request caused the device to move out of airplane mode.
+         */
+        public boolean isExitedFromAirplaneMode() {
+            return mIsExitedFromAirplaneMode;
+        }
+
+        /**
+         * @return The PS disconnect cause if trying over PS resulted in a failure and
+         *         reselection is required.
+         */
+        public @Nullable ImsReasonInfo getPsDisconnectCause() {
+            return mImsReasonInfo;
+        }
+
+        /**
+         * @return The CS disconnect cause if trying over CS resulted in a failure and
+         *         reselection is required.
+         */
+        public @PreciseDisconnectCauses int getCsDisconnectCause() {
+            return mCause;
+        }
+
+        /**
+         * @return The current registration state of cellular network.
+         */
+        public @Nullable EmergencyRegistrationResult getEmergencyRegistrationResult() {
+            return mEmergencyRegistrationResult;
+        }
+
+        @Override
+        public @NonNull String toString() {
+            return "{ slotIndex=" + mSlotIndex
+                    + ", subId=" + mSubId
+                    + ", callId=" + mCallId
+                    + ", address=" + (Build.IS_DEBUGGABLE ? mAddress : "***")
+                    + ", type=" + mSelectorType
+                    + ", videoCall=" + mIsVideoCall
+                    + ", emergency=" + mIsEmergency
+                    + ", isTest=" + mIsTestEmergencyNumber
+                    + ", airplaneMode=" + mIsExitedFromAirplaneMode
+                    + ", reasonInfo=" + mImsReasonInfo
+                    + ", cause=" + mCause
+                    + ", regResult=" + mEmergencyRegistrationResult
+                    + " }";
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            SelectionAttributes that = (SelectionAttributes) o;
+            return mSlotIndex == that.mSlotIndex && mSubId == that.mSubId
+                    && TextUtils.equals(mCallId, that.mCallId)
+                    && equalsHandlesNulls(mAddress, that.mAddress)
+                    && mSelectorType == that.mSelectorType && mIsVideoCall == that.mIsVideoCall
+                    && mIsEmergency == that.mIsEmergency
+                    && mIsTestEmergencyNumber == that.mIsTestEmergencyNumber
+                    && mIsExitedFromAirplaneMode == that.mIsExitedFromAirplaneMode
+                    && equalsHandlesNulls(mImsReasonInfo, that.mImsReasonInfo)
+                    && mCause == that.mCause
+                    && equalsHandlesNulls(mEmergencyRegistrationResult,
+                            that.mEmergencyRegistrationResult);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mCallId, mAddress, mImsReasonInfo,
+                    mIsVideoCall, mIsEmergency, mIsTestEmergencyNumber, mIsExitedFromAirplaneMode,
+                    mEmergencyRegistrationResult, mSlotIndex, mSubId, mSelectorType, mCause);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel out, int flags) {
+            out.writeInt(mSlotIndex);
+            out.writeInt(mSubId);
+            out.writeString8(mCallId);
+            out.writeParcelable(mAddress, 0);
+            out.writeInt(mSelectorType);
+            out.writeBoolean(mIsVideoCall);
+            out.writeBoolean(mIsEmergency);
+            out.writeBoolean(mIsTestEmergencyNumber);
+            out.writeBoolean(mIsExitedFromAirplaneMode);
+            out.writeParcelable(mImsReasonInfo, 0);
+            out.writeInt(mCause);
+            out.writeParcelable(mEmergencyRegistrationResult, 0);
+        }
+
+        private void readFromParcel(@NonNull Parcel in) {
+            mSlotIndex = in.readInt();
+            mSubId = in.readInt();
+            mCallId = in.readString8();
+            mAddress = in.readParcelable(Uri.class.getClassLoader(),
+                    android.net.Uri.class);
+            mSelectorType = in.readInt();
+            mIsVideoCall = in.readBoolean();
+            mIsEmergency = in.readBoolean();
+            mIsTestEmergencyNumber = in.readBoolean();
+            mIsExitedFromAirplaneMode = in.readBoolean();
+            mImsReasonInfo = in.readParcelable(ImsReasonInfo.class.getClassLoader(),
+                    android.telephony.ims.ImsReasonInfo.class);
+            mCause = in.readInt();
+            mEmergencyRegistrationResult = in.readParcelable(
+                    EmergencyRegistrationResult.class.getClassLoader(),
+                    EmergencyRegistrationResult.class);
+        }
+
+        public static final @NonNull Creator<SelectionAttributes> CREATOR =
+                new Creator<SelectionAttributes>() {
+            @Override
+            public SelectionAttributes createFromParcel(@NonNull Parcel in) {
+                return new SelectionAttributes(in);
+            }
+
+            @Override
+            public SelectionAttributes[] newArray(int size) {
+                return new SelectionAttributes[size];
+            }
+        };
+
+        private static boolean equalsHandlesNulls(Object a, Object b) {
+            return (a == null) ? (b == null) : a.equals(b);
+        }
+
+        /**
+         * Builder class creating a new instance.
+         */
+        @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+        public static final class Builder {
+            private final int mSlotIndex;
+            private final int mSubId;
+            private @Nullable String mCallId;
+            private @Nullable Uri mAddress;
+            private final @SelectorType int mSelectorType;
+            private boolean mIsVideoCall;
+            private boolean mIsEmergency;
+            private boolean mIsTestEmergencyNumber;
+            private boolean mIsExitedFromAirplaneMode;
+            private @Nullable ImsReasonInfo mImsReasonInfo;
+            private @PreciseDisconnectCauses int mCause;
+            private @Nullable EmergencyRegistrationResult mEmergencyRegistrationResult;
+
+            /**
+             * Default constructor for Builder.
+             */
+            public Builder(int slotIndex, int subscriptionId, @SelectorType int selectorType) {
+                mSlotIndex = slotIndex;
+                mSubId = subscriptionId;
+                mSelectorType = selectorType;
+            }
+
+            /**
+             * Sets the call identifier.
+             *
+             * @param callId The call identifier.
+             * @return The same instance of the builder.
+             */
+            public @NonNull Builder setCallId(@Nullable String callId) {
+                mCallId = callId;
+                return this;
+            }
+
+            /**
+             * Sets the dialed address.
+             *
+             * @param address The dialed address.
+             * @return The same instance of the builder.
+             */
+            public @NonNull Builder setAddress(@Nullable Uri address) {
+                mAddress = address;
+                return this;
+            }
+
+            /**
+             * Sets whether it's a video call or not.
+             *
+             * @param isVideo Indicates it's a video call.
+             * @return The same instance of the builder.
+             */
+            public @NonNull Builder setVideoCall(boolean isVideo) {
+                mIsVideoCall = isVideo;
+                return this;
+            }
+
+            /**
+             * Sets whether it's an emergency service or not.
+             *
+             * @param isEmergency Indicates it's emergency service.
+             * @return The same instance of the builder.
+             */
+            public @NonNull Builder setEmergency(boolean isEmergency) {
+                mIsEmergency = isEmergency;
+                return this;
+            }
+
+            /**
+             * Sets whether it's a test emergency number or not.
+             *
+             * @param isTest Indicates it's a test emergency number.
+             * @return The same instance of the builder.
+             */
+            public @NonNull Builder setTestEmergencyNumber(boolean isTest) {
+                mIsTestEmergencyNumber = isTest;
+                return this;
+            }
+
+            /**
+             * Sets whether the request caused the device to move out of airplane mode.
+             *
+             * @param exited {@code true} if the request caused the device to move out of
+             *        airplane mode.
+             * @return The same instance of the builder.
+             */
+            public @NonNull Builder setExitedFromAirplaneMode(boolean exited) {
+                mIsExitedFromAirplaneMode = exited;
+                return this;
+            }
+
+            /**
+             * Sets an optional reason why the last PS attempt failed.
+             *
+             * @param info The reason why the last PS attempt failed.
+             * @return The same instance of the builder.
+             */
+            public @NonNull Builder setPsDisconnectCause(@Nullable ImsReasonInfo info) {
+                mImsReasonInfo = info;
+                return this;
+            }
+
+            /**
+             * Sets an optional reason why the last CS attempt failed.
+             *
+             * @param cause The reason why the last CS attempt failed.
+             * @return The same instance of the builder.
+             */
+            public @NonNull Builder setCsDisconnectCause(@PreciseDisconnectCauses int cause) {
+                mCause = cause;
+                return this;
+            }
+
+            /**
+             * Sets the current registration result for emergency services.
+             *
+             * @param regResult The current registration result for emergency services.
+             * @return The same instance of the builder.
+             */
+            public @NonNull Builder setEmergencyRegistrationResult(
+                    @Nullable EmergencyRegistrationResult regResult) {
+                mEmergencyRegistrationResult = regResult;
+                return this;
+            }
+
+            /**
+             * Build the SelectionAttributes.
+             * @return The SelectionAttributes object.
+             */
+            public @NonNull SelectionAttributes build() {
+                return new SelectionAttributes(mSlotIndex, mSubId, mCallId, mAddress,
+                        mSelectorType, mIsVideoCall, mIsEmergency, mIsTestEmergencyNumber,
+                        mIsExitedFromAirplaneMode, mImsReasonInfo,
+                        mCause, mEmergencyRegistrationResult);
+            }
+        }
+    }
+
+    /**
+     * A wrapper class for ITransportSelectorCallback interface.
+     */
+    private final class TransportSelectorCallbackWrapper implements TransportSelectorCallback {
+        private static final String TAG = "TransportSelectorCallbackWrapper";
+
+        private final @NonNull ITransportSelectorCallback mCallback;
+        private final @NonNull Executor mExecutor;
+
+        private @Nullable ITransportSelectorResultCallbackAdapter mResultCallback;
+        private @Nullable DomainSelectorWrapper mSelectorWrapper;
+
+        TransportSelectorCallbackWrapper(@NonNull ITransportSelectorCallback cb,
+                @NonNull Executor executor) {
+            mCallback = cb;
+            mExecutor = executor;
+        }
+
+        @Override
+        public void onCreated(@NonNull DomainSelector selector) {
+            try {
+                mSelectorWrapper = new DomainSelectorWrapper(selector, mExecutor);
+                mCallback.onCreated(mSelectorWrapper.getCallbackBinder());
+            } catch (Exception e) {
+                Rlog.e(TAG, "onCreated e=" + e);
+            }
+        }
+
+        @Override
+        public void onWlanSelected(boolean useEmergencyPdn) {
+            try {
+                mCallback.onWlanSelected(useEmergencyPdn);
+            } catch (Exception e) {
+                Rlog.e(TAG, "onWlanSelected e=" + e);
+            }
+        }
+
+        @Override
+        public void onWwanSelected(Consumer<WwanSelectorCallback> consumer) {
+            try {
+                mResultCallback = new ITransportSelectorResultCallbackAdapter(consumer, mExecutor);
+                mCallback.onWwanSelectedAsync(mResultCallback);
+            } catch (Exception e) {
+                Rlog.e(TAG, "onWwanSelected e=" + e);
+                executeMethodAsyncNoException(mExecutor,
+                        () -> consumer.accept(null), TAG, "onWwanSelectedAsync-Exception");
+            }
+        }
+
+        @Override
+        public void onSelectionTerminated(@DisconnectCauses int cause) {
+            try {
+                mCallback.onSelectionTerminated(cause);
+                mSelectorWrapper = null;
+            } catch (Exception e) {
+                Rlog.e(TAG, "onSelectionTerminated e=" + e);
+            }
+        }
+
+        private class ITransportSelectorResultCallbackAdapter
+                extends ITransportSelectorResultCallback.Stub {
+            private final @NonNull Consumer<WwanSelectorCallback> mConsumer;
+            private final @NonNull Executor mExecutor;
+
+            ITransportSelectorResultCallbackAdapter(
+                    @NonNull Consumer<WwanSelectorCallback> consumer,
+                    @NonNull Executor executor) {
+                mConsumer = consumer;
+                mExecutor = executor;
+            }
+
+            @Override
+            public void onCompleted(@NonNull IWwanSelectorCallback cb) {
+                if (mConsumer == null) return;
+
+                WwanSelectorCallback callback = new WwanSelectorCallbackWrapper(cb, mExecutor);
+                executeMethodAsyncNoException(mExecutor,
+                        () -> mConsumer.accept(callback), TAG, "onWwanSelectedAsync-Completed");
+            }
+        }
+    }
+
+    /**
+     * A wrapper class for IDomainSelector interface.
+     */
+    private final class DomainSelectorWrapper {
+        private static final String TAG = "DomainSelectorWrapper";
+
+        private @NonNull IDomainSelector mCallbackBinder;
+
+        DomainSelectorWrapper(@NonNull DomainSelector cb, @NonNull Executor executor) {
+            mCallbackBinder = new IDomainSelectorAdapter(cb, executor);
+        }
+
+        private class IDomainSelectorAdapter extends IDomainSelector.Stub {
+            private final @NonNull WeakReference<DomainSelector> mDomainSelectorWeakRef;
+            private final @NonNull Executor mExecutor;
+
+            IDomainSelectorAdapter(@NonNull DomainSelector domainSelector,
+                    @NonNull Executor executor) {
+                mDomainSelectorWeakRef =
+                        new WeakReference<DomainSelector>(domainSelector);
+                mExecutor = executor;
+            }
+
+            @Override
+            public void reselectDomain(@NonNull SelectionAttributes attr) {
+                final DomainSelector domainSelector = mDomainSelectorWeakRef.get();
+                if (domainSelector == null) return;
+
+                executeMethodAsyncNoException(mExecutor,
+                        () -> domainSelector.reselectDomain(attr), TAG, "reselectDomain");
+            }
+
+            @Override
+            public void finishSelection() {
+                final DomainSelector domainSelector = mDomainSelectorWeakRef.get();
+                if (domainSelector == null) return;
+
+                executeMethodAsyncNoException(mExecutor,
+                        () -> domainSelector.finishSelection(), TAG, "finishSelection");
+            }
+        }
+
+        public @NonNull IDomainSelector getCallbackBinder() {
+            return mCallbackBinder;
+        }
+    }
+
+    /**
+     * A wrapper class for IWwanSelectorCallback and IWwanSelectorResultCallback.
+     */
+    private final class WwanSelectorCallbackWrapper
+            implements WwanSelectorCallback, CancellationSignal.OnCancelListener {
+        private static final String TAG = "WwanSelectorCallbackWrapper";
+
+        private final @NonNull IWwanSelectorCallback mCallback;
+        private final @NonNull Executor mExecutor;
+
+        private @Nullable IWwanSelectorResultCallbackAdapter mResultCallback;
+
+        WwanSelectorCallbackWrapper(@NonNull IWwanSelectorCallback cb,
+                @NonNull Executor executor) {
+            mCallback = cb;
+            mExecutor = executor;
+        }
+
+        @Override
+        public void onCancel() {
+            try {
+                mCallback.onCancel();
+            } catch (Exception e) {
+                Rlog.e(TAG, "onCancel e=" + e);
+            }
+        }
+
+        @Override
+        public void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks,
+                @EmergencyScanType int scanType, boolean resetScan,
+                @NonNull CancellationSignal signal,
+                @NonNull Consumer<EmergencyRegistrationResult> consumer) {
+            try {
+                if (signal != null) signal.setOnCancelListener(this);
+                mResultCallback = new IWwanSelectorResultCallbackAdapter(consumer, mExecutor);
+                mCallback.onRequestEmergencyNetworkScan(
+                        preferredNetworks.stream().mapToInt(Integer::intValue).toArray(),
+                        scanType, resetScan, mResultCallback);
+            } catch (Exception e) {
+                Rlog.e(TAG, "onRequestEmergencyNetworkScan e=" + e);
+            }
+        }
+
+        @Override
+        public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
+                boolean useEmergencyPdn) {
+            try {
+                mCallback.onDomainSelected(domain, useEmergencyPdn);
+            } catch (Exception e) {
+                Rlog.e(TAG, "onDomainSelected e=" + e);
+            }
+        }
+
+        private class IWwanSelectorResultCallbackAdapter
+                extends IWwanSelectorResultCallback.Stub {
+            private final @NonNull Consumer<EmergencyRegistrationResult> mConsumer;
+            private final @NonNull Executor mExecutor;
+
+            IWwanSelectorResultCallbackAdapter(
+                    @NonNull Consumer<EmergencyRegistrationResult> consumer,
+                    @NonNull Executor executor) {
+                mConsumer = consumer;
+                mExecutor = executor;
+            }
+
+            @Override
+            public void onComplete(@NonNull EmergencyRegistrationResult result) {
+                if (mConsumer == null) return;
+
+                executeMethodAsyncNoException(mExecutor,
+                        () -> mConsumer.accept(result), TAG, "onScanComplete");
+            }
+        }
+    }
+
+    private final Object mExecutorLock = new Object();
+
+    /** Executor used to execute methods called remotely by the framework. */
+    private @NonNull Executor mExecutor;
+
+    /**
+     * Selects a calling domain given the SelectionAttributes of the call request.
+     * <p>
+     * When the framework generates a request to place a call, {@link #onDomainSelection}
+     * will be called in order to determine the domain (CS or PS). For PS calls, the transport
+     * (WWAN or WLAN) will also need to be determined.
+     * <p>
+     * Once the domain/transport has been selected or an error has occurred,
+     * {@link TransportSelectorCallback} must be used to communicate the result back
+     * to the framework.
+     *
+     * @param attr Required to determine the domain.
+     * @param callback The callback instance being registered.
+     */
+    public abstract void onDomainSelection(@NonNull SelectionAttributes attr,
+            @NonNull TransportSelectorCallback callback);
+
+    /**
+     * Notifies the change in {@link ServiceState} for a specific logical slot index.
+     *
+     * @param slotIndex For which the state changed.
+     * @param subscriptionId For which the state changed.
+     * @param serviceState Updated {@link ServiceState}.
+     */
+    public void onServiceStateUpdated(int slotIndex, int subscriptionId,
+            @NonNull ServiceState serviceState) {
+    }
+
+    /**
+     * Notifies the change in {@link BarringInfo} for a specific logical slot index.
+     *
+     * @param slotIndex For which the state changed.
+     * @param subscriptionId For which the state changed.
+     * @param info Updated {@link BarringInfo}.
+     */
+    public void onBarringInfoUpdated(int slotIndex, int subscriptionId, @NonNull BarringInfo info) {
+    }
+
+    private final IBinder mDomainSelectionServiceController =
+            new IDomainSelectionServiceController.Stub() {
+        @Override
+        public void selectDomain(@NonNull SelectionAttributes attr,
+                @NonNull ITransportSelectorCallback callback)  throws RemoteException {
+            executeMethodAsync(getCachedExecutor(),
+                    () -> DomainSelectionService.this.onDomainSelection(attr,
+                            new TransportSelectorCallbackWrapper(callback, getCachedExecutor())),
+                    LOG_TAG, "onDomainSelection");
+        }
+
+        @Override
+        public void updateServiceState(int slotIndex, int subscriptionId,
+                @NonNull ServiceState serviceState) {
+            executeMethodAsyncNoException(getCachedExecutor(),
+                    () -> DomainSelectionService.this.onServiceStateUpdated(slotIndex,
+                            subscriptionId, serviceState), LOG_TAG, "onServiceStateUpdated");
+        }
+
+        @Override
+        public void updateBarringInfo(int slotIndex, int subscriptionId,
+                @NonNull BarringInfo info) {
+            executeMethodAsyncNoException(getCachedExecutor(),
+                    () -> DomainSelectionService.this.onBarringInfoUpdated(slotIndex,
+                    subscriptionId, info),
+                    LOG_TAG, "onBarringInfoUpdated");
+        }
+    };
+
+    private static void executeMethodAsync(@NonNull Executor executor, @NonNull Runnable r,
+            @NonNull String tag, @NonNull String errorLogName) throws RemoteException {
+        try {
+            CompletableFuture.runAsync(
+                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), executor).join();
+        } catch (CancellationException | CompletionException e) {
+            Rlog.w(tag, "Binder - " + errorLogName + " exception: " + e.getMessage());
+            throw new RemoteException(e.getMessage());
+        }
+    }
+
+    private void executeMethodAsyncNoException(@NonNull Executor executor, @NonNull Runnable r,
+            @NonNull String tag, @NonNull String errorLogName) {
+        try {
+            CompletableFuture.runAsync(
+                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), executor);
+        } catch (CancellationException | CompletionException e) {
+            Rlog.w(tag, "Binder - " + errorLogName + " exception: " + e.getMessage());
+        }
+    }
+
+    /** @hide */
+    @Override
+    public final @Nullable IBinder onBind(@Nullable Intent intent) {
+        if (intent == null) return null;
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            Log.i(LOG_TAG, "DomainSelectionService Bound.");
+            return mDomainSelectionServiceController;
+        }
+        return null;
+    }
+
+    /**
+     * The Executor to use when calling callback methods from the framework.
+     * <p>
+     * By default, calls from the framework will use Binder threads to call these methods.
+     *
+     * @return an {@link Executor} used to execute methods called remotely by the framework.
+     */
+    @SuppressLint("OnNameExpected")
+    public @NonNull Executor getCreateExecutor() {
+        return Runnable::run;
+    }
+
+    /**
+     * Gets the {@link Executor} which executes methods of this service.
+     * This method should be private when this service is implemented in a separated process
+     * other than telephony framework.
+     * @return {@link Executor} instance.
+     * @hide
+     */
+    public final @NonNull Executor getCachedExecutor() {
+        synchronized (mExecutorLock) {
+            if (mExecutor == null) {
+                Executor e = getCreateExecutor();
+                mExecutor = (e != null) ? e : Runnable::run;
+            }
+            return mExecutor;
+        }
+    }
+
+    /**
+     * Returns a string representation of the domain.
+     * @param domain The domain.
+     * @return The name of the domain.
+     * @hide
+     */
+    public static @NonNull String getDomainName(@NetworkRegistrationInfo.Domain int domain) {
+        return NetworkRegistrationInfo.domainToString(domain);
+    }
+}
diff --git a/android-35/android/telephony/DomainSelector.java b/android-35/android/telephony/DomainSelector.java
new file mode 100644
index 0000000..5d25d3a
--- /dev/null
+++ b/android-35/android/telephony/DomainSelector.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * Implemented as part of the {@link DomainSelectionService} to implement domain selection
+ * for a specific use case and receive signals from the framework to reselect a new domain
+ * when a previous domain selection fails or finish a selection when the call connects successfully.
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+public interface DomainSelector {
+    /**
+     * Reselect a domain due to the call not setting up properly.
+     *
+     * @param attr attributes required to select the domain.
+     */
+    void reselectDomain(@NonNull SelectionAttributes attr);
+
+    /**
+     * Finish the selection procedure and clean everything up.
+     */
+    void finishSelection();
+}
diff --git a/android-35/android/telephony/EmergencyRegistrationResult.java b/android-35/android/telephony/EmergencyRegistrationResult.java
new file mode 100644
index 0000000..7041f5b
--- /dev/null
+++ b/android-35/android/telephony/EmergencyRegistrationResult.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * Contains attributes required to determine the domain for a telephony service, including
+ * the network registration state.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+public final class EmergencyRegistrationResult implements Parcelable {
+
+    /**
+     * Indicates the cellular network type of the acquired system.
+     */
+    private @AccessNetworkConstants.RadioAccessNetworkType int mAccessNetworkType;
+
+    /**
+     * Registration state of the acquired system.
+     */
+    private @NetworkRegistrationInfo.RegistrationState int mRegState;
+
+    /**
+     * EMC domain indicates the current domain of the acquired system.
+     */
+    private @NetworkRegistrationInfo.Domain int mDomain;
+
+    /**
+     * Indicates whether the network supports voice over PS network.
+     */
+    private boolean mIsVopsSupported;
+
+    /**
+     * This indicates if camped network support VoLTE emergency bearers.
+     * This should only be set if the UE is in LTE mode.
+     */
+    private boolean mIsEmcBearerSupported;
+
+    /**
+     * The value of the network provided EMC in 5G Registration ACCEPT.
+     * This should be set only if the UE is in 5G mode.
+     */
+    private int mNwProvidedEmc;
+
+    /**
+     * The value of the network provided EMF(EPS Fallback) in 5G Registration ACCEPT.
+     * This should be set only if the UE is in 5G mode.
+     */
+    private int mNwProvidedEmf;
+
+    /** 3-digit Mobile Country Code, 000..999, empty string if unknown. */
+    private @NonNull String mMcc;
+
+    /** 2 or 3-digit Mobile Network Code, 00..999, empty string if unknown. */
+    private @NonNull String mMnc;
+
+    /**
+     * The ISO-3166-1 alpha-2 country code equivalent for the network's country code,
+     * empty string if unknown.
+     */
+    private @NonNull String mCountryIso;
+
+    /**
+     * Constructor
+     * @param accessNetwork Indicates the network type of the acquired system.
+     * @param regState Indicates the registration state of the acquired system.
+     * @param domain Indicates the current domain of the acquired system.
+     * @param isVopsSupported Indicates whether the network supports voice over PS network.
+     * @param isEmcBearerSupported  Indicates if camped network support VoLTE emergency bearers.
+     * @param emc The value of the network provided EMC in 5G Registration ACCEPT.
+     * @param emf The value of the network provided EMF(EPS Fallback) in 5G Registration ACCEPT.
+     * @param mcc Mobile country code, empty string if unknown.
+     * @param mnc Mobile network code, empty string if unknown.
+     * @param iso The ISO-3166-1 alpha-2 country code equivalent, empty string if unknown.
+     * @hide
+     */
+    public EmergencyRegistrationResult(
+            @AccessNetworkConstants.RadioAccessNetworkType int accessNetwork,
+            @NetworkRegistrationInfo.RegistrationState int regState,
+            @NetworkRegistrationInfo.Domain int domain,
+            boolean isVopsSupported, boolean isEmcBearerSupported, int emc, int emf,
+            @NonNull String mcc, @NonNull String mnc, @NonNull String iso) {
+        mAccessNetworkType = accessNetwork;
+        mRegState = regState;
+        mDomain = domain;
+        mIsVopsSupported = isVopsSupported;
+        mIsEmcBearerSupported = isEmcBearerSupported;
+        mNwProvidedEmc = emc;
+        mNwProvidedEmf = emf;
+        mMcc = mcc;
+        mMnc = mnc;
+        mCountryIso = iso;
+    }
+
+    /**
+     * Copy constructors
+     *
+     * @param s Source emergency scan result
+     * @hide
+     */
+    public EmergencyRegistrationResult(@NonNull EmergencyRegistrationResult s) {
+        mAccessNetworkType = s.mAccessNetworkType;
+        mRegState = s.mRegState;
+        mDomain = s.mDomain;
+        mIsVopsSupported = s.mIsVopsSupported;
+        mIsEmcBearerSupported = s.mIsEmcBearerSupported;
+        mNwProvidedEmc = s.mNwProvidedEmc;
+        mNwProvidedEmf = s.mNwProvidedEmf;
+        mMcc = s.mMcc;
+        mMnc = s.mMnc;
+        mCountryIso = s.mCountryIso;
+    }
+
+    /**
+     * Construct a EmergencyRegistrationResult object from the given parcel.
+     */
+    private EmergencyRegistrationResult(@NonNull Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * Returns the cellular access network type of the acquired system.
+     *
+     * @return the cellular network type.
+     */
+    public @AccessNetworkConstants.RadioAccessNetworkType int getAccessNetwork() {
+        return mAccessNetworkType;
+    }
+
+    /**
+     * Returns the registration state of the acquired system.
+     *
+     * @return the registration state.
+     */
+    public @NetworkRegistrationInfo.RegistrationState int getRegState() {
+        return mRegState;
+    }
+
+    /**
+     * Returns the current domain of the acquired system.
+     *
+     * @return the current domain.
+     */
+    public @NetworkRegistrationInfo.Domain int getDomain() {
+        return mDomain;
+    }
+
+    /**
+     * Returns whether the network supports voice over PS network.
+     *
+     * @return {@code true} if the network supports voice over PS network.
+     */
+    public boolean isVopsSupported() {
+        return mIsVopsSupported;
+    }
+
+    /**
+     * Returns whether camped network support VoLTE emergency bearers.
+     * This is not valid if the UE is not in LTE mode.
+     *
+     * @return {@code true} if the network supports VoLTE emergency bearers.
+     */
+    public boolean isEmcBearerSupported() {
+        return mIsEmcBearerSupported;
+    }
+
+    /**
+     * Returns the value of the network provided EMC in 5G Registration ACCEPT.
+     * This is not valid if UE is not in 5G mode.
+     *
+     * @return the value of the network provided EMC.
+     */
+    public int getNwProvidedEmc() {
+        return mNwProvidedEmc;
+    }
+
+    /**
+     * Returns the value of the network provided EMF(EPS Fallback) in 5G Registration ACCEPT.
+     * This is not valid if UE is not in 5G mode.
+     *
+     * @return the value of the network provided EMF.
+     */
+    public int getNwProvidedEmf() {
+        return mNwProvidedEmf;
+    }
+
+    /**
+     * Returns 3-digit Mobile Country Code.
+     *
+     * @return Mobile Country Code.
+     */
+    public @NonNull String getMcc() {
+        return mMcc;
+    }
+
+    /**
+     * Returns 2 or 3-digit Mobile Network Code.
+     *
+     * @return Mobile Network Code.
+     */
+    public @NonNull String getMnc() {
+        return mMnc;
+    }
+
+    /**
+     * Returns the ISO-3166-1 alpha-2 country code is provided in lowercase 2 character format.
+     *
+     * @return Country code.
+     */
+    public @NonNull String getCountryIso() {
+        return mCountryIso;
+    }
+
+    @Override
+    public @NonNull String toString() {
+        return "{ accessNetwork="
+                + AccessNetworkConstants.AccessNetworkType.toString(mAccessNetworkType)
+                + ", regState=" + NetworkRegistrationInfo.registrationStateToString(mRegState)
+                + ", domain=" + NetworkRegistrationInfo.domainToString(mDomain)
+                + ", vops=" + mIsVopsSupported
+                + ", emcBearer=" + mIsEmcBearerSupported
+                + ", emc=" + mNwProvidedEmc
+                + ", emf=" + mNwProvidedEmf
+                + ", mcc=" + mMcc
+                + ", mnc=" + mMnc
+                + ", iso=" + mCountryIso
+                + " }";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        EmergencyRegistrationResult that = (EmergencyRegistrationResult) o;
+        return mAccessNetworkType == that.mAccessNetworkType
+                && mRegState == that.mRegState
+                && mDomain == that.mDomain
+                && mIsVopsSupported == that.mIsVopsSupported
+                && mIsEmcBearerSupported == that.mIsEmcBearerSupported
+                && mNwProvidedEmc == that.mNwProvidedEmc
+                && mNwProvidedEmf == that.mNwProvidedEmf
+                && TextUtils.equals(mMcc, that.mMcc)
+                && TextUtils.equals(mMnc, that.mMnc)
+                && TextUtils.equals(mCountryIso, that.mCountryIso);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAccessNetworkType, mRegState, mDomain,
+                mIsVopsSupported, mIsEmcBearerSupported,
+                mNwProvidedEmc, mNwProvidedEmf,
+                mMcc, mMnc, mCountryIso);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mAccessNetworkType);
+        out.writeInt(mRegState);
+        out.writeInt(mDomain);
+        out.writeBoolean(mIsVopsSupported);
+        out.writeBoolean(mIsEmcBearerSupported);
+        out.writeInt(mNwProvidedEmc);
+        out.writeInt(mNwProvidedEmf);
+        out.writeString8(mMcc);
+        out.writeString8(mMnc);
+        out.writeString8(mCountryIso);
+    }
+
+    private void readFromParcel(@NonNull Parcel in) {
+        mAccessNetworkType = in.readInt();
+        mRegState = in.readInt();
+        mDomain = in.readInt();
+        mIsVopsSupported = in.readBoolean();
+        mIsEmcBearerSupported = in.readBoolean();
+        mNwProvidedEmc = in.readInt();
+        mNwProvidedEmf = in.readInt();
+        mMcc = in.readString8();
+        mMnc = in.readString8();
+        mCountryIso = in.readString8();
+    }
+
+    public static final @NonNull Creator<EmergencyRegistrationResult> CREATOR =
+            new Creator<EmergencyRegistrationResult>() {
+                @Override
+                public EmergencyRegistrationResult createFromParcel(@NonNull Parcel in) {
+                    return new EmergencyRegistrationResult(in);
+                }
+
+                @Override
+                public EmergencyRegistrationResult[] newArray(int size) {
+                    return new EmergencyRegistrationResult[size];
+                }
+            };
+}
diff --git a/android-35/android/telephony/IccOpenLogicalChannelResponse.java b/android-35/android/telephony/IccOpenLogicalChannelResponse.java
new file mode 100644
index 0000000..44ba70c
--- /dev/null
+++ b/android-35/android/telephony/IccOpenLogicalChannelResponse.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+/**
+ * Response to the {@link TelephonyManager#iccOpenLogicalChannel} command.
+ */
+public class IccOpenLogicalChannelResponse implements Parcelable {
+    /**
+     * Indicates an invalid channel.
+     */
+    public static final int INVALID_CHANNEL = -1;
+
+    /**
+     * Possible status values returned by open channel command.
+     *
+     * STATUS_NO_ERROR: Open channel command returned successfully.
+     * STATUS_MISSING_RESOURCE: No logical channels available.
+     * STATUS_NO_SUCH_ELEMENT: AID not found on UICC.
+     * STATUS_UNKNOWN_ERROR: Unknown error in open channel command.
+     */
+    public static final int STATUS_NO_ERROR = 1;
+    public static final int STATUS_MISSING_RESOURCE = 2;
+    public static final int STATUS_NO_SUCH_ELEMENT = 3;
+    public static final int STATUS_UNKNOWN_ERROR = 4;
+
+    private final int mChannel;
+    private final int mStatus;
+    private final byte[] mSelectResponse;
+
+    /**
+     * Constructor.
+     *
+     * @hide
+     */
+    public IccOpenLogicalChannelResponse(int channel, int status, byte[] selectResponse) {
+        mChannel = channel;
+        mStatus = status;
+        mSelectResponse = selectResponse;
+    }
+
+    /**
+     * Construct a IccOpenLogicalChannelResponse from a given parcel.
+     */
+    private IccOpenLogicalChannelResponse(Parcel in) {
+        mChannel = in.readInt();
+        mStatus = in.readInt();
+        int arrayLength = in.readInt();
+        if (arrayLength > 0) {
+            mSelectResponse = new byte[arrayLength];
+            in.readByteArray(mSelectResponse);
+        } else {
+            mSelectResponse = null;
+        }
+    }
+
+    /**
+     * @return the channel id.
+     */
+    public int getChannel() {
+        return mChannel;
+    }
+
+    /**
+     * @return the status of the command.
+     */
+    public int getStatus() {
+        return mStatus;
+    }
+
+    /**
+     * @return the select response.
+     */
+    public byte[] getSelectResponse() {
+        return mSelectResponse;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mChannel);
+        out.writeInt(mStatus);
+        if (mSelectResponse != null && mSelectResponse.length > 0) {
+            out.writeInt(mSelectResponse.length);
+            out.writeByteArray(mSelectResponse);
+        } else {
+            out.writeInt(0);
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<IccOpenLogicalChannelResponse> CREATOR
+             = new Parcelable.Creator<IccOpenLogicalChannelResponse>() {
+
+        @Override
+        public IccOpenLogicalChannelResponse createFromParcel(Parcel in) {
+             return new IccOpenLogicalChannelResponse(in);
+         }
+
+         public IccOpenLogicalChannelResponse[] newArray(int size) {
+             return new IccOpenLogicalChannelResponse[size];
+         }
+     };
+
+    @Override
+    public String toString() {
+        return "Channel: " + mChannel + " Status: " + mStatus;
+    }
+}
diff --git a/android-35/android/telephony/ImsiEncryptionInfo.java b/android-35/android/telephony/ImsiEncryptionInfo.java
new file mode 100644
index 0000000..82333a4
--- /dev/null
+++ b/android-35/android/telephony/ImsiEncryptionInfo.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Date;
+
+/**
+ * Class to represent information sent by the carrier, which will be used to encrypt
+ * the IMSI + IMPI. The ecryption is being done by WLAN, and the modem.
+ * @hide
+ */
+@SystemApi
+public final class ImsiEncryptionInfo implements Parcelable {
+
+    private static final String LOG_TAG = "ImsiEncryptionInfo";
+
+    private final String mcc;
+    private final String mnc;
+    private final PublicKey publicKey;
+    private final String keyIdentifier;
+    private final int keyType;
+    //Date-Time in UTC when the key will expire.
+    private final Date expirationTime;
+    private final int carrierId;
+
+    /** @hide */
+    public ImsiEncryptionInfo(String mcc, String mnc, int keyType, String keyIdentifier,
+            byte[] key, Date expirationTime, int carrierId) {
+        this(mcc, mnc, keyType, keyIdentifier, makeKeyObject(key), expirationTime, carrierId);
+    }
+
+    /** @hide */
+    public ImsiEncryptionInfo(String mcc, String mnc, int keyType, String keyIdentifier,
+            PublicKey publicKey, Date expirationTime, int carrierId) {
+        // todo need to validate that ImsiEncryptionInfo is being created with the correct params.
+        //      Including validating that the public key is in "X.509" format. This will be done in
+        //      a subsequent CL.
+        this.mcc = mcc;
+        this.mnc = mnc;
+        this.keyType = keyType;
+        this.publicKey = publicKey;
+        this.keyIdentifier = keyIdentifier;
+        this.expirationTime = expirationTime;
+        this.carrierId = carrierId;
+    }
+
+    /** @hide */
+    public ImsiEncryptionInfo(Parcel in) {
+        int length = in.readInt();
+        byte b[] = new byte[length];
+        in.readByteArray(b);
+        publicKey = makeKeyObject(b);
+        mcc = in.readString();
+        mnc = in.readString();
+        keyIdentifier = in.readString();
+        keyType = in.readInt();
+        expirationTime = new Date(in.readLong());
+        carrierId = in.readInt();
+    }
+
+    /** @hide */
+    public String getMnc() {
+        return this.mnc;
+    }
+
+    /** @hide */
+    public String getMcc() {
+        return this.mcc;
+    }
+
+    /** @hide */
+    public int getCarrierId() {
+        return carrierId;
+    }
+
+    /**
+     * Returns key identifier, a string that helps the authentication server to locate the
+     * private key to decrypt the permanent identity, or {@code null} when uavailable.
+     */
+    @Nullable
+    public String getKeyIdentifier() {
+        return this.keyIdentifier;
+    }
+
+    /** @hide */
+    public int getKeyType() {
+        return this.keyType;
+    }
+
+    /**
+     * Returns the carrier public key that is used for the IMSI encryption,
+     * or {@code null} when uavailable.
+     */
+    @Nullable
+    public PublicKey getPublicKey() {
+        return this.publicKey;
+    }
+
+    /** @hide */
+    public Date getExpirationTime() {
+        return this.expirationTime;
+    }
+
+    private static PublicKey makeKeyObject(byte[] publicKeyBytes) {
+        try {
+            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(publicKeyBytes);
+            return KeyFactory.getInstance("RSA").generatePublic(pubKeySpec);
+        } catch (InvalidKeySpecException | NoSuchAlgorithmException ex) {
+            Log.e(LOG_TAG, "Error makeKeyObject: unable to convert into PublicKey", ex);
+        }
+        throw new IllegalArgumentException();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Parcelable.Creator<ImsiEncryptionInfo> CREATOR =
+            new Parcelable.Creator<ImsiEncryptionInfo>() {
+                @Override
+                public ImsiEncryptionInfo createFromParcel(Parcel in) {
+                    return new ImsiEncryptionInfo(in);
+                }
+
+                @Override
+                public ImsiEncryptionInfo[] newArray(int size) {
+                    return new ImsiEncryptionInfo[size];
+                }
+            };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        byte[] b = publicKey.getEncoded();
+        dest.writeInt(b.length);
+        dest.writeByteArray(b);
+        dest.writeString(mcc);
+        dest.writeString(mnc);
+        dest.writeString(keyIdentifier);
+        dest.writeInt(keyType);
+        dest.writeLong(expirationTime.getTime());
+        dest.writeInt(carrierId);
+    }
+
+    @Override
+    public String toString(){
+        return "[ImsiEncryptionInfo "
+                + "mcc=" + mcc
+                + " mnc=" + mnc
+                + ", publicKey=" + publicKey
+                + ", keyIdentifier=" + keyIdentifier
+                + ", keyType=" + keyType
+                + ", expirationTime=" + expirationTime
+                + ", carrier_id=" + carrierId
+                + "]";
+    }
+}
diff --git a/android-35/android/telephony/JapanesePhoneNumberFormatter.java b/android-35/android/telephony/JapanesePhoneNumberFormatter.java
new file mode 100644
index 0000000..1c31368
--- /dev/null
+++ b/android-35/android/telephony/JapanesePhoneNumberFormatter.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2008 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;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.text.Editable;
+
+/*
+ * Japanese Phone number formatting rule is a bit complicated.
+ * Here are some valid examples:
+ *
+ * 022-229-1234 0223-23-1234 022-301-9876 015-482-7849 0154-91-3478
+ * 01547-5-4534 090-1234-1234 080-0123-6789
+ * 050-0000-0000 060-0000-0000
+ * 0800-000-9999 0570-000-000 0276-00-0000
+ *
+ * As you can see, there is no straight-forward rule here.
+ * In order to handle this, a big array is prepared.
+ */
+/* package */ class JapanesePhoneNumberFormatter {
+    private static short FORMAT_MAP[] = {
+    -100, 10, 220, -15, 410, 530, 1200, 670, 780, 1060,
+    -100, -25, 20, 40, 70, 100, 150, 190, 200, 210,
+    -36, -100, -100, -35, -35, -35, 30, -100, -100, -100,
+    -35, -35, -35, -35, -35, -35, -35, -45, -35, -35,
+    -100, -100, -100, -35, -35, -35, -35, 50, -35, 60,
+    -35, -35, -45, -35, -45, -35, -35, -45, -35, -35,
+    -35, -35, -45, -35, -35, -35, -35, -45, -45, -35,
+    -100, -100, -35, -35, -35, 80, 90, -100, -100, -100,
+    -35, -35, -35, -35, -35, -35, -45, -45, -35, -35,
+    -35, -35, -35, -35, -35, -35, -45, -35, -35, -35,
+    -25, -25, -35, -35, 110, 120, 130, -35, 140, -25,
+    -35, -25, -35, -35, -35, -35, -35, -45, -25, -35,
+    -35, -25, -35, -35, -35, -35, -35, -25, -45, -35,
+    -35, -35, -35, -35, -45, -35, -35, -35, -35, -35,
+    -35, -35, -35, -35, -35, -35, -45, -45, -35, -35,
+    -100, -100, -35, 160, 170, 180, -35, -35, -100, -100,
+    -35, -35, -45, -35, -45, -45, -35, -35, -35, -35,
+    -35, -35, -35, -35, -35, -35, -35, -35, -45, -35,
+    -35, -35, -35, -35, -45, -45, -45, -35, -45, -35,
+    -25, -25, -35, -35, -35, -35, -35, -25, -35, -35,
+    -25, -25, -35, -35, -35, -35, -35, -35, -25, -25,
+    -25, -35, -35, -35, -35, -35, -25, -35, -35, -25,
+    -100, -100, 230, 250, 260, 270, 320, 340, 360, 390,
+    -35, -25, -25, 240, -35, -35, -35, -25, -35, -35,
+    -25, -35, -35, -35, -25, -25, -25, -25, -25, -25,
+    -25, -25, -25, -35, -35, -35, -25, -35, -35, -25,
+    -35, -35, -35, -35, -35, -25, -35, -35, -35, -25,
+    -35, -25, -25, -25, -35, 280, 290, 300, 310, -35,
+    -25, -25, -25, -25, -25, -25, -25, -35, -35, -25,
+    -25, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+    -25, -25, -35, -35, -35, -25, -25, -25, -25, -25,
+    -25, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+    -35, -35, -25, -35, 330, -35, -35, -35, -35, -35,
+    -25, -35, -35, -35, -35, -35, -25, -25, -25, -25,
+    -35, -25, -25, -25, -35, -25, -35, -35, 350, -35,
+    -25, -35, -35, -35, -35, -35, -35, -35, -25, -25,
+    -35, -25, -35, 370, -35, -35, -25, -35, -35, 380,
+    -25, -35, -35, -25, -25, -35, -35, -35, -35, -35,
+    -25, -35, -25, -25, -25, -25, -35, -35, -35, -35,
+    -25, -35, -25, 400, -35, -35, -35, -35, -25, -35,
+    -25, -35, -35, -35, -35, -25, -25, -25, -25, -25,
+    -15, -15, 420, 460, -25, -25, 470, 480, 500, 510,
+    -15, -25, 430, -25, -25, -25, -25, -25, 440, 450,
+    -25, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+    -25, -25, -35, -35, -25, -25, -25, -35, -35, -35,
+    -15, -25, -15, -15, -15, -15, -15, -25, -25, -15,
+    -25, -25, -25, -25, -25, -25, -35, -25, -35, -35,
+    -35, -25, -25, -35, -25, -35, -35, -35, -25, -25,
+    490, -15, -25, -25, -25, -35, -35, -25, -35, -35,
+    -15, -35, -35, -35, -35, -35, -35, -35, -35, -15,
+    -35, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+    -25, -25, -25, -35, -35, -35, -25, -25, -25, 520,
+    -100, -100, -45, -100, -45, -100, -45, -100, -45, -100,
+    -26, -100, -25, 540, 580, 590, 600, 610, 630, 640,
+    -25, -35, -35, -35, -25, -25, -35, -35, -35, 550,
+    -35, -35, -25, -25, -25, -25, 560, 570, -25, -35,
+    -35, -35, -35, -35, -25, -25, -25, -25, -25, -25,
+    -25, -25, -25, -25, -35, -25, -25, -35, -25, -25,
+    -25, -25, -25, -25, -35, -35, -25, -35, -35, -25,
+    -35, -35, -25, -35, -35, -35, -35, -35, -35, -25,
+    -100, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+    -36, -100, -35, -35, -35, -35, 620, -35, -35, -100,
+    -35, -35, -35, -35, -35, -35, -35, -35, -35, -45,
+    -25, -35, -25, -25, -35, -35, -35, -35, -25, -25,
+    -25, -25, -25, -25, -35, -35, -35, 650, -35, 660,
+    -35, -35, -35, -35, -45, -35, -35, -35, -35, -45,
+    -35, -35, -35, -35, -35, -35, -35, -35, -35, -25,
+    -26, -100, 680, 690, 700, -25, 720, 730, -25, 740,
+    -25, -35, -25, -25, -25, -35, -25, -25, -25, -25,
+    -25, -25, -25, -25, -25, -35, -35, -35, -35, -35,
+    -35, -100, -35, -35, -35, -35, 710, -35, -35, -35,
+    -35, -35, -35, -35, -35, -35, -35, -35, -45, -35,
+    -25, -35, -25, -35, -25, -35, -35, -35, -35, -25,
+    -35, -35, -35, -35, -35, -25, -35, -25, -35, -35,
+    -35, -35, -25, -25, 750, 760, 770, -35, -35, -35,
+    -25, -35, -25, -25, -25, -25, -35, -35, -35, -25,
+    -25, -35, -35, -35, -35, -25, -25, -35, -35, -25,
+    -25, -35, -35, -35, -35, -35, -25, -25, -35, -35,
+    790, -100, 800, 850, 900, 920, 940, 1030, 1040, 1050,
+    -36, -26, -26, -26, -26, -26, -26, -26, -26, -26,
+    -35, -25, -25, -35, 810, -25, -35, -35, -25, 820,
+    -25, -35, -25, -25, -35, -35, -35, -35, -35, -25,
+    -25, -35, 830, -35, 840, -35, -25, -35, -35, -25,
+    -35, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+    -100, -25, -25, -25, -100, -100, -100, -100, -100, -100,
+    -25, -25, -35, -35, -35, -35, 860, -35, 870, 880,
+    -25, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+    -35, -35, -35, -35, -35, -35, -35, -45, -45, -35,
+    -100, -100, -100, -100, -100, -100, 890, -100, -100, -100,
+    -25, -45, -45, -25, -45, -45, -25, -45, -45, -45,
+    -25, -25, -25, -25, -25, -35, -35, 910, -35, -25,
+    -35, -35, -35, -35, -35, -35, -35, -45, -35, -35,
+    -100, 930, -35, -35, -35, -35, -35, -35, -35, -35,
+    -100, -100, -45, -100, -45, -100, -100, -100, -100, -100,
+    -25, -25, -25, 950, -25, 970, 990, -35, 1000, 1010,
+    -35, -35, -35, -35, -35, -35, 960, -35, -35, -35,
+    -45, -45, -45, -45, -45, -45, -35, -45, -45, -45,
+    -35, -35, -25, -35, -35, 980, -35, -35, -35, -35,
+    -100, -100, -25, -25, -100, -100, -100, -100, -100, -100,
+    -25, -35, -35, -35, -35, -35, -35, -35, -35, -35,
+    -25, -35, -35, -35, -35, -35, -35, -35, -35, -25,
+    -25, -35, -35, -35, -25, -25, -35, -35, -35, 1020,
+    -45, -45, -35, -35, -45, -45, -45, -45, -45, -45,
+    -25, -25, -25, -25, -25, -35, -25, -35, -25, -35,
+    -35, -25, -25, -35, -35, -35, -25, -35, -25, -35,
+    -25, -25, -35, -35, -35, -35, -35, -35, -35, -25,
+    -26, -100, 1070, 1080, 1090, 1110, 1120, 1130, 1140, 1160,
+    -35, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+    -35, -25, -25, -25, -25, -25, -25, -25, -25, -25,
+    -35, -100, -35, -35, -35, -100, -35, -35, -35, 1100,
+    -35, -35, -35, -35, -35, -35, -45, -35, -35, -35,
+    -35, -25, -35, -25, -35, -35, -35, -35, -25, -35,
+    -25, -25, -25, -25, -35, -35, -35, -35, -35, -35,
+    -25, -25, -35, -35, -35, -25, -25, -35, -35, -35,
+    1150, -25, -35, -35, -35, -35, -35, -35, -25, -25,
+    -35, -35, -45, -35, -35, -35, -35, -35, -35, -35,
+    -35, 1170, -25, -35, 1180, -35, 1190, -35, -25, -25,
+    -100, -100, -45, -45, -100, -100, -100, -100, -100, -100,
+    -25, -35, -35, -35, -35, -35, -35, -25, -25, -35,
+    -35, -35, -35, -35, -35, -35, -35, -35, -35, -45,
+    -26, -15, -15, -15, -15, -15, -15, -15, -15, -15};
+
+    @UnsupportedAppUsage
+    public static void format(Editable text) {
+        // Here, "root" means the position of "'":
+        // 0'3, 0'90, and +81'-90
+        // (dash will be deleted soon, so it is actually +81'90).
+        int rootIndex = 1;
+        int length = text.length();
+        if (length > 3
+                && text.subSequence(0, 3).toString().equals("+81")) {
+            rootIndex = 3;
+        } else if (length < 1 || text.charAt(0) != '0') {
+            return;
+        }
+
+        CharSequence saved = text.subSequence(0, length);
+
+        // Strip the dashes first, as we're going to add them back
+        int i = 0;
+        while (i < text.length()) {
+            if (text.charAt(i) == '-') {
+                text.delete(i, i + 1);
+            } else {
+                i++;
+            }
+        }
+
+        length = text.length();
+        int dashposition;
+
+        i = rootIndex;
+        int base = 0;
+        while (i < length) {
+            char ch = text.charAt(i);
+            if (!Character.isDigit(ch)) {
+                text.replace(0, length, saved);
+                return;
+            }
+            short value = FORMAT_MAP[base + ch - '0'];
+            if (value < 0) {
+                if (value <= -100) {
+                    text.replace(0, length, saved);
+                    return;
+                }
+                int dashPos2 = rootIndex + (Math.abs(value) % 10);
+                if (length > dashPos2) {
+                    text.insert(dashPos2, "-");
+                }
+                int dashPos1 = rootIndex + (Math.abs(value) / 10);
+                if (length > dashPos1) {
+                    text.insert(dashPos1, "-");
+                }
+                break;
+            } else {
+                base = value;
+                i++;
+            }
+        }
+
+        if (length > 3 && rootIndex == 3) {
+            text.insert(rootIndex, "-");
+        }
+    }
+}
\ No newline at end of file
diff --git a/android-35/android/telephony/LinkCapacityEstimate.java b/android-35/android/telephony/LinkCapacityEstimate.java
new file mode 100644
index 0000000..deeb809
--- /dev/null
+++ b/android-35/android/telephony/LinkCapacityEstimate.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2021 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Link Capacity Estimate from the modem
+ * @hide
+ */
+@SystemApi
+public final class LinkCapacityEstimate implements Parcelable {
+    /** A value indicates that the capacity estimate is not available */
+    public static final int INVALID = -1;
+
+    /**
+     * LCE for the primary network
+     */
+    public static final int LCE_TYPE_PRIMARY = 0;
+
+    /**
+     * LCE for the secondary network
+     */
+    public static final int LCE_TYPE_SECONDARY = 1;
+
+    /**
+     * Combined LCE for primary network and secondary network reported by the legacy modem
+     */
+    public static final int LCE_TYPE_COMBINED = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "LCE_TYPE_" }, value = {
+            LCE_TYPE_PRIMARY,
+            LCE_TYPE_SECONDARY,
+            LCE_TYPE_COMBINED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LceType {}
+
+    private final @LceType int mType;
+
+    /** Downlink capacity estimate in kbps */
+    private final int mDownlinkCapacityKbps;
+
+    /** Uplink capacity estimate in kbps */
+    private final int mUplinkCapacityKbps;
+
+    /**
+     * Constructor for link capacity estimate
+     */
+    public LinkCapacityEstimate(@LceType int type,
+            int downlinkCapacityKbps, int uplinkCapacityKbps) {
+        mDownlinkCapacityKbps = downlinkCapacityKbps;
+        mUplinkCapacityKbps = uplinkCapacityKbps;
+        mType = type;
+    }
+
+    /**
+     * @hide
+     */
+    public LinkCapacityEstimate(Parcel in) {
+        mDownlinkCapacityKbps = in.readInt();
+        mUplinkCapacityKbps = in.readInt();
+        mType = in.readInt();
+    }
+
+    /**
+     * Retrieves the type of LCE
+     * @return The type of link capacity estimate
+     */
+    public @LceType int getType() {
+        return mType;
+    }
+
+    /**
+     * Retrieves the downlink bandwidth in Kbps.
+     * This will be {@link #INVALID} if the network is not connected
+     * @return The estimated first hop downstream (network to device) bandwidth.
+     */
+    public int getDownlinkCapacityKbps() {
+        return mDownlinkCapacityKbps;
+    }
+
+    /**
+     * Retrieves the uplink bandwidth in Kbps.
+     * This will be {@link #INVALID} if the network is not connected
+     *
+     * @return The estimated first hop upstream (device to network) bandwidth.
+     */
+    public int getUplinkCapacityKbps() {
+        return mUplinkCapacityKbps;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+                .append("{mType=")
+                .append(mType)
+                .append(", mDownlinkCapacityKbps=")
+                .append(mDownlinkCapacityKbps)
+                .append(", mUplinkCapacityKbps=")
+                .append(mUplinkCapacityKbps)
+                .append("}")
+                .toString();
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     * @hide
+     */
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mDownlinkCapacityKbps);
+        dest.writeInt(mUplinkCapacityKbps);
+        dest.writeInt(mType);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o == null || !(o instanceof LinkCapacityEstimate) || hashCode() != o.hashCode())  {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        LinkCapacityEstimate that = (LinkCapacityEstimate) o;
+        return mDownlinkCapacityKbps == that.mDownlinkCapacityKbps
+                && mUplinkCapacityKbps == that.mUplinkCapacityKbps
+                && mType == that.mType;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDownlinkCapacityKbps, mUplinkCapacityKbps, mType);
+    }
+
+    public static final
+            @android.annotation.NonNull Parcelable.Creator<LinkCapacityEstimate> CREATOR =
+            new Parcelable.Creator() {
+        public LinkCapacityEstimate createFromParcel(Parcel in) {
+            return new LinkCapacityEstimate(in);
+        }
+
+        public LinkCapacityEstimate[] newArray(int size) {
+            return new LinkCapacityEstimate[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/LocationAccessPolicy.java b/android-35/android/telephony/LocationAccessPolicy.java
new file mode 100644
index 0000000..d4b6c91
--- /dev/null
+++ b/android-35/android/telephony/LocationAccessPolicy.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.internal.telephony.util.TelephonyUtils;
+
+/**
+ * Helper for performing location access checks.
+ * @hide
+ */
+public final class LocationAccessPolicy {
+    private static final String TAG = "LocationAccessPolicy";
+    private static final boolean DBG = false;
+    public static final int MAX_SDK_FOR_ANY_ENFORCEMENT = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+    public enum LocationPermissionResult {
+        ALLOWED,
+        /**
+         * Indicates that the denial is due to a transient device state
+         * (e.g. app-ops, location main switch)
+         */
+        DENIED_SOFT,
+        /**
+         * Indicates that the denial is due to a misconfigured app (e.g. missing entry in manifest)
+         */
+        DENIED_HARD,
+    }
+
+    /** Data structure for location permission query */
+    public static class LocationPermissionQuery {
+        public final String callingPackage;
+        public final String callingFeatureId;
+        public final int callingUid;
+        public final int callingPid;
+        public final int minSdkVersionForCoarse;
+        public final int minSdkVersionForFine;
+        public final boolean logAsInfo;
+        public final String method;
+
+        private LocationPermissionQuery(String callingPackage, @Nullable String callingFeatureId,
+                int callingUid, int callingPid, int minSdkVersionForCoarse,
+                int minSdkVersionForFine, boolean logAsInfo, String method) {
+            this.callingPackage = callingPackage;
+            this.callingFeatureId = callingFeatureId;
+            this.callingUid = callingUid;
+            this.callingPid = callingPid;
+            this.minSdkVersionForCoarse = minSdkVersionForCoarse;
+            this.minSdkVersionForFine = minSdkVersionForFine;
+            this.logAsInfo = logAsInfo;
+            this.method = method;
+        }
+
+        /** Builder for LocationPermissionQuery */
+        public static class Builder {
+            private String mCallingPackage;
+            private String mCallingFeatureId;
+            private int mCallingUid;
+            private int mCallingPid;
+            private int mMinSdkVersionForCoarse = -1;
+            private int mMinSdkVersionForFine = -1;
+            private int mMinSdkVersionForEnforcement = -1;
+            private boolean mLogAsInfo = false;
+            private String mMethod;
+
+            /**
+             * Mandatory parameter, used for performing permission checks.
+             */
+            public Builder setCallingPackage(String callingPackage) {
+                mCallingPackage = callingPackage;
+                return this;
+            }
+
+            /**
+             * Mandatory parameter, used for performing permission checks.
+             */
+            public Builder setCallingFeatureId(@Nullable String callingFeatureId) {
+                mCallingFeatureId = callingFeatureId;
+                return this;
+            }
+
+            /**
+             * Mandatory parameter, used for performing permission checks.
+             */
+            public Builder setCallingUid(int callingUid) {
+                mCallingUid = callingUid;
+                return this;
+            }
+
+            /**
+             * Mandatory parameter, used for performing permission checks.
+             */
+            public Builder setCallingPid(int callingPid) {
+                mCallingPid = callingPid;
+                return this;
+            }
+
+            /**
+             * Apps that target at least this sdk version will be checked for coarse location
+             * permission. This method MUST be called before calling {@link #build()}. Otherwise, an
+             * {@link IllegalArgumentException} will be thrown.
+             *
+             * Additionally, if both the argument to this method and
+             * {@link #setMinSdkVersionForFine} are greater than {@link Build.VERSION_CODES#BASE},
+             * you must call {@link #setMinSdkVersionForEnforcement} with the min of the two to
+             * affirm that you do not want any location checks below a certain SDK version.
+             * Otherwise, {@link #build} will throw an {@link IllegalArgumentException}.
+             */
+            public Builder setMinSdkVersionForCoarse(
+                    int minSdkVersionForCoarse) {
+                mMinSdkVersionForCoarse = minSdkVersionForCoarse;
+                return this;
+            }
+
+            /**
+             * Apps that target at least this sdk version will be checked for fine location
+             * permission.  This method MUST be called before calling {@link #build()}.
+             * Otherwise, an {@link IllegalArgumentException} will be thrown.
+             *
+             * Additionally, if both the argument to this method and
+             * {@link #setMinSdkVersionForCoarse} are greater than {@link Build.VERSION_CODES#BASE},
+             * you must call {@link #setMinSdkVersionForEnforcement} with the min of the two to
+             * affirm that you do not want any location checks below a certain SDK version.
+             * Otherwise, {@link #build} will throw an {@link IllegalArgumentException}.
+             */
+            public Builder setMinSdkVersionForFine(
+                    int minSdkVersionForFine) {
+                mMinSdkVersionForFine = minSdkVersionForFine;
+                return this;
+            }
+
+            /**
+             * If both the argument to {@link #setMinSdkVersionForFine} and
+             * {@link #setMinSdkVersionForCoarse} are greater than {@link Build.VERSION_CODES#BASE},
+             * this method must be called with the min of the two to
+             * affirm that you do not want any location checks below a certain SDK version.
+             */
+            public Builder setMinSdkVersionForEnforcement(int minSdkVersionForEnforcement) {
+                mMinSdkVersionForEnforcement = minSdkVersionForEnforcement;
+                return this;
+            }
+
+            /**
+             * Optional, for logging purposes only.
+             */
+            public Builder setMethod(String method) {
+                mMethod = method;
+                return this;
+            }
+
+            /**
+             * If called with {@code true}, log messages will only be printed at the info level.
+             */
+            public Builder setLogAsInfo(boolean logAsInfo) {
+                mLogAsInfo = logAsInfo;
+                return this;
+            }
+
+            /** build LocationPermissionQuery */
+            public LocationPermissionQuery build() {
+                if (mMinSdkVersionForCoarse < 0 || mMinSdkVersionForFine < 0) {
+                    throw new IllegalArgumentException("Must specify min sdk versions for"
+                            + " enforcement for both coarse and fine permissions");
+                }
+                if (mMinSdkVersionForFine > Build.VERSION_CODES.BASE
+                        && mMinSdkVersionForCoarse > Build.VERSION_CODES.BASE) {
+                    if (mMinSdkVersionForEnforcement != Math.min(
+                            mMinSdkVersionForCoarse, mMinSdkVersionForFine)) {
+                        throw new IllegalArgumentException("setMinSdkVersionForEnforcement must be"
+                                + " called.");
+                    }
+                }
+
+                if (mMinSdkVersionForFine < mMinSdkVersionForCoarse) {
+                    throw new IllegalArgumentException("Since fine location permission includes"
+                            + " access to coarse location, the min sdk level for enforcement of"
+                            + " the fine location permission must not be less than the min sdk"
+                            + " level for enforcement of the coarse location permission.");
+                }
+
+                return new LocationPermissionQuery(mCallingPackage, mCallingFeatureId,
+                        mCallingUid, mCallingPid, mMinSdkVersionForCoarse, mMinSdkVersionForFine,
+                        mLogAsInfo, mMethod);
+            }
+        }
+    }
+
+    private static void logError(Context context, LocationPermissionQuery query, String errorMsg) {
+        if (query.logAsInfo) {
+            Log.i(TAG, errorMsg);
+            return;
+        }
+        Log.e(TAG, errorMsg);
+        try {
+            if (TelephonyUtils.IS_DEBUGGABLE) {
+                Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show();
+            }
+        } catch (Throwable t) {
+            // whatever, not important
+        }
+    }
+
+    private static LocationPermissionResult appOpsModeToPermissionResult(int appOpsMode) {
+        switch (appOpsMode) {
+            case AppOpsManager.MODE_ALLOWED:
+                return LocationPermissionResult.ALLOWED;
+            case AppOpsManager.MODE_ERRORED:
+                return LocationPermissionResult.DENIED_HARD;
+            default:
+                return LocationPermissionResult.DENIED_SOFT;
+        }
+    }
+
+    private static String getAppOpsString(String manifestPermission) {
+        switch (manifestPermission) {
+            case Manifest.permission.ACCESS_FINE_LOCATION:
+                return AppOpsManager.OPSTR_FINE_LOCATION;
+            case Manifest.permission.ACCESS_COARSE_LOCATION:
+                return AppOpsManager.OPSTR_COARSE_LOCATION;
+            default:
+                return null;
+        }
+    }
+
+    private static LocationPermissionResult checkAppLocationPermissionHelper(Context context,
+            LocationPermissionQuery query, String permissionToCheck) {
+        String locationTypeForLog =
+                Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck)
+                        ? "fine" : "coarse";
+
+        // Do the app-ops and the manifest check without any of the allow-overrides first.
+        boolean hasManifestPermission = checkManifestPermission(context, query.callingPid,
+                query.callingUid, permissionToCheck);
+
+        if (hasManifestPermission) {
+            // Only check the app op if the app has the permission.
+            int appOpMode = context.getSystemService(AppOpsManager.class)
+                    .noteOpNoThrow(getAppOpsString(permissionToCheck), query.callingUid,
+                            query.callingPackage, query.callingFeatureId, null);
+            if (appOpMode == AppOpsManager.MODE_ALLOWED) {
+                // If the app did everything right, return without logging.
+                return LocationPermissionResult.ALLOWED;
+            } else {
+                // If the app has the manifest permission but not the app-op permission, it means
+                // that it's aware of the requirement and the user denied permission explicitly.
+                // If we see this, don't let any of the overrides happen.
+                Log.i(TAG, query.callingPackage + " is aware of " + locationTypeForLog + " but the"
+                        + " app-ops permission is specifically denied.");
+                return appOpsModeToPermissionResult(appOpMode);
+            }
+        }
+
+        int minSdkVersion = Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck)
+                ? query.minSdkVersionForFine : query.minSdkVersionForCoarse;
+
+        // If the app fails for some reason, see if it should be allowed to proceed.
+        if (minSdkVersion > MAX_SDK_FOR_ANY_ENFORCEMENT) {
+            String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
+                    + " because we're not enforcing API " + minSdkVersion + " yet."
+                    + " Please fix this app because it will break in the future. Called from "
+                    + query.method;
+            logError(context, query, errorMsg);
+            return null;
+        } else if (!isAppAtLeastSdkVersion(context, query.callingPackage, minSdkVersion)) {
+            String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
+                    + " because it doesn't target API " + minSdkVersion + " yet."
+                    + " Please fix this app. Called from " + query.method;
+            logError(context, query, errorMsg);
+            return null;
+        } else {
+            // If we're not allowing it due to the above two conditions, this means that the app
+            // did not declare the permission in their manifest.
+            return LocationPermissionResult.DENIED_HARD;
+        }
+    }
+
+    /** Check if location permissions have been granted */
+    public static LocationPermissionResult checkLocationPermission(
+            Context context, LocationPermissionQuery query) {
+        // Always allow the phone process, system server, and network stack to access location.
+        // This avoid breaking legacy code that rely on public-facing APIs to access cell location,
+        // and it doesn't create an info leak risk because the cell location is stored in the phone
+        // process anyway, and the system server already has location access.
+        if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
+                || query.callingUid == Process.NETWORK_STACK_UID
+                || query.callingUid == Process.ROOT_UID) {
+            return LocationPermissionResult.ALLOWED;
+        }
+
+        // Check the system-wide requirements. If the location main switch is off and the caller is
+        // not in the allowlist of apps that always have loation access or the app's profile
+        // isn't in the foreground, return a soft denial.
+        if (!checkSystemLocationAccess(context, query.callingUid, query.callingPid,
+                query.callingPackage)) {
+            return LocationPermissionResult.DENIED_SOFT;
+        }
+
+        // Do the check for fine, then for coarse.
+        if (query.minSdkVersionForFine < Integer.MAX_VALUE) {
+            LocationPermissionResult resultForFine = checkAppLocationPermissionHelper(
+                    context, query, Manifest.permission.ACCESS_FINE_LOCATION);
+            if (resultForFine != null) {
+                return resultForFine;
+            }
+        }
+
+        if (query.minSdkVersionForCoarse < Integer.MAX_VALUE) {
+            LocationPermissionResult resultForCoarse = checkAppLocationPermissionHelper(
+                    context, query, Manifest.permission.ACCESS_COARSE_LOCATION);
+            if (resultForCoarse != null) {
+                return resultForCoarse;
+            }
+        }
+
+        // At this point, we're out of location checks to do. If the app bypassed all the previous
+        // ones due to the SDK backwards compatibility schemes, allow it access.
+        return LocationPermissionResult.ALLOWED;
+    }
+
+    private static boolean checkManifestPermission(Context context, int pid, int uid,
+            String permissionToCheck) {
+        return context.checkPermission(permissionToCheck, pid, uid)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private static boolean checkSystemLocationAccess(@NonNull Context context, int uid, int pid,
+            @NonNull String callingPackage) {
+        if (!isLocationModeEnabled(context, UserHandle.getUserHandleForUid(uid).getIdentifier())
+                && !isLocationBypassAllowed(context, callingPackage)) {
+            if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")");
+            return false;
+        }
+        // If the user or profile is current, permission is granted.
+        // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+        return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, pid, uid);
+    }
+
+    /**
+     * @return Whether location is enabled for the given user.
+     */
+    public static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
+        LocationManager locationManager = context.getSystemService(LocationManager.class);
+        if (locationManager == null) {
+            Log.w(TAG, "Couldn't get location manager, denying location access");
+            return false;
+        }
+        return locationManager.isLocationEnabledForUser(UserHandle.of(userId));
+    }
+
+    private static boolean isLocationBypassAllowed(@NonNull Context context,
+            @NonNull String callingPackage) {
+        for (String bypassPackage : getLocationBypassPackages(context)) {
+            if (callingPackage.equals(bypassPackage)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return An array of packages that are always allowed to access location.
+     */
+    public static @NonNull String[] getLocationBypassPackages(@NonNull Context context) {
+        return context.getResources().getStringArray(
+                com.android.internal.R.array.config_serviceStateLocationAllowedPackages);
+    }
+
+    private static boolean checkInteractAcrossUsersFull(
+            @NonNull Context context, int pid, int uid) {
+        return checkManifestPermission(context, pid, uid,
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    }
+
+    private static boolean isCurrentProfile(@NonNull Context context, int uid) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            if (UserHandle.getUserHandleForUid(uid).getIdentifier()
+                    == ActivityManager.getCurrentUser()) {
+                return true;
+            }
+            ActivityManager activityManager = context.getSystemService(ActivityManager.class);
+            if (activityManager != null) {
+                return activityManager.isProfileForeground(
+                        UserHandle.getUserHandleForUid(ActivityManager.getCurrentUser()));
+            } else {
+                return false;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private static boolean isAppAtLeastSdkVersion(Context context, String pkgName, int sdkVersion) {
+        try {
+            if (context.getPackageManager().getApplicationInfo(pkgName, 0).targetSdkVersion
+                    >= sdkVersion) {
+                return true;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // In case of exception, assume known app (more strict checking)
+            // Note: This case will never happen since checkPackage is
+            // called to verify validity before checking app's version.
+        }
+        return false;
+    }
+}
diff --git a/android-35/android/telephony/LteVopsSupportInfo.java b/android-35/android/telephony/LteVopsSupportInfo.java
new file mode 100644
index 0000000..87761e2
--- /dev/null
+++ b/android-35/android/telephony/LteVopsSupportInfo.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Class stores information related to LTE network VoPS support
+ * @hide
+ */
+@SystemApi
+public final class LteVopsSupportInfo extends VopsSupportInfo {
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            value = {LTE_STATUS_NOT_AVAILABLE, LTE_STATUS_SUPPORTED,
+                    LTE_STATUS_NOT_SUPPORTED}, prefix = "LTE_STATUS_")
+    public @interface LteVopsStatus {}
+    /**
+     * Indicates information not available from modem.
+     *
+     * @deprecated as no instance will be created in this case
+     */
+    @Deprecated
+    public static final int LTE_STATUS_NOT_AVAILABLE = 1;
+
+    /**
+     * Indicates network support the feature.
+     */
+    public static final int LTE_STATUS_SUPPORTED = 2;
+
+    /**
+     * Indicates network does not support the feature.
+     */
+    public static final int LTE_STATUS_NOT_SUPPORTED = 3;
+
+    @LteVopsStatus
+    private final int mVopsSupport;
+    @LteVopsStatus
+    private final int mEmcBearerSupport;
+
+    public LteVopsSupportInfo(@LteVopsStatus int vops, @LteVopsStatus int emergency) {
+        mVopsSupport = vops;
+        mEmcBearerSupport = emergency;
+    }
+
+    /**
+     * Provides the LTE VoPS support capability as described in:
+     * 3GPP 24.301 EPS network feature support -> IMS VoPS
+     */
+    public @LteVopsStatus int getVopsSupport() {
+        return mVopsSupport;
+    }
+
+    /**
+     * Provides the LTE Emergency bearer support capability as described in:
+     *    3GPP 24.301 EPS network feature support -> EMC BS
+     *    25.331 LTE RRC SIB1 : ims-EmergencySupport-r9
+     */
+    public @LteVopsStatus int getEmcBearerSupport() {
+        return mEmcBearerSupport;
+    }
+
+    /**
+     * Returns whether VoPS is supported by the network
+     */
+    @Override
+    public boolean isVopsSupported() {
+        return mVopsSupport == LTE_STATUS_SUPPORTED;
+    }
+
+    /**
+     * Returns whether emergency service is supported by the network
+     */
+    @Override
+    public boolean isEmergencyServiceSupported() {
+        return mEmcBearerSupport == LTE_STATUS_SUPPORTED;
+    }
+
+    /**
+     * Returns whether emergency service fallback is supported by the network
+     */
+    @Override
+    public boolean isEmergencyServiceFallbackSupported() {
+        return false;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        super.writeToParcel(out, flags, AccessNetworkType.EUTRAN);
+        out.writeInt(mVopsSupport);
+        out.writeInt(mEmcBearerSupport);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o == null || !(o instanceof LteVopsSupportInfo)) {
+            return false;
+        }
+        if (this == o) return true;
+        LteVopsSupportInfo other = (LteVopsSupportInfo) o;
+        return mVopsSupport == other.mVopsSupport
+            && mEmcBearerSupport == other.mEmcBearerSupport;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mVopsSupport, mEmcBearerSupport);
+    }
+
+    /**
+     * @return string representation.
+     */
+    @NonNull
+    @Override
+    public String toString() {
+        return ("LteVopsSupportInfo : "
+                + " mVopsSupport = " + mVopsSupport
+                + " mEmcBearerSupport = " + mEmcBearerSupport);
+    }
+
+    public static final @android.annotation.NonNull Creator<LteVopsSupportInfo> CREATOR =
+            new Creator<LteVopsSupportInfo>() {
+        @Override
+        public LteVopsSupportInfo createFromParcel(Parcel in) {
+            // Skip the type info.
+            in.readInt();
+            return new LteVopsSupportInfo(in);
+        }
+
+        @Override
+        public LteVopsSupportInfo[] newArray(int size) {
+            return new LteVopsSupportInfo[size];
+        }
+    };
+
+    /** @hide */
+    protected static LteVopsSupportInfo createFromParcelBody(Parcel in) {
+        return new LteVopsSupportInfo(in);
+    }
+
+    private LteVopsSupportInfo(Parcel in) {
+        mVopsSupport = in.readInt();
+        mEmcBearerSupport = in.readInt();
+    }
+}
diff --git a/android-35/android/telephony/MbmsDownloadSession.java b/android-35/android/telephony/MbmsDownloadSession.java
new file mode 100644
index 0000000..18d6f46
--- /dev/null
+++ b/android-35/android/telephony/MbmsDownloadSession.java
@@ -0,0 +1,1135 @@
+/*
+ * Copyright (C) 2016 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;
+
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.mbms.DownloadProgressListener;
+import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.DownloadStatusListener;
+import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.InternalDownloadProgressListener;
+import android.telephony.mbms.InternalDownloadSessionCallback;
+import android.telephony.mbms.InternalDownloadStatusListener;
+import android.telephony.mbms.MbmsDownloadReceiver;
+import android.telephony.mbms.MbmsDownloadSessionCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsTempFileProvider;
+import android.telephony.mbms.MbmsUtils;
+import android.telephony.mbms.vendor.IMbmsDownloadService;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * This class provides functionality for file download over MBMS.
+ */
+public class MbmsDownloadSession implements AutoCloseable {
+    private static final String LOG_TAG = MbmsDownloadSession.class.getSimpleName();
+
+    /**
+     * Service action which must be handled by the middleware implementing the MBMS file download
+     * interface.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String MBMS_DOWNLOAD_SERVICE_ACTION =
+            "android.telephony.action.EmbmsDownload";
+
+    /**
+     * Metadata key that specifies the component name of the service to bind to for file-download.
+     * @hide
+     */
+    @TestApi
+    public static final String MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA =
+            "mbms-download-service-override";
+
+    /**
+     * Integer extra that Android will attach to the intent supplied via
+     * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
+     * Indicates the result code of the download. One of
+     * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, {@link #RESULT_CANCELLED},
+     * {@link #RESULT_IO_ERROR}, {@link #RESULT_DOWNLOAD_FAILURE}, {@link #RESULT_OUT_OF_STORAGE},
+     * {@link #RESULT_SERVICE_ID_NOT_DEFINED}, or {@link #RESULT_FILE_ROOT_UNREACHABLE}.
+     *
+     * This extra may also be used by the middleware when it is sending intents to the app.
+     */
+    public static final String EXTRA_MBMS_DOWNLOAD_RESULT =
+            "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
+
+    /**
+     * {@link FileInfo} extra that Android will attach to the intent supplied via
+     * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
+     * Indicates the file for which the download result is for. Never null.
+     *
+     * This extra may also be used by the middleware when it is sending intents to the app.
+     */
+    public static final String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
+
+    /**
+     * {@link Uri} extra that Android will attach to the intent supplied via
+     * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
+     * Indicates the location of the successfully downloaded file within the directory that the
+     * app provided via the builder.
+     *
+     * Will always be set to a non-null value if
+     * {@link #EXTRA_MBMS_DOWNLOAD_RESULT} is set to {@link #RESULT_SUCCESSFUL}.
+     */
+    public static final String EXTRA_MBMS_COMPLETED_FILE_URI =
+            "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
+
+    /**
+     * Extra containing the {@link DownloadRequest} for which the download result or file
+     * descriptor request is for. Must not be null.
+     */
+    public static final String EXTRA_MBMS_DOWNLOAD_REQUEST =
+            "android.telephony.extra.MBMS_DOWNLOAD_REQUEST";
+
+    /**
+     * The default directory name for all MBMS temp files. If you call
+     * {@link #download(DownloadRequest)} without first calling
+     * {@link #setTempFileRootDirectory(File)}, this directory will be created for you under the
+     * path returned by {@link Context#getFilesDir()}.
+     */
+    public static final String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
+
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {RESULT_SUCCESSFUL, RESULT_CANCELLED, RESULT_EXPIRED, RESULT_IO_ERROR,
+            RESULT_SERVICE_ID_NOT_DEFINED, RESULT_DOWNLOAD_FAILURE, RESULT_OUT_OF_STORAGE,
+            RESULT_FILE_ROOT_UNREACHABLE}, prefix = { "RESULT_" })
+    public @interface DownloadResultCode{}
+
+    /**
+     * Indicates that the download was successful.
+     */
+    public static final int RESULT_SUCCESSFUL = 1;
+
+    /**
+     * Indicates that the download was cancelled via {@link #cancelDownload(DownloadRequest)}.
+     */
+    public static final int RESULT_CANCELLED = 2;
+
+    /**
+     * Indicates that the download will not be completed due to the expiration of its download
+     * window on the carrier's network.
+     */
+    public static final int RESULT_EXPIRED = 3;
+
+    /**
+     * Indicates that the download will not be completed due to an I/O error incurred while
+     * writing to temp files.
+     *
+     * This is likely a transient error and another {@link DownloadRequest} should be sent to try
+     * the download again.
+     */
+    public static final int RESULT_IO_ERROR = 4;
+
+    /**
+     * Indicates that the Service ID specified in the {@link DownloadRequest} is incorrect due to
+     * the Id being incorrect, stale, expired, or similar.
+     */
+    public static final int RESULT_SERVICE_ID_NOT_DEFINED = 5;
+
+    /**
+     * Indicates that there was an error while processing downloaded files, such as a file repair or
+     * file decoding error and is not due to a file I/O error.
+     *
+     * This is likely a transient error and another {@link DownloadRequest} should be sent to try
+     * the download again.
+     */
+    public static final int RESULT_DOWNLOAD_FAILURE = 6;
+
+    /**
+     * Indicates that the file system is full and the {@link DownloadRequest} can not complete.
+     * Either space must be made on the current file system or the temp file root location must be
+     * changed to a location that is not full to download the temp files.
+     */
+    public static final int RESULT_OUT_OF_STORAGE = 7;
+
+    /**
+     * Indicates that the file root that was set is currently unreachable. This can happen if the
+     * temp files are set to be stored on external storage and the SD card was removed, for example.
+     * The temp file root should be changed before sending another DownloadRequest.
+     */
+    public static final int RESULT_FILE_ROOT_UNREACHABLE = 8;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({STATUS_UNKNOWN, STATUS_ACTIVELY_DOWNLOADING, STATUS_PENDING_DOWNLOAD,
+            STATUS_PENDING_REPAIR, STATUS_PENDING_DOWNLOAD_WINDOW})
+    public @interface DownloadStatus {}
+
+    /**
+     * Indicates that the middleware has no information on the file.
+     */
+    public static final int STATUS_UNKNOWN = 0;
+
+    /**
+     * Indicates that the file is actively being downloaded.
+     */
+    public static final int STATUS_ACTIVELY_DOWNLOADING = 1;
+
+    /**
+     * Indicates that the file is awaiting the next download or repair operations. When a more
+     * precise status is known, the status will change to either {@link #STATUS_PENDING_REPAIR} or
+     * {@link #STATUS_PENDING_DOWNLOAD_WINDOW}.
+     */
+    public static final int STATUS_PENDING_DOWNLOAD = 2;
+
+    /**
+     * Indicates that the file is awaiting file repair after the download has ended.
+     */
+    public static final int STATUS_PENDING_REPAIR = 3;
+
+    /**
+     * Indicates that the file is waiting to download because its download window has not yet
+     * started and is scheduled for a future time.
+     */
+    public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
+
+    private static final String DESTINATION_SANITY_CHECK_FILE_NAME = "destinationSanityCheckFile";
+
+    private static final int MAX_SERVICE_ANNOUNCEMENT_SIZE = 10 * 1024; // 10KB
+
+    private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
+    private final Context mContext;
+    private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
+    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, "Received death notification");
+        }
+    };
+
+    private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
+    private ServiceConnection mServiceConnection;
+    private final InternalDownloadSessionCallback mInternalCallback;
+    private final Map<DownloadStatusListener, InternalDownloadStatusListener>
+            mInternalDownloadStatusListeners = new HashMap<>();
+    private final Map<DownloadProgressListener, InternalDownloadProgressListener>
+            mInternalDownloadProgressListeners = new HashMap<>();
+
+    private MbmsDownloadSession(Context context, Executor executor, int subscriptionId,
+            MbmsDownloadSessionCallback callback) {
+        mContext = context;
+        mSubscriptionId = subscriptionId;
+        mInternalCallback = new InternalDownloadSessionCallback(callback, executor);
+    }
+
+    /**
+     * Create a new {@link MbmsDownloadSession} using the system default data subscription ID.
+     * See {@link #create(Context, Executor, int, MbmsDownloadSessionCallback)}
+     */
+    public static MbmsDownloadSession create(@NonNull Context context,
+            @NonNull Executor executor, @NonNull MbmsDownloadSessionCallback callback) {
+        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
+    }
+
+    /**
+     * Create a new MbmsDownloadManager using the given subscription ID.
+     *
+     * Note that this call will bind a remote service and that may take a bit. The instance of
+     * {@link MbmsDownloadSession} that is returned will not be ready for use until
+     * {@link MbmsDownloadSessionCallback#onMiddlewareReady()} is called on the provided callback.
+     * If you attempt to use the instance before it is ready, an {@link IllegalStateException}
+     * will be thrown or an error will be delivered through
+     * {@link MbmsDownloadSessionCallback#onError(int, String)}.
+     *
+     * This also may throw an {@link IllegalArgumentException}.
+     *
+     * You may only have one instance of {@link MbmsDownloadSession} per UID. If you call this
+     * method while there is an active instance of {@link MbmsDownloadSession} in your process
+     * (in other words, one that has not had {@link #close()} called on it), this method will
+     * throw an {@link IllegalStateException}. If you call this method in a different process
+     * running under the same UID, an error will be indicated via
+     * {@link MbmsDownloadSessionCallback#onError(int, String)}.
+     *
+     * Note that initialization may fail asynchronously. If you wish to try again after you
+     * receive such an asynchronous error, you must call {@link #close()} on the instance of
+     * {@link MbmsDownloadSession} that you received before calling this method again.
+     *
+     * @param context The instance of {@link Context} to use
+     * @param executor The executor on which you wish to execute callbacks.
+     * @param subscriptionId The data subscription ID to use
+     * @param callback A callback to get asynchronous error messages and file service updates.
+     * @return A new instance of {@link MbmsDownloadSession}, or null if an error occurred during
+     * setup.
+     */
+    public static @Nullable MbmsDownloadSession create(@NonNull Context context,
+            @NonNull Executor executor, int subscriptionId,
+            final @NonNull MbmsDownloadSessionCallback callback) {
+        if (!sIsInitialized.compareAndSet(false, true)) {
+            throw new IllegalStateException("Cannot have two active instances");
+        }
+        MbmsDownloadSession session =
+                new MbmsDownloadSession(context, executor, subscriptionId, callback);
+        final int result = session.bindAndInitialize();
+        if (result != MbmsErrors.SUCCESS) {
+            sIsInitialized.set(false);
+            executor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onError(result, null);
+                }
+            });
+            return null;
+        }
+        return session;
+    }
+
+    /**
+     * Returns the maximum size of the service announcement descriptor that can be provided via
+     * {@link #addServiceAnnouncement}
+     * @return The maximum length of the byte array passed as an argument to
+     *         {@link #addServiceAnnouncement}.
+     */
+    public static int getMaximumServiceAnnouncementSize() {
+        return MAX_SERVICE_ANNOUNCEMENT_SIZE;
+    }
+
+    private int bindAndInitialize() {
+        mServiceConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                IMbmsDownloadService downloadService =
+                        IMbmsDownloadService.Stub.asInterface(service);
+                int result;
+                try {
+                    result = downloadService.initialize(mSubscriptionId, mInternalCallback);
+                } catch (RemoteException e) {
+                    Log.e(LOG_TAG, "Service died before initialization");
+                    sIsInitialized.set(false);
+                    return;
+                } catch (RuntimeException e) {
+                    Log.e(LOG_TAG, "Runtime exception during initialization");
+                    sendErrorToApp(
+                            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                            e.toString());
+                    sIsInitialized.set(false);
+                    return;
+                }
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not return an"
+                            + " unknown error code");
+                }
+                if (result != MbmsErrors.SUCCESS) {
+                    sendErrorToApp(result, "Error returned during initialization");
+                    sIsInitialized.set(false);
+                    return;
+                }
+                try {
+                    downloadService.asBinder().linkToDeath(mDeathRecipient, 0);
+                } catch (RemoteException e) {
+                    sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                            "Middleware lost during initialization");
+                    sIsInitialized.set(false);
+                    return;
+                }
+                mService.set(downloadService);
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                Log.w(LOG_TAG, "bindAndInitialize: Remote service disconnected");
+                sIsInitialized.set(false);
+                mService.set(null);
+            }
+
+            @Override
+            public void onNullBinding(ComponentName name) {
+                Log.w(LOG_TAG, "bindAndInitialize: Remote service returned null");
+                sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                        "Middleware service binding returned null");
+                sIsInitialized.set(false);
+                mService.set(null);
+                mContext.unbindService(this);
+            }
+        };
+        return MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION, mServiceConnection);
+    }
+
+    /**
+     * An inspection API to retrieve the list of available
+     * {@link android.telephony.mbms.FileServiceInfo}s currently being advertised.
+     * The results are returned asynchronously via a call to
+     * {@link MbmsDownloadSessionCallback#onFileServicesUpdated(List)}
+     *
+     * Asynchronous error codes via the {@link MbmsDownloadSessionCallback#onError(int, String)}
+     * callback may include any of the errors that are not specific to the streaming use-case.
+     *
+     * May throw an {@link IllegalStateException} or {@link IllegalArgumentException}.
+     *
+     * @param classList A list of service classes which the app wishes to receive
+     *                  {@link MbmsDownloadSessionCallback#onFileServicesUpdated(List)} callbacks
+     *                  about. Subsequent calls to this method will replace this list of service
+     *                  classes (i.e. the middleware will no longer send updates for services
+     *                  matching classes only in the old list).
+     *                  Values in this list should be negotiated with the wireless carrier prior
+     *                  to using this API.
+     */
+    public void requestUpdateFileServices(@NonNull List<String> classList) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+        try {
+            int returnCode = downloadService.requestUpdateFileServices(mSubscriptionId, classList);
+            if (returnCode == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (returnCode != MbmsErrors.SUCCESS) {
+                sendErrorToApp(returnCode, null);
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        }
+    }
+
+    /**
+     * Inform the middleware of a service announcement descriptor received from a group
+     * communication server.
+     *
+     * When participating in a group call via the {@link MbmsGroupCallSession} API, applications may
+     * receive a service announcement descriptor from the group call server that informs them of
+     * files that may be relevant to users communicating on the group call.
+     *
+     * After supplying the service announcement descriptor received from the server to the
+     * middleware via this API, applications will receive information on the available files via
+     * {@link MbmsDownloadSessionCallback#onFileServicesUpdated}, and the available files will be
+     * downloadable via {@link MbmsDownloadSession#download} like other files published via
+     * {@link MbmsDownloadSessionCallback#onFileServicesUpdated}.
+     *
+     * Asynchronous error codes via the {@link MbmsDownloadSessionCallback#onError(int, String)}
+     * callback may include any of the errors that are not specific to the streaming use-case.
+     *
+     * May throw an {@link IllegalStateException} when the middleware has not yet been bound,
+     * or an {@link IllegalArgumentException} if the byte array is too large, or an
+     * {@link UnsupportedOperationException} if the middleware has not implemented this method.
+     *
+     * @param contents The contents of the service announcement descriptor received from the
+     *                     group call server. If the size of this array is greater than the value of
+     *                     {@link #getMaximumServiceAnnouncementSize()}, an
+     *                     {@link IllegalArgumentException} will be thrown.
+     */
+    public void addServiceAnnouncement(@NonNull byte[] contents) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        if (contents.length > MAX_SERVICE_ANNOUNCEMENT_SIZE) {
+            throw new IllegalArgumentException("File too large");
+        }
+
+        try {
+            int returnCode = downloadService.addServiceAnnouncement(
+                    mSubscriptionId, contents);
+            if (returnCode == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (returnCode != MbmsErrors.SUCCESS) {
+                sendErrorToApp(returnCode, null);
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        }
+    }
+
+    /**
+     * Sets the temp file root for downloads.
+     * All temp files created for the middleware to write to will be contained in the specified
+     * directory. Applications that wish to specify a location only need to call this method once
+     * as long their data is persisted in storage -- the argument will be stored both in a
+     * local instance of {@link android.content.SharedPreferences} and by the middleware.
+     *
+     * If this method is not called at least once before calling
+     * {@link #download(DownloadRequest)}, the framework
+     * will default to a directory formed by the concatenation of the app's files directory and
+     * {@link MbmsDownloadSession#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}.
+     *
+     * Before calling this method, the app must cancel all of its pending
+     * {@link DownloadRequest}s via {@link #cancelDownload(DownloadRequest)}. If this is not done,
+     * you will receive an asynchronous error with code
+     * {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} unless the
+     * provided directory is the same as what has been previously configured.
+     *
+     * The {@link File} supplied as a root temp file directory must already exist. If not, an
+     * {@link IllegalArgumentException} will be thrown. In addition, as an additional correctness
+     * check, an {@link IllegalArgumentException} will be thrown if you attempt to set the temp
+     * file root directory to one of your data roots (the value of {@link Context#getDataDir()},
+     * {@link Context#getFilesDir()}, or {@link Context#getCacheDir()}).
+     * @param tempFileRootDirectory A directory to place temp files in.
+     */
+    public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+        try {
+            validateTempFileRootSanity(tempFileRootDirectory);
+        } catch (IOException e) {
+            throw new IllegalStateException("Got IOException checking directory sanity");
+        }
+        String filePath;
+        try {
+            filePath = tempFileRootDirectory.getCanonicalPath();
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Unable to canonicalize the provided path: " + e);
+        }
+
+        try {
+            int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (result != MbmsErrors.SUCCESS) {
+                sendErrorToApp(result, null);
+                return;
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return;
+        }
+
+        SharedPreferences prefs = mContext.getSharedPreferences(
+                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+        prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply();
+    }
+
+    private void validateTempFileRootSanity(File tempFileRootDirectory) throws IOException {
+        if (!tempFileRootDirectory.exists()) {
+            throw new IllegalArgumentException("Provided directory does not exist");
+        }
+        if (!tempFileRootDirectory.isDirectory()) {
+            throw new IllegalArgumentException("Provided File is not a directory");
+        }
+        String canonicalTempFilePath = tempFileRootDirectory.getCanonicalPath();
+        if (mContext.getDataDir().getCanonicalPath().equals(canonicalTempFilePath)) {
+            throw new IllegalArgumentException("Temp file root cannot be your data dir");
+        }
+        if (mContext.getCacheDir().getCanonicalPath().equals(canonicalTempFilePath)) {
+            throw new IllegalArgumentException("Temp file root cannot be your cache dir");
+        }
+        if (mContext.getFilesDir().getCanonicalPath().equals(canonicalTempFilePath)) {
+            throw new IllegalArgumentException("Temp file root cannot be your files dir");
+        }
+    }
+    /**
+     * Retrieves the currently configured temp file root directory. Returns the file that was
+     * configured via {@link #setTempFileRootDirectory(File)} or the default directory
+     * {@link #download(DownloadRequest)} was called without ever
+     * setting the temp file root. If neither method has been called since the last time the app's
+     * shared preferences were reset, returns {@code null}.
+     *
+     * @return A {@link File} pointing to the configured temp file directory, or null if not yet
+     *         configured.
+     */
+    public @Nullable File getTempFileRootDirectory() {
+        SharedPreferences prefs = mContext.getSharedPreferences(
+                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+        String path = prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null);
+        if (path != null) {
+            return new File(path);
+        }
+        return null;
+    }
+
+    /**
+     * Requests the download of a file or set of files that the carrier has indicated to be
+     * available.
+     *
+     * May throw an {@link IllegalArgumentException}
+     *
+     * If {@link #setTempFileRootDirectory(File)} has not called after the app has been installed,
+     * this method will create a directory at the default location defined at
+     * {@link MbmsDownloadSession#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
+     * file root directory.
+     *
+     * If the {@link DownloadRequest} has a destination that is not on the same filesystem as the
+     * temp file directory provided via {@link #getTempFileRootDirectory()}, an
+     * {@link IllegalArgumentException} will be thrown.
+     *
+     * Asynchronous errors through the callback may include any error not specific to the
+     * streaming use-case.
+     *
+     * If no error is delivered via the callback after calling this method, that means that the
+     * middleware has successfully started the download or scheduled the download, if the download
+     * is at a future time.
+     * @param request The request that specifies what should be downloaded.
+     */
+    public void download(@NonNull DownloadRequest request) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        // Check to see whether the app's set a temp root dir yet, and set it if not.
+        SharedPreferences prefs = mContext.getSharedPreferences(
+                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+        if (prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null) == null) {
+            File tempRootDirectory = new File(mContext.getFilesDir(),
+                    DEFAULT_TOP_LEVEL_TEMP_DIRECTORY);
+            tempRootDirectory.mkdirs();
+            setTempFileRootDirectory(tempRootDirectory);
+        }
+
+        checkDownloadRequestDestination(request);
+
+        try {
+            int result = downloadService.download(request);
+            if (result == MbmsErrors.SUCCESS) {
+                writeDownloadRequestToken(request);
+            } else {
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not return an unknown"
+                            + " error code");
+                }
+                sendErrorToApp(result, null);
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        }
+    }
+
+    /**
+     * Returns a list of pending {@link DownloadRequest}s that originated from this application.
+     * A pending request is one that was issued via
+     * {@link #download(DownloadRequest)} but not cancelled through
+     * {@link #cancelDownload(DownloadRequest)}.
+     * @return A list, possibly empty, of {@link DownloadRequest}s
+     */
+    public @NonNull List<DownloadRequest> listPendingDownloads() {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        try {
+            return downloadService.listPendingDownloads(mSubscriptionId);
+        } catch (RemoteException e) {
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Registers a download status listener for a {@link DownloadRequest} previously requested via
+     * {@link #download(DownloadRequest)}. This callback will only be called as long as both this
+     * app and the middleware are both running -- if either one stops, no further calls on the
+     * provided {@link DownloadStatusListener} will be enqueued.
+     *
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
+     *
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
+     *
+     * Repeated calls to this method for the same {@link DownloadRequest} will replace the
+     * previously registered listener.
+     *
+     * @param request The {@link DownloadRequest} that you want updates on.
+     * @param executor The {@link Executor} on which calls to {@code listener } should be executed.
+     * @param listener The listener that should be called when the middleware has information to
+     *                 share on the status download.
+     */
+    public void addStatusListener(@NonNull DownloadRequest request,
+            @NonNull Executor executor, @NonNull DownloadStatusListener listener) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        InternalDownloadStatusListener internalListener =
+                new InternalDownloadStatusListener(listener, executor);
+
+        try {
+            int result = downloadService.addStatusListener(request, internalListener);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (result != MbmsErrors.SUCCESS) {
+                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                sendErrorToApp(result, null);
+                return;
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return;
+        }
+        mInternalDownloadStatusListeners.put(listener, internalListener);
+    }
+
+    /**
+     * Un-register a listener previously registered via
+     * {@link #addStatusListener(DownloadRequest, Executor, DownloadStatusListener)}. After
+     * this method is called, no further calls will be enqueued on the {@link Executor}
+     * provided upon registration, even if this method throws an exception.
+     *
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
+     *
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
+     *
+     * @param request The {@link DownloadRequest} provided during registration
+     * @param listener The listener provided during registration.
+     */
+    public void removeStatusListener(@NonNull DownloadRequest request,
+            @NonNull DownloadStatusListener listener) {
+        try {
+            IMbmsDownloadService downloadService = mService.get();
+            if (downloadService == null) {
+                throw new IllegalStateException("Middleware not yet bound");
+            }
+
+            InternalDownloadStatusListener internalListener =
+                    mInternalDownloadStatusListeners.get(listener);
+            if (internalListener == null) {
+                throw new IllegalArgumentException("Provided listener was never registered");
+            }
+
+            try {
+                int result = downloadService.removeStatusListener(request, internalListener);
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not return an"
+                            + " unknown error code");
+                }
+                if (result != MbmsErrors.SUCCESS) {
+                    if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                        throw new IllegalArgumentException("Unknown download request.");
+                    }
+                    sendErrorToApp(result, null);
+                    return;
+                }
+            } catch (RemoteException e) {
+                mService.set(null);
+                sIsInitialized.set(false);
+                sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+                return;
+            }
+        } finally {
+            InternalDownloadStatusListener internalCallback =
+                    mInternalDownloadStatusListeners.remove(listener);
+            if (internalCallback != null) {
+                internalCallback.stop();
+            }
+        }
+    }
+
+    /**
+     * Registers a progress listener for a {@link DownloadRequest} previously requested via
+     * {@link #download(DownloadRequest)}. This listener will only be called as long as both this
+     * app and the middleware are both running -- if either one stops, no further calls on the
+     * provided {@link DownloadProgressListener} will be enqueued.
+     *
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
+     *
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
+     *
+     * Repeated calls to this method for the same {@link DownloadRequest} will replace the
+     * previously registered listener.
+     *
+     * @param request The {@link DownloadRequest} that you want updates on.
+     * @param executor The {@link Executor} on which calls to {@code listener} should be executed.
+     * @param listener The listener that should be called when the middleware has information to
+     *                 share on the progress of the download.
+     */
+    public void addProgressListener(@NonNull DownloadRequest request,
+            @NonNull Executor executor, @NonNull DownloadProgressListener listener) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        InternalDownloadProgressListener internalListener =
+                new InternalDownloadProgressListener(listener, executor);
+
+        try {
+            int result = downloadService.addProgressListener(request, internalListener);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (result != MbmsErrors.SUCCESS) {
+                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                sendErrorToApp(result, null);
+                return;
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return;
+        }
+        mInternalDownloadProgressListeners.put(listener, internalListener);
+    }
+
+    /**
+     * Un-register a listener previously registered via
+     * {@link #addProgressListener(DownloadRequest, Executor, DownloadProgressListener)}. After
+     * this method is called, no further callbacks will be enqueued on the {@link Handler}
+     * provided upon registration, even if this method throws an exception.
+     *
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
+     *
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
+     *
+     * @param request The {@link DownloadRequest} provided during registration
+     * @param listener The listener provided during registration.
+     */
+    public void removeProgressListener(@NonNull DownloadRequest request,
+            @NonNull DownloadProgressListener listener) {
+        try {
+            IMbmsDownloadService downloadService = mService.get();
+            if (downloadService == null) {
+                throw new IllegalStateException("Middleware not yet bound");
+            }
+
+            InternalDownloadProgressListener internalListener =
+                    mInternalDownloadProgressListeners.get(listener);
+            if (internalListener == null) {
+                throw new IllegalArgumentException("Provided listener was never registered");
+            }
+
+            try {
+                int result = downloadService.removeProgressListener(request, internalListener);
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not"
+                            + " return an unknown error code");
+                }
+                if (result != MbmsErrors.SUCCESS) {
+                    if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                        throw new IllegalArgumentException("Unknown download request.");
+                    }
+                    sendErrorToApp(result, null);
+                    return;
+                }
+            } catch (RemoteException e) {
+                mService.set(null);
+                sIsInitialized.set(false);
+                sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+                return;
+            }
+        } finally {
+            InternalDownloadProgressListener internalCallback =
+                    mInternalDownloadProgressListeners.remove(listener);
+            if (internalCallback != null) {
+                internalCallback.stop();
+            }
+        }
+    }
+
+    /**
+     * Attempts to cancel the specified {@link DownloadRequest}.
+     *
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
+     *
+     * @param downloadRequest The download request that you wish to cancel.
+     */
+    public void cancelDownload(@NonNull DownloadRequest downloadRequest) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        try {
+            int result = downloadService.cancelDownload(downloadRequest);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (result != MbmsErrors.SUCCESS) {
+                sendErrorToApp(result, null);
+            } else {
+                deleteDownloadRequestToken(downloadRequest);
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        }
+    }
+
+    /**
+     * Requests information about the state of a file pending download.
+     *
+     * The state will be delivered as a callback via
+     * {@link DownloadStatusListener#onStatusUpdated(DownloadRequest, FileInfo, int)}. If no such
+     * callback has been registered via
+     * {@link #addProgressListener(DownloadRequest, Executor, DownloadProgressListener)}, this
+     * method will be a no-op.
+     *
+     * If the middleware has no record of the
+     * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
+     * an {@link IllegalArgumentException} will be thrown.
+     *
+     * @param downloadRequest The download request to query.
+     * @param fileInfo The particular file within the request to get information on.
+     */
+    public void requestDownloadState(DownloadRequest downloadRequest, FileInfo fileInfo) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        try {
+            int result = downloadService.requestDownloadState(downloadRequest, fileInfo);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (result != MbmsErrors.SUCCESS) {
+                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_FILE_INFO) {
+                    throw new IllegalArgumentException("Unknown file.");
+                }
+                sendErrorToApp(result, null);
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        }
+    }
+
+    /**
+     * Resets the middleware's knowledge of previously-downloaded files in this download request.
+     *
+     * Normally, the middleware keeps track of the hashes of downloaded files and won't re-download
+     * files whose server-reported hash matches one of the already-downloaded files. This means
+     * that if the file is accidentally deleted by the user or by the app, the middleware will
+     * not try to download it again.
+     * This method will reset the middleware's cache of hashes for the provided
+     * {@link DownloadRequest}, so that previously downloaded content will be downloaded again
+     * when available.
+     * This will not interrupt in-progress downloads.
+     *
+     * This is distinct from cancelling and re-issuing the download request -- if you cancel and
+     * re-issue, the middleware will not clear its cache of download state information.
+     *
+     * If the middleware is not aware of the specified download request, an
+     * {@link IllegalArgumentException} will be thrown.
+     *
+     * @param downloadRequest The request to re-download files for.
+     */
+    public void resetDownloadKnowledge(DownloadRequest downloadRequest) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        try {
+            int result = downloadService.resetDownloadKnowledge(downloadRequest);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (result != MbmsErrors.SUCCESS) {
+                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                sendErrorToApp(result, null);
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        }
+    }
+
+    /**
+     * Terminates this instance.
+     *
+     * After this method returns,
+     * no further callbacks originating from the middleware will be enqueued on the provided
+     * instance of {@link MbmsDownloadSessionCallback}, but callbacks that have already been
+     * enqueued will still be delivered.
+     *
+     * It is safe to call {@link #create(Context, Executor, int, MbmsDownloadSessionCallback)} to
+     * obtain another instance of {@link MbmsDownloadSession} immediately after this method
+     * returns.
+     *
+     * May throw an {@link IllegalStateException}
+     */
+    @Override
+    public void close() {
+        try {
+            IMbmsDownloadService downloadService = mService.get();
+            if (downloadService == null || mServiceConnection == null) {
+                Log.i(LOG_TAG, "Service already dead");
+                return;
+            }
+            downloadService.dispose(mSubscriptionId);
+            mContext.unbindService(mServiceConnection);
+        } catch (RemoteException e) {
+            // Ignore
+            Log.i(LOG_TAG, "Remote exception while disposing of service");
+        } finally {
+            mService.set(null);
+            sIsInitialized.set(false);
+            mServiceConnection = null;
+            mInternalCallback.stop();
+        }
+    }
+
+    private void writeDownloadRequestToken(DownloadRequest request) {
+        File token = getDownloadRequestTokenPath(request);
+        if (!token.getParentFile().exists()) {
+            token.getParentFile().mkdirs();
+        }
+        if (token.exists()) {
+            Log.w(LOG_TAG, "Download token " + token.getName() + " already exists");
+            return;
+        }
+        try {
+            if (!token.createNewFile()) {
+                throw new RuntimeException("Failed to create download token for request "
+                        + request + ". Token location is " + token.getPath());
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to create download token for request " + request
+                    + " due to IOException " + e + ". Attempted to write to " + token.getPath());
+        }
+    }
+
+    private void deleteDownloadRequestToken(DownloadRequest request) {
+        File token = getDownloadRequestTokenPath(request);
+        if (!token.isFile()) {
+            Log.w(LOG_TAG, "Attempting to delete non-existent download token at " + token);
+            return;
+        }
+        if (!token.delete()) {
+            Log.w(LOG_TAG, "Couldn't delete download token at " + token);
+        }
+    }
+
+    private void checkDownloadRequestDestination(DownloadRequest request) {
+        File downloadRequestDestination = new File(request.getDestinationUri().getPath());
+        if (!downloadRequestDestination.isDirectory()) {
+            throw new IllegalArgumentException("The destination path must be a directory");
+        }
+        // Check if the request destination is okay to use by attempting to rename an empty
+        // file to there.
+        File testFile = new File(MbmsTempFileProvider.getEmbmsTempFileDir(mContext),
+                DESTINATION_SANITY_CHECK_FILE_NAME);
+        File testFileDestination = new File(downloadRequestDestination,
+                DESTINATION_SANITY_CHECK_FILE_NAME);
+
+        try {
+            if (!testFile.exists()) {
+                testFile.createNewFile();
+            }
+            if (!testFile.renameTo(testFileDestination)) {
+                throw new IllegalArgumentException("Destination provided in the download request " +
+                        "is invalid -- files in the temp file directory cannot be directly moved " +
+                        "there.");
+            }
+        } catch (IOException e) {
+            throw new IllegalStateException("Got IOException while testing out the destination: "
+                    + e);
+        } finally {
+            testFile.delete();
+            testFileDestination.delete();
+        }
+    }
+
+    private File getDownloadRequestTokenPath(DownloadRequest request) {
+        File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
+                request.getFileServiceId());
+        String downloadTokenFileName = request.getHash()
+                + MbmsDownloadReceiver.DOWNLOAD_TOKEN_SUFFIX;
+        return new File(tempFileLocation, downloadTokenFileName);
+    }
+
+    private void sendErrorToApp(int errorCode, String message) {
+        mInternalCallback.onError(errorCode, message);
+    }
+}
diff --git a/android-35/android/telephony/MbmsGroupCallSession.java b/android-35/android/telephony/MbmsGroupCallSession.java
new file mode 100644
index 0000000..d54071f
--- /dev/null
+++ b/android-35/android/telephony/MbmsGroupCallSession.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.mbms.GroupCall;
+import android.telephony.mbms.GroupCallCallback;
+import android.telephony.mbms.InternalGroupCallCallback;
+import android.telephony.mbms.InternalGroupCallSessionCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsGroupCallSessionCallback;
+import android.telephony.mbms.MbmsUtils;
+import android.telephony.mbms.vendor.IMbmsGroupCallService;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * This class provides functionality for accessing group call functionality over MBMS.
+ */
+public class MbmsGroupCallSession implements AutoCloseable {
+    private static final String LOG_TAG = "MbmsGroupCallSession";
+
+    /**
+     * Service action which must be handled by the middleware implementing the MBMS group call
+     * interface.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String MBMS_GROUP_CALL_SERVICE_ACTION =
+            "android.telephony.action.EmbmsGroupCall";
+
+    /**
+     * Metadata key that specifies the component name of the service to bind to for group calls.
+     * @hide
+     */
+    @TestApi
+    public static final String MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA =
+            "mbms-group-call-service-override";
+
+    private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
+    private AtomicReference<IMbmsGroupCallService> mService = new AtomicReference<>(null);
+    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            sIsInitialized.set(false);
+            mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                    "Received death notification");
+        }
+    };
+
+    private InternalGroupCallSessionCallback mInternalCallback;
+    private ServiceConnection mServiceConnection;
+    private Set<GroupCall> mKnownActiveGroupCalls = new ArraySet<>();
+
+    private final Context mContext;
+    private int mSubscriptionId;
+
+    /** @hide */
+    private MbmsGroupCallSession(Context context, Executor executor, int subscriptionId,
+            MbmsGroupCallSessionCallback callback) {
+        mContext = context;
+        mSubscriptionId = subscriptionId;
+        mInternalCallback = new InternalGroupCallSessionCallback(callback, executor);
+    }
+
+    /**
+     * Create a new {@link MbmsGroupCallSession} using the given subscription ID.
+     *
+     * You may only have one instance of {@link MbmsGroupCallSession} per UID. If you call this
+     * method while there is an active instance of {@link MbmsGroupCallSession} in your process
+     * (in other words, one that has not had {@link #close()} called on it), this method will
+     * throw an {@link IllegalStateException}. If you call this method in a different process
+     * running under the same UID, an error will be indicated via
+     * {@link MbmsGroupCallSessionCallback#onError(int, String)}.
+     *
+     * Note that initialization may fail asynchronously. If you wish to try again after you
+     * receive such an asynchronous error, you must call {@link #close()} on the instance of
+     * {@link MbmsGroupCallSession} that you received before calling this method again.
+     *
+     * @param context The {@link Context} to use.
+     * @param subscriptionId The subscription ID to use.
+     * @param executor The executor on which you wish to execute callbacks.
+     * @param callback A callback object on which you wish to receive results of asynchronous
+     *                 operations.
+     * @return An instance of {@link MbmsGroupCallSession}, or null if an error occurred.
+     */
+    public static @Nullable MbmsGroupCallSession create(@NonNull Context context,
+            int subscriptionId, @NonNull Executor executor,
+            final @NonNull MbmsGroupCallSessionCallback callback) {
+        if (!sIsInitialized.compareAndSet(false, true)) {
+            throw new IllegalStateException("Cannot create two instances of MbmsGroupCallSession");
+        }
+        MbmsGroupCallSession session = new MbmsGroupCallSession(context, executor,
+                subscriptionId, callback);
+
+        final int result = session.bindAndInitialize();
+        if (result != MbmsErrors.SUCCESS) {
+            sIsInitialized.set(false);
+            executor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onError(result, null);
+                }
+            });
+            return null;
+        }
+        return session;
+    }
+
+    /**
+     * Create a new {@link MbmsGroupCallSession} using the system default data subscription ID.
+     * See {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
+     */
+    public static @Nullable MbmsGroupCallSession create(@NonNull Context context,
+            @NonNull Executor executor, @NonNull MbmsGroupCallSessionCallback callback) {
+        return create(context, SubscriptionManager.getDefaultSubscriptionId(), executor, callback);
+    }
+
+    /**
+     * Terminates this instance. Also terminates
+     * any group calls spawned from this instance as if
+     * {@link GroupCall#close()} had been called on them. After this method returns,
+     * no further callbacks originating from the middleware will be enqueued on the provided
+     * instance of {@link MbmsGroupCallSessionCallback}, but callbacks that have already been
+     * enqueued will still be delivered.
+     *
+     * It is safe to call {@link #create(Context, int, Executor, MbmsGroupCallSessionCallback)} to
+     * obtain another instance of {@link MbmsGroupCallSession} immediately after this method
+     * returns.
+     *
+     * May throw an {@link IllegalStateException}
+     */
+    public void close() {
+        try {
+            IMbmsGroupCallService groupCallService = mService.get();
+            if (groupCallService == null || mServiceConnection == null) {
+                // Ignore and return, assume already disposed.
+                return;
+            }
+            groupCallService.dispose(mSubscriptionId);
+            for (GroupCall s : mKnownActiveGroupCalls) {
+                s.getCallback().stop();
+            }
+            mKnownActiveGroupCalls.clear();
+            mContext.unbindService(mServiceConnection);
+        } catch (RemoteException e) {
+            // Ignore for now
+        } finally {
+            mService.set(null);
+            sIsInitialized.set(false);
+            mServiceConnection = null;
+            mInternalCallback.stop();
+        }
+    }
+
+    /**
+     * Starts the requested group call, reporting status to the indicated callback.
+     * Returns an object used to control that call.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * Asynchronous errors through the callback include any of the errors in
+     * {@link MbmsErrors.GeneralErrors}.
+     *
+     * @param tmgi The TMGI, an identifier for the group call you want to join.
+     * @param saiList A list of SAIs for the group call that should be negotiated separately with
+     *                the carrier.
+     * @param frequencyList A lost of frequencies for the group call that should be negotiated
+     *                separately with the carrier.
+     * @param executor The executor on which you wish to execute callbacks for this stream.
+     * @param callback The callback that you want to receive information about the call on.
+     * @return An instance of {@link GroupCall} through which the call can be controlled.
+     *         May be {@code null} if an error occurred.
+     */
+    public @Nullable GroupCall startGroupCall(long tmgi, @NonNull List<Integer> saiList,
+            @NonNull List<Integer> frequencyList, @NonNull Executor executor,
+            @NonNull GroupCallCallback callback) {
+        IMbmsGroupCallService groupCallService = mService.get();
+        if (groupCallService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        InternalGroupCallCallback serviceCallback = new InternalGroupCallCallback(
+                callback, executor);
+
+        GroupCall serviceForApp = new GroupCall(mSubscriptionId,
+                groupCallService, this, tmgi, serviceCallback);
+        mKnownActiveGroupCalls.add(serviceForApp);
+
+        try {
+            int returnCode = groupCallService.startGroupCall(
+                    mSubscriptionId, tmgi, saiList, frequencyList, serviceCallback);
+            if (returnCode == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (returnCode != MbmsErrors.SUCCESS) {
+                mInternalCallback.onError(returnCode, null);
+                return null;
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService.set(null);
+            sIsInitialized.set(false);
+            mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return null;
+        }
+
+        return serviceForApp;
+    }
+
+    /** @hide */
+    public void onGroupCallStopped(GroupCall service) {
+        mKnownActiveGroupCalls.remove(service);
+    }
+
+    private int bindAndInitialize() {
+        mServiceConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                IMbmsGroupCallService groupCallService =
+                        IMbmsGroupCallService.Stub.asInterface(service);
+                int result;
+                try {
+                    result = groupCallService.initialize(mInternalCallback,
+                            mSubscriptionId);
+                } catch (RemoteException e) {
+                    Log.e(LOG_TAG, "Service died before initialization");
+                    mInternalCallback.onError(
+                            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                            e.toString());
+                    sIsInitialized.set(false);
+                    return;
+                } catch (RuntimeException e) {
+                    Log.e(LOG_TAG, "Runtime exception during initialization");
+                    mInternalCallback.onError(
+                            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                            e.toString());
+                    sIsInitialized.set(false);
+                    return;
+                }
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not return"
+                            + " an unknown error code");
+                }
+                if (result != MbmsErrors.SUCCESS) {
+                    mInternalCallback.onError(result,
+                            "Error returned during initialization");
+                    sIsInitialized.set(false);
+                    return;
+                }
+                try {
+                    groupCallService.asBinder().linkToDeath(mDeathRecipient, 0);
+                } catch (RemoteException e) {
+                    mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                            "Middleware lost during initialization");
+                    sIsInitialized.set(false);
+                    return;
+                }
+                mService.set(groupCallService);
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                sIsInitialized.set(false);
+                mService.set(null);
+            }
+
+            @Override
+            public void onNullBinding(ComponentName name) {
+                Log.w(LOG_TAG, "bindAndInitialize: Remote service returned null");
+                mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                        "Middleware service binding returned null");
+                sIsInitialized.set(false);
+                mService.set(null);
+                mContext.unbindService(this);
+            }
+        };
+        return MbmsUtils.startBinding(mContext, MBMS_GROUP_CALL_SERVICE_ACTION, mServiceConnection);
+    }
+}
diff --git a/android-35/android/telephony/MbmsStreamingSession.java b/android-35/android/telephony/MbmsStreamingSession.java
new file mode 100644
index 0000000..3fbbc03
--- /dev/null
+++ b/android-35/android/telephony/MbmsStreamingSession.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2016 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;
+
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.mbms.InternalStreamingServiceCallback;
+import android.telephony.mbms.InternalStreamingSessionCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsStreamingSessionCallback;
+import android.telephony.mbms.MbmsUtils;
+import android.telephony.mbms.StreamingService;
+import android.telephony.mbms.StreamingServiceCallback;
+import android.telephony.mbms.StreamingServiceInfo;
+import android.telephony.mbms.vendor.IMbmsStreamingService;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * This class provides functionality for streaming media over MBMS.
+ */
+public class MbmsStreamingSession implements AutoCloseable {
+    private static final String LOG_TAG = "MbmsStreamingSession";
+
+    /**
+     * Service action which must be handled by the middleware implementing the MBMS streaming
+     * interface.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String MBMS_STREAMING_SERVICE_ACTION =
+            "android.telephony.action.EmbmsStreaming";
+
+    /**
+     * Metadata key that specifies the component name of the service to bind to for file-download.
+     * @hide
+     */
+    @TestApi
+    public static final String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA =
+            "mbms-streaming-service-override";
+
+    private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
+    private AtomicReference<IMbmsStreamingService> mService = new AtomicReference<>(null);
+    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, "Received death notification");
+        }
+    };
+
+    private InternalStreamingSessionCallback mInternalCallback;
+    private ServiceConnection mServiceConnection;
+    private Set<StreamingService> mKnownActiveStreamingServices = new ArraySet<>();
+
+    private final Context mContext;
+    private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
+
+    /** @hide */
+    private MbmsStreamingSession(Context context, Executor executor, int subscriptionId,
+            MbmsStreamingSessionCallback callback) {
+        mContext = context;
+        mSubscriptionId = subscriptionId;
+        mInternalCallback = new InternalStreamingSessionCallback(callback, executor);
+    }
+
+    /**
+     * Create a new {@link MbmsStreamingSession} using the given subscription ID.
+     *
+     * Note that this call will bind a remote service. You may not call this method on your app's
+     * main thread.
+     *
+     * You may only have one instance of {@link MbmsStreamingSession} per UID. If you call this
+     * method while there is an active instance of {@link MbmsStreamingSession} in your process
+     * (in other words, one that has not had {@link #close()} called on it), this method will
+     * throw an {@link IllegalStateException}. If you call this method in a different process
+     * running under the same UID, an error will be indicated via
+     * {@link MbmsStreamingSessionCallback#onError(int, String)}.
+     *
+     * Note that initialization may fail asynchronously. If you wish to try again after you
+     * receive such an asynchronous error, you must call {@link #close()} on the instance of
+     * {@link MbmsStreamingSession} that you received before calling this method again.
+     *
+     * @param context The {@link Context} to use.
+     * @param executor The executor on which you wish to execute callbacks.
+     * @param subscriptionId The subscription ID to use.
+     * @param callback A callback object on which you wish to receive results of asynchronous
+     *                 operations.
+     * @return An instance of {@link MbmsStreamingSession}, or null if an error occurred.
+     */
+    public static @Nullable MbmsStreamingSession create(@NonNull Context context,
+            @NonNull Executor executor, int subscriptionId,
+            final @NonNull MbmsStreamingSessionCallback callback) {
+        if (!sIsInitialized.compareAndSet(false, true)) {
+            throw new IllegalStateException("Cannot create two instances of MbmsStreamingSession");
+        }
+        MbmsStreamingSession session = new MbmsStreamingSession(context, executor,
+                subscriptionId, callback);
+
+        final int result = session.bindAndInitialize();
+        if (result != MbmsErrors.SUCCESS) {
+            sIsInitialized.set(false);
+            executor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onError(result, null);
+                }
+            });
+            return null;
+        }
+        return session;
+    }
+
+    /**
+     * Create a new {@link MbmsStreamingSession} using the system default data subscription ID.
+     * See {@link #create(Context, Executor, int, MbmsStreamingSessionCallback)}.
+     */
+    public static MbmsStreamingSession create(@NonNull Context context,
+            @NonNull Executor executor, @NonNull MbmsStreamingSessionCallback callback) {
+        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
+    }
+
+    /**
+     * Terminates this instance. Also terminates
+     * any streaming services spawned from this instance as if
+     * {@link StreamingService#close()} had been called on them. After this method returns,
+     * no further callbacks originating from the middleware will be enqueued on the provided
+     * instance of {@link MbmsStreamingSessionCallback}, but callbacks that have already been
+     * enqueued will still be delivered.
+     *
+     * It is safe to call {@link #create(Context, Executor, int, MbmsStreamingSessionCallback)} to
+     * obtain another instance of {@link MbmsStreamingSession} immediately after this method
+     * returns.
+     *
+     * May throw an {@link IllegalStateException}
+     */
+    public void close() {
+        try {
+            IMbmsStreamingService streamingService = mService.get();
+            if (streamingService == null || mServiceConnection == null) {
+                // Ignore and return, assume already disposed.
+                return;
+            }
+            streamingService.dispose(mSubscriptionId);
+            for (StreamingService s : mKnownActiveStreamingServices) {
+                s.getCallback().stop();
+            }
+            mKnownActiveStreamingServices.clear();
+            mContext.unbindService(mServiceConnection);
+        } catch (RemoteException e) {
+            // Ignore for now
+        } finally {
+            mService.set(null);
+            sIsInitialized.set(false);
+            mServiceConnection = null;
+            mInternalCallback.stop();
+        }
+    }
+
+    /**
+     * An inspection API to retrieve the list of streaming media currently be advertised.
+     * The results are returned asynchronously via
+     * {@link MbmsStreamingSessionCallback#onStreamingServicesUpdated(List)} on the callback
+     * provided upon creation.
+     *
+     * Multiple calls replace the list of service classes of interest.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
+     *
+     * @param serviceClassList A list of streaming service classes that the app would like updates
+     *                         on. The exact names of these classes should be negotiated with the
+     *                         wireless carrier separately.
+     */
+    public void requestUpdateStreamingServices(List<String> serviceClassList) {
+        IMbmsStreamingService streamingService = mService.get();
+        if (streamingService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+        try {
+            int returnCode = streamingService.requestUpdateStreamingServices(
+                    mSubscriptionId, serviceClassList);
+            if (returnCode == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (returnCode != MbmsErrors.SUCCESS) {
+                sendErrorToApp(returnCode, null);
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        }
+    }
+
+    /**
+     * Starts streaming a requested service, reporting status to the indicated callback.
+     * Returns an object used to control that stream. The stream may not be ready for consumption
+     * immediately upon return from this method -- wait until the streaming state has been
+     * reported via
+     * {@link android.telephony.mbms.StreamingServiceCallback#onStreamStateUpdated(int, int)}
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * Asynchronous errors through the callback include any of the errors in
+     * {@link MbmsErrors.GeneralErrors} or
+     * {@link MbmsErrors.StreamingErrors}.
+     *
+     * @param serviceInfo The information about the service to stream.
+     * @param executor The executor on which you wish to execute callbacks for this stream.
+     * @param callback A callback that'll be called when something about the stream changes.
+     * @return An instance of {@link StreamingService} through which the stream can be controlled.
+     *         May be {@code null} if an error occurred.
+     */
+    public @Nullable StreamingService startStreaming(StreamingServiceInfo serviceInfo,
+            @NonNull Executor executor, StreamingServiceCallback callback) {
+        IMbmsStreamingService streamingService = mService.get();
+        if (streamingService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
+                callback, executor);
+
+        StreamingService serviceForApp = new StreamingService(
+                mSubscriptionId, streamingService, this, serviceInfo, serviceCallback);
+        mKnownActiveStreamingServices.add(serviceForApp);
+
+        try {
+            int returnCode = streamingService.startStreaming(
+                    mSubscriptionId, serviceInfo.getServiceId(), serviceCallback);
+            if (returnCode == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (returnCode != MbmsErrors.SUCCESS) {
+                sendErrorToApp(returnCode, null);
+                return null;
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return null;
+        }
+
+        return serviceForApp;
+    }
+
+    /** @hide */
+    public void onStreamingServiceStopped(StreamingService service) {
+        mKnownActiveStreamingServices.remove(service);
+    }
+
+    private int bindAndInitialize() {
+        mServiceConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                IMbmsStreamingService streamingService =
+                        IMbmsStreamingService.Stub.asInterface(service);
+                int result;
+                try {
+                    result = streamingService.initialize(mInternalCallback,
+                            mSubscriptionId);
+                } catch (RemoteException e) {
+                    Log.e(LOG_TAG, "Service died before initialization");
+                    sendErrorToApp(
+                            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                            e.toString());
+                    sIsInitialized.set(false);
+                    return;
+                } catch (RuntimeException e) {
+                    Log.e(LOG_TAG, "Runtime exception during initialization");
+                    sendErrorToApp(
+                            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                            e.toString());
+                    sIsInitialized.set(false);
+                    return;
+                }
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not return"
+                            + " an unknown error code");
+                }
+                if (result != MbmsErrors.SUCCESS) {
+                    sendErrorToApp(result, "Error returned during initialization");
+                    sIsInitialized.set(false);
+                    return;
+                }
+                try {
+                    streamingService.asBinder().linkToDeath(mDeathRecipient, 0);
+                } catch (RemoteException e) {
+                    sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                            "Middleware lost during initialization");
+                    sIsInitialized.set(false);
+                    return;
+                }
+                mService.set(streamingService);
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                sIsInitialized.set(false);
+                mService.set(null);
+            }
+
+            @Override
+            public void onNullBinding(ComponentName name) {
+                Log.w(LOG_TAG, "bindAndInitialize: Remote service returned null");
+                sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                        "Middleware service binding returned null");
+                sIsInitialized.set(false);
+                mService.set(null);
+                mContext.unbindService(this);
+            }
+        };
+        return MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION, mServiceConnection);
+    }
+
+    private void sendErrorToApp(int errorCode, String message) {
+        try {
+            mInternalCallback.onError(errorCode, message);
+        } catch (RemoteException e) {
+            // Ignore, should not happen locally.
+        }
+    }
+}
diff --git a/android-35/android/telephony/MmsManager.java b/android-35/android/telephony/MmsManager.java
new file mode 100644
index 0000000..b893b45
--- /dev/null
+++ b/android-35/android/telephony/MmsManager.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2019 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.app.ActivityThread;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.IMms;
+
+/**
+ * Manages MMS operations such as sending multimedia messages.
+ * Get this object by calling Context#getSystemService(Context#MMS_SERVICE).
+ * @hide
+ */
+@SystemService(Context.MMS_SERVICE)
+public class MmsManager {
+    private static final String TAG = "MmsManager";
+    private final Context mContext;
+
+    /**
+     * @hide
+     */
+    public MmsManager(@NonNull Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Send an MMS message
+     *
+     * @param subId the subscription id
+     * @param contentUri the content Uri from which the message pdu will be read
+     * @param locationUrl the optional location url where message should be sent to
+     * @param configOverrides the carrier-specific messaging configuration values to override for
+     *                        sending the message.
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is broadcast when the message
+     *                   is successfully sent, or failed
+     * @param messageId an id that uniquely identifies the message requested to be sent.
+     *                  Used for logging and diagnostics purposes. The id may be 0. The messageId
+     *                  can be found in radio logs from logcat.
+     */
+    public void sendMultimediaMessage(int subId, @NonNull Uri contentUri,
+            @Nullable String locationUrl, @Nullable Bundle configOverrides,
+            @Nullable PendingIntent sentIntent, long messageId) {
+        try {
+            final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+            if (iMms == null) {
+                return;
+            }
+
+            iMms.sendMessage(subId, ActivityThread.currentPackageName(), contentUri,
+                    locationUrl, configOverrides, sentIntent, messageId,
+                    mContext.getAttributionTag());
+        } catch (RemoteException e) {
+            // Ignore it
+        }
+    }
+
+    /**
+     * Download an MMS message from carrier by a given location URL
+     *
+     * @param subId the subscription id
+     * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
+     *  from the MMS WAP push notification
+     * @param contentUri the content uri to which the downloaded pdu will be written
+     * @param configOverrides the carrier-specific messaging configuration values to override for
+     *  downloading the message.
+     * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is downloaded, or the download is failed
+     * @param messageId an id that uniquely identifies the message requested to be downloaded.
+     *                  Used for logging and diagnostics purposes. The id may be 0. The messageId
+     *                  can be found in radio logs from logcat.
+     * @throws IllegalArgumentException if locationUrl or contentUri is empty
+     */
+    public void downloadMultimediaMessage(int subId, @NonNull String locationUrl,
+            @NonNull Uri contentUri, @Nullable Bundle configOverrides,
+            @Nullable PendingIntent downloadedIntent, long messageId) {
+        try {
+            final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+            if (iMms == null) {
+                return;
+            }
+            iMms.downloadMessage(subId, ActivityThread.currentPackageName(),
+                    locationUrl, contentUri, configOverrides, downloadedIntent,
+                    messageId, mContext.getAttributionTag());
+        } catch (RemoteException e) {
+            // Ignore it
+        }
+    }
+}
diff --git a/android-35/android/telephony/ModemActivityInfo.java b/android-35/android/telephony/ModemActivityInfo.java
new file mode 100644
index 0000000..3d3c2e8
--- /dev/null
+++ b/android-35/android/telephony/ModemActivityInfo.java
@@ -0,0 +1,604 @@
+/*
+ * Copyright (C) 2015 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;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.telephony.ServiceState.FrequencyRange;
+import android.util.Range;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Contains information about the modem's activity. May be useful for power stats reporting.
+ * @hide
+ */
[email protected]
+@SystemApi
+public final class ModemActivityInfo implements Parcelable {
+    private static final int TX_POWER_LEVELS = 5;
+
+    /**
+     * Corresponds to transmit power of less than 0dBm.
+     */
+    public static final int TX_POWER_LEVEL_0 = 0;
+
+    /**
+     * Corresponds to transmit power between 0dBm and 5dBm.
+     */
+    public static final int TX_POWER_LEVEL_1 = 1;
+
+    /**
+     * Corresponds to transmit power between 5dBm and 15dBm.
+     */
+    public static final int TX_POWER_LEVEL_2 = 2;
+
+    /**
+     * Corresponds to transmit power between 15dBm and 20dBm.
+     */
+    public static final int TX_POWER_LEVEL_3 = 3;
+
+    /**
+     * Corresponds to transmit power above 20dBm.
+     */
+    public static final int TX_POWER_LEVEL_4 = 4;
+
+    /**
+     * The number of transmit power levels. Fixed by HAL definition.
+     */
+    public static int getNumTxPowerLevels() {
+        return TX_POWER_LEVELS;
+    }
+
+    /** @hide */
+    @IntDef(prefix = {"TX_POWER_LEVEL_"}, value = {
+            TX_POWER_LEVEL_0,
+            TX_POWER_LEVEL_1,
+            TX_POWER_LEVEL_2,
+            TX_POWER_LEVEL_3,
+            TX_POWER_LEVEL_4,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TxPowerLevel {}
+
+    private static final Range<Integer>[] TX_POWER_RANGES = new Range[] {
+        new Range<>(Integer.MIN_VALUE, 0),
+        new Range<>(0, 5),
+        new Range<>(5, 15),
+        new Range<>(15, 20),
+        new Range<>(20, Integer.MAX_VALUE)
+    };
+
+    private long mTimestamp;
+    private int mSleepTimeMs;
+    private int mIdleTimeMs;
+    private int[] mTotalTxTimeMs;
+    private int mTotalRxTimeMs;
+    private int mSizeOfSpecificInfo;
+    private ActivityStatsTechSpecificInfo[] mActivityStatsTechSpecificInfo;
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
+                        @NonNull int[] txTimeMs, int rxTimeMs) {
+        Objects.requireNonNull(txTimeMs);
+        if (txTimeMs.length != TX_POWER_LEVELS) {
+            throw new IllegalArgumentException("txTimeMs must have length == TX_POWER_LEVELS");
+        }
+        mTimestamp = timestamp;
+        mSleepTimeMs = sleepTimeMs;
+        mIdleTimeMs = idleTimeMs;
+        mTotalTxTimeMs = txTimeMs;
+        mTotalRxTimeMs = rxTimeMs;
+
+        mActivityStatsTechSpecificInfo = new ActivityStatsTechSpecificInfo[1];
+        mSizeOfSpecificInfo = mActivityStatsTechSpecificInfo.length;
+        mActivityStatsTechSpecificInfo[0] =
+                new ActivityStatsTechSpecificInfo(
+                        AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+                        ServiceState.FREQUENCY_RANGE_UNKNOWN,
+                        txTimeMs,
+                        rxTimeMs);
+    }
+
+    /**
+     * Provided for convenience in manipulation since the API exposes long values but internal
+     * representations are ints.
+     * @hide
+     */
+    public ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs,
+            @NonNull int[] txTimeMs, long rxTimeMs) {
+        this(timestamp, (int) sleepTimeMs, (int) idleTimeMs, txTimeMs, (int) rxTimeMs);
+    }
+
+    /** @hide */
+    public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
+                        @NonNull ActivityStatsTechSpecificInfo[] activityStatsTechSpecificInfo) {
+        mTimestamp = timestamp;
+        mSleepTimeMs = sleepTimeMs;
+        mIdleTimeMs = idleTimeMs;
+        mActivityStatsTechSpecificInfo = activityStatsTechSpecificInfo;
+        mSizeOfSpecificInfo = mActivityStatsTechSpecificInfo.length;
+        mTotalTxTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+        for (int i = 0; i < getNumTxPowerLevels(); i++) {
+            for (int j = 0; j < getSpecificInfoLength(); j++) {
+                mTotalTxTimeMs[i] = mTotalTxTimeMs[i]
+                            + (int) mActivityStatsTechSpecificInfo[j].getTransmitTimeMillis(i);
+            }
+        }
+        mTotalRxTimeMs = 0;
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            mTotalRxTimeMs =
+                    mTotalRxTimeMs + (int) mActivityStatsTechSpecificInfo[i].getReceiveTimeMillis();
+        }
+    }
+
+    /**
+     * Provided for convenience in manipulation since the API exposes long values but internal
+     * representations are ints.
+     * @hide
+     */
+    public ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs,
+                        @NonNull ActivityStatsTechSpecificInfo[] activityStatsTechSpecificInfo) {
+        this(timestamp, (int) sleepTimeMs, (int) idleTimeMs, activityStatsTechSpecificInfo);
+    }
+
+    @Override
+    public String toString() {
+        return "ModemActivityInfo{"
+            + " mTimestamp="
+            + mTimestamp
+            + " mSleepTimeMs="
+            + mSleepTimeMs
+            + " mIdleTimeMs="
+            + mIdleTimeMs
+            + " mActivityStatsTechSpecificInfo="
+            + Arrays.toString(mActivityStatsTechSpecificInfo)
+            + "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ModemActivityInfo> CREATOR =
+            new Parcelable.Creator<ModemActivityInfo>() {
+        public ModemActivityInfo createFromParcel(@NonNull Parcel in) {
+            long timestamp = in.readLong();
+            int sleepTimeMs = in.readInt();
+            int idleTimeMs = in.readInt();
+            Parcelable[] tempSpecifiers =
+                    in.createTypedArray(ActivityStatsTechSpecificInfo.CREATOR);
+            ActivityStatsTechSpecificInfo[] activityStatsTechSpecificInfo;
+            activityStatsTechSpecificInfo =
+                    new ActivityStatsTechSpecificInfo[tempSpecifiers.length];
+            for (int i = 0; i < tempSpecifiers.length; i++) {
+                activityStatsTechSpecificInfo[i] =
+                                (ActivityStatsTechSpecificInfo) tempSpecifiers[i];
+                    }
+            return new ModemActivityInfo(
+                    timestamp, sleepTimeMs, idleTimeMs, activityStatsTechSpecificInfo);
+        }
+
+        public ModemActivityInfo[] newArray(int size) {
+            return new ModemActivityInfo[size];
+        }
+    };
+
+    /**
+     * @param dest The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written.
+     */
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeLong(mTimestamp);
+        dest.writeInt(mSleepTimeMs);
+        dest.writeInt(mIdleTimeMs);
+        dest.writeTypedArray(mActivityStatsTechSpecificInfo, flags);
+    }
+
+    /**
+     * Gets the timestamp at which this modem activity info was recorded.
+     *
+     * @return The timestamp, as returned by {@link SystemClock#elapsedRealtime()}, when this {@link
+     *     ModemActivityInfo} was recorded.
+     */
+    public @ElapsedRealtimeLong long getTimestampMillis() {
+        return mTimestamp;
+    }
+
+    /** @hide */
+    public void setTimestamp(long timestamp) {
+        mTimestamp = timestamp;
+    }
+
+    /**
+     * Gets the amount of time the modem spent transmitting at a certain power level.
+     *
+     * @param powerLevel The power level to query.
+     * @return The amount of time, in milliseconds, that the modem spent transmitting at the given
+     *     power level.
+     */
+    public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel(
+            @TxPowerLevel int powerLevel) {
+        long txTimeMsAtPowerLevel = 0;
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            txTimeMsAtPowerLevel +=
+                    mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis(powerLevel);
+        }
+        return txTimeMsAtPowerLevel;
+    }
+
+    /** @hide */
+    public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel(
+            @TxPowerLevel int powerLevel, int rat) {
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
+                return mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis(powerLevel);
+            }
+        }
+        return 0;
+    }
+
+    /** @hide */
+    public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel(
+            @TxPowerLevel int powerLevel, int rat, @FrequencyRange int freq) {
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat
+                    && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
+                return mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis(powerLevel);
+            }
+        }
+        return 0;
+    }
+    /**
+     * Gets the range of transmit powers corresponding to a certain power level.
+     *
+     * @param powerLevel The power level to query
+     * @return A {@link Range} object representing the range of intensities (in dBm) to which this
+     * power level corresponds.
+     */
+    public @NonNull Range<Integer> getTransmitPowerRange(@TxPowerLevel int powerLevel) {
+        return TX_POWER_RANGES[powerLevel];
+    }
+
+    /** @hide */
+    public int getSpecificInfoRat(int index) {
+        return mActivityStatsTechSpecificInfo[index].getRat();
+    }
+
+    /** @hide */
+    public int getSpecificInfoFrequencyRange(int index) {
+        return mActivityStatsTechSpecificInfo[index].getFrequencyRange();
+    }
+    /** @hide */
+    public void setTransmitTimeMillis(int[] txTimeMs) {
+        mTotalTxTimeMs = Arrays.copyOf(txTimeMs, TX_POWER_LEVELS);
+    }
+    /** @hide */
+    public void setTransmitTimeMillis(int rat, int[] txTimeMs) {
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
+                mActivityStatsTechSpecificInfo[i].setTransmitTimeMillis(txTimeMs);
+            }
+        }
+    }
+    /** @hide */
+    public void setTransmitTimeMillis(int rat, int freq, int[] txTimeMs) {
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat
+                    && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
+                mActivityStatsTechSpecificInfo[i].setTransmitTimeMillis(txTimeMs);
+            }
+        }
+    }
+    /**
+     * @return The raw array of transmit power durations
+     * @hide
+     */
+    @NonNull
+    public int[] getTransmitTimeMillis() {
+        return mTotalTxTimeMs;
+    }
+
+    /** @hide */
+    public int[] getTransmitTimeMillis(@AccessNetworkConstants.RadioAccessNetworkType int rat) {
+        for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
+                return mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis();
+            }
+        }
+        return new int[5];
+    }
+
+    /** @hide */
+    public int[] getTransmitTimeMillis(
+            @AccessNetworkConstants.RadioAccessNetworkType int rat, @FrequencyRange int freq) {
+        for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat
+                    && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
+                return mActivityStatsTechSpecificInfo[i].getTransmitTimeMillis();
+            }
+        }
+        return new int[5];
+    }
+
+    /**
+     * Gets the amount of time (in milliseconds) when the modem is in a low power or sleep state.
+     *
+     * @return Time in milliseconds.
+     */
+    public @DurationMillisLong long getSleepTimeMillis() {
+        return mSleepTimeMs;
+    }
+
+    /** @hide */
+    public void setSleepTimeMillis(int sleepTimeMillis) {
+        mSleepTimeMs = sleepTimeMillis;
+    }
+
+    /**
+     * Provided for convenience, since the API surface needs to return longs but internal
+     * representations are ints.
+     *
+     * @hide
+     */
+    public void setSleepTimeMillis(long sleepTimeMillis) {
+        mSleepTimeMs = (int) sleepTimeMillis;
+    }
+
+    /**
+     * Computes the difference between this instance of {@link ModemActivityInfo} and another
+     * instance.
+     *
+     * This method should be used to compute the amount of activity that has happened between two
+     * samples of modem activity taken at separate times. The sample passed in as an argument to
+     * this method should be the one that's taken later in time (and therefore has more activity).
+     * @param other The other instance of {@link ModemActivityInfo} to diff against.
+     * @return An instance of {@link ModemActivityInfo} representing the difference in modem
+     * activity.
+     */
+    public @NonNull ModemActivityInfo getDelta(@NonNull ModemActivityInfo other) {
+        ActivityStatsTechSpecificInfo[] mDeltaSpecificInfo;
+        mDeltaSpecificInfo = new ActivityStatsTechSpecificInfo[other.getSpecificInfoLength()];
+
+        boolean matched;
+        for (int i = 0; i < other.getSpecificInfoLength(); i++) {
+            matched = false;
+            for (int j = 0; j < getSpecificInfoLength(); j++) {
+                int rat = mActivityStatsTechSpecificInfo[j].getRat();
+                if (rat == other.mActivityStatsTechSpecificInfo[i].getRat() && !matched) {
+                    if (mActivityStatsTechSpecificInfo[j].getRat()
+                            == AccessNetworkConstants.AccessNetworkType.NGRAN) {
+                        if (other.mActivityStatsTechSpecificInfo[i].getFrequencyRange()
+                                == mActivityStatsTechSpecificInfo[j].getFrequencyRange()) {
+                            int freq = mActivityStatsTechSpecificInfo[j].getFrequencyRange();
+                            int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+                            for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
+                                txTimeMs[lvl] =
+                                        (int) (other.getTransmitDurationMillisAtPowerLevel(
+                                                            lvl, rat, freq)
+                                                        - getTransmitDurationMillisAtPowerLevel(
+                                                            lvl, rat, freq));
+                            }
+                            matched = true;
+                            mDeltaSpecificInfo[i] =
+                                    new ActivityStatsTechSpecificInfo(
+                                            rat,
+                                            freq,
+                                            txTimeMs,
+                                            (int) (other.getReceiveTimeMillis(rat, freq)
+                                                        - getReceiveTimeMillis(rat, freq)));
+                        }
+                    } else {
+                        int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+                        for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
+                            txTimeMs[lvl] =
+                                    (int) (other.getTransmitDurationMillisAtPowerLevel(lvl, rat)
+                                                - getTransmitDurationMillisAtPowerLevel(lvl, rat));
+                        }
+                        matched = true;
+                        mDeltaSpecificInfo[i] =
+                                new ActivityStatsTechSpecificInfo(
+                                        rat,
+                                        ServiceState.FREQUENCY_RANGE_UNKNOWN,
+                                        txTimeMs,
+                                        (int) (other.getReceiveTimeMillis(rat)
+                                                     - getReceiveTimeMillis(rat)));
+                    }
+                }
+            }
+            if (!matched) {
+                mDeltaSpecificInfo[i] = other.mActivityStatsTechSpecificInfo[i];
+            }
+        }
+        return new ModemActivityInfo(
+                other.getTimestampMillis(),
+                other.getSleepTimeMillis() - getSleepTimeMillis(),
+                other.getIdleTimeMillis() - getIdleTimeMillis(),
+                mDeltaSpecificInfo);
+    }
+
+    /**
+     * Gets the amount of time (in milliseconds) when the modem is awake but neither transmitting
+     * nor receiving.
+     *
+     * @return Time in milliseconds.
+     */
+    public @DurationMillisLong long getIdleTimeMillis() {
+        return mIdleTimeMs;
+    }
+
+    /** @hide */
+    public void setIdleTimeMillis(int idleTimeMillis) {
+        mIdleTimeMs = idleTimeMillis;
+    }
+
+    /**
+     * Provided for convenience, since the API surface needs to return longs but internal
+     * representations are ints.
+     *
+     * @hide
+     */
+    public void setIdleTimeMillis(long idleTimeMillis) {
+        mIdleTimeMs = (int) idleTimeMillis;
+    }
+
+    /**
+     * Gets the amount of time (in milliseconds) when the modem is awake and receiving data.
+     *
+     * @return Time in milliseconds.
+     */
+    public @DurationMillisLong long getReceiveTimeMillis() {
+        return mTotalRxTimeMs;
+    }
+
+    /** @hide */
+    public @DurationMillisLong long getReceiveTimeMillis(int rat) {
+        for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
+                return mActivityStatsTechSpecificInfo[i].getReceiveTimeMillis();
+            }
+        }
+        return 0;
+    }
+    /** @hide */
+    public @DurationMillisLong long getReceiveTimeMillis(int rat, int freq) {
+        for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat
+                    && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
+                return mActivityStatsTechSpecificInfo[i].getReceiveTimeMillis();
+            }
+        }
+        return 0;
+    }
+
+    /** @hide */
+    public void setReceiveTimeMillis(int rxTimeMillis) {
+        mTotalRxTimeMs = rxTimeMillis;
+    }
+
+    /**
+     * Provided for convenience, since the API surface needs to return longs but internal
+     * representations are ints.
+     *
+     * @hide
+     */
+    public void setReceiveTimeMillis(long receiveTimeMillis) {
+        mTotalRxTimeMs = (int) receiveTimeMillis;
+    }
+
+    /** @hide */
+    public void setReceiveTimeMillis(int rat, long receiveTimeMillis) {
+        for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat) {
+                mActivityStatsTechSpecificInfo[i].setReceiveTimeMillis(receiveTimeMillis);
+            }
+        }
+    }
+
+    /** @hide */
+    public void setReceiveTimeMillis(int rat, int freq, long receiveTimeMillis) {
+        for (int i = 0; i < mActivityStatsTechSpecificInfo.length; i++) {
+            if (mActivityStatsTechSpecificInfo[i].getRat() == rat
+                    && mActivityStatsTechSpecificInfo[i].getFrequencyRange() == freq) {
+                mActivityStatsTechSpecificInfo[i].setReceiveTimeMillis(receiveTimeMillis);
+            }
+        }
+    }
+
+    /** @hide */
+    public int getSpecificInfoLength() {
+        return mSizeOfSpecificInfo;
+    }
+
+    /**
+     * Indicates if the modem has reported valid {@link ModemActivityInfo}.
+     *
+     * @return {@code true} if this {@link ModemActivityInfo} record is valid,
+     * {@code false} otherwise.
+     * @hide
+     */
+    @TestApi
+    public boolean isValid() {
+        if (mActivityStatsTechSpecificInfo == null) {
+            return false;
+        } else {
+            boolean isTxPowerValid = true;
+            boolean isRxPowerValid = true;
+            for (int i = 0; i < getSpecificInfoLength(); i++) {
+                if (!mActivityStatsTechSpecificInfo[i].isTxPowerValid()) {
+                    isTxPowerValid = false;
+                }
+                if (!mActivityStatsTechSpecificInfo[i].isRxPowerValid()) {
+                    isRxPowerValid = false;
+                }
+            }
+            return isTxPowerValid
+                    && isRxPowerValid
+                    && ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0) && !isEmpty());
+        }
+    }
+
+    /** @hide */
+    @TestApi
+    public boolean isEmpty() {
+        boolean isTxPowerEmpty = true;
+        boolean isRxPowerEmpty = true;
+        for (int i = 0; i < getSpecificInfoLength(); i++) {
+            if (!mActivityStatsTechSpecificInfo[i].isTxPowerEmpty()) {
+                isTxPowerEmpty = false;
+            }
+            if (!mActivityStatsTechSpecificInfo[i].isRxPowerEmpty()) {
+                isRxPowerEmpty = false;
+            }
+        }
+        return isTxPowerEmpty
+                && ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0) && isRxPowerEmpty);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ModemActivityInfo that = (ModemActivityInfo) o;
+        return mTimestamp == that.mTimestamp
+                && mSleepTimeMs == that.mSleepTimeMs
+                && mIdleTimeMs == that.mIdleTimeMs
+                && mSizeOfSpecificInfo == that.mSizeOfSpecificInfo
+                && Arrays.equals(
+                        mActivityStatsTechSpecificInfo, that.mActivityStatsTechSpecificInfo);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mTimestamp, mSleepTimeMs, mIdleTimeMs, mTotalRxTimeMs);
+        result = 31 * result + Arrays.hashCode(mTotalTxTimeMs);
+        return result;
+    }
+}
diff --git a/android-35/android/telephony/ModemInfo.java b/android-35/android/telephony/ModemInfo.java
new file mode 100644
index 0000000..c0833af
--- /dev/null
+++ b/android-35/android/telephony/ModemInfo.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Information of a single logical modem indicating
+ * its id, supported rats and whether it supports voice or data, etc.
+ * @hide
+ */
+public class ModemInfo implements Parcelable {
+    public final int modemId;
+    public final int rat; /* bitset */
+    public final boolean isVoiceSupported;
+    public final boolean isDataSupported;
+
+    // TODO b/121394331: Clean up this class after V1_1.PhoneCapability cleanup.
+    public ModemInfo(int modemId) {
+        this(modemId, 0, true, true);
+    }
+
+    public ModemInfo(int modemId, int rat, boolean isVoiceSupported, boolean isDataSupported) {
+        this.modemId = modemId;
+        this.rat = rat;
+        this.isVoiceSupported = isVoiceSupported;
+        this.isDataSupported = isDataSupported;
+    }
+
+    public ModemInfo(Parcel in) {
+        modemId = in.readInt();
+        rat = in.readInt();
+        isVoiceSupported = in.readBoolean();
+        isDataSupported = in.readBoolean();
+    }
+
+    @Override
+    public String toString() {
+        return "modemId=" + modemId + " rat=" + rat + " isVoiceSupported:" + isVoiceSupported
+                + " isDataSupported:" + isDataSupported;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(modemId, rat, isVoiceSupported, isDataSupported);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof ModemInfo) || hashCode() != o.hashCode()) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        ModemInfo s = (ModemInfo) o;
+
+        return (modemId == s.modemId
+                && rat == s.rat
+                && isVoiceSupported == s.isVoiceSupported
+                && isDataSupported == s.isDataSupported);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public @ContentsFlags int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(Parcel dest, @WriteFlags int flags) {
+        dest.writeInt(modemId);
+        dest.writeInt(rat);
+        dest.writeBoolean(isVoiceSupported);
+        dest.writeBoolean(isDataSupported);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ModemInfo> CREATOR = new Parcelable.Creator() {
+        public ModemInfo createFromParcel(Parcel in) {
+            return new ModemInfo(in);
+        }
+
+        public ModemInfo[] newArray(int size) {
+            return new ModemInfo[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/NeighboringCellInfo.java b/android-35/android/telephony/NeighboringCellInfo.java
new file mode 100644
index 0000000..5f46799
--- /dev/null
+++ b/android-35/android/telephony/NeighboringCellInfo.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2006 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;
+
+import static android.telephony.TelephonyManager.NETWORK_TYPE_EDGE;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_GPRS;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_HSDPA;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_HSPA;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_HSUPA;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_UMTS;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+/**
+ * Represents the neighboring cell information, including
+ * Received Signal Strength and Cell ID location.
+ *
+ * @deprecated This class should not be used by any app targeting
+ *     {@link android.os.Build.VERSION_CODES#Q Android Q} or higher. Instead callers should use
+ *     {@link android.telephony.CellInfo CellInfo}.
+ */
+@Deprecated
+public class NeighboringCellInfo implements Parcelable
+{
+    /**
+     * Signal strength is not available
+     */
+    static final public int UNKNOWN_RSSI = 99;
+    /**
+     * Cell location is not available
+     */
+    static final public int UNKNOWN_CID = -1;
+
+    /**
+     * In GSM, mRssi is the Received RSSI;
+     * In UMTS, mRssi is the Level index of CPICH Received Signal Code Power
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mRssi;
+    /**
+     * CID in 16 bits format in GSM. Return UNKNOWN_CID in UMTS and CMDA.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mCid;
+    /**
+     * LAC in 16 bits format in GSM. Return UNKNOWN_CID in UMTS and CMDA.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mLac;
+    /**
+     * Primary Scrambling Code in 9 bits format in UMTS
+     * Return UNKNOWN_CID in GSM and CMDA.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mPsc;
+    /**
+     * Radio network type, value is one of following
+     * TelephonyManager.NETWORK_TYPE_XXXXXX.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mNetworkType;
+
+    /**
+     * Empty constructor.  Initializes the RSSI and CID.
+     *
+     * NeighboringCellInfo is one time shot for the neighboring cells based on
+     * the radio network type at that moment. Its constructor needs radio network
+     * type.
+     *
+     * @deprecated by {@link #NeighboringCellInfo(int, String, int)}
+     */
+    @Deprecated
+    public NeighboringCellInfo() {
+        mRssi = UNKNOWN_RSSI;
+        mLac = UNKNOWN_CID;
+        mCid = UNKNOWN_CID;
+        mPsc = UNKNOWN_CID;
+        mNetworkType = NETWORK_TYPE_UNKNOWN;
+    }
+
+    /**
+     * Initialize the object from rssi and cid.
+     *
+     * NeighboringCellInfo is one time shot for the neighboring cells based on
+     * the radio network type at that moment. Its constructor needs radio network
+     * type.
+     *
+     * @deprecated by {@link #NeighboringCellInfo(int, String, int)}
+     */
+    @Deprecated
+    public NeighboringCellInfo(int rssi, int cid) {
+        mRssi = rssi;
+        mCid = cid;
+    }
+
+    /** @hide */
+    public NeighboringCellInfo(final CellInfoGsm info) {
+        mNetworkType = TelephonyManager.NETWORK_TYPE_GPRS;
+
+        mRssi = info.getCellSignalStrength().getAsuLevel();
+        if (mRssi == Integer.MAX_VALUE) mRssi = UNKNOWN_RSSI;
+
+        mLac = info.getCellIdentity().getLac();
+        if (mLac == Integer.MAX_VALUE) mLac = UNKNOWN_CID;
+
+        mCid = info.getCellIdentity().getCid();
+        if (mCid == Integer.MAX_VALUE) mCid = UNKNOWN_CID;
+
+        mPsc = UNKNOWN_CID;
+    }
+
+    /** @hide */
+    public NeighboringCellInfo(final CellInfoWcdma info) {
+        mNetworkType = TelephonyManager.NETWORK_TYPE_UMTS;
+
+        mRssi = info.getCellSignalStrength().getAsuLevel();
+        if (mRssi == Integer.MAX_VALUE) mRssi = UNKNOWN_RSSI;
+
+        mLac = info.getCellIdentity().getLac();
+        if (mLac == Integer.MAX_VALUE) mLac = UNKNOWN_CID;
+
+        mCid = info.getCellIdentity().getCid();
+        if (mCid == Integer.MAX_VALUE) mCid = UNKNOWN_CID;
+
+        mPsc = info.getCellIdentity().getPsc();
+        if (mPsc == Integer.MAX_VALUE) mPsc = UNKNOWN_CID;
+    }
+
+    /**
+     * Initialize the object from rssi, location string, and radioType
+     * radioType is one of following
+     * {@link TelephonyManager#NETWORK_TYPE_GPRS TelephonyManager.NETWORK_TYPE_GPRS},
+     * {@link TelephonyManager#NETWORK_TYPE_EDGE TelephonyManager.NETWORK_TYPE_EDGE},
+     * {@link TelephonyManager#NETWORK_TYPE_UMTS TelephonyManager.NETWORK_TYPE_UMTS},
+     * {@link TelephonyManager#NETWORK_TYPE_HSDPA TelephonyManager.NETWORK_TYPE_HSDPA},
+     * {@link TelephonyManager#NETWORK_TYPE_HSUPA TelephonyManager.NETWORK_TYPE_HSUPA},
+     * and {@link TelephonyManager#NETWORK_TYPE_HSPA TelephonyManager.NETWORK_TYPE_HSPA}.
+     */
+    public NeighboringCellInfo(int rssi, String location, int radioType) {
+        // set default value
+        mRssi = rssi;
+        mNetworkType = NETWORK_TYPE_UNKNOWN;
+        mPsc = UNKNOWN_CID;
+        mLac = UNKNOWN_CID;
+        mCid = UNKNOWN_CID;
+
+
+        // pad location string with leading "0"
+        int l = location.length();
+        if (l > 8) return;
+        if (l < 8) {
+            for (int i = 0; i < (8-l); i++) {
+                location = "0" + location;
+            }
+        }
+        // TODO - handle LTE and eHRPD (or find they can't be supported)
+        try {// set LAC/CID or PSC based on radioType
+            switch (radioType) {
+            case NETWORK_TYPE_GPRS:
+            case NETWORK_TYPE_EDGE:
+                mNetworkType = radioType;
+                // check if 0xFFFFFFFF for UNKNOWN_CID
+                if (!location.equalsIgnoreCase("FFFFFFFF")) {
+                    mCid = Integer.parseInt(location.substring(4), 16);
+                    mLac = Integer.parseInt(location.substring(0, 4), 16);
+                }
+                break;
+            case NETWORK_TYPE_UMTS:
+            case NETWORK_TYPE_HSDPA:
+            case NETWORK_TYPE_HSUPA:
+            case NETWORK_TYPE_HSPA:
+                mNetworkType = radioType;
+                mPsc = Integer.parseInt(location, 16);
+                break;
+            }
+        } catch (NumberFormatException e) {
+            // parsing location error
+            mPsc = UNKNOWN_CID;
+            mLac = UNKNOWN_CID;
+            mCid = UNKNOWN_CID;
+            mNetworkType = NETWORK_TYPE_UNKNOWN;
+        }
+    }
+
+    /**
+     * Initialize the object from a parcel.
+     */
+    public NeighboringCellInfo(Parcel in) {
+        mRssi = in.readInt();
+        mLac = in.readInt();
+        mCid = in.readInt();
+        mPsc = in.readInt();
+        mNetworkType = in.readInt();
+    }
+
+    /**
+     * @return received signal strength or UNKNOWN_RSSI if unknown
+     *
+     * For GSM, it is in "asu" ranging from 0 to 31 (dBm = -113 + 2*asu)
+     * 0 means "-113 dBm or less" and 31 means "-51 dBm or greater"
+     * For UMTS, it is the Level index of CPICH RSCP defined in TS 25.125
+     */
+    public int getRssi() {
+        return mRssi;
+    }
+
+    /**
+     * @return LAC in GSM, 0xffff max legal value
+     *  UNKNOWN_CID if in UMTS or CMDA or unknown
+     */
+    public int getLac() {
+        return mLac;
+    }
+
+    /**
+     * @return cell id in GSM, 0xffff max legal value
+     *  UNKNOWN_CID if in UMTS or CDMA or unknown
+     */
+    public int getCid() {
+        return mCid;
+    }
+
+    /**
+     * @return Primary Scrambling Code in 9 bits format in UMTS, 0x1ff max value
+     *  UNKNOWN_CID if in GSM or CMDA or unknown
+     */
+    public int getPsc() {
+        return mPsc;
+    }
+
+    /**
+     * @return Radio network type while neighboring cell location is stored.
+     *
+     * Return {@link TelephonyManager#NETWORK_TYPE_UNKNOWN TelephonyManager.NETWORK_TYPE_UNKNOWN}
+     * means that the location information is unavailable.
+     *
+     * Return {@link TelephonyManager#NETWORK_TYPE_GPRS TelephonyManager.NETWORK_TYPE_GPRS} or
+     * {@link TelephonyManager#NETWORK_TYPE_EDGE TelephonyManager.NETWORK_TYPE_EDGE}
+     * means that Neighboring Cell information is stored for GSM network, in
+     * which {@link NeighboringCellInfo#getLac NeighboringCellInfo.getLac} and
+     * {@link NeighboringCellInfo#getCid NeighboringCellInfo.getCid} should be
+     * called to access location.
+     *
+     * Return {@link TelephonyManager#NETWORK_TYPE_UMTS TelephonyManager.NETWORK_TYPE_UMTS},
+     * {@link TelephonyManager#NETWORK_TYPE_HSDPA TelephonyManager.NETWORK_TYPE_HSDPA},
+     * {@link TelephonyManager#NETWORK_TYPE_HSUPA TelephonyManager.NETWORK_TYPE_HSUPA},
+     * or {@link TelephonyManager#NETWORK_TYPE_HSPA TelephonyManager.NETWORK_TYPE_HSPA}
+     * means that Neighboring Cell information is stored for UMTS network, in
+     * which {@link NeighboringCellInfo#getPsc NeighboringCellInfo.getPsc}
+     * should be called to access location.
+     */
+    public int getNetworkType() {
+        return mNetworkType;
+    }
+    /**
+     * Set the cell id.
+     *
+     * NeighboringCellInfo is a one time shot for the neighboring cells based on
+     * the radio network type at that moment. It shouldn't be changed after
+     * creation.
+     *
+     * @deprecated cid value passed as in location parameter passed to constructor
+     *              {@link #NeighboringCellInfo(int, String, int)}
+     */
+    @Deprecated
+    public void setCid(int cid) {
+        mCid = cid;
+    }
+
+    /**
+     * Set the signal strength of the cell.
+     *
+     * NeighboringCellInfo is a one time shot for the neighboring cells based on
+     * the radio network type at that moment. It shouldn't be changed after
+     * creation.
+     *
+     * @deprecated initial rssi value passed as parameter to constructor
+     *              {@link #NeighboringCellInfo(int, String, int)}
+     */
+    @Deprecated
+    public void setRssi(int rssi) {
+        mRssi = rssi;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("[");
+        if (mPsc != UNKNOWN_CID) {
+            sb.append(Integer.toHexString(mPsc))
+                    .append("@").append(((mRssi == UNKNOWN_RSSI)? "-" : mRssi));
+        } else if(mLac != UNKNOWN_CID && mCid != UNKNOWN_CID) {
+            sb.append(Integer.toHexString(mLac))
+                    .append(Integer.toHexString(mCid))
+                    .append("@").append(((mRssi == UNKNOWN_RSSI)? "-" : mRssi));
+        }
+        sb.append("]");
+
+        return sb.toString();
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRssi);
+        dest.writeInt(mLac);
+        dest.writeInt(mCid);
+        dest.writeInt(mPsc);
+        dest.writeInt(mNetworkType);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<NeighboringCellInfo> CREATOR
+    = new Parcelable.Creator<NeighboringCellInfo>() {
+        public NeighboringCellInfo createFromParcel(Parcel in) {
+            return new NeighboringCellInfo(in);
+        }
+
+        public NeighboringCellInfo[] newArray(int size) {
+            return new NeighboringCellInfo[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/NetworkRegistrationInfo.java b/android-35/android/telephony/NetworkRegistrationInfo.java
new file mode 100644
index 0000000..0c324e6
--- /dev/null
+++ b/android-35/android/telephony/NetworkRegistrationInfo.java
@@ -0,0 +1,1220 @@
+/*
+ * Copyright 2017 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;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.Annotation.NetworkType;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * Description of a mobile network registration info
+ */
+public final class NetworkRegistrationInfo implements Parcelable {
+
+    /**
+     * A new registration state, REGISTRATION_STATE_EMERGENCY, is added to
+     * {@link NetworkRegistrationInfo}. This change will affect the result of getRegistration().
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final long RETURN_REGISTRATION_STATE_EMERGENCY = 255938466L;
+
+    /**
+     * Network domain
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "DOMAIN_", value = {DOMAIN_UNKNOWN, DOMAIN_CS, DOMAIN_PS, DOMAIN_CS_PS})
+    public @interface Domain {}
+
+    /** Unknown / Unspecified domain */
+    public static final int DOMAIN_UNKNOWN = 0;
+    /** Circuit switched domain */
+    public static final int DOMAIN_CS = android.hardware.radio.network.Domain.CS;
+    /** Packet switched domain */
+    public static final int DOMAIN_PS = android.hardware.radio.network.Domain.PS;
+    /** Applicable to both CS and PS Domain */
+    public static final int DOMAIN_CS_PS = DOMAIN_CS | DOMAIN_PS;
+
+    /**
+     * Network registration state
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "REGISTRATION_STATE_",
+            value = {REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, REGISTRATION_STATE_HOME,
+                    REGISTRATION_STATE_NOT_REGISTERED_SEARCHING, REGISTRATION_STATE_DENIED,
+                    REGISTRATION_STATE_UNKNOWN, REGISTRATION_STATE_ROAMING,
+                    REGISTRATION_STATE_EMERGENCY})
+    public @interface RegistrationState {}
+
+    /**
+     * Not registered. The device is not currently searching a new operator to register.
+     * @hide
+     */
+    @SystemApi
+    public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0;
+    /**
+     * Registered on home network.
+     * @hide
+     */
+    @SystemApi
+    public static final int REGISTRATION_STATE_HOME = 1;
+    /**
+     * Not registered. The device is currently searching a new operator to register.
+     * @hide
+     */
+    @SystemApi
+    public static final int REGISTRATION_STATE_NOT_REGISTERED_SEARCHING = 2;
+    /**
+     * Registration denied.
+     * @hide
+     */
+    @SystemApi
+    public static final int REGISTRATION_STATE_DENIED = 3;
+    /**
+     * Registration state is unknown.
+     * @hide
+     */
+    @SystemApi
+    public static final int REGISTRATION_STATE_UNKNOWN = 4;
+    /**
+     * Registered on roaming network.
+     * @hide
+     */
+    @SystemApi
+    public static final int REGISTRATION_STATE_ROAMING = 5;
+    /**
+     * Emergency attached in EPS or in 5GS.
+     * IMS service will skip emergency registration if the device is in
+     * emergency attached state. {@link #mEmergencyOnly} can be true
+     * even in case it's not in emergency attached state.
+     *
+     * Reference: 3GPP TS 24.301 9.9.3.11 EPS attach type.
+     * Reference: 3GPP TS 24.501 9.11.3.6 5GS registration result.
+     * @hide
+     */
+    @SystemApi
+    public static final int REGISTRATION_STATE_EMERGENCY = 6;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "NR_STATE_",
+            value = {NR_STATE_NONE, NR_STATE_RESTRICTED, NR_STATE_NOT_RESTRICTED,
+                    NR_STATE_CONNECTED})
+    public @interface NRState {}
+
+    /**
+     * The device isn't camped on an LTE cell or the LTE cell doesn't support E-UTRA-NR
+     * Dual Connectivity(EN-DC).
+     */
+    public static final int NR_STATE_NONE = 0;
+
+    /**
+     * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) but
+     * either the use of dual connectivity with NR(DCNR) is restricted or NR is not supported by
+     * the selected PLMN.
+     */
+    public static final int NR_STATE_RESTRICTED = 1;
+
+    /**
+     * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) and both
+     * the use of dual connectivity with NR(DCNR) is not restricted and NR is supported by the
+     * selected PLMN.
+     */
+    public static final int NR_STATE_NOT_RESTRICTED = 2;
+
+    /**
+     * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) and
+     * also connected to at least one 5G cell as a secondary serving cell.
+     */
+    public static final int NR_STATE_CONNECTED = 3;
+
+    /**
+     * Supported service type
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "SERVICE_TYPE_",
+            value = {SERVICE_TYPE_UNKNOWN, SERVICE_TYPE_VOICE, SERVICE_TYPE_DATA, SERVICE_TYPE_SMS,
+                    SERVICE_TYPE_VIDEO, SERVICE_TYPE_EMERGENCY, SERVICE_TYPE_MMS})
+    public @interface ServiceType {}
+
+    /**
+     * Unknown service
+     */
+    public static final int SERVICE_TYPE_UNKNOWN    = 0;
+
+    /**
+     * Voice service
+     */
+    public static final int SERVICE_TYPE_VOICE      = 1;
+
+    /**
+     * Data service
+     */
+    public static final int SERVICE_TYPE_DATA       = 2;
+
+    /**
+     * SMS service
+     */
+    public static final int SERVICE_TYPE_SMS        = 3;
+
+    /**
+     * Video service
+     */
+    public static final int SERVICE_TYPE_VIDEO      = 4;
+
+    /**
+     * Emergency service
+     */
+    public static final int SERVICE_TYPE_EMERGENCY  = 5;
+
+    /**
+     * MMS service
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final int SERVICE_TYPE_MMS = 6;
+
+    /** @hide  */
+    public static final int FIRST_SERVICE_TYPE = SERVICE_TYPE_VOICE;
+
+    /** @hide  */
+    public static final int LAST_SERVICE_TYPE = SERVICE_TYPE_MMS;
+
+    @Domain
+    private final int mDomain;
+
+    @TransportType
+    private final int mTransportType;
+
+    /**
+     * The true registration state of network, This is not affected by any carrier config or
+     * resource overlay.
+     */
+    @RegistrationState
+    private final int mNetworkRegistrationState;
+
+    /**
+     * The registration state that might have been overridden by config
+     */
+    @RegistrationState
+    private int mRegistrationState;
+
+    /**
+     * Save the {@link ServiceState.RoamingType roaming type}. it can be overridden roaming type
+     * from resource overlay or carrier config.
+     */
+    @ServiceState.RoamingType
+    private int mRoamingType;
+
+    @NetworkType
+    private int mAccessNetworkTechnology;
+
+    @NRState
+    private int mNrState;
+
+    private final int mRejectCause;
+
+    private final boolean mEmergencyOnly;
+
+    @ServiceType
+    private ArrayList<Integer> mAvailableServices;
+
+    @Nullable
+    private CellIdentity mCellIdentity;
+
+    @Nullable
+    private VoiceSpecificRegistrationInfo mVoiceSpecificInfo;
+
+    @Nullable
+    private DataSpecificRegistrationInfo mDataSpecificInfo;
+
+    @NonNull
+    private String mRplmn;
+
+    // Updated based on the accessNetworkTechnology
+    private boolean mIsUsingCarrierAggregation;
+
+    // Set to {@code true} when network is a non-terrestrial network.
+    private boolean mIsNonTerrestrialNetwork;
+
+    /**
+     * @param domain Network domain. Must be a {@link Domain}. For transport type
+     * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, this must set to {@link #DOMAIN_PS}.
+     * @param transportType Transport type.
+     * @param registrationState Network registration state. For transport type
+     * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, only
+     * {@link #REGISTRATION_STATE_HOME} and {@link #REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING}
+     * are valid states.
+     * @param accessNetworkTechnology Access network technology.For transport type
+     * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, set to
+     * {@link TelephonyManager#NETWORK_TYPE_IWLAN}.
+     * @param rejectCause Reason for denial if the registration state is
+     * {@link #REGISTRATION_STATE_DENIED}. Depending on {@code accessNetworkTechnology}, the values
+     * are defined in 3GPP TS 24.008 10.5.3.6 for UMTS, 3GPP TS 24.301 9.9.3.9 for LTE, and 3GPP2
+     * A.S0001 6.2.2.44 for CDMA. If the reject cause is not supported or unknown, set it to 0.
+     * // TODO: Add IWLAN reject cause reference
+     * @param emergencyOnly True if this registration is for emergency only.
+     * @param availableServices The list of the supported services.
+     * @param cellIdentity The identity representing a unique cell or wifi AP. Set to null if the
+     * information is not available.
+     * @param rplmn the registered plmn or the last plmn for attempted registration if reg failed.
+     * @param voiceSpecificInfo Voice specific registration information.
+     * @param dataSpecificInfo Data specific registration information.
+     * @param isNonTerrestrialNetwork {@code true} if network is a non-terrestrial network.
+     */
+    private NetworkRegistrationInfo(@Domain int domain, @TransportType int transportType,
+            @RegistrationState int registrationState,
+            @NetworkType int accessNetworkTechnology, int rejectCause,
+            boolean emergencyOnly, @Nullable @ServiceType List<Integer> availableServices,
+            @Nullable CellIdentity cellIdentity, @Nullable String rplmn,
+            @Nullable VoiceSpecificRegistrationInfo voiceSpecificInfo,
+            @Nullable DataSpecificRegistrationInfo dataSpecificInfo,
+            boolean isNonTerrestrialNetwork) {
+        mDomain = domain;
+        mTransportType = transportType;
+        mRegistrationState = registrationState;
+        mNetworkRegistrationState = registrationState;
+        mRoamingType = (registrationState == REGISTRATION_STATE_ROAMING)
+                ? ServiceState.ROAMING_TYPE_UNKNOWN : ServiceState.ROAMING_TYPE_NOT_ROAMING;
+        setAccessNetworkTechnology(accessNetworkTechnology);
+        mRejectCause = rejectCause;
+        mAvailableServices = (availableServices != null)
+                ? new ArrayList<>(availableServices) : new ArrayList<>();
+        mCellIdentity = cellIdentity;
+        mEmergencyOnly = emergencyOnly;
+        mNrState = NR_STATE_NONE;
+        mRplmn = rplmn;
+        mVoiceSpecificInfo = voiceSpecificInfo;
+        mDataSpecificInfo = dataSpecificInfo;
+        mIsNonTerrestrialNetwork = isNonTerrestrialNetwork;
+
+        updateNrState();
+    }
+
+    /**
+     * Constructor for voice network registration info.
+     * @hide
+     */
+    public NetworkRegistrationInfo(int domain, @TransportType int transportType,
+                                   int registrationState, int accessNetworkTechnology,
+                                   int rejectCause, boolean emergencyOnly,
+                                   @Nullable List<Integer> availableServices,
+                                   @Nullable CellIdentity cellIdentity, @Nullable String rplmn,
+                                   boolean cssSupported, int roamingIndicator, int systemIsInPrl,
+                                   int defaultRoamingIndicator) {
+        this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause,
+                emergencyOnly, availableServices, cellIdentity, rplmn,
+                new VoiceSpecificRegistrationInfo(cssSupported, roamingIndicator,
+                        systemIsInPrl, defaultRoamingIndicator), null, false);
+    }
+
+    /**
+     * Constructor for data network registration info.
+     * @hide
+     */
+    public NetworkRegistrationInfo(int domain, @TransportType int transportType,
+                                   int registrationState, int accessNetworkTechnology,
+                                   int rejectCause, boolean emergencyOnly,
+                                   @Nullable List<Integer> availableServices,
+                                   @Nullable CellIdentity cellIdentity, @Nullable String rplmn,
+                                   int maxDataCalls, boolean isDcNrRestricted,
+                                   boolean isNrAvailable, boolean isEndcAvailable,
+                                   @Nullable VopsSupportInfo vopsSupportInfo) {
+        this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause,
+                emergencyOnly, availableServices, cellIdentity, rplmn, null,
+                new DataSpecificRegistrationInfo.Builder(maxDataCalls)
+                        .setDcNrRestricted(isDcNrRestricted)
+                        .setNrAvailable(isNrAvailable)
+                        .setEnDcAvailable(isEndcAvailable)
+                        .setVopsSupportInfo(vopsSupportInfo)
+                        .build(), false);
+    }
+
+    private NetworkRegistrationInfo(Parcel source) {
+        mDomain = source.readInt();
+        mTransportType = source.readInt();
+        mRegistrationState = source.readInt();
+        mNetworkRegistrationState = source.readInt();
+        mRoamingType = source.readInt();
+        mAccessNetworkTechnology = source.readInt();
+        mRejectCause = source.readInt();
+        mEmergencyOnly = source.readBoolean();
+        mAvailableServices = new ArrayList<>();
+        source.readList(mAvailableServices, Integer.class.getClassLoader(), java.lang.Integer.class);
+        mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader(), android.telephony.CellIdentity.class);
+        mVoiceSpecificInfo = source.readParcelable(
+                VoiceSpecificRegistrationInfo.class.getClassLoader(), android.telephony.VoiceSpecificRegistrationInfo.class);
+        mDataSpecificInfo = source.readParcelable(
+                DataSpecificRegistrationInfo.class.getClassLoader(), android.telephony.DataSpecificRegistrationInfo.class);
+        mNrState = source.readInt();
+        mRplmn = source.readString();
+        mIsUsingCarrierAggregation = source.readBoolean();
+        mIsNonTerrestrialNetwork = source.readBoolean();
+    }
+
+    /**
+     * Constructor from another network registration info
+     *
+     * @param nri Another network registration info
+     * @hide
+     */
+    public NetworkRegistrationInfo(NetworkRegistrationInfo nri) {
+        mDomain = nri.mDomain;
+        mTransportType = nri.mTransportType;
+        mRegistrationState = nri.mRegistrationState;
+        mNetworkRegistrationState = nri.mNetworkRegistrationState;
+        mRoamingType = nri.mRoamingType;
+        mAccessNetworkTechnology = nri.mAccessNetworkTechnology;
+        mIsUsingCarrierAggregation = nri.mIsUsingCarrierAggregation;
+        mIsNonTerrestrialNetwork = nri.mIsNonTerrestrialNetwork;
+        mRejectCause = nri.mRejectCause;
+        mEmergencyOnly = nri.mEmergencyOnly;
+        mAvailableServices = new ArrayList<>(nri.mAvailableServices);
+        if (nri.mCellIdentity != null) {
+            Parcel p = Parcel.obtain();
+            nri.mCellIdentity.writeToParcel(p, 0);
+            p.setDataPosition(0);
+            // TODO: Instead of doing this, we should create a formal way for cloning cell identity.
+            // Cell identity is not an immutable object so we have to deep copy it.
+            mCellIdentity = CellIdentity.CREATOR.createFromParcel(p);
+            p.recycle();
+        }
+
+        if (nri.mVoiceSpecificInfo != null) {
+            mVoiceSpecificInfo = new VoiceSpecificRegistrationInfo(nri.mVoiceSpecificInfo);
+        }
+        if (nri.mDataSpecificInfo != null) {
+            mDataSpecificInfo = new DataSpecificRegistrationInfo(nri.mDataSpecificInfo);
+        }
+        mNrState = nri.mNrState;
+        mRplmn = nri.mRplmn;
+    }
+
+    /**
+     * @return The transport type.
+     */
+    public @TransportType int getTransportType() { return mTransportType; }
+
+    /**
+     * @return The network domain.
+     */
+    public @Domain int getDomain() { return mDomain; }
+
+    /**
+     * Get the 5G NR connection state.
+     *
+     * @return the 5G NR connection state.
+     * @hide
+     */
+    public @NRState int getNrState() {
+        return mNrState;
+    }
+
+    /** @hide */
+    public void setNrState(@NRState int nrState) {
+        mNrState = nrState;
+    }
+
+    /**
+     * @return The registration state. Note this value can be affected by the carrier config
+     * override.
+     *
+     * @deprecated Use {@link #getNetworkRegistrationState}, which is not affected by any carrier
+     * config or resource overlay, instead.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    public @RegistrationState int getRegistrationState() {
+        if (mRegistrationState == REGISTRATION_STATE_EMERGENCY) {
+            if (!CompatChanges.isChangeEnabled(RETURN_REGISTRATION_STATE_EMERGENCY)) {
+                if (mAccessNetworkTechnology == TelephonyManager.NETWORK_TYPE_LTE) {
+                    return REGISTRATION_STATE_DENIED;
+                } else if (mAccessNetworkTechnology == TelephonyManager.NETWORK_TYPE_NR) {
+                    return REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+                }
+            }
+        }
+        return mRegistrationState;
+    }
+
+    /**
+     * @return The true registration state of network. (This value is not affected by any carrier
+     * config or resource overlay override).
+     *
+     * @hide
+     */
+    @SystemApi
+    public @RegistrationState int getNetworkRegistrationState() {
+        return mNetworkRegistrationState;
+    }
+
+    /**
+     * @return {@code true} if registered on roaming or home network. Note this value can be
+     * affected by the carrier config override.
+     *
+     * @deprecated Use {@link #isNetworkRegistered}, which is not affected by any carrier config or
+     * resource overlay, instead.
+     */
+    @Deprecated
+    public boolean isRegistered() {
+        return mRegistrationState == REGISTRATION_STATE_HOME
+                || mRegistrationState == REGISTRATION_STATE_ROAMING;
+    }
+
+    /**
+     * @return {@code true} if registered on roaming or home network, {@code false} otherwise. (This
+     * value is not affected by any carrier config or resource overlay override).
+     */
+    public boolean isNetworkRegistered() {
+        return mNetworkRegistrationState == REGISTRATION_STATE_HOME
+                || mNetworkRegistrationState == REGISTRATION_STATE_ROAMING;
+    }
+
+    /**
+     * @return {@code true} if searching for service, {@code false} otherwise.
+     *
+     * @deprecated Use {@link #isNetworkRegistered}, which is not affected by any carrier config or
+     * resource overlay, instead.
+     */
+    @Deprecated
+    public boolean isSearching() {
+        return mRegistrationState == REGISTRATION_STATE_NOT_REGISTERED_SEARCHING;
+    }
+
+    /**
+     * @return {@code true} if searching for service, {@code false} otherwise. (This value is not
+     * affected by any carrier config or resource overlay override).
+     */
+    public boolean isNetworkSearching() {
+        return mNetworkRegistrationState == REGISTRATION_STATE_NOT_REGISTERED_SEARCHING;
+    }
+
+    /**
+     * Get the PLMN-ID for this Network Registration, also known as the RPLMN.
+     *
+     * <p>If the device is registered, this will return the registered PLMN-ID. If registration
+     * has failed, then this will return the PLMN ID of the last attempted registration. If the
+     * device is not registered, or if is registered to a non-3GPP radio technology, then this
+     * will return null.
+     *
+     * <p>See 3GPP TS 23.122 for further information about the Registered PLMN.
+     *
+     * @return the registered PLMN-ID or null.
+     */
+    @Nullable public String getRegisteredPlmn() {
+        return mRplmn;
+    }
+
+    /**
+     * @return {@code true} if registered on roaming network overridden by config. Note this value
+     * can be affected by the carrier config override.
+     *
+     * @deprecated Use {@link TelephonyDisplayInfo#isRoaming} instead.
+     */
+    @Deprecated
+    public boolean isRoaming() {
+        return mRoamingType != ServiceState.ROAMING_TYPE_NOT_ROAMING;
+    }
+
+    /**
+     * @return {@code true} if registered on roaming network. (This value is not affected by any
+     * carrier config or resource overlay override).
+     */
+    public boolean isNetworkRoaming() {
+        return mNetworkRegistrationState == REGISTRATION_STATE_ROAMING;
+    }
+
+    /**
+     * @hide
+     * @return {@code true} if in service.
+     */
+    public boolean isInService() {
+        return mRegistrationState == REGISTRATION_STATE_HOME
+                || mRegistrationState == REGISTRATION_STATE_ROAMING;
+    }
+
+    /**
+     * Set {@link ServiceState.RoamingType roaming type}. This could override
+     * roaming type based on resource overlay or carrier config.
+     * @hide
+     */
+    public void setRoamingType(@ServiceState.RoamingType int roamingType) {
+        mRoamingType = roamingType;
+
+        // make sure mRegistrationState to be consistent in case of any roaming type override
+        if (isRoaming()) {
+            if (mRegistrationState == REGISTRATION_STATE_HOME) {
+                mRegistrationState = REGISTRATION_STATE_ROAMING;
+            }
+        } else {
+            if (mRegistrationState == REGISTRATION_STATE_ROAMING) {
+                mRegistrationState = REGISTRATION_STATE_HOME;
+            }
+        }
+    }
+
+    /**
+     * @return the current network roaming type. Note that this value can be possibly overridden by
+     * the carrier config or resource overlay.
+     * @hide
+     */
+    @SystemApi
+    public @ServiceState.RoamingType int getRoamingType() {
+        return mRoamingType;
+    }
+
+    /**
+     * @return Whether emergency is enabled.
+     * @hide
+     */
+    @SystemApi
+    public boolean isEmergencyEnabled() { return mEmergencyOnly; }
+
+    /**
+     * @return List of available service types.
+     */
+    @NonNull
+    @ServiceType
+    public List<Integer> getAvailableServices() {
+        return Collections.unmodifiableList(mAvailableServices);
+    }
+
+    /**
+     * Set available service types.
+     *
+     * @param availableServices The list of available services for this network.
+     * @hide
+     */
+    public void setAvailableServices(@NonNull @ServiceType List<Integer> availableServices) {
+        mAvailableServices = new ArrayList<>(availableServices);
+    }
+
+    /**
+     * @return The access network technology network type..
+     */
+    public @NetworkType int getAccessNetworkTechnology() {
+        return mAccessNetworkTechnology;
+    }
+
+    /**
+     * override the access network technology {@link NetworkType} e.g, rat ratchet.
+     * @hide
+     */
+    public void setAccessNetworkTechnology(@NetworkType int tech) {
+        if (tech == TelephonyManager.NETWORK_TYPE_LTE_CA) {
+            // For old device backward compatibility support
+            tech = TelephonyManager.NETWORK_TYPE_LTE;
+            mIsUsingCarrierAggregation = true;
+        }
+        mAccessNetworkTechnology = tech;
+    }
+
+    /**
+     * Get the 3GPP/3GPP2 reason code indicating why registration failed.
+     *
+     * Returns the reason code for non-transient registration failures. Typically this method will
+     * only return the last reason code received during a network selection procedure. The reason
+     * code is system-specific; however, the reason codes for both 3GPP and 3GPP2 systems are
+     * largely equivalent across generations.
+     *
+     * @return registration reject cause if available, otherwise 0. Depending on
+     * {@link #getAccessNetworkTechnology}, the values are defined in 3GPP TS 24.008 10.5.3.6 for
+     * WCDMA/UMTS, 3GPP TS 24.301 9.9.3.9 for LTE/EPS, 3GPP 24.501 Annex A for NR/5GS, or 3GPP2
+     * A.S0001 6.2.2.44 for CDMA.
+     */
+    @FlaggedApi(Flags.FLAG_NETWORK_REGISTRATION_INFO_REJECT_CAUSE)
+    public int getRejectCause() {
+        return mRejectCause;
+    }
+
+    /**
+     * Require {@link android.Manifest.permission#ACCESS_FINE_LOCATION}, otherwise return null.
+     *
+     * @return The cell information.
+     */
+    @Nullable
+    public CellIdentity getCellIdentity() {
+        return mCellIdentity;
+    }
+
+    /**
+     * Set whether network has configured carrier aggregation or not.
+     *
+     * @param isUsingCarrierAggregation set whether or not carrier aggregation is used.
+     *
+     * @hide
+     */
+    public void setIsUsingCarrierAggregation(boolean isUsingCarrierAggregation) {
+        mIsUsingCarrierAggregation = isUsingCarrierAggregation;
+    }
+
+    /**
+     * Get whether network has configured carrier aggregation or not.
+     *
+     * @return {@code true} if using carrier aggregation.
+     * @hide
+     */
+    public boolean isUsingCarrierAggregation() {
+        return mIsUsingCarrierAggregation;
+    }
+
+    /**
+     * Set whether the network is a non-terrestrial network.
+     *
+     * @param isNonTerrestrialNetwork {@code true} if network is a non-terrestrial network
+     *                                            else {@code false}.
+     * @hide
+     */
+    public void setIsNonTerrestrialNetwork(boolean isNonTerrestrialNetwork) {
+        mIsNonTerrestrialNetwork = isNonTerrestrialNetwork;
+    }
+
+    /**
+     * Get whether the network is a non-terrestrial network.
+     *
+     * @return {@code true} if network is a non-terrestrial network else {@code false}.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public boolean isNonTerrestrialNetwork() {
+        return mIsNonTerrestrialNetwork;
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public VoiceSpecificRegistrationInfo getVoiceSpecificInfo() {
+        return mVoiceSpecificInfo;
+    }
+
+    /**
+     * @return Data registration related info
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public DataSpecificRegistrationInfo getDataSpecificInfo() {
+        return mDataSpecificInfo;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Convert service type to string
+     *
+     * @hide
+     *
+     * @param serviceType The service type
+     * @return The service type in string format
+     */
+    public static String serviceTypeToString(@ServiceType int serviceType) {
+        switch (serviceType) {
+            case SERVICE_TYPE_VOICE: return "VOICE";
+            case SERVICE_TYPE_DATA: return "DATA";
+            case SERVICE_TYPE_SMS: return "SMS";
+            case SERVICE_TYPE_VIDEO: return "VIDEO";
+            case SERVICE_TYPE_EMERGENCY: return "EMERGENCY";
+            case SERVICE_TYPE_MMS: return "MMS";
+        }
+        return "Unknown service type " + serviceType;
+    }
+
+    /**
+     * Convert registration state to string
+     *
+     * @hide
+     *
+     * @param registrationState The registration state
+     * @return The reg state in string
+     */
+    public static String registrationStateToString(@RegistrationState int registrationState) {
+        switch (registrationState) {
+            case REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING: return "NOT_REG_OR_SEARCHING";
+            case REGISTRATION_STATE_HOME: return "HOME";
+            case REGISTRATION_STATE_NOT_REGISTERED_SEARCHING: return "NOT_REG_SEARCHING";
+            case REGISTRATION_STATE_DENIED: return "DENIED";
+            case REGISTRATION_STATE_UNKNOWN: return "UNKNOWN";
+            case REGISTRATION_STATE_ROAMING: return "ROAMING";
+            case REGISTRATION_STATE_EMERGENCY: return "EMERGENCY";
+        }
+        return "Unknown reg state " + registrationState;
+    }
+
+    /** @hide */
+    public static String nrStateToString(@NRState int nrState) {
+        switch (nrState) {
+            case NR_STATE_RESTRICTED:
+                return "RESTRICTED";
+            case NR_STATE_NOT_RESTRICTED:
+                return "NOT_RESTRICTED";
+            case NR_STATE_CONNECTED:
+                return "CONNECTED";
+            default:
+                return "NONE";
+        }
+    }
+
+    /** @hide */
+    static @NonNull String domainToString(@Domain int domain) {
+        switch (domain) {
+            case DOMAIN_CS: return "CS";
+            case DOMAIN_PS: return "PS";
+            case DOMAIN_CS_PS: return "CS_PS";
+            default: return "UNKNOWN";
+        }
+    }
+
+    /**
+     * Convert isNonTerrestrialNetwork to string
+     *
+     * @param isNonTerrestrialNetwork boolean indicating whether network is a non-terrestrial
+     *                                network
+     * @return string format of isNonTerrestrialNetwork.
+     * @hide
+     */
+    public static String isNonTerrestrialNetworkToString(boolean isNonTerrestrialNetwork) {
+        return isNonTerrestrialNetwork ? "NON-TERRESTRIAL" : "TERRESTRIAL";
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return new StringBuilder("NetworkRegistrationInfo{")
+                .append(" domain=").append(domainToString(mDomain))
+                .append(" transportType=").append(
+                        AccessNetworkConstants.transportTypeToString(mTransportType))
+                .append(" registrationState=").append(registrationStateToString(mRegistrationState))
+                .append(" networkRegistrationState=")
+                .append(registrationStateToString(mNetworkRegistrationState))
+                .append(" roamingType=").append(ServiceState.roamingTypeToString(mRoamingType))
+                .append(" accessNetworkTechnology=")
+                .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology))
+                .append(" rejectCause=").append(mRejectCause)
+                .append(" emergencyEnabled=").append(mEmergencyOnly)
+                .append(" availableServices=").append("[" + (mAvailableServices != null
+                        ? mAvailableServices.stream().map(type -> serviceTypeToString(type))
+                        .collect(Collectors.joining(",")) : null) + "]")
+                .append(" cellIdentity=").append(mCellIdentity)
+                .append(" voiceSpecificInfo=").append(mVoiceSpecificInfo)
+                .append(" dataSpecificInfo=").append(mDataSpecificInfo)
+                .append(" nrState=").append(Build.IS_DEBUGGABLE
+                        ? nrStateToString(mNrState) : "****")
+                .append(" rRplmn=").append(mRplmn)
+                .append(" isUsingCarrierAggregation=").append(mIsUsingCarrierAggregation)
+                .append(" isNonTerrestrialNetwork=").append(
+                        isNonTerrestrialNetworkToString(mIsNonTerrestrialNetwork))
+                .append("}").toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDomain, mTransportType, mRegistrationState, mNetworkRegistrationState,
+                mRoamingType, mAccessNetworkTechnology, mRejectCause, mEmergencyOnly,
+                mAvailableServices, mCellIdentity, mVoiceSpecificInfo, mDataSpecificInfo, mNrState,
+                mRplmn, mIsUsingCarrierAggregation, mIsNonTerrestrialNetwork);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+
+        if (!(o instanceof NetworkRegistrationInfo)) {
+            return false;
+        }
+
+        NetworkRegistrationInfo other = (NetworkRegistrationInfo) o;
+        return mDomain == other.mDomain
+                && mTransportType == other.mTransportType
+                && mRegistrationState == other.mRegistrationState
+                && mNetworkRegistrationState == other.mNetworkRegistrationState
+                && mRoamingType == other.mRoamingType
+                && mAccessNetworkTechnology == other.mAccessNetworkTechnology
+                && mRejectCause == other.mRejectCause
+                && mEmergencyOnly == other.mEmergencyOnly
+                && mAvailableServices.equals(other.mAvailableServices)
+                && mIsUsingCarrierAggregation == other.mIsUsingCarrierAggregation
+                && Objects.equals(mCellIdentity, other.mCellIdentity)
+                && Objects.equals(mVoiceSpecificInfo, other.mVoiceSpecificInfo)
+                && Objects.equals(mDataSpecificInfo, other.mDataSpecificInfo)
+                && TextUtils.equals(mRplmn, other.mRplmn)
+                && mNrState == other.mNrState
+                && mIsNonTerrestrialNetwork == other.mIsNonTerrestrialNetwork;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    @SystemApi
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mDomain);
+        dest.writeInt(mTransportType);
+        dest.writeInt(mRegistrationState);
+        dest.writeInt(mNetworkRegistrationState);
+        dest.writeInt(mRoamingType);
+        dest.writeInt(mAccessNetworkTechnology);
+        dest.writeInt(mRejectCause);
+        dest.writeBoolean(mEmergencyOnly);
+        dest.writeList(mAvailableServices);
+        dest.writeParcelable(mCellIdentity, 0);
+        dest.writeParcelable(mVoiceSpecificInfo, 0);
+        dest.writeParcelable(mDataSpecificInfo, 0);
+        dest.writeInt(mNrState);
+        dest.writeString(mRplmn);
+        dest.writeBoolean(mIsUsingCarrierAggregation);
+        dest.writeBoolean(mIsNonTerrestrialNetwork);
+    }
+
+    /**
+     * Use the 5G NR Non-Standalone indicators from the network registration state to update the
+     * NR state. There are 3 indicators in the network registration state:
+     *
+     * 1. if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the primary serving cell.
+     * 2. if NR is supported by the selected PLMN.
+     * 3. if the use of dual connectivity with NR is restricted.
+     *
+     * The network has 5G NR capability if E-UTRA-NR Dual Connectivity is supported by the primary
+     * serving cell.
+     *
+     * The use of NR 5G is not restricted If the network has 5G NR capability and both the use of
+     * DCNR is not restricted and NR is supported by the selected PLMN. Otherwise the use of 5G
+     * NR is restricted.
+     *
+     * @hide
+     */
+    public void updateNrState() {
+        mNrState = NR_STATE_NONE;
+        if (mDataSpecificInfo != null && mDataSpecificInfo.isEnDcAvailable) {
+            if (!mDataSpecificInfo.isDcNrRestricted && mDataSpecificInfo.isNrAvailable) {
+                mNrState = NR_STATE_NOT_RESTRICTED;
+            } else {
+                mNrState = NR_STATE_RESTRICTED;
+            }
+        }
+    }
+
+    public static final @NonNull Parcelable.Creator<NetworkRegistrationInfo> CREATOR =
+            new Parcelable.Creator<NetworkRegistrationInfo>() {
+                @Override
+                public NetworkRegistrationInfo createFromParcel(Parcel source) {
+                    return new NetworkRegistrationInfo(source);
+                }
+
+                @Override
+                public NetworkRegistrationInfo[] newArray(int size) {
+                    return new NetworkRegistrationInfo[size];
+                }
+            };
+
+    /**
+     * @hide
+     */
+    public NetworkRegistrationInfo sanitizeLocationInfo() {
+        NetworkRegistrationInfo result = copy();
+        result.mCellIdentity = null;
+        return result;
+    }
+
+    private NetworkRegistrationInfo copy() {
+        Parcel p = Parcel.obtain();
+        this.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        NetworkRegistrationInfo result = new NetworkRegistrationInfo(p);
+        p.recycle();
+        return result;
+    }
+
+    /**
+     * Provides a convenient way to set the fields of a {@link NetworkRegistrationInfo} when
+     * creating a new instance.
+     *
+     * <p>The example below shows how you might create a new {@code NetworkRegistrationInfo}:
+     *
+     * <pre><code>
+     *
+     * NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+     *     .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+     *     .setRegistrationState(REGISTRATION_STATE_HOME)
+     *     .build();
+     * </code></pre>
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        @Domain
+        private int mDomain;
+
+        @TransportType
+        private int mTransportType;
+
+        @RegistrationState
+        private int mNetworkRegistrationState;
+
+        @NetworkType
+        private int mAccessNetworkTechnology;
+
+        private int mRejectCause;
+
+        private boolean mEmergencyOnly;
+
+        @ServiceType
+        private List<Integer> mAvailableServices;
+
+        @Nullable
+        private CellIdentity mCellIdentity;
+
+        @NonNull
+        private String mRplmn = "";
+
+        @Nullable
+        private DataSpecificRegistrationInfo mDataSpecificRegistrationInfo;
+
+        @Nullable
+        private VoiceSpecificRegistrationInfo mVoiceSpecificRegistrationInfo;
+
+        private boolean mIsNonTerrestrialNetwork;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {}
+
+        /**
+         * Builder from the existing {@link NetworkRegistrationInfo}.
+         *
+         * @param nri The network registration info object.
+         * @hide
+         */
+        public Builder(@NonNull NetworkRegistrationInfo nri) {
+            mDomain = nri.mDomain;
+            mTransportType = nri.mTransportType;
+            mNetworkRegistrationState = nri.mNetworkRegistrationState;
+            mAccessNetworkTechnology = nri.mAccessNetworkTechnology;
+            mRejectCause = nri.mRejectCause;
+            mEmergencyOnly = nri.mEmergencyOnly;
+            mAvailableServices = new ArrayList<>(nri.mAvailableServices);
+            mCellIdentity = nri.mCellIdentity;
+            if (nri.mDataSpecificInfo != null) {
+                mDataSpecificRegistrationInfo = new DataSpecificRegistrationInfo(
+                        nri.mDataSpecificInfo);
+            }
+            if (nri.mVoiceSpecificInfo != null) {
+                mVoiceSpecificRegistrationInfo = new VoiceSpecificRegistrationInfo(
+                        nri.mVoiceSpecificInfo);
+            }
+            mIsNonTerrestrialNetwork = nri.mIsNonTerrestrialNetwork;
+        }
+
+        /**
+         * Set the network domain.
+         *
+         * @param domain Network domain.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setDomain(@Domain int domain) {
+            mDomain = domain;
+            return this;
+        }
+
+        /**
+         * Set the transport type.
+         *
+         * @param transportType Transport type.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setTransportType(@TransportType int transportType) {
+            mTransportType = transportType;
+            return this;
+        }
+
+        /**
+         * Set the registration state.
+         *
+         * @param registrationState The registration state.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setRegistrationState(@RegistrationState int registrationState) {
+            mNetworkRegistrationState = registrationState;
+            return this;
+        }
+
+        /**
+         * Set tne access network technology.
+         *
+         * @return The same instance of the builder.
+         *
+         * @param accessNetworkTechnology The access network technology
+         */
+        public @NonNull Builder setAccessNetworkTechnology(
+                @NetworkType int accessNetworkTechnology) {
+            mAccessNetworkTechnology = accessNetworkTechnology;
+            return this;
+        }
+
+        /**
+         * Set the network reject cause.
+         *
+         * @param rejectCause Reason for denial if the registration state is
+         * {@link #REGISTRATION_STATE_DENIED}.Depending on {@code accessNetworkTechnology}, the
+         * values are defined in 3GPP TS 24.008 10.5.3.6 for UMTS, 3GPP TS 24.301 9.9.3.9 for LTE,
+         * and 3GPP2 A.S0001 6.2.2.44 for CDMA. If the reject cause is not supported or unknown, set
+         * it to 0.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setRejectCause(int rejectCause) {
+            mRejectCause = rejectCause;
+            return this;
+        }
+
+        /**
+         * Set emergency only.
+         *
+         * @param emergencyOnly True if this network registration is for emergency use only.
+         *
+         * @return The same instance of the builder.
+         * @hide
+         */
+        @SystemApi
+        public @NonNull Builder setEmergencyOnly(boolean emergencyOnly) {
+            mEmergencyOnly = emergencyOnly;
+            return this;
+        }
+
+        /**
+         * Set the available services.
+         *
+         * @param availableServices Available services.
+         *
+         * @return The same instance of the builder.
+         * @hide
+         */
+        @SystemApi
+        public @NonNull Builder setAvailableServices(
+                @NonNull @ServiceType List<Integer> availableServices) {
+            mAvailableServices = availableServices;
+            return this;
+        }
+
+        /**
+         * Set the cell identity.
+         *
+         * @param cellIdentity The cell identity.
+         *
+         * @return The same instance of the builder.
+         * @hide
+         */
+        @SystemApi
+        public @NonNull Builder setCellIdentity(@Nullable CellIdentity cellIdentity) {
+            mCellIdentity = cellIdentity;
+            return this;
+        }
+
+        /**
+         * Set the registered PLMN.
+         *
+         * @param rplmn the registered plmn.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setRegisteredPlmn(@Nullable String rplmn) {
+            mRplmn = rplmn;
+            return this;
+        }
+
+        /**
+         * Set voice specific registration information.
+         *
+         * @param info The voice specific registration information.
+         * @return The builder.
+         * @hide
+         */
+        public @NonNull Builder setVoiceSpecificInfo(@NonNull VoiceSpecificRegistrationInfo info) {
+            mVoiceSpecificRegistrationInfo = info;
+            return this;
+        }
+
+        /**
+         * Set data specific registration information.
+         *
+         * @param info The data specific registration information.
+         * @return The builder.
+         * @hide
+         */
+        public @NonNull Builder setDataSpecificInfo(@NonNull DataSpecificRegistrationInfo info) {
+            mDataSpecificRegistrationInfo = info;
+            return this;
+        }
+
+        /**
+         * Set whether the network is a non-terrestrial network.
+         *
+         * @param isNonTerrestrialNetwork {@code true} if network is a non-terrestrial network
+         *                                            else {@code false}.
+         * @return The builder.
+         */
+        @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+        public @NonNull Builder setIsNonTerrestrialNetwork(boolean isNonTerrestrialNetwork) {
+            mIsNonTerrestrialNetwork = isNonTerrestrialNetwork;
+            return this;
+        }
+
+        /**
+         * Build the NetworkRegistrationInfo.
+         * @return the NetworkRegistrationInfo object.
+         * @hide
+         */
+        @SystemApi
+        public @NonNull NetworkRegistrationInfo build() {
+            return new NetworkRegistrationInfo(mDomain, mTransportType, mNetworkRegistrationState,
+                    mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices,
+                    mCellIdentity, mRplmn, mVoiceSpecificRegistrationInfo,
+                    mDataSpecificRegistrationInfo, mIsNonTerrestrialNetwork);
+        }
+    }
+}
diff --git a/android-35/android/telephony/NetworkScan.java b/android-35/android/telephony/NetworkScan.java
new file mode 100644
index 0000000..adf31ed
--- /dev/null
+++ b/android-35/android/telephony/NetworkScan.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.annotation.IntDef;
+import android.os.RemoteException;
+
+import com.android.internal.telephony.ITelephony;
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The caller of
+ * {@link TelephonyManager#requestNetworkScan(NetworkScanRequest, Executor, NetworkScanCallback)}
+ * will receive an instance of {@link NetworkScan}, which contains a callback method
+ * {@link #stopScan()} for stopping the in-progress scan.
+ */
+public class NetworkScan {
+
+    private static final String TAG = "NetworkScan";
+
+    // Below errors are mapped from RadioError which is returned from RIL. We will consolidate
+    // RadioErrors during the mapping if those RadioErrors mean no difference to the users.
+
+    /**
+     * Defines acceptable values of scan error code.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ERROR_MODEM_ERROR, ERROR_INVALID_SCAN, ERROR_MODEM_UNAVAILABLE, ERROR_UNSUPPORTED,
+            ERROR_RADIO_INTERFACE_ERROR, ERROR_INVALID_SCANID, ERROR_INTERRUPTED})
+    public @interface ScanErrorCode {}
+
+    /**
+     * The RIL has successfully performed the network scan.
+     */
+    public static final int SUCCESS = 0;                    // RadioError:NONE
+
+    /**
+     * The scan has failed due to some modem errors.
+     */
+    public static final int ERROR_MODEM_ERROR = 1;          // RadioError:RADIO_NOT_AVAILABLE
+                                                            // RadioError:NO_MEMORY
+                                                            // RadioError:INTERNAL_ERR
+                                                            // RadioError:MODEM_ERR
+                                                            // RadioError:OPERATION_NOT_ALLOWED
+
+    /**
+     * The parameters of the scan is invalid.
+     */
+    public static final int ERROR_INVALID_SCAN = 2;         // RadioError:INVALID_ARGUMENTS
+
+    /**
+     * The modem can not perform the scan because it is doing something else.
+     */
+    public static final int ERROR_MODEM_UNAVAILABLE = 3;    // RadioError:DEVICE_IN_USE
+
+    /**
+     * The modem does not support the request scan.
+     */
+    public static final int ERROR_UNSUPPORTED = 4;          // RadioError:REQUEST_NOT_SUPPORTED
+
+
+    // Below errors are generated at the Telephony.
+
+    /**
+     * The RIL returns nothing or exceptions.
+     */
+    public static final int ERROR_RADIO_INTERFACE_ERROR = 10000;
+
+    /**
+     * The scan ID is invalid. The user is either trying to stop a scan which does not exist
+     * or started by others.
+     */
+    public static final int ERROR_INVALID_SCANID = 10001;
+
+    /**
+     * The scan has been interrupted by another scan with higher priority.
+     */
+    public static final int ERROR_INTERRUPTED = 10002;
+
+    private final int mScanId;
+    private final int mSubId;
+
+    /**
+     * Stops the network scan
+     *
+     * Use this method to stop an ongoing scan. When user requests a new scan, a {@link NetworkScan}
+     * object will be returned, and the user can stop the scan by calling this method.
+     */
+    public void stopScan() {
+        ITelephony telephony = getITelephony();
+        if (telephony == null) {
+            Rlog.e(TAG, "Failed to get the ITelephony instance.");
+        }
+        try {
+            telephony.stopNetworkScan(mSubId, mScanId);
+        } catch (IllegalArgumentException ex) {
+            Rlog.d(TAG,  "stopNetworkScan - no active scan for ScanID=" + mScanId);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "stopNetworkScan  RemoteException", ex);
+        } catch (RuntimeException ex) {
+            Rlog.e(TAG, "stopNetworkScan  RuntimeException", ex);
+        }
+    }
+
+    /**
+     * @deprecated Use {@link #stopScan()}
+     * @removed
+     */
+    @Deprecated
+    public void stop() throws RemoteException {
+        try {
+            stopScan();
+        } catch (RuntimeException ex) {
+            throw new RemoteException("Failed to stop the network scan with id " + mScanId);
+        }
+    }
+
+    /**
+     * Creates a new NetworkScan with scanId
+     *
+     * @param scanId The id of the scan
+     * @param subId the id of the subscription
+     * @hide
+     */
+    public NetworkScan(int scanId, int subId) {
+        mScanId = scanId;
+        mSubId = subId;
+    }
+
+    private ITelephony getITelephony() {
+        return ITelephony.Stub.asInterface(
+            TelephonyFrameworkInitializer
+                    .getTelephonyServiceManager()
+                    .getTelephonyServiceRegisterer()
+                    .get());
+    }
+}
diff --git a/android-35/android/telephony/NetworkScanRequest.java b/android-35/android/telephony/NetworkScanRequest.java
new file mode 100644
index 0000000..3285bc3
--- /dev/null
+++ b/android-35/android/telephony/NetworkScanRequest.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Defines a request to perform a network scan.
+ *
+ * This class defines whether the network scan will be performed only once or periodically until
+ * cancelled, when the scan is performed periodically, the time interval is not controlled by the
+ * user but defined by the modem vendor.
+ */
+public final class NetworkScanRequest implements Parcelable {
+
+    // Below size limits for RAN/Band/Channel are for pre-treble modems and will be removed later.
+    /** @hide */
+    public static final int MAX_RADIO_ACCESS_NETWORKS = 8;
+    /** @hide */
+    public static final int MAX_BANDS = 8;
+    /** @hide */
+    public static final int MAX_CHANNELS = 32;
+    /** @hide */
+    public static final int MAX_MCC_MNC_LIST_SIZE = 20;
+    /** @hide */
+    public static final int MIN_SEARCH_PERIODICITY_SEC = 5;
+    /** @hide */
+    public static final int MAX_SEARCH_PERIODICITY_SEC = 300;
+    /** @hide */
+    public static final int MIN_SEARCH_MAX_SEC = 60;
+    /** @hide */
+    public static final int MAX_SEARCH_MAX_SEC = 3600;
+    /** @hide */
+    public static final int MIN_INCREMENTAL_PERIODICITY_SEC = 1;
+    /** @hide */
+    public static final int MAX_INCREMENTAL_PERIODICITY_SEC = 10;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        SCAN_TYPE_ONE_SHOT,
+        SCAN_TYPE_PERIODIC,
+    })
+    public @interface ScanType {}
+
+    /** Performs the scan only once */
+    public static final int SCAN_TYPE_ONE_SHOT = 0;
+    /**
+     * Performs the scan periodically until cancelled
+     *
+     * The modem will start new scans periodically, and the interval between two scans is usually
+     * multiple minutes.
+     */
+    public static final int SCAN_TYPE_PERIODIC = 1;
+
+    /** Defines the type of the scan. */
+    private int mScanType;
+
+    /**
+     * Search periodicity (in seconds).
+     * Expected range for the input is [5s - 300s]
+     * This value must be less than or equal to mMaxSearchTime
+     */
+    private int mSearchPeriodicity;
+
+    /**
+     * Maximum duration of the periodic search (in seconds).
+     * Expected range for the input is [60s - 3600s]
+     * If the search lasts this long, it will be terminated.
+     */
+    private int mMaxSearchTime;
+
+    /**
+     * Indicates whether the modem should report incremental
+     * results of the network scan to the client.
+     * FALSE – Incremental results are not reported.
+     * TRUE (default) – Incremental results are reported
+     */
+    private boolean mIncrementalResults;
+
+    /**
+     * Indicates the periodicity with which the modem should
+     * report incremental results to the client (in seconds).
+     * Expected range for the input is [1s - 10s]
+     * This value must be less than or equal to mMaxSearchTime
+     */
+    private int mIncrementalResultsPeriodicity;
+
+    /** Describes the radio access technologies with bands or channels that need to be scanned. */
+    @Nullable
+    private RadioAccessSpecifier[] mSpecifiers;
+
+    /**
+     * Describes the List of PLMN ids (MCC-MNC)
+     * If any PLMN of this list is found, search should end at that point and
+     * results with all PLMN found till that point should be sent as response.
+     * If list not sent, search to be completed till end and all PLMNs found to be reported.
+     * Max size of array is MAX_MCC_MNC_LIST_SIZE
+     */
+    @NonNull
+    private ArrayList<String> mMccMncs;
+
+    /**
+     * Creates a new NetworkScanRequest with mScanType and network mSpecifiers
+     *
+     * @param scanType The type of the scan, can be either one shot or periodic
+     * @param specifiers the radio network with bands / channels to be scanned
+     * @param searchPeriodicity The modem will restart the scan every searchPeriodicity seconds if
+     *                          no network has been found, until it reaches the maxSearchTime. Only
+     *                          valid when scan type is periodic scan.
+     * @param maxSearchTime Maximum duration of the search (in seconds)
+     * @param incrementalResults Indicates whether the modem should report incremental
+     *                           results of the network scan to the client
+     * @param incrementalResultsPeriodicity Indicates the periodicity with which the modem should
+     *                                      report incremental results to the client (in seconds),
+     *                                      only valid when incrementalResults is true
+     * @param mccMncs Describes the list of PLMN ids (MCC-MNC), once any network in the list has
+     *                been found, the scan will be terminated by the modem.
+     */
+    public NetworkScanRequest(int scanType, RadioAccessSpecifier[] specifiers,
+                    int searchPeriodicity,
+                    int maxSearchTime,
+                    boolean incrementalResults,
+                    int incrementalResultsPeriodicity,
+                    ArrayList<String> mccMncs) {
+        this.mScanType = scanType;
+        if (specifiers != null) {
+            this.mSpecifiers = specifiers.clone();
+        } else {
+            this.mSpecifiers = null;
+        }
+        this.mSearchPeriodicity = searchPeriodicity;
+        this.mMaxSearchTime = maxSearchTime;
+        this.mIncrementalResults = incrementalResults;
+        this.mIncrementalResultsPeriodicity = incrementalResultsPeriodicity;
+        if (mccMncs != null) {
+            this.mMccMncs = (ArrayList<String>) mccMncs.clone();
+        } else {
+            this.mMccMncs = new ArrayList<>();
+        }
+    }
+
+    /** Returns the type of the scan. */
+    @ScanType
+    public int getScanType() {
+        return mScanType;
+    }
+
+    /** Returns the search periodicity in seconds. */
+    public int getSearchPeriodicity() {
+        return mSearchPeriodicity;
+    }
+
+    /** Returns maximum duration of the periodic search in seconds. */
+    public int getMaxSearchTime() {
+        return mMaxSearchTime;
+    }
+
+    /**
+     * Returns whether incremental result is enabled.
+     * FALSE – Incremental results is not enabled.
+     * TRUE – Incremental results is reported.
+     */
+    public boolean getIncrementalResults() {
+        return mIncrementalResults;
+    }
+
+    /** Returns the periodicity in seconds of incremental results. */
+    public int getIncrementalResultsPeriodicity() {
+        return mIncrementalResultsPeriodicity;
+    }
+
+    /** Returns the radio access technologies with bands or channels that need to be scanned. */
+    public RadioAccessSpecifier[] getSpecifiers() {
+        return mSpecifiers == null ? null : mSpecifiers.clone();
+    }
+
+    /**
+     * Returns the List of PLMN ids (MCC-MNC) for early termination of scan.
+     * If any PLMN of this list is found, search should end at that point and
+     * results with all PLMN found till that point should be sent as response.
+     */
+    public ArrayList<String> getPlmns() {
+        return (ArrayList<String>) mMccMncs.clone();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mScanType);
+        dest.writeParcelableArray(mSpecifiers, flags);
+        dest.writeInt(mSearchPeriodicity);
+        dest.writeInt(mMaxSearchTime);
+        dest.writeBoolean(mIncrementalResults);
+        dest.writeInt(mIncrementalResultsPeriodicity);
+        dest.writeStringList(mMccMncs);
+    }
+
+    private NetworkScanRequest(Parcel in) {
+        mScanType = in.readInt();
+        Parcelable[] tempSpecifiers = in.readParcelableArray(Object.class.getClassLoader(),
+                RadioAccessSpecifier.class);
+        if (tempSpecifiers != null) {
+            mSpecifiers = new RadioAccessSpecifier[tempSpecifiers.length];
+            for (int i = 0; i < tempSpecifiers.length; i++) {
+                mSpecifiers[i] = (RadioAccessSpecifier) tempSpecifiers[i];
+            }
+        } else {
+            mSpecifiers = null;
+        }
+        mSearchPeriodicity = in.readInt();
+        mMaxSearchTime = in.readInt();
+        mIncrementalResults = in.readBoolean();
+        mIncrementalResultsPeriodicity = in.readInt();
+        mMccMncs = new ArrayList<>();
+        in.readStringList(mMccMncs);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) return true;
+
+        if (!(other instanceof NetworkScanRequest)) return false;
+
+        NetworkScanRequest nsr = (NetworkScanRequest) other;
+
+        return mScanType == nsr.mScanType
+                && Arrays.equals(mSpecifiers, nsr.mSpecifiers)
+                && mSearchPeriodicity == nsr.mSearchPeriodicity
+                && mMaxSearchTime == nsr.mMaxSearchTime
+                && mIncrementalResults == nsr.mIncrementalResults
+                && mIncrementalResultsPeriodicity == nsr.mIncrementalResultsPeriodicity
+                && Objects.equals(mMccMncs, nsr.mMccMncs);
+    }
+
+    @Override
+    public int hashCode () {
+        return ((mScanType * 31)
+                + (Arrays.hashCode(mSpecifiers)) * 37
+                + (mSearchPeriodicity * 41)
+                + (mMaxSearchTime * 43)
+                + ((mIncrementalResults == true? 1 : 0) * 47)
+                + (mIncrementalResultsPeriodicity * 53)
+                + (mMccMncs.hashCode() * 59));
+    }
+
+    public static final @android.annotation.NonNull Creator<NetworkScanRequest> CREATOR =
+            new Creator<NetworkScanRequest>() {
+                @Override
+                public NetworkScanRequest createFromParcel(Parcel in) {
+                    return new NetworkScanRequest(in);
+                }
+
+                @Override
+                public NetworkScanRequest[] newArray(int size) {
+                    return new NetworkScanRequest[size];
+                }
+            };
+}
diff --git a/android-35/android/telephony/NetworkService.java b/android-35/android/telephony/NetworkService.java
new file mode 100644
index 0000000..ac892da
--- /dev/null
+++ b/android-35/android/telephony/NetworkService.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright 2017 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.NetworkRegistrationInfo.Domain;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.telephony.Rlog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base class of network service. Services that extend NetworkService must register the service in
+ * their AndroidManifest to be detected by the framework. They must be protected by the permission
+ * "android.permission.BIND_TELEPHONY_NETWORK_SERVICE". The network service definition in the
+ * manifest must follow the following format:
+ * ...
+ * <service android:name=".xxxNetworkService"
+ *     android:permission="android.permission.BIND_TELEPHONY_NETWORK_SERVICE" >
+ *     <intent-filter>
+ *         <action android:name="android.telephony.NetworkService" />
+ *     </intent-filter>
+ * </service>
+ * @hide
+ */
+@SystemApi
+public abstract class NetworkService extends Service {
+
+    private final String TAG = NetworkService.class.getSimpleName();
+
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE = "android.telephony.NetworkService";
+
+    private static final int NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER                 = 1;
+    private static final int NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER                 = 2;
+    private static final int NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS            = 3;
+    private static final int NETWORK_SERVICE_GET_REGISTRATION_INFO                           = 4;
+    private static final int NETWORK_SERVICE_REGISTER_FOR_INFO_CHANGE                        = 5;
+    private static final int NETWORK_SERVICE_UNREGISTER_FOR_INFO_CHANGE                      = 6;
+    private static final int NETWORK_SERVICE_INDICATION_NETWORK_INFO_CHANGED                 = 7;
+
+
+    private final HandlerThread mHandlerThread;
+
+    private final NetworkServiceHandler mHandler;
+
+    private final SparseArray<NetworkServiceProvider> mServiceMap = new SparseArray<>();
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public final INetworkServiceWrapper mBinder = new INetworkServiceWrapper();
+
+    /**
+     * The abstract class of the actual network service implementation. The network service provider
+     * must extend this class to support network connection. Note that each instance of network
+     * service is associated with one physical SIM slot.
+     */
+    public abstract class NetworkServiceProvider implements AutoCloseable {
+        private final int mSlotIndex;
+
+        private final List<INetworkServiceCallback>
+                mNetworkRegistrationInfoChangedCallbacks = new ArrayList<>();
+
+        /**
+         * Constructor
+         * @param slotIndex SIM slot id the data service provider associated with.
+         */
+        public NetworkServiceProvider(int slotIndex) {
+            mSlotIndex = slotIndex;
+        }
+
+        /**
+         * @return SIM slot index the network service associated with.
+         */
+        public final int getSlotIndex() {
+            return mSlotIndex;
+        }
+
+        /**
+         * Request network registration info. The result will be passed to the callback.
+         *
+         * @param domain Network domain
+         * @param callback The callback for reporting network registration info
+         */
+        public void requestNetworkRegistrationInfo(@Domain int domain,
+                                                   @NonNull NetworkServiceCallback callback) {
+            callback.onRequestNetworkRegistrationInfoComplete(
+                    NetworkServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
+        }
+
+        /**
+         * Notify the system that network registration info is changed.
+         */
+        public final void notifyNetworkRegistrationInfoChanged() {
+            mHandler.obtainMessage(NETWORK_SERVICE_INDICATION_NETWORK_INFO_CHANGED,
+                    mSlotIndex, 0, null).sendToTarget();
+        }
+
+        private void registerForInfoChanged(@NonNull INetworkServiceCallback callback) {
+            synchronized (mNetworkRegistrationInfoChangedCallbacks) {
+                mNetworkRegistrationInfoChangedCallbacks.add(callback);
+            }
+        }
+
+        private void unregisterForInfoChanged(@NonNull INetworkServiceCallback callback) {
+            synchronized (mNetworkRegistrationInfoChangedCallbacks) {
+                mNetworkRegistrationInfoChangedCallbacks.remove(callback);
+            }
+        }
+
+        private void notifyInfoChangedToCallbacks() {
+            for (INetworkServiceCallback callback : mNetworkRegistrationInfoChangedCallbacks) {
+                try {
+                    callback.onNetworkStateChanged();
+                } catch (RemoteException exception) {
+                    // Doing nothing.
+                }
+            }
+        }
+
+        /**
+         * Called when the instance of network service is destroyed (e.g. got unbind or binder died)
+         * or when the network service provider is removed. The extended class should implement this
+         * method to perform cleanup works.
+         */
+        @Override
+        public abstract void close();
+    }
+
+    private class NetworkServiceHandler extends Handler {
+
+        NetworkServiceHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            final int slotIndex = message.arg1;
+            final INetworkServiceCallback callback = (INetworkServiceCallback) message.obj;
+
+            NetworkServiceProvider serviceProvider = mServiceMap.get(slotIndex);
+
+            switch (message.what) {
+                case NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER:
+                    // If the service provider doesn't exist yet, we try to create it.
+                    if (serviceProvider == null) {
+                        mServiceMap.put(slotIndex, onCreateNetworkServiceProvider(slotIndex));
+                    }
+                    break;
+                case NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER:
+                    // If the service provider doesn't exist yet, we try to create it.
+                    if (serviceProvider != null) {
+                        serviceProvider.close();
+                        mServiceMap.remove(slotIndex);
+                    }
+                    break;
+                case NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS:
+                    for (int i = 0; i < mServiceMap.size(); i++) {
+                        serviceProvider = mServiceMap.get(i);
+                        if (serviceProvider != null) {
+                            serviceProvider.close();
+                        }
+                    }
+                    mServiceMap.clear();
+                    break;
+                case NETWORK_SERVICE_GET_REGISTRATION_INFO:
+                    if (serviceProvider == null) break;
+                    int domainId = message.arg2;
+                    serviceProvider.requestNetworkRegistrationInfo(domainId,
+                            new NetworkServiceCallback(callback));
+
+                    break;
+                case NETWORK_SERVICE_REGISTER_FOR_INFO_CHANGE:
+                    if (serviceProvider == null) break;
+                    serviceProvider.registerForInfoChanged(callback);
+                    break;
+                case NETWORK_SERVICE_UNREGISTER_FOR_INFO_CHANGE:
+                    if (serviceProvider == null) break;
+                    serviceProvider.unregisterForInfoChanged(callback);
+                    break;
+                case NETWORK_SERVICE_INDICATION_NETWORK_INFO_CHANGED:
+                    if (serviceProvider == null) break;
+                    serviceProvider.notifyInfoChangedToCallbacks();
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Default constructor.
+     */
+    public NetworkService() {
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+
+        mHandler = new NetworkServiceHandler(mHandlerThread.getLooper());
+        log("network service created");
+    }
+
+    /**
+     * Create the instance of {@link NetworkServiceProvider}. Network service provider must override
+     * this method to facilitate the creation of {@link NetworkServiceProvider} instances. The system
+     * will call this method after binding the network service for each active SIM slot id.
+     *
+     * This methead is guaranteed to be invoked in {@link NetworkService}'s internal handler thread
+     * whose looper can be retrieved with {@link Looper.myLooper()} when override this method.
+     *
+     * @param slotIndex SIM slot id the network service associated with.
+     * @return Network service object. Null if failed to create the provider (e.g. invalid slot
+     * index)
+     */
+    @Nullable
+    public abstract NetworkServiceProvider onCreateNetworkServiceProvider(int slotIndex);
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (intent == null || !SERVICE_INTERFACE.equals(intent.getAction())) {
+            loge("Unexpected intent " + intent);
+            return null;
+        }
+
+        return mBinder;
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS, 0,
+                0, null).sendToTarget();
+
+        return false;
+    }
+
+    /** @hide */
+    @Override
+    public void onDestroy() {
+        mHandlerThread.quitSafely();
+        super.onDestroy();
+    }
+
+    /**
+     * A wrapper around INetworkService that forwards calls to implementations of
+     * {@link NetworkService}.
+     */
+    private class INetworkServiceWrapper extends INetworkService.Stub {
+
+        @Override
+        public void createNetworkServiceProvider(int slotIndex) {
+            mHandler.obtainMessage(NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER, slotIndex,
+                    0, null).sendToTarget();
+        }
+
+        @Override
+        public void removeNetworkServiceProvider(int slotIndex) {
+            mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER, slotIndex,
+                    0, null).sendToTarget();
+        }
+
+        @Override
+        public void requestNetworkRegistrationInfo(int slotIndex, int domain,
+                                                   INetworkServiceCallback callback) {
+            mHandler.obtainMessage(NETWORK_SERVICE_GET_REGISTRATION_INFO, slotIndex,
+                    domain, callback).sendToTarget();
+        }
+
+        @Override
+        public void registerForNetworkRegistrationInfoChanged(
+                int slotIndex, INetworkServiceCallback callback) {
+            mHandler.obtainMessage(NETWORK_SERVICE_REGISTER_FOR_INFO_CHANGE, slotIndex,
+                    0, callback).sendToTarget();
+        }
+
+        @Override
+        public void unregisterForNetworkRegistrationInfoChanged(
+                int slotIndex, INetworkServiceCallback callback) {
+            mHandler.obtainMessage(NETWORK_SERVICE_UNREGISTER_FOR_INFO_CHANGE, slotIndex,
+                    0, callback).sendToTarget();
+        }
+    }
+
+    private final void log(String s) {
+        Rlog.d(TAG, s);
+    }
+
+    private final void loge(String s) {
+        Rlog.e(TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/NetworkServiceCallback.java b/android-35/android/telephony/NetworkServiceCallback.java
new file mode 100644
index 0000000..e8e73ee
--- /dev/null
+++ b/android-35/android/telephony/NetworkServiceCallback.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2017 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;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.NetworkService.NetworkServiceProvider;
+
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Network service callback. Object of this class is passed to NetworkServiceProvider upon
+ * calling requestNetworkRegistrationInfo, to receive asynchronous feedback from
+ * NetworkServiceProvider upon onRequestNetworkRegistrationInfoComplete. It's like a wrapper of
+ * INetworkServiceCallback because INetworkServiceCallback can't be a parameter type in public APIs.
+ *
+ * @hide
+ */
+@SystemApi
+public class NetworkServiceCallback {
+
+    private static final String mTag = NetworkServiceCallback.class.getSimpleName();
+
+    /**
+     * Result of network requests
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
+            RESULT_ERROR_ILLEGAL_STATE, RESULT_ERROR_FAILED})
+    public @interface Result {}
+
+    /** Request is completed successfully */
+    public static final int RESULT_SUCCESS              = 0;
+    /** Request is not support */
+    public static final int RESULT_ERROR_UNSUPPORTED    = 1;
+    /** Request contains invalid arguments */
+    public static final int RESULT_ERROR_INVALID_ARG    = 2;
+    /** Service is busy */
+    public static final int RESULT_ERROR_BUSY           = 3;
+    /** Request sent in illegal state */
+    public static final int RESULT_ERROR_ILLEGAL_STATE  = 4;
+    /** Request failed */
+    public static final int RESULT_ERROR_FAILED         = 5;
+
+    private final INetworkServiceCallback mCallback;
+
+    /** @hide */
+    public NetworkServiceCallback(INetworkServiceCallback callback) {
+        mCallback = callback;
+    }
+
+    /**
+     * Called to indicate result of
+     * {@link NetworkServiceProvider#requestNetworkRegistrationInfo(int, NetworkServiceCallback)}
+     *
+     * @param result Result status like {@link NetworkServiceCallback#RESULT_SUCCESS} or
+     * {@link NetworkServiceCallback#RESULT_ERROR_UNSUPPORTED}
+     * @param state The state information to be returned to callback.
+     */
+    public void onRequestNetworkRegistrationInfoComplete(int result,
+                                                         @Nullable NetworkRegistrationInfo state) {
+        if (mCallback != null) {
+            try {
+                mCallback.onRequestNetworkRegistrationInfoComplete(result, state);
+            } catch (RemoteException e) {
+                Rlog.e(mTag, "Failed to onRequestNetworkRegistrationInfoComplete on the remote");
+            }
+        } else {
+            Rlog.e(mTag, "onRequestNetworkRegistrationInfoComplete callback is null.");
+        }
+    }
+}
\ No newline at end of file
diff --git a/android-35/android/telephony/NrVopsSupportInfo.java b/android-35/android/telephony/NrVopsSupportInfo.java
new file mode 100644
index 0000000..155ee38
--- /dev/null
+++ b/android-35/android/telephony/NrVopsSupportInfo.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2021 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Class stores information related to NR network VoPS support
+ * @hide
+ */
+@SystemApi
+public final class NrVopsSupportInfo extends VopsSupportInfo {
+
+    /**
+     * Indicates network does not support vops
+     */
+    public static final int NR_STATUS_VOPS_NOT_SUPPORTED = 0;
+
+    /**
+     * Indicates network supports vops over 3gpp access.
+     */
+    public static final int NR_STATUS_VOPS_3GPP_SUPPORTED = 1;
+
+    /**
+     * Indicates network supports vops over non 3gpp access
+     */
+    public static final int NR_STATUS_VOPS_NON_3GPP_SUPPORTED = 2;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = {"NR_STATUS_VOPS_"},
+        value = {
+            NR_STATUS_VOPS_NOT_SUPPORTED,
+            NR_STATUS_VOPS_3GPP_SUPPORTED,
+            NR_STATUS_VOPS_NON_3GPP_SUPPORTED
+        })
+    public @interface NrVopsStatus {}
+
+    /**
+     * Indicates network does not support emergency service
+     */
+    public static final int NR_STATUS_EMC_NOT_SUPPORTED = 0;
+
+    /**
+     * Indicates network supports emergency service in NR connected to 5GCN only
+     */
+    public static final int NR_STATUS_EMC_5GCN_ONLY = 1;
+
+    /**
+     * Indicates network supports emergency service in E-UTRA connected to 5GCN only
+     */
+    public static final int NR_STATUS_EMC_EUTRA_5GCN_ONLY = 2;
+
+    /**
+     * Indicates network supports emergency service in NR connected to 5GCN and
+     * E-UTRA connected to 5GCN
+     */
+    public static final int NR_STATUS_EMC_NR_EUTRA_5GCN = 3;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = {"NR_STATUS_EMC_"},
+        value = {
+            NR_STATUS_EMC_NOT_SUPPORTED,
+            NR_STATUS_EMC_5GCN_ONLY,
+            NR_STATUS_EMC_EUTRA_5GCN_ONLY,
+            NR_STATUS_EMC_NR_EUTRA_5GCN
+        })
+    public @interface NrEmcStatus {}
+
+    /**
+     * Indicates network does not support emergency service
+     */
+    public static final int NR_STATUS_EMF_NOT_SUPPORTED = 0;
+
+    /**
+     * Indicates network supports emergency service fallback in NR connected to 5GCN only
+     */
+    public static final int NR_STATUS_EMF_5GCN_ONLY = 1;
+
+    /**
+     * Indicates network supports emergency service fallback in E-UTRA connected to 5GCN only
+     */
+    public static final int NR_STATUS_EMF_EUTRA_5GCN_ONLY = 2;
+
+    /**
+     * Indicates network supports emergency service fallback in NR connected to 5GCN
+     * and E-UTRA connected to 5GCN
+     */
+    public static final int NR_STATUS_EMF_NR_EUTRA_5GCN = 3;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = {"NR_STATUS_EMF_"},
+        value = {
+            NR_STATUS_EMF_NOT_SUPPORTED,
+            NR_STATUS_EMF_5GCN_ONLY,
+            NR_STATUS_EMF_EUTRA_5GCN_ONLY,
+            NR_STATUS_EMF_NR_EUTRA_5GCN
+        })
+    public @interface NrEmfStatus {}
+
+    @NrVopsStatus
+    private final int mVopsSupport;
+    @NrEmcStatus
+    private final int mEmcSupport;
+    @NrEmfStatus
+    private final int mEmfSupport;
+
+    public NrVopsSupportInfo(@NrVopsStatus int vops, @NrEmcStatus int emc, @NrEmcStatus int emf) {
+        mVopsSupport = vops;
+        mEmcSupport = emc;
+        mEmfSupport = emf;
+    }
+
+    /**
+     * Provides the NR VoPS support capability as described in:
+     * 3GPP 24.501 EPS network feature support -> IMS VoPS
+     */
+    public @NrVopsStatus int getVopsSupport() {
+        return mVopsSupport;
+    }
+
+    /**
+     * Provides the NR Emergency bearer support capability as described in:
+     * 3GPP 24.501 EPS network feature support -> EMC, and
+     * 38.331 SIB1 : ims-EmergencySupport
+     */
+    public @NrEmcStatus int getEmcSupport() {
+        return mEmcSupport;
+    }
+
+    /**
+     * Provides the NR emergency service fallback support capability as
+     * described in 3GPP 24.501 EPS network feature support -> EMF
+     */
+    public @NrEmfStatus int getEmfSupport() {
+        return mEmfSupport;
+    }
+
+    /**
+     * Returns whether VoPS is supported by the network
+     */
+    @Override
+    public boolean isVopsSupported() {
+        return mVopsSupport != NR_STATUS_VOPS_NOT_SUPPORTED;
+    }
+
+    /**
+     * Returns whether emergency service is supported by the network
+     */
+    @Override
+    public boolean isEmergencyServiceSupported() {
+        return mEmcSupport != NR_STATUS_EMC_NOT_SUPPORTED;
+    }
+
+    /**
+     * Returns whether emergency service fallback is supported by the network
+     */
+    public boolean isEmergencyServiceFallbackSupported() {
+        return mEmfSupport != NR_STATUS_EMF_NOT_SUPPORTED;
+    }
+
+    /**
+     * Implement the Parcelable interface
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        super.writeToParcel(out, flags, AccessNetworkType.NGRAN);
+        out.writeInt(mVopsSupport);
+        out.writeInt(mEmcSupport);
+        out.writeInt(mEmfSupport);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o == null || !(o instanceof NrVopsSupportInfo)) {
+            return false;
+        }
+        if (this == o) return true;
+        NrVopsSupportInfo other = (NrVopsSupportInfo) o;
+        return mVopsSupport == other.mVopsSupport
+            && mEmcSupport == other.mEmcSupport
+            && mEmfSupport == other.mEmfSupport;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mVopsSupport, mEmcSupport, mEmfSupport);
+    }
+
+    /**
+     * @return string representation.
+     */
+    @NonNull
+    @Override
+    public String toString() {
+        return ("NrVopsSupportInfo : "
+                + " mVopsSupport = " + mVopsSupport
+                + " mEmcSupport = " + mEmcSupport
+                + " mEmfSupport = " + mEmfSupport);
+    }
+
+    public static final @android.annotation.NonNull Creator<NrVopsSupportInfo> CREATOR =
+            new Creator<NrVopsSupportInfo>() {
+        @Override
+        public NrVopsSupportInfo createFromParcel(Parcel in) {
+            // Skip the type info.
+            in.readInt();
+            return new NrVopsSupportInfo(in);
+        }
+
+        @Override
+        public NrVopsSupportInfo[] newArray(int size) {
+            return new NrVopsSupportInfo[size];
+        }
+    };
+
+    /** @hide */
+    protected static NrVopsSupportInfo createFromParcelBody(Parcel in) {
+        return new NrVopsSupportInfo(in);
+    }
+
+    private NrVopsSupportInfo(Parcel in) {
+        mVopsSupport = in.readInt();
+        mEmcSupport = in.readInt();
+        mEmfSupport = in.readInt();
+    }
+}
diff --git a/android-35/android/telephony/NumberVerificationCallback.java b/android-35/android/telephony/NumberVerificationCallback.java
new file mode 100644
index 0000000..71df1f2
--- /dev/null
+++ b/android-35/android/telephony/NumberVerificationCallback.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A callback for number verification. After a request for number verification is received,
+ * the system will call {@link #onCallReceived(String)} if a phone call was received from a number
+ * matching the provided {@link PhoneNumberRange} or it will call {@link #onVerificationFailed(int)}
+ * if an error occurs.
+ * @hide
+ */
+@SystemApi
+public interface NumberVerificationCallback {
+    /** @hide */
+    @IntDef(value = {REASON_UNSPECIFIED, REASON_TIMED_OUT, REASON_NETWORK_NOT_AVAILABLE,
+            REASON_TOO_MANY_CALLS, REASON_CONCURRENT_REQUESTS, REASON_IN_ECBM,
+            REASON_IN_EMERGENCY_CALL},
+            prefix = {"REASON_"})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface NumberVerificationFailureReason {}
+
+    /**
+     * Verification failed for an unspecified reason.
+     */
+    int REASON_UNSPECIFIED = 0;
+
+    /**
+     * Verification failed because no phone call was received from a matching number within the
+     * provided timeout.
+     */
+    int REASON_TIMED_OUT = 1;
+
+    /**
+     * Verification failed because no cellular voice network is available.
+     */
+    int REASON_NETWORK_NOT_AVAILABLE = 2;
+
+    /**
+     * Verification failed because there are currently too many ongoing phone calls for a new
+     * incoming phone call to be received.
+     */
+    int REASON_TOO_MANY_CALLS = 3;
+
+    /**
+     * Verification failed because a previous request for verification has not yet completed.
+     */
+    int REASON_CONCURRENT_REQUESTS = 4;
+
+    /**
+     * Verification failed because the phone is in emergency callback mode.
+     */
+    int REASON_IN_ECBM = 5;
+
+    /**
+     * Verification failed because the phone is currently in an emergency call.
+     */
+    int REASON_IN_EMERGENCY_CALL = 6;
+
+    /**
+     * Called when the device receives a phone call from the provided {@link PhoneNumberRange}.
+     * @param phoneNumber The phone number within the range that called. May or may not contain the
+     *                    country code, but will be entirely numeric.
+     */
+    default void onCallReceived(@NonNull String phoneNumber) { }
+
+    /**
+     * Called when verification fails for some reason.
+     * @param reason The reason for failure.
+     */
+    default void onVerificationFailed(@NumberVerificationFailureReason int reason) { }
+}
diff --git a/android-35/android/telephony/PcoData.java b/android-35/android/telephony/PcoData.java
new file mode 100644
index 0000000..39e4f2f
--- /dev/null
+++ b/android-35/android/telephony/PcoData.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Contains Carrier-specific (and opaque) Protocol configuration Option
+ * Data.  In general this is only passed on to carrier-specific applications
+ * for interpretation.
+ *
+ * @hide
+ */
+public class PcoData implements Parcelable {
+
+    public final int cid;
+    public final String bearerProto;
+    public final int pcoId;
+    public final byte[] contents;
+
+    public PcoData(int cid, String bearerProto, int pcoId, byte[]contents) {
+        this.cid = cid;
+        this.bearerProto = bearerProto;
+        this.pcoId = pcoId;
+        this.contents = contents;
+    }
+
+    public PcoData(Parcel in) {
+        cid = in.readInt();
+        bearerProto = in.readString();
+        pcoId = in.readInt();
+        contents = in.createByteArray();
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(cid);
+        out.writeString(bearerProto);
+        out.writeInt(pcoId);
+        out.writeByteArray(contents);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable.Creator}
+     *
+     * @hide
+     */
+    public static final @android.annotation.NonNull Parcelable.Creator<PcoData> CREATOR = new Parcelable.Creator() {
+        public PcoData createFromParcel(Parcel in) {
+            return new PcoData(in);
+        }
+
+        public PcoData[] newArray(int size) {
+            return new PcoData[size];
+        }
+    };
+
+    @Override
+    public String toString() {
+        return "PcoData(" + cid + ", " + bearerProto + ", " + pcoId + ", contents[" +
+                contents.length + "])";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PcoData pcoData = (PcoData) o;
+        return cid == pcoData.cid
+                && pcoId == pcoData.pcoId
+                && Objects.equals(bearerProto, pcoData.bearerProto)
+                && Arrays.equals(contents, pcoData.contents);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(cid, bearerProto, pcoId);
+        result = 31 * result + Arrays.hashCode(contents);
+        return result;
+    }
+}
diff --git a/android-35/android/telephony/PhoneCapability.java b/android-35/android/telephony/PhoneCapability.java
new file mode 100644
index 0000000..48170df
--- /dev/null
+++ b/android-35/android/telephony/PhoneCapability.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Phone capability which describes the data connection capability of modem.
+ * It's used to evaluate possible phone config change, for example from single
+ * SIM device to multi-SIM device.
+ * @hide
+ */
+@SystemApi
+public final class PhoneCapability implements Parcelable {
+    // Hardcoded default DSDS capability.
+    /** @hide */
+    public static final PhoneCapability DEFAULT_DSDS_CAPABILITY;
+    // Hardcoded default Single SIM single standby capability.
+    /** @hide */
+    public static final PhoneCapability DEFAULT_SSSS_CAPABILITY;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "DEVICE_NR_CAPABILITY_" }, value = {
+            DEVICE_NR_CAPABILITY_NSA,
+            DEVICE_NR_CAPABILITY_SA,
+    })
+    public @interface DeviceNrCapability {}
+
+    /**
+     * Indicates DEVICE_NR_CAPABILITY_NSA determine that the device enable the non-standalone
+     * (NSA) mode of 5G NR.
+     * @hide
+     */
+    @SystemApi
+    public static final int DEVICE_NR_CAPABILITY_NSA = 1;
+
+    /**
+     * Indicates DEVICE_NR_CAPABILITY_SA determine that the device enable the standalone (SA)
+     * mode of 5G NR.
+     * @hide
+     */
+    @SystemApi
+    public static final int DEVICE_NR_CAPABILITY_SA = 2;
+
+    static {
+        ModemInfo modemInfo1 = new ModemInfo(0, 0, true, true);
+        ModemInfo modemInfo2 = new ModemInfo(1, 0, true, true);
+
+        List<ModemInfo> logicalModemList = new ArrayList<>();
+        logicalModemList.add(modemInfo1);
+        logicalModemList.add(modemInfo2);
+        int[] deviceNrCapabilities = new int[0];
+
+        DEFAULT_DSDS_CAPABILITY = new PhoneCapability(1, 1, logicalModemList, false,
+                deviceNrCapabilities);
+
+        logicalModemList = new ArrayList<>();
+        logicalModemList.add(modemInfo1);
+        DEFAULT_SSSS_CAPABILITY = new PhoneCapability(1, 1, logicalModemList, false,
+                deviceNrCapabilities);
+    }
+
+    /**
+     * mMaxActiveVoiceSubscriptions defines the maximum subscriptions that can support
+     * simultaneous voice calls. For a dual sim dual standby (DSDS) device it would be one, but
+     * for a dual sim dual active (DSDA) device, or a DSDS device that supports "virtual DSDA" (
+     * using the data line of 1 SIM to temporarily provide IMS voice connectivity to the other SIM)
+     * it would be 2.
+     *
+     * @hide
+     */
+    private final int mMaxActiveVoiceSubscriptions;
+
+    /**
+     * mMaxActiveDataSubscriptions defines the maximum subscriptions that can support
+     * simultaneous data connections.
+     * For example, for dual sim dual active L+L device it should be 2.
+     *
+     * @hide
+     */
+    private final int mMaxActiveDataSubscriptions;
+
+    /**
+     * Whether modem supports both internet PDN up so
+     * that we can do ping test before tearing down the
+     * other one.
+     *
+     * @hide
+     */
+    private final boolean mNetworkValidationBeforeSwitchSupported;
+
+    /**
+     * List of logical modem information.
+     *
+     * @hide
+     */
+    @NonNull
+    private final List<ModemInfo> mLogicalModemList;
+
+    /**
+     * Device NR capabilities.
+     *
+     * @hide
+     */
+    @NonNull
+    private final int[] mDeviceNrCapabilities;
+
+    /** @hide */
+    public PhoneCapability(int maxActiveVoiceSubscriptions, int maxActiveDataSubscriptions,
+            List<ModemInfo> logicalModemList, boolean networkValidationBeforeSwitchSupported,
+            int[] deviceNrCapabilities) {
+        this.mMaxActiveVoiceSubscriptions = maxActiveVoiceSubscriptions;
+        this.mMaxActiveDataSubscriptions = maxActiveDataSubscriptions;
+        // Make sure it's not null.
+        this.mLogicalModemList = logicalModemList == null ? new ArrayList<>() : logicalModemList;
+        this.mNetworkValidationBeforeSwitchSupported = networkValidationBeforeSwitchSupported;
+        this.mDeviceNrCapabilities = deviceNrCapabilities;
+    }
+
+    private PhoneCapability(@NonNull Builder builder) {
+        this.mMaxActiveVoiceSubscriptions = builder.mMaxActiveVoiceSubscriptions;
+        this.mMaxActiveDataSubscriptions = builder.mMaxActiveDataSubscriptions;
+        // Make sure it's not null.
+        this.mLogicalModemList = builder.mLogicalModemList == null ? new ArrayList<>()
+                : builder.mLogicalModemList;
+        this.mNetworkValidationBeforeSwitchSupported =
+                builder.mNetworkValidationBeforeSwitchSupported;
+        this.mDeviceNrCapabilities = builder.mDeviceNrCapabilities;
+
+    }
+
+    @Override
+    public String toString() {
+        return "mMaxActiveVoiceSubscriptions=" + mMaxActiveVoiceSubscriptions
+                + " mMaxActiveDataSubscriptions=" + mMaxActiveDataSubscriptions
+                + " mNetworkValidationBeforeSwitchSupported="
+                + mNetworkValidationBeforeSwitchSupported
+                + " mDeviceNrCapability " + Arrays.toString(mDeviceNrCapabilities);
+    }
+
+    private PhoneCapability(Parcel in) {
+        mMaxActiveVoiceSubscriptions = in.readInt();
+        mMaxActiveDataSubscriptions = in.readInt();
+        mNetworkValidationBeforeSwitchSupported = in.readBoolean();
+        mLogicalModemList = new ArrayList<>();
+        in.readList(mLogicalModemList, ModemInfo.class.getClassLoader(), android.telephony.ModemInfo.class);
+        mDeviceNrCapabilities = in.createIntArray();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mMaxActiveVoiceSubscriptions,
+                mMaxActiveDataSubscriptions,
+                mLogicalModemList,
+                mNetworkValidationBeforeSwitchSupported,
+                Arrays.hashCode(mDeviceNrCapabilities));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof PhoneCapability) || hashCode() != o.hashCode()) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        PhoneCapability s = (PhoneCapability) o;
+
+        return (mMaxActiveVoiceSubscriptions == s.mMaxActiveVoiceSubscriptions
+                && mMaxActiveDataSubscriptions == s.mMaxActiveDataSubscriptions
+                && mNetworkValidationBeforeSwitchSupported
+                == s.mNetworkValidationBeforeSwitchSupported
+                && mLogicalModemList.equals(s.mLogicalModemList)
+                && Arrays.equals(mDeviceNrCapabilities, s.mDeviceNrCapabilities));
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(@NonNull Parcel dest, @Parcelable.WriteFlags int flags) {
+        dest.writeInt(mMaxActiveVoiceSubscriptions);
+        dest.writeInt(mMaxActiveDataSubscriptions);
+        dest.writeBoolean(mNetworkValidationBeforeSwitchSupported);
+        dest.writeList(mLogicalModemList);
+        dest.writeIntArray(mDeviceNrCapabilities);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<PhoneCapability> CREATOR =
+            new Parcelable.Creator() {
+        public PhoneCapability createFromParcel(Parcel in) {
+            return new PhoneCapability(in);
+        }
+
+        public PhoneCapability[] newArray(int size) {
+            return new PhoneCapability[size];
+        }
+    };
+
+    /**
+     * @return the maximum subscriptions that can support simultaneous voice calls. For a dual
+     * sim dual standby (DSDS) device it would be one, but for a dual sim dual active device it
+     * would be 2.
+     * @hide
+     */
+    @SystemApi
+    public @IntRange(from = 1) int getMaxActiveVoiceSubscriptions() {
+        return mMaxActiveVoiceSubscriptions;
+    }
+
+    /**
+     * @return the maximum subscriptions that can support simultaneous data connections.
+     * For example, for L+L device it should be 2.
+     * @hide
+     */
+    @SystemApi
+    public @IntRange(from = 1) int getMaxActiveDataSubscriptions() {
+        return mMaxActiveDataSubscriptions;
+    }
+
+    /**
+     * @return Check whether the Citizens Broadband Radio Service(CBRS) network validation before
+     * CBRS switch is supported or not.
+     *
+     * @hide
+     */
+    public boolean isNetworkValidationBeforeSwitchSupported() {
+        return mNetworkValidationBeforeSwitchSupported;
+    }
+
+    /**
+     * @return The list of logical modem information.
+     * @hide
+     */
+    public List<ModemInfo> getLogicalModemList() {
+        return mLogicalModemList;
+    }
+
+    /**
+     * Return List of the device's NR capability. If the device doesn't support NR capability,
+     * then this api return empty array.
+     * @see DEVICE_NR_CAPABILITY_NSA
+     * @see DEVICE_NR_CAPABILITY_SA
+     *
+     * @return List of the device's NR capability.
+     * @hide
+     */
+    @SystemApi
+    public @NonNull @DeviceNrCapability int[] getDeviceNrCapabilities() {
+        return mDeviceNrCapabilities == null ? (new int[0]) : mDeviceNrCapabilities;
+    }
+
+
+    /**
+     * Builder for {@link PhoneCapability}.
+     *
+     * @hide
+     */
+    public static class Builder {
+        /**
+         * mMaxActiveVoiceSubscriptions defines the maximum subscriptions that can support
+         * simultaneous voice calls. For a dual sim dual standby (DSDS) device it would be one, but
+         * for a dual sim dual active (DSDA) device, or a DSDS device that supports "virtual DSDA"
+         * (using the data line of 1 SIM to temporarily provide IMS voice connectivity to the other
+         * SIM) it would be 2.
+         *
+         * @hide
+         */
+        private int mMaxActiveVoiceSubscriptions = 0;
+
+        /**
+         * mMaxActiveDataSubscriptions defines the maximum subscriptions that can support
+         * simultaneous data connections. For example, for L+L device it should be 2.
+         *
+         * @hide
+         */
+        private int mMaxActiveDataSubscriptions = 0;
+
+        /**
+         * Whether modem supports both internet PDN up so that we can do ping test before tearing
+         * down the other one.
+         *
+         * @hide
+         */
+        private boolean mNetworkValidationBeforeSwitchSupported = false;
+
+        /**
+         * List of logical modem information.
+         *
+         * @hide
+         */
+        @NonNull
+        private List<ModemInfo> mLogicalModemList = new ArrayList<>();
+
+        /**
+         * Device NR capabilities.
+         *
+         * @hide
+         */
+        @NonNull
+        private int[] mDeviceNrCapabilities = new int[0];
+
+        /**
+         * Default constructor.
+         */
+        public Builder() {
+        }
+
+        public Builder(@NonNull PhoneCapability phoneCapability) {
+            mMaxActiveVoiceSubscriptions = phoneCapability.mMaxActiveVoiceSubscriptions;
+            mMaxActiveDataSubscriptions = phoneCapability.mMaxActiveDataSubscriptions;
+            mNetworkValidationBeforeSwitchSupported =
+                    phoneCapability.mNetworkValidationBeforeSwitchSupported;
+            mLogicalModemList = phoneCapability.mLogicalModemList;
+            mDeviceNrCapabilities = phoneCapability.mDeviceNrCapabilities;
+        }
+
+        /**
+         * Sets the max active voice subscriptions supported by the device.
+         */
+        public Builder setMaxActiveVoiceSubscriptions(int maxActiveVoiceSubscriptions) {
+            mMaxActiveVoiceSubscriptions = maxActiveVoiceSubscriptions;
+            return this;
+        }
+
+        /**
+         * Sets the max active voice subscriptions supported by the device.
+         */
+        public Builder setMaxActiveDataSubscriptions(int maxActiveDataSubscriptions) {
+            mMaxActiveDataSubscriptions = maxActiveDataSubscriptions;
+            return this;
+        }
+
+        /**
+         * Sets the max active data subscriptions supported by the device. Can be fewer than the
+         * active voice subscriptions.
+         */
+        public Builder setNetworkValidationBeforeSwitchSupported(
+                boolean networkValidationBeforeSwitchSupported) {
+            mNetworkValidationBeforeSwitchSupported = networkValidationBeforeSwitchSupported;
+            return this;
+        }
+
+        /**
+         * Sets the logical modem list of the device.
+         */
+        public Builder setLogicalModemList(@NonNull List<ModemInfo> logicalModemList) {
+            mLogicalModemList = logicalModemList;
+            return this;
+        }
+
+        /**
+         * Sets the NR capabilities supported by the device.
+         */
+        public Builder setDeviceNrCapabilities(@NonNull int[] deviceNrCapabilities) {
+            mDeviceNrCapabilities = deviceNrCapabilities;
+            return this;
+        }
+
+        /**
+         * Build the {@link PhoneCapability}.
+         *
+         * @return The {@link PhoneCapability} instance.
+         */
+        public PhoneCapability build() {
+            return new PhoneCapability(this);
+        }
+    }
+}
diff --git a/android-35/android/telephony/PhoneNumberFormattingTextWatcher.java b/android-35/android/telephony/PhoneNumberFormattingTextWatcher.java
new file mode 100644
index 0000000..2506360
--- /dev/null
+++ b/android-35/android/telephony/PhoneNumberFormattingTextWatcher.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2008 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;
+
+import android.annotation.WorkerThread;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.TextWatcher;
+import android.text.style.TtsSpan;
+
+import com.android.i18n.phonenumbers.AsYouTypeFormatter;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+
+import java.util.Locale;
+
+/**
+ * Watches a {@link android.widget.TextView} and if a phone number is entered
+ * will format it.
+ * <p>
+ * Stop formatting when the user
+ * <ul>
+ * <li>Inputs non-dialable characters</li>
+ * <li>Removes the separator in the middle of string.</li>
+ * </ul>
+ * <p>
+ * The formatting will be restarted once the text is cleared.
+ *
+ * @deprecated This is a thin wrapper on a `libphonenumber` `AsYouTypeFormatter`; it is recommended
+ * to use that instead.
+ */
+public class PhoneNumberFormattingTextWatcher implements TextWatcher {
+
+    /**
+     * Indicates the change was caused by ourselves.
+     */
+    private boolean mSelfChange = false;
+
+    /**
+     * Indicates the formatting has been stopped.
+     */
+    private boolean mStopFormatting;
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private AsYouTypeFormatter mFormatter;
+
+    /**
+     * The formatting is based on the current system locale and future locale changes
+     * may not take effect on this instance.
+     */
+    public PhoneNumberFormattingTextWatcher() {
+        this(Locale.getDefault().getCountry());
+    }
+
+    /**
+     * The formatting is based on the given <code>countryCode</code>.
+     *
+     * @param countryCode the ISO 3166-1 two-letter country code that indicates the country/region
+     * where the phone number is being entered.
+     */
+    @WorkerThread
+    public PhoneNumberFormattingTextWatcher(String countryCode) {
+        if (countryCode == null) throw new IllegalArgumentException();
+        mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode);
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count,
+            int after) {
+        if (mSelfChange || mStopFormatting) {
+            return;
+        }
+        // If the user manually deleted any non-dialable characters, stop formatting
+        if (count > 0 && hasSeparator(s, start, count)) {
+            stopFormatting();
+        }
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+        if (mSelfChange || mStopFormatting) {
+            return;
+        }
+        // If the user inserted any non-dialable characters, stop formatting
+        if (count > 0 && hasSeparator(s, start, count)) {
+            stopFormatting();
+        }
+    }
+
+    @Override
+    public synchronized void afterTextChanged(Editable s) {
+        if (mStopFormatting) {
+            // Restart the formatting when all texts were clear.
+            mStopFormatting = !(s.length() == 0);
+            return;
+        }
+        if (mSelfChange) {
+            // Ignore the change caused by s.replace().
+            return;
+        }
+        String formatted = reformat(s, Selection.getSelectionEnd(s));
+        if (formatted != null) {
+            int rememberedPos = mFormatter.getRememberedPosition();
+            mSelfChange = true;
+            s.replace(0, s.length(), formatted, 0, formatted.length());
+            // The text could be changed by other TextWatcher after we changed it. If we found the
+            // text is not the one we were expecting, just give up calling setSelection().
+            if (formatted.equals(s.toString())) {
+                Selection.setSelection(s, rememberedPos);
+            }
+            mSelfChange = false;
+        }
+
+        //remove previous TTS spans
+        TtsSpan[] ttsSpans = s.getSpans(0, s.length(), TtsSpan.class);
+        for (TtsSpan ttsSpan : ttsSpans) {
+            s.removeSpan(ttsSpan);
+        }
+
+        PhoneNumberUtils.ttsSpanAsPhoneNumber(s, 0, s.length());
+    }
+
+    /**
+     * Generate the formatted number by ignoring all non-dialable chars and stick the cursor to the
+     * nearest dialable char to the left. For instance, if the number is  (650) 123-45678 and '4' is
+     * removed then the cursor should be behind '3' instead of '-'.
+     */
+    private String reformat(CharSequence s, int cursor) {
+        // The index of char to the leftward of the cursor.
+        int curIndex = cursor - 1;
+        String formatted = null;
+        mFormatter.clear();
+        char lastNonSeparator = 0;
+        boolean hasCursor = false;
+        int len = s.length();
+        for (int i = 0; i < len; i++) {
+            char c = s.charAt(i);
+            if (PhoneNumberUtils.isNonSeparator(c)) {
+                if (lastNonSeparator != 0) {
+                    formatted = getFormattedNumber(lastNonSeparator, hasCursor);
+                    hasCursor = false;
+                }
+                lastNonSeparator = c;
+            }
+            if (i == curIndex) {
+                hasCursor = true;
+            }
+        }
+        if (lastNonSeparator != 0) {
+            formatted = getFormattedNumber(lastNonSeparator, hasCursor);
+        }
+        return formatted;
+    }
+
+    private String getFormattedNumber(char lastNonSeparator, boolean hasCursor) {
+        return hasCursor ? mFormatter.inputDigitAndRememberPosition(lastNonSeparator)
+                : mFormatter.inputDigit(lastNonSeparator);
+    }
+
+    private void stopFormatting() {
+        mStopFormatting = true;
+        mFormatter.clear();
+    }
+
+    private boolean hasSeparator(final CharSequence s, final int start, final int count) {
+        for (int i = start; i < start + count; i++) {
+            char c = s.charAt(i);
+            if (!PhoneNumberUtils.isNonSeparator(c)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/android-35/android/telephony/PhoneNumberRange.java b/android-35/android/telephony/PhoneNumberRange.java
new file mode 100644
index 0000000..2b199d2
--- /dev/null
+++ b/android-35/android/telephony/PhoneNumberRange.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * This class is used to represent a range of phone numbers. Each range corresponds to a contiguous
+ * block of phone numbers.
+ *
+ * Example:
+ * {@code
+ * {
+ *     mCountryCode = "1"
+ *     mPrefix = "650555"
+ *     mLowerBound = "0055"
+ *     mUpperBound = "0899"
+ * }
+ * }
+ * would match 16505550089 and 6505550472, but not 63827593759 or 16505550900
+ * @hide
+ */
+@SystemApi
+public final class PhoneNumberRange implements Parcelable {
+    public static final @android.annotation.NonNull Creator<PhoneNumberRange> CREATOR = new Creator<PhoneNumberRange>() {
+        @Override
+        public PhoneNumberRange createFromParcel(Parcel in) {
+            return new PhoneNumberRange(in);
+        }
+
+        @Override
+        public PhoneNumberRange[] newArray(int size) {
+            return new PhoneNumberRange[size];
+        }
+    };
+
+    private final String mCountryCode;
+    private final String mPrefix;
+    private final String mLowerBound;
+    private final String mUpperBound;
+
+    /**
+     * @param countryCode The country code, omitting the leading "+"
+     * @param prefix A prefix that all numbers matching the range must have.
+     * @param lowerBound When concatenated with the prefix, represents the lower bound of phone
+     *                   numbers that match this range.
+     * @param upperBound When concatenated with the prefix, represents the upper bound of phone
+     *                   numbers that match this range.
+     */
+    public PhoneNumberRange(@NonNull String countryCode, @NonNull String prefix,
+            @NonNull String lowerBound, @NonNull String upperBound) {
+        validateLowerAndUpperBounds(lowerBound, upperBound);
+        if (!Pattern.matches("[0-9]*", countryCode)) {
+            throw new IllegalArgumentException("Country code must be all numeric");
+        }
+        if (!Pattern.matches("[0-9]*", prefix)) {
+            throw new IllegalArgumentException("Prefix must be all numeric");
+        }
+        mCountryCode = countryCode;
+        mPrefix = prefix;
+        mLowerBound = lowerBound;
+        mUpperBound = upperBound;
+    }
+
+    private PhoneNumberRange(Parcel in) {
+        mCountryCode = in.readString();
+        mPrefix = in.readString();
+        mLowerBound = in.readString();
+        mUpperBound = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mCountryCode);
+        dest.writeString(mPrefix);
+        dest.writeString(mLowerBound);
+        dest.writeString(mUpperBound);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PhoneNumberRange that = (PhoneNumberRange) o;
+        return Objects.equals(mCountryCode, that.mCountryCode)
+                && Objects.equals(mPrefix, that.mPrefix)
+                && Objects.equals(mLowerBound, that.mLowerBound)
+                && Objects.equals(mUpperBound, that.mUpperBound);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCountryCode, mPrefix, mLowerBound, mUpperBound);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "PhoneNumberRange{"
+                + "mCountryCode='" + mCountryCode + '\''
+                + ", mPrefix='" + mPrefix + '\''
+                + ", mLowerBound='" + mLowerBound + '\''
+                + ", mUpperBound='" + mUpperBound + '\''
+                + '}';
+    }
+
+    private void validateLowerAndUpperBounds(String lowerBound, String upperBound) {
+        if (lowerBound.length() != upperBound.length()) {
+            throw new IllegalArgumentException("Lower and upper bounds must have the same length");
+        }
+        if (!Pattern.matches("[0-9]*", lowerBound)) {
+            throw new IllegalArgumentException("Lower bound must be all numeric");
+        }
+        if (!Pattern.matches("[0-9]*", upperBound)) {
+            throw new IllegalArgumentException("Upper bound must be all numeric");
+        }
+        if (Integer.parseInt(lowerBound) > Integer.parseInt(upperBound)) {
+            throw new IllegalArgumentException("Lower bound must be lower than upper bound");
+        }
+    }
+
+    /**
+     * Checks to see if the provided phone number matches this range.
+     * @param number A phone number, with or without separators or a country code.
+     * @return {@code true} if the number matches, {@code false} otherwise.
+     */
+    public boolean matches(@NonNull String number) {
+        // Check the prefix, make sure it matches either with or without the country code.
+        String normalizedNumber = number.replaceAll("[^0-9]", "");
+        String prefixWithCountryCode = mCountryCode + mPrefix;
+        String numberPostfix;
+        if (normalizedNumber.startsWith(prefixWithCountryCode)) {
+            numberPostfix = normalizedNumber.substring(prefixWithCountryCode.length());
+        } else if (normalizedNumber.startsWith(mPrefix)) {
+            numberPostfix = normalizedNumber.substring(mPrefix.length());
+        } else {
+            return false;
+        }
+
+        // Next check the postfix to make sure it lies within the bounds.
+        try {
+            int lower = Integer.parseInt(mLowerBound);
+            int upper = Integer.parseInt(mUpperBound);
+            int numberToCheck = Integer.parseInt(numberPostfix);
+            return numberToCheck <= upper && numberToCheck >= lower;
+        } catch (NumberFormatException e) {
+            Log.e(PhoneNumberRange.class.getSimpleName(), "Invalid bounds or number.", e);
+            return false;
+        }
+    }
+}
diff --git a/android-35/android/telephony/PhoneNumberUtils.java b/android-35/android/telephony/PhoneNumberUtils.java
new file mode 100644
index 0000000..0ecafc7
--- /dev/null
+++ b/android-35/android/telephony/PhoneNumberUtils.java
@@ -0,0 +1,2981 @@
+/*
+ * Copyright (C) 2006 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;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.PersistableBundle;
+import android.provider.Contacts;
+import android.provider.ContactsContract;
+import android.sysprop.TelephonyProperties;
+import android.telecom.PhoneAccount;
+import android.text.Editable;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.style.TtsSpan;
+import android.util.SparseIntArray;
+
+import com.android.i18n.phonenumbers.NumberParseException;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
+import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.android.internal.telephony.flags.Flags;
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Various utilities for dealing with phone number strings.
+ */
+public class PhoneNumberUtils {
+    /** {@hide} */
+    @IntDef(prefix = "BCD_EXTENDED_TYPE_", value = {
+            BCD_EXTENDED_TYPE_EF_ADN,
+            BCD_EXTENDED_TYPE_CALLED_PARTY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BcdExtendType {}
+
+    /*
+     * The BCD extended type used to determine the extended char for the digit which is greater than
+     * 9.
+     *
+     * see TS 51.011 section 10.5.1 EF_ADN(Abbreviated dialling numbers)
+     */
+    public static final int BCD_EXTENDED_TYPE_EF_ADN = 1;
+
+    /*
+     * The BCD extended type used to determine the extended char for the digit which is greater than
+     * 9.
+     *
+     * see TS 24.008 section 10.5.4.7 Called party BCD number
+     */
+    public static final int BCD_EXTENDED_TYPE_CALLED_PARTY = 2;
+
+    /*
+     * Special characters
+     *
+     * (See "What is a phone number?" doc)
+     * 'p' --- GSM pause character, same as comma
+     * 'n' --- GSM wild character
+     * 'w' --- GSM wait character
+     */
+    public static final char PAUSE = ',';
+    public static final char WAIT = ';';
+    public static final char WILD = 'N';
+
+    /*
+     * Calling Line Identification Restriction (CLIR)
+     */
+    private static final String CLIR_ON = "*31#";
+    private static final String CLIR_OFF = "#31#";
+
+    /*
+     * TOA = TON + NPI
+     * See TS 24.008 section 10.5.4.7 for details.
+     * These are the only really useful TOA values
+     */
+    public static final int TOA_International = 0x91;
+    public static final int TOA_Unknown = 0x81;
+
+    static final String LOG_TAG = "PhoneNumberUtils";
+    private static final boolean DBG = false;
+
+    private static final String BCD_EF_ADN_EXTENDED = "*#,N;";
+    private static final String BCD_CALLED_PARTY_EXTENDED = "*#abc";
+
+    private static final String PREFIX_WPS = "*272";
+
+    // WPS prefix when CLIR is being activated for the call.
+    private static final String PREFIX_WPS_CLIR_ACTIVATE = "*31#*272";
+
+    // WPS prefix when CLIR is being deactivated for the call.
+    private static final String PREFIX_WPS_CLIR_DEACTIVATE = "#31#*272";
+
+    /*
+     * global-phone-number = ["+"] 1*( DIGIT / written-sep )
+     * written-sep         = ("-"/".")
+     */
+    private static final Pattern GLOBAL_PHONE_NUMBER_PATTERN =
+            Pattern.compile("[\\+]?[0-9.-]+");
+
+    /** True if c is ISO-LATIN characters 0-9 */
+    public static boolean
+    isISODigit (char c) {
+        return c >= '0' && c <= '9';
+    }
+
+    /** True if c is ISO-LATIN characters 0-9, *, # */
+    public final static boolean
+    is12Key(char c) {
+        return (c >= '0' && c <= '9') || c == '*' || c == '#';
+    }
+
+    /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD  */
+    public final static boolean
+    isDialable(char c) {
+        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == WILD;
+    }
+
+    /** True if c is ISO-LATIN characters 0-9, *, # , + (no WILD)  */
+    public final static boolean
+    isReallyDialable(char c) {
+        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+';
+    }
+
+    /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE   */
+    public final static boolean
+    isNonSeparator(char c) {
+        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+'
+                || c == WILD || c == WAIT || c == PAUSE;
+    }
+
+    /** This any anything to the right of this char is part of the
+     *  post-dial string (eg this is PAUSE or WAIT)
+     */
+    public final static boolean
+    isStartsPostDial (char c) {
+        return c == PAUSE || c == WAIT;
+    }
+
+    private static boolean
+    isPause (char c){
+        return c == 'p'||c == 'P';
+    }
+
+    private static boolean
+    isToneWait (char c){
+        return c == 'w'||c == 'W';
+    }
+
+    private static int sMinMatch = 0;
+
+    private static int getMinMatch() {
+        if (sMinMatch == 0) {
+            sMinMatch = Resources.getSystem().getInteger(
+                    com.android.internal.R.integer.config_phonenumber_compare_min_match);
+        }
+        return sMinMatch;
+    }
+
+    /**
+     * A Test API to get current sMinMatch.
+     * @hide
+     */
+    @TestApi
+    public static int getMinMatchForTest() {
+        return getMinMatch();
+    }
+
+    /**
+     * A Test API to set sMinMatch.
+     * @hide
+     */
+    @TestApi
+    public static void setMinMatchForTest(int minMatch) {
+        sMinMatch = minMatch;
+    }
+
+    /** Returns true if ch is not dialable or alpha char */
+    private static boolean isSeparator(char ch) {
+        return !isDialable(ch) && !(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'));
+    }
+
+    /** Extracts the phone number from an Intent.
+     *
+     * @param intent the intent to get the number of
+     * @param context a context to use for database access
+     *
+     * @return the phone number that would be called by the intent, or
+     *         <code>null</code> if the number cannot be found.
+     */
+    public static String getNumberFromIntent(Intent intent, Context context) {
+        String number = null;
+
+        Uri uri = intent.getData();
+
+        if (uri == null) {
+            return null;
+        }
+
+        String scheme = uri.getScheme();
+        if (scheme == null) {
+            return null;
+        }
+
+        if (scheme.equals("tel") || scheme.equals("sip")) {
+            return uri.getSchemeSpecificPart();
+        }
+
+        if (context == null) {
+            return null;
+        }
+
+        String type = intent.resolveType(context);
+        String phoneColumn = null;
+
+        // Correctly read out the phone entry based on requested provider
+        final String authority = uri.getAuthority();
+        if (Contacts.AUTHORITY.equals(authority)) {
+            phoneColumn = Contacts.People.Phones.NUMBER;
+        } else if (ContactsContract.AUTHORITY.equals(authority)) {
+            phoneColumn = ContactsContract.CommonDataKinds.Phone.NUMBER;
+        }
+
+        Cursor c = null;
+        try {
+            c = context.getContentResolver().query(uri, new String[] { phoneColumn },
+                    null, null, null);
+            if (c != null) {
+                if (c.moveToFirst()) {
+                    number = c.getString(c.getColumnIndex(phoneColumn));
+                }
+            }
+        } catch (RuntimeException e) {
+            Rlog.e(LOG_TAG, "Error getting phone number.", e);
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+
+        return number;
+    }
+
+    /** Extracts the network address portion and canonicalizes
+     *  (filters out separators.)
+     *  Network address portion is everything up to DTMF control digit
+     *  separators (pause or wait), but without non-dialable characters.
+     *
+     *  Please note that the GSM wild character is allowed in the result.
+     *  This must be resolved before dialing.
+     *
+     *  Returns null if phoneNumber == null
+     */
+    public static String
+    extractNetworkPortion(String phoneNumber) {
+        if (phoneNumber == null) {
+            return null;
+        }
+
+        int len = phoneNumber.length();
+        StringBuilder ret = new StringBuilder(len);
+
+        for (int i = 0; i < len; i++) {
+            char c = phoneNumber.charAt(i);
+            // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
+            int digit = Character.digit(c, 10);
+            if (digit != -1) {
+                ret.append(digit);
+            } else if (c == '+') {
+                // Allow '+' as first character or after CLIR MMI prefix
+                String prefix = ret.toString();
+                if (prefix.length() == 0 || prefix.equals(CLIR_ON) || prefix.equals(CLIR_OFF)) {
+                    ret.append(c);
+                }
+            } else if (isDialable(c)) {
+                ret.append(c);
+            } else if (isStartsPostDial (c)) {
+                break;
+            }
+        }
+
+        return ret.toString();
+    }
+
+    /**
+     * Extracts the network address portion and canonicalize.
+     *
+     * This function is equivalent to extractNetworkPortion(), except
+     * for allowing the PLUS character to occur at arbitrary positions
+     * in the address portion, not just the first position.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static String extractNetworkPortionAlt(String phoneNumber) {
+        if (phoneNumber == null) {
+            return null;
+        }
+
+        int len = phoneNumber.length();
+        StringBuilder ret = new StringBuilder(len);
+        boolean haveSeenPlus = false;
+
+        for (int i = 0; i < len; i++) {
+            char c = phoneNumber.charAt(i);
+            if (c == '+') {
+                if (haveSeenPlus) {
+                    continue;
+                }
+                haveSeenPlus = true;
+            }
+            if (isDialable(c)) {
+                ret.append(c);
+            } else if (isStartsPostDial (c)) {
+                break;
+            }
+        }
+
+        return ret.toString();
+    }
+
+    /**
+     * Strips separators from a phone number string.
+     * @param phoneNumber phone number to strip.
+     * @return phone string stripped of separators.
+     */
+    public static String stripSeparators(String phoneNumber) {
+        if (phoneNumber == null) {
+            return null;
+        }
+        int len = phoneNumber.length();
+        StringBuilder ret = new StringBuilder(len);
+
+        for (int i = 0; i < len; i++) {
+            char c = phoneNumber.charAt(i);
+            // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
+            int digit = Character.digit(c, 10);
+            if (digit != -1) {
+                ret.append(digit);
+            } else if (isNonSeparator(c)) {
+                ret.append(c);
+            }
+        }
+
+        return ret.toString();
+    }
+
+    /**
+     * Translates keypad letters to actual digits (e.g. 1-800-GOOG-411 will
+     * become 1-800-4664-411), and then strips all separators (e.g. 1-800-4664-411 will become
+     * 18004664411).
+     *
+     * @see #convertKeypadLettersToDigits(String)
+     * @see #stripSeparators(String)
+     *
+     * @hide
+     */
+    public static String convertAndStrip(String phoneNumber) {
+        return stripSeparators(convertKeypadLettersToDigits(phoneNumber));
+    }
+
+    /**
+     * Converts pause and tonewait pause characters
+     * to Android representation.
+     * RFC 3601 says pause is 'p' and tonewait is 'w'.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static String convertPreDial(String phoneNumber) {
+        if (phoneNumber == null) {
+            return null;
+        }
+        int len = phoneNumber.length();
+        StringBuilder ret = new StringBuilder(len);
+
+        for (int i = 0; i < len; i++) {
+            char c = phoneNumber.charAt(i);
+
+            if (isPause(c)) {
+                c = PAUSE;
+            } else if (isToneWait(c)) {
+                c = WAIT;
+            }
+            ret.append(c);
+        }
+        return ret.toString();
+    }
+
+    /** or -1 if both are negative */
+    static private int
+    minPositive (int a, int b) {
+        if (a >= 0 && b >= 0) {
+            return (a < b) ? a : b;
+        } else if (a >= 0) { /* && b < 0 */
+            return a;
+        } else if (b >= 0) { /* && a < 0 */
+            return b;
+        } else { /* a < 0 && b < 0 */
+            return -1;
+        }
+    }
+
+    private static void log(String msg) {
+        Rlog.d(LOG_TAG, msg);
+    }
+    /** index of the last character of the network portion
+     *  (eg anything after is a post-dial string)
+     */
+    static private int
+    indexOfLastNetworkChar(String a) {
+        int pIndex, wIndex;
+        int origLength;
+        int trimIndex;
+
+        origLength = a.length();
+
+        pIndex = a.indexOf(PAUSE);
+        wIndex = a.indexOf(WAIT);
+
+        trimIndex = minPositive(pIndex, wIndex);
+
+        if (trimIndex < 0) {
+            return origLength - 1;
+        } else {
+            return trimIndex - 1;
+        }
+    }
+
+    /**
+     * Extracts the post-dial sequence of DTMF control digits, pauses, and
+     * waits. Strips separators. This string may be empty, but will not be null
+     * unless phoneNumber == null.
+     *
+     * Returns null if phoneNumber == null
+     */
+
+    public static String
+    extractPostDialPortion(String phoneNumber) {
+        if (phoneNumber == null) return null;
+
+        int trimIndex;
+        StringBuilder ret = new StringBuilder();
+
+        trimIndex = indexOfLastNetworkChar (phoneNumber);
+
+        for (int i = trimIndex + 1, s = phoneNumber.length()
+                ; i < s; i++
+        ) {
+            char c = phoneNumber.charAt(i);
+            if (isNonSeparator(c)) {
+                ret.append(c);
+            }
+        }
+
+        return ret.toString();
+    }
+
+    /**
+     * Compare phone numbers a and b, return true if they're identical enough for caller ID purposes.
+     * @deprecated use {@link #areSamePhoneNumber(String, String, String)} instead
+     */
+    @Deprecated
+    public static boolean compare(String a, String b) {
+        // We've used loose comparation at least Eclair, which may change in the future.
+
+        return compare(a, b, false);
+    }
+
+    /**
+     * Compare phone numbers a and b, and return true if they're identical
+     * enough for caller ID purposes. Checks a resource to determine whether
+     * to use a strict or loose comparison algorithm.
+     * @deprecated use {@link #areSamePhoneNumber(String, String, String)} instead
+     */
+    @Deprecated
+    public static boolean compare(Context context, String a, String b) {
+        boolean useStrict = context.getResources().getBoolean(
+               com.android.internal.R.bool.config_use_strict_phone_number_comparation);
+        return compare(a, b, useStrict);
+    }
+
+    /**
+     * @hide only for testing.
+     */
+    @UnsupportedAppUsage
+    public static boolean compare(String a, String b, boolean useStrictComparation) {
+        return (useStrictComparation ? compareStrictly(a, b) : compareLoosely(a, b));
+    }
+
+    /**
+     * Compare phone numbers a and b, return true if they're identical
+     * enough for caller ID purposes.
+     *
+     * - Compares from right to left
+     * - requires minimum characters to match
+     * - handles common trunk prefixes and international prefixes
+     *   (basically, everything except the Russian trunk prefix)
+     *
+     * Note that this method does not return false even when the two phone numbers
+     * are not exactly same; rather; we can call this method "similar()", not "equals()".
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static boolean
+    compareLoosely(String a, String b) {
+        int ia, ib;
+        int matched;
+        int numNonDialableCharsInA = 0;
+        int numNonDialableCharsInB = 0;
+        int minMatch = getMinMatch();
+
+        if (a == null || b == null) return a == b;
+
+        if (a.length() == 0 || b.length() == 0) {
+            return false;
+        }
+
+        ia = indexOfLastNetworkChar (a);
+        ib = indexOfLastNetworkChar (b);
+        matched = 0;
+
+        while (ia >= 0 && ib >=0) {
+            char ca, cb;
+            boolean skipCmp = false;
+
+            ca = a.charAt(ia);
+
+            if (!isDialable(ca)) {
+                ia--;
+                skipCmp = true;
+                numNonDialableCharsInA++;
+            }
+
+            cb = b.charAt(ib);
+
+            if (!isDialable(cb)) {
+                ib--;
+                skipCmp = true;
+                numNonDialableCharsInB++;
+            }
+
+            if (!skipCmp) {
+                if (cb != ca && ca != WILD && cb != WILD) {
+                    break;
+                }
+                ia--; ib--; matched++;
+            }
+        }
+
+        if (matched < minMatch) {
+            int effectiveALen = a.length() - numNonDialableCharsInA;
+            int effectiveBLen = b.length() - numNonDialableCharsInB;
+
+
+            // if the number of dialable chars in a and b match, but the matched chars < minMatch,
+            // treat them as equal (i.e. 404-04 and 40404)
+            if (effectiveALen == effectiveBLen && effectiveALen == matched) {
+                return true;
+            }
+
+            return false;
+        }
+
+        // At least one string has matched completely;
+        if (matched >= minMatch && (ia < 0 || ib < 0)) {
+            return true;
+        }
+
+        /*
+         * Now, what remains must be one of the following for a
+         * match:
+         *
+         *  - a '+' on one and a '00' or a '011' on the other
+         *  - a '0' on one and a (+,00)<country code> on the other
+         *     (for this, a '0' and a '00' prefix would have succeeded above)
+         */
+
+        if (matchIntlPrefix(a, ia + 1)
+            && matchIntlPrefix (b, ib +1)
+        ) {
+            return true;
+        }
+
+        if (matchTrunkPrefix(a, ia + 1)
+            && matchIntlPrefixAndCC(b, ib +1)
+        ) {
+            return true;
+        }
+
+        if (matchTrunkPrefix(b, ib + 1)
+            && matchIntlPrefixAndCC(a, ia +1)
+        ) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static boolean
+    compareStrictly(String a, String b) {
+        return compareStrictly(a, b, true);
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static boolean
+    compareStrictly(String a, String b, boolean acceptInvalidCCCPrefix) {
+        if (a == null || b == null) {
+            return a == b;
+        } else if (a.length() == 0 && b.length() == 0) {
+            return false;
+        }
+
+        int forwardIndexA = 0;
+        int forwardIndexB = 0;
+
+        CountryCallingCodeAndNewIndex cccA =
+            tryGetCountryCallingCodeAndNewIndex(a, acceptInvalidCCCPrefix);
+        CountryCallingCodeAndNewIndex cccB =
+            tryGetCountryCallingCodeAndNewIndex(b, acceptInvalidCCCPrefix);
+        boolean bothHasCountryCallingCode = false;
+        boolean okToIgnorePrefix = true;
+        boolean trunkPrefixIsOmittedA = false;
+        boolean trunkPrefixIsOmittedB = false;
+        if (cccA != null && cccB != null) {
+            if (cccA.countryCallingCode != cccB.countryCallingCode) {
+                // Different Country Calling Code. Must be different phone number.
+                return false;
+            }
+            // When both have ccc, do not ignore trunk prefix. Without this,
+            // "+81123123" becomes same as "+810123123" (+81 == Japan)
+            okToIgnorePrefix = false;
+            bothHasCountryCallingCode = true;
+            forwardIndexA = cccA.newIndex;
+            forwardIndexB = cccB.newIndex;
+        } else if (cccA == null && cccB == null) {
+            // When both do not have ccc, do not ignore trunk prefix. Without this,
+            // "123123" becomes same as "0123123"
+            okToIgnorePrefix = false;
+        } else {
+            if (cccA != null) {
+                forwardIndexA = cccA.newIndex;
+            } else {
+                int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
+                if (tmp >= 0) {
+                    forwardIndexA = tmp;
+                    trunkPrefixIsOmittedA = true;
+                }
+            }
+            if (cccB != null) {
+                forwardIndexB = cccB.newIndex;
+            } else {
+                int tmp = tryGetTrunkPrefixOmittedIndex(b, 0);
+                if (tmp >= 0) {
+                    forwardIndexB = tmp;
+                    trunkPrefixIsOmittedB = true;
+                }
+            }
+        }
+
+        int backwardIndexA = a.length() - 1;
+        int backwardIndexB = b.length() - 1;
+        while (backwardIndexA >= forwardIndexA && backwardIndexB >= forwardIndexB) {
+            boolean skip_compare = false;
+            final char chA = a.charAt(backwardIndexA);
+            final char chB = b.charAt(backwardIndexB);
+            if (isSeparator(chA)) {
+                backwardIndexA--;
+                skip_compare = true;
+            }
+            if (isSeparator(chB)) {
+                backwardIndexB--;
+                skip_compare = true;
+            }
+
+            if (!skip_compare) {
+                if (chA != chB) {
+                    return false;
+                }
+                backwardIndexA--;
+                backwardIndexB--;
+            }
+        }
+
+        if (okToIgnorePrefix) {
+            if ((trunkPrefixIsOmittedA && forwardIndexA <= backwardIndexA) ||
+                !checkPrefixIsIgnorable(a, forwardIndexA, backwardIndexA)) {
+                if (acceptInvalidCCCPrefix) {
+                    // Maybe the code handling the special case for Thailand makes the
+                    // result garbled, so disable the code and try again.
+                    // e.g. "16610001234" must equal to "6610001234", but with
+                    //      Thailand-case handling code, they become equal to each other.
+                    //
+                    // Note: we select simplicity rather than adding some complicated
+                    //       logic here for performance(like "checking whether remaining
+                    //       numbers are just 66 or not"), assuming inputs are small
+                    //       enough.
+                    return compare(a, b, false);
+                } else {
+                    return false;
+                }
+            }
+            if ((trunkPrefixIsOmittedB && forwardIndexB <= backwardIndexB) ||
+                !checkPrefixIsIgnorable(b, forwardIndexA, backwardIndexB)) {
+                if (acceptInvalidCCCPrefix) {
+                    return compare(a, b, false);
+                } else {
+                    return false;
+                }
+            }
+        } else {
+            // In the US, 1-650-555-1234 must be equal to 650-555-1234,
+            // while 090-1234-1234 must not be equal to 90-1234-1234 in Japan.
+            // This request exists just in US (with 1 trunk (NDD) prefix).
+            // In addition, "011 11 7005554141" must not equal to "+17005554141",
+            // while "011 1 7005554141" must equal to "+17005554141"
+            //
+            // In this comparison, we ignore the prefix '1' just once, when
+            // - at least either does not have CCC, or
+            // - the remaining non-separator number is 1
+            boolean maybeNamp = !bothHasCountryCallingCode;
+            while (backwardIndexA >= forwardIndexA) {
+                final char chA = a.charAt(backwardIndexA);
+                if (isDialable(chA)) {
+                    if (maybeNamp && tryGetISODigit(chA) == 1) {
+                        maybeNamp = false;
+                    } else {
+                        return false;
+                    }
+                }
+                backwardIndexA--;
+            }
+            while (backwardIndexB >= forwardIndexB) {
+                final char chB = b.charAt(backwardIndexB);
+                if (isDialable(chB)) {
+                    if (maybeNamp && tryGetISODigit(chB) == 1) {
+                        maybeNamp = false;
+                    } else {
+                        return false;
+                    }
+                }
+                backwardIndexB--;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns the rightmost minimum matched characters in the network portion
+     * in *reversed* order
+     *
+     * This can be used to do a database lookup against the column
+     * that stores getStrippedReversed()
+     *
+     * Returns null if phoneNumber == null
+     */
+    public static String
+    toCallerIDMinMatch(String phoneNumber) {
+        String np = extractNetworkPortionAlt(phoneNumber);
+        return internalGetStrippedReversed(np, getMinMatch());
+    }
+
+    /**
+     * Returns the network portion reversed.
+     * This string is intended to go into an index column for a
+     * database lookup.
+     *
+     * Returns null if phoneNumber == null
+     */
+    public static String
+    getStrippedReversed(String phoneNumber) {
+        String np = extractNetworkPortionAlt(phoneNumber);
+
+        if (np == null) return null;
+
+        return internalGetStrippedReversed(np, np.length());
+    }
+
+    /**
+     * Returns the last numDigits of the reversed phone number
+     * Returns null if np == null
+     */
+    private static String
+    internalGetStrippedReversed(String np, int numDigits) {
+        if (np == null) return null;
+
+        StringBuilder ret = new StringBuilder(numDigits);
+        int length = np.length();
+
+        for (int i = length - 1, s = length
+            ; i >= 0 && (s - i) <= numDigits ; i--
+        ) {
+            char c = np.charAt(i);
+
+            ret.append(c);
+        }
+
+        return ret.toString();
+    }
+
+    /**
+     * Basically: makes sure there's a + in front of a
+     * TOA_International number
+     *
+     * Returns null if s == null
+     */
+    public static String
+    stringFromStringAndTOA(String s, int TOA) {
+        if (s == null) return null;
+
+        if (TOA == TOA_International && s.length() > 0 && s.charAt(0) != '+') {
+            return "+" + s;
+        }
+
+        return s;
+    }
+
+    /**
+     * Returns the TOA for the given dial string
+     * Basically, returns TOA_International if there's a + prefix
+     */
+
+    public static int
+    toaFromString(String s) {
+        if (s != null && s.length() > 0 && s.charAt(0) == '+') {
+            return TOA_International;
+        }
+
+        return TOA_Unknown;
+    }
+
+    /**
+     *  3GPP TS 24.008 10.5.4.7
+     *  Called Party BCD Number
+     *
+     *  See Also TS 51.011 10.5.1 "dialing number/ssc string"
+     *  and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
+     *
+     * @param bytes the data buffer
+     * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
+     * @param length is the number of bytes including TOA byte
+     *                and must be at least 2
+     *
+     * @return partial string on invalid decode
+     *
+     * @deprecated use {@link #calledPartyBCDToString(byte[], int, int, int)} instead. Calling this
+     * method is equivalent to calling {@link #calledPartyBCDToString(byte[], int, int)} with
+     * {@link #BCD_EXTENDED_TYPE_EF_ADN} as the extended type.
+     */
+    @Deprecated
+    public static String calledPartyBCDToString(byte[] bytes, int offset, int length) {
+        return calledPartyBCDToString(bytes, offset, length, BCD_EXTENDED_TYPE_EF_ADN);
+    }
+
+    /**
+     *  3GPP TS 24.008 10.5.4.7
+     *  Called Party BCD Number
+     *
+     *  See Also TS 51.011 10.5.1 "dialing number/ssc string"
+     *  and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
+     *
+     * @param bytes the data buffer
+     * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
+     * @param length is the number of bytes including TOA byte
+     *                and must be at least 2
+     * @param bcdExtType used to determine the extended bcd coding
+     * @see #BCD_EXTENDED_TYPE_EF_ADN
+     * @see #BCD_EXTENDED_TYPE_CALLED_PARTY
+     *
+     */
+    public static String calledPartyBCDToString(
+            byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
+        boolean prependPlus = false;
+        StringBuilder ret = new StringBuilder(1 + length * 2);
+
+        if (length < 2) {
+            return "";
+        }
+
+        //Only TON field should be taken in consideration
+        if ((bytes[offset] & 0xf0) == (TOA_International & 0xf0)) {
+            prependPlus = true;
+        }
+
+        internalCalledPartyBCDFragmentToString(
+                ret, bytes, offset + 1, length - 1, bcdExtType);
+
+        if (prependPlus && ret.length() == 0) {
+            // If the only thing there is a prepended plus, return ""
+            return "";
+        }
+
+        if (prependPlus) {
+            // This is an "international number" and should have
+            // a plus prepended to the dialing number. But there
+            // can also be GSM MMI codes as defined in TS 22.030 6.5.2
+            // so we need to handle those also.
+            //
+            // http://web.telia.com/~u47904776/gsmkode.htm
+            // has a nice list of some of these GSM codes.
+            //
+            // Examples are:
+            //   **21*+886988171479#
+            //   **21*8311234567#
+            //   *21#
+            //   #21#
+            //   *#21#
+            //   *31#+11234567890
+            //   #31#+18311234567
+            //   #31#8311234567
+            //   18311234567
+            //   +18311234567#
+            //   +18311234567
+            // Odd ball cases that some phones handled
+            // where there is no dialing number so they
+            // append the "+"
+            //   *21#+
+            //   **21#+
+            String retString = ret.toString();
+            Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$");
+            Matcher m = p.matcher(retString);
+            if (m.matches()) {
+                if ("".equals(m.group(2))) {
+                    // Started with two [#*] ends with #
+                    // So no dialing number and we'll just
+                    // append a +, this handles **21#+
+                    ret = new StringBuilder();
+                    ret.append(m.group(1));
+                    ret.append(m.group(3));
+                    ret.append(m.group(4));
+                    ret.append(m.group(5));
+                    ret.append("+");
+                } else {
+                    // Starts with [#*] and ends with #
+                    // Assume group 4 is a dialing number
+                    // such as *21*+1234554#
+                    ret = new StringBuilder();
+                    ret.append(m.group(1));
+                    ret.append(m.group(2));
+                    ret.append(m.group(3));
+                    ret.append("+");
+                    ret.append(m.group(4));
+                    ret.append(m.group(5));
+                }
+            } else {
+                p = Pattern.compile("(^[#*])(.*)([#*])(.*)");
+                m = p.matcher(retString);
+                if (m.matches()) {
+                    // Starts with [#*] and only one other [#*]
+                    // Assume the data after last [#*] is dialing
+                    // number (i.e. group 4) such as *31#+11234567890.
+                    // This also includes the odd ball *21#+
+                    ret = new StringBuilder();
+                    ret.append(m.group(1));
+                    ret.append(m.group(2));
+                    ret.append(m.group(3));
+                    ret.append("+");
+                    ret.append(m.group(4));
+                } else {
+                    // Does NOT start with [#*] just prepend '+'
+                    ret = new StringBuilder();
+                    ret.append('+');
+                    ret.append(retString);
+                }
+            }
+        }
+
+        return ret.toString();
+    }
+
+    private static void internalCalledPartyBCDFragmentToString(
+            StringBuilder sb, byte [] bytes, int offset, int length,
+            @BcdExtendType int bcdExtType) {
+        for (int i = offset ; i < length + offset ; i++) {
+            byte b;
+            char c;
+
+            c = bcdToChar((byte)(bytes[i] & 0xf), bcdExtType);
+
+            if (c == 0) {
+                return;
+            }
+            sb.append(c);
+
+            // FIXME(mkf) TS 23.040 9.1.2.3 says
+            // "if a mobile receives 1111 in a position prior to
+            // the last semi-octet then processing shall commence with
+            // the next semi-octet and the intervening
+            // semi-octet shall be ignored"
+            // How does this jive with 24.008 10.5.4.7
+
+            b = (byte)((bytes[i] >> 4) & 0xf);
+
+            if (b == 0xf && i + 1 == length + offset) {
+                //ignore final 0xf
+                break;
+            }
+
+            c = bcdToChar(b, bcdExtType);
+            if (c == 0) {
+                return;
+            }
+
+            sb.append(c);
+        }
+
+    }
+
+    /**
+     * Like calledPartyBCDToString, but field does not start with a
+     * TOA byte. For example: SIM ADN extension fields
+     *
+     * @deprecated use {@link #calledPartyBCDFragmentToString(byte[], int, int, int)} instead.
+     * Calling this method is equivalent to calling
+     * {@link #calledPartyBCDFragmentToString(byte[], int, int, int)} with
+     * {@link #BCD_EXTENDED_TYPE_EF_ADN} as the extended type.
+     */
+    @Deprecated
+    public static String calledPartyBCDFragmentToString(byte[] bytes, int offset, int length) {
+        return calledPartyBCDFragmentToString(bytes, offset, length, BCD_EXTENDED_TYPE_EF_ADN);
+    }
+
+    /**
+     * Like calledPartyBCDToString, but field does not start with a
+     * TOA byte. For example: SIM ADN extension fields
+     */
+    public static String calledPartyBCDFragmentToString(
+            byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
+        StringBuilder ret = new StringBuilder(length * 2);
+        internalCalledPartyBCDFragmentToString(ret, bytes, offset, length, bcdExtType);
+        return ret.toString();
+    }
+
+    /**
+     * Returns the correspond character for given {@code b} based on {@code bcdExtType}, or 0 on
+     * invalid code.
+     */
+    private static char bcdToChar(byte b, @BcdExtendType int bcdExtType) {
+        if (b < 0xa) {
+            return (char) ('0' + b);
+        }
+
+        String extended = null;
+        if (BCD_EXTENDED_TYPE_EF_ADN == bcdExtType) {
+            extended = BCD_EF_ADN_EXTENDED;
+        } else if (BCD_EXTENDED_TYPE_CALLED_PARTY == bcdExtType) {
+            extended = BCD_CALLED_PARTY_EXTENDED;
+        }
+        if (extended == null || b - 0xa >= extended.length()) {
+            return 0;
+        }
+
+        return extended.charAt(b - 0xa);
+    }
+
+    private static int charToBCD(char c, @BcdExtendType int bcdExtType) {
+        if ('0' <= c && c <= '9') {
+            return c - '0';
+        }
+
+        String extended = null;
+        if (BCD_EXTENDED_TYPE_EF_ADN == bcdExtType) {
+            extended = BCD_EF_ADN_EXTENDED;
+        } else if (BCD_EXTENDED_TYPE_CALLED_PARTY == bcdExtType) {
+            extended = BCD_CALLED_PARTY_EXTENDED;
+        }
+        if (extended == null || extended.indexOf(c) == -1) {
+            throw new RuntimeException("invalid char for BCD " + c);
+        }
+        return 0xa + extended.indexOf(c);
+    }
+
+    /**
+     * Return true iff the network portion of <code>address</code> is,
+     * as far as we can tell on the device, suitable for use as an SMS
+     * destination address.
+     */
+    public static boolean isWellFormedSmsAddress(String address) {
+        String networkPortion =
+                PhoneNumberUtils.extractNetworkPortion(address);
+
+        return (!(networkPortion.equals("+")
+                  || TextUtils.isEmpty(networkPortion)))
+               && isDialable(networkPortion);
+    }
+
+    public static boolean isGlobalPhoneNumber(String phoneNumber) {
+        if (TextUtils.isEmpty(phoneNumber)) {
+            return false;
+        }
+
+        Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
+        return match.matches();
+    }
+
+    private static boolean isDialable(String address) {
+        for (int i = 0, count = address.length(); i < count; i++) {
+            if (!isDialable(address.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean isNonSeparator(String address) {
+        for (int i = 0, count = address.length(); i < count; i++) {
+            if (!isNonSeparator(address.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+    /**
+     * Note: calls extractNetworkPortion(), so do not use for
+     * SIM EF[ADN] style records
+     *
+     * Returns null if network portion is empty.
+     */
+    public static byte[] networkPortionToCalledPartyBCD(String s) {
+        String networkPortion = extractNetworkPortion(s);
+        return numberToCalledPartyBCDHelper(
+                networkPortion, false, BCD_EXTENDED_TYPE_EF_ADN);
+    }
+
+    /**
+     * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
+     * one-byte length prefix.
+     */
+    public static byte[] networkPortionToCalledPartyBCDWithLength(String s) {
+        String networkPortion = extractNetworkPortion(s);
+        return numberToCalledPartyBCDHelper(
+                networkPortion, true, BCD_EXTENDED_TYPE_EF_ADN);
+    }
+
+    /**
+     * Convert a dialing number to BCD byte array
+     *
+     * @param number dialing number string. If the dialing number starts with '+', set to
+     * international TOA
+     *
+     * @return BCD byte array
+     *
+     * @deprecated use {@link #numberToCalledPartyBCD(String, int)} instead. Calling this method
+     * is equivalent to calling {@link #numberToCalledPartyBCD(String, int)} with
+     * {@link #BCD_EXTENDED_TYPE_EF_ADN} as the extended type.
+     */
+    @Deprecated
+    public static byte[] numberToCalledPartyBCD(String number) {
+        return numberToCalledPartyBCD(number, BCD_EXTENDED_TYPE_EF_ADN);
+    }
+
+    /**
+     * Convert a dialing number to BCD byte array
+     *
+     * @param number dialing number string. If the dialing number starts with '+', set to
+     * international TOA
+     * @param bcdExtType used to determine the extended bcd coding
+     * @see #BCD_EXTENDED_TYPE_EF_ADN
+     * @see #BCD_EXTENDED_TYPE_CALLED_PARTY
+     *
+     * @return BCD byte array
+     */
+    public static byte[] numberToCalledPartyBCD(String number, @BcdExtendType int bcdExtType) {
+        return numberToCalledPartyBCDHelper(number, false, bcdExtType);
+    }
+
+    /**
+     * If includeLength is true, prepend a one-byte length value to
+     * the return array.
+     */
+    private static byte[] numberToCalledPartyBCDHelper(
+            String number, boolean includeLength, @BcdExtendType int bcdExtType) {
+        int numberLenReal = number.length();
+        int numberLenEffective = numberLenReal;
+        boolean hasPlus = number.indexOf('+') != -1;
+        if (hasPlus) numberLenEffective--;
+
+        if (numberLenEffective == 0) return null;
+
+        int resultLen = (numberLenEffective + 1) / 2;  // Encoded numbers require only 4 bits each.
+        int extraBytes = 1;                            // Prepended TOA byte.
+        if (includeLength) extraBytes++;               // Optional prepended length byte.
+        resultLen += extraBytes;
+
+        byte[] result = new byte[resultLen];
+
+        int digitCount = 0;
+        for (int i = 0; i < numberLenReal; i++) {
+            char c = number.charAt(i);
+            if (c == '+') continue;
+            int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
+            result[extraBytes + (digitCount >> 1)] |=
+                    (byte)((charToBCD(c, bcdExtType) & 0x0F) << shift);
+            digitCount++;
+        }
+
+        // 1-fill any trailing odd nibble/quartet.
+        if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
+
+        int offset = 0;
+        if (includeLength) result[offset++] = (byte)(resultLen - 1);
+        result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
+
+        return result;
+    }
+
+    //================ Number formatting =========================
+
+    /** The current locale is unknown, look for a country code or don't format */
+    public static final int FORMAT_UNKNOWN = 0;
+    /** NANP formatting */
+    public static final int FORMAT_NANP = 1;
+    /** Japanese formatting */
+    public static final int FORMAT_JAPAN = 2;
+
+    /** List of country codes for countries that use the NANP */
+    private static final String[] NANP_COUNTRIES = new String[] {
+        "US", // United States
+        "CA", // Canada
+        "AS", // American Samoa
+        "AI", // Anguilla
+        "AG", // Antigua and Barbuda
+        "BS", // Bahamas
+        "BB", // Barbados
+        "BM", // Bermuda
+        "VG", // British Virgin Islands
+        "KY", // Cayman Islands
+        "DM", // Dominica
+        "DO", // Dominican Republic
+        "GD", // Grenada
+        "GU", // Guam
+        "JM", // Jamaica
+        "PR", // Puerto Rico
+        "MS", // Montserrat
+        "MP", // Northern Mariana Islands
+        "KN", // Saint Kitts and Nevis
+        "LC", // Saint Lucia
+        "VC", // Saint Vincent and the Grenadines
+        "TT", // Trinidad and Tobago
+        "TC", // Turks and Caicos Islands
+        "VI", // U.S. Virgin Islands
+    };
+
+    private static final String KOREA_ISO_COUNTRY_CODE = "KR";
+
+    private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
+
+    private static final String SINGAPORE_ISO_COUNTRY_CODE = "SG";
+
+    /**
+     * Breaks the given number down and formats it according to the rules
+     * for the country the number is from.
+     *
+     * @param source The phone number to format
+     * @return A locally acceptable formatting of the input, or the raw input if
+     *  formatting rules aren't known for the number
+     *
+     * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
+     */
+    @Deprecated
+    public static String formatNumber(String source) {
+        SpannableStringBuilder text = new SpannableStringBuilder(source);
+        formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
+        return text.toString();
+    }
+
+    /**
+     * Formats the given number with the given formatting type. Currently
+     * {@link #FORMAT_NANP} and {@link #FORMAT_JAPAN} are supported as a formating type.
+     *
+     * @param source the phone number to format
+     * @param defaultFormattingType The default formatting rules to apply if the number does
+     * not begin with +[country_code]
+     * @return The phone number formatted with the given formatting type.
+     *
+     * @hide
+     * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public static String formatNumber(String source, int defaultFormattingType) {
+        SpannableStringBuilder text = new SpannableStringBuilder(source);
+        formatNumber(text, defaultFormattingType);
+        return text.toString();
+    }
+
+    /**
+     * Returns the phone number formatting type for the given locale.
+     *
+     * @param locale The locale of interest, usually {@link Locale#getDefault()}
+     * @return The formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
+     * rules are not known for the given locale
+     *
+     * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
+     */
+    @Deprecated
+    public static int getFormatTypeForLocale(Locale locale) {
+        String country = locale.getCountry();
+
+        return getFormatTypeFromCountryCode(country);
+    }
+
+    /**
+     * Formats a phone number in-place. Currently {@link #FORMAT_JAPAN} and {@link #FORMAT_NANP}
+     * is supported as a second argument.
+     *
+     * @param text The number to be formatted, will be modified with the formatting
+     * @param defaultFormattingType The default formatting rules to apply if the number does
+     * not begin with +[country_code]
+     *
+     * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
+     */
+    @Deprecated
+    public static void formatNumber(Editable text, int defaultFormattingType) {
+        int formatType = defaultFormattingType;
+
+        if (text.length() > 2 && text.charAt(0) == '+') {
+            if (text.charAt(1) == '1') {
+                formatType = FORMAT_NANP;
+            } else if (text.length() >= 3 && text.charAt(1) == '8'
+                && text.charAt(2) == '1') {
+                formatType = FORMAT_JAPAN;
+            } else {
+                formatType = FORMAT_UNKNOWN;
+            }
+        }
+
+        switch (formatType) {
+            case FORMAT_NANP:
+                formatNanpNumber(text);
+                return;
+            case FORMAT_JAPAN:
+                formatJapaneseNumber(text);
+                return;
+            case FORMAT_UNKNOWN:
+                removeDashes(text);
+                return;
+        }
+    }
+
+    private static final int NANP_STATE_DIGIT = 1;
+    private static final int NANP_STATE_PLUS = 2;
+    private static final int NANP_STATE_ONE = 3;
+    private static final int NANP_STATE_DASH = 4;
+
+    /**
+     * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
+     * as:
+     *
+     * <p><code>
+     * xxxxx
+     * xxx-xxxx
+     * xxx-xxx-xxxx
+     * 1-xxx-xxx-xxxx
+     * +1-xxx-xxx-xxxx
+     * </code></p>
+     *
+     * @param text the number to be formatted, will be modified with the formatting
+     *
+     * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
+     */
+    @Deprecated
+    public static void formatNanpNumber(Editable text) {
+        int length = text.length();
+        if (length > "+1-nnn-nnn-nnnn".length()) {
+            // The string is too long to be formatted
+            return;
+        } else if (length <= 5) {
+            // The string is either a shortcode or too short to be formatted
+            return;
+        }
+
+        CharSequence saved = text.subSequence(0, length);
+
+        // Strip the dashes first, as we're going to add them back
+        removeDashes(text);
+        length = text.length();
+
+        // When scanning the number we record where dashes need to be added,
+        // if they're non-0 at the end of the scan the dashes will be added in
+        // the proper places.
+        int dashPositions[] = new int[3];
+        int numDashes = 0;
+
+        int state = NANP_STATE_DIGIT;
+        int numDigits = 0;
+        for (int i = 0; i < length; i++) {
+            char c = text.charAt(i);
+            switch (c) {
+                case '1':
+                    if (numDigits == 0 || state == NANP_STATE_PLUS) {
+                        state = NANP_STATE_ONE;
+                        break;
+                    }
+                    // fall through
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                case '0':
+                    if (state == NANP_STATE_PLUS) {
+                        // Only NANP number supported for now
+                        text.replace(0, length, saved);
+                        return;
+                    } else if (state == NANP_STATE_ONE) {
+                        // Found either +1 or 1, follow it up with a dash
+                        dashPositions[numDashes++] = i;
+                    } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
+                        // Found a digit that should be after a dash that isn't
+                        dashPositions[numDashes++] = i;
+                    }
+                    state = NANP_STATE_DIGIT;
+                    numDigits++;
+                    break;
+
+                case '-':
+                    state = NANP_STATE_DASH;
+                    break;
+
+                case '+':
+                    if (i == 0) {
+                        // Plus is only allowed as the first character
+                        state = NANP_STATE_PLUS;
+                        break;
+                    }
+                    // Fall through
+                default:
+                    // Unknown character, bail on formatting
+                    text.replace(0, length, saved);
+                    return;
+            }
+        }
+
+        if (numDigits == 7) {
+            // With 7 digits we want xxx-xxxx, not xxx-xxx-x
+            numDashes--;
+        }
+
+        // Actually put the dashes in place
+        for (int i = 0; i < numDashes; i++) {
+            int pos = dashPositions[i];
+            text.replace(pos + i, pos + i, "-");
+        }
+
+        // Remove trailing dashes
+        int len = text.length();
+        while (len > 0) {
+            if (text.charAt(len - 1) == '-') {
+                text.delete(len - 1, len);
+                len--;
+            } else {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Formats a phone number in-place using the Japanese formatting rules.
+     * Numbers will be formatted as:
+     *
+     * <p><code>
+     * 03-xxxx-xxxx
+     * 090-xxxx-xxxx
+     * 0120-xxx-xxx
+     * +81-3-xxxx-xxxx
+     * +81-90-xxxx-xxxx
+     * </code></p>
+     *
+     * @param text the number to be formatted, will be modified with
+     * the formatting
+     *
+     * @deprecated Use link #formatNumber(String phoneNumber, String defaultCountryIso) instead
+     */
+    @Deprecated
+    public static void formatJapaneseNumber(Editable text) {
+        JapanesePhoneNumberFormatter.format(text);
+    }
+
+    /**
+     * Removes all dashes from the number.
+     *
+     * @param text the number to clear from dashes
+     */
+    private static void removeDashes(Editable text) {
+        int p = 0;
+        while (p < text.length()) {
+            if (text.charAt(p) == '-') {
+                text.delete(p, p + 1);
+           } else {
+                p++;
+           }
+        }
+    }
+
+    /**
+     * Formats the specified {@code phoneNumber} to the E.164 representation.
+     *
+     * @param phoneNumber the phone number to format.
+     * @param defaultCountryIso the ISO 3166-1 two letters country code in UPPER CASE.
+     * @return the E.164 representation, or null if the given phone number is not valid.
+     */
+    public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) {
+        if (defaultCountryIso != null) {
+            defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+        }
+
+        return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.E164);
+    }
+
+    /**
+     * Formats the specified {@code phoneNumber} to the RFC3966 representation.
+     *
+     * @param phoneNumber the phone number to format.
+     * @param defaultCountryIso the ISO 3166-1 two letters country code in UPPER CASE.
+     * @return the RFC3966 representation, or null if the given phone number is not valid.
+     */
+    public static String formatNumberToRFC3966(String phoneNumber, String defaultCountryIso) {
+        if (defaultCountryIso != null) {
+            defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+        }
+
+        return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.RFC3966);
+    }
+
+    /**
+     * Formats the raw phone number (string) using the specified {@code formatIdentifier}.
+     * <p>
+     * The given phone number must have an area code and could have a country code.
+     * <p>
+     * The defaultCountryIso is used to validate the given number and generate the formatted number
+     * if the specified number doesn't have a country code.
+     *
+     * @param rawPhoneNumber The phone number to format.
+     * @param defaultCountryIso The ISO 3166-1 two letters country code.
+     * @param formatIdentifier The (enum) identifier of the desired format.
+     * @return the formatted representation, or null if the specified number is not valid.
+     */
+    private static String formatNumberInternal(
+            String rawPhoneNumber, String defaultCountryIso, PhoneNumberFormat formatIdentifier) {
+
+        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+        try {
+            PhoneNumber phoneNumber = util.parse(rawPhoneNumber, defaultCountryIso);
+            if (util.isValidNumber(phoneNumber)) {
+                return util.format(phoneNumber, formatIdentifier);
+            }
+        } catch (NumberParseException ignored) { }
+
+        return null;
+    }
+
+    /**
+     * Determines if a {@param phoneNumber} is international if dialed from
+     * {@param defaultCountryIso}.
+     *
+     * @param phoneNumber The phone number.
+     * @param defaultCountryIso The current country ISO.
+     * @return {@code true} if the number is international, {@code false} otherwise.
+     * @hide
+     */
+    public static boolean isInternationalNumber(String phoneNumber, String defaultCountryIso) {
+        // If no phone number is provided, it can't be international.
+        if (TextUtils.isEmpty(phoneNumber)) {
+            return false;
+        }
+
+        // If it starts with # or * its not international.
+        if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
+            return false;
+        }
+
+        if (defaultCountryIso != null) {
+            defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+        }
+
+        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+        try {
+            PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
+            return pn.getCountryCode() != util.getCountryCodeForRegion(defaultCountryIso);
+        } catch (NumberParseException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Format a phone number.
+     * <p>
+     * If the given number doesn't have the country code, the phone will be
+     * formatted to the default country's convention.
+     *
+     * @param phoneNumber
+     *            the number to be formatted.
+     * @param defaultCountryIso
+     *            the ISO 3166-1 two letters country code whose convention will
+     *            be used if the given number doesn't have the country code.
+     * @return the formatted number, or null if the given number is not valid.
+     */
+    public static String formatNumber(String phoneNumber, String defaultCountryIso) {
+        // Do not attempt to format numbers that start with a hash or star symbol.
+        if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
+            return phoneNumber;
+        }
+
+        if (defaultCountryIso != null) {
+            defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+        }
+
+        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+        String result = null;
+        try {
+            PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
+
+            if (KOREA_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
+                    (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE)) &&
+                    (pn.getCountryCodeSource() ==
+                            PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
+                /**
+                 * Need to reformat any local Korean phone numbers (when the user is in Korea) with
+                 * country code to corresponding national format which would replace the leading
+                 * +82 with 0.
+                 */
+                result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+            } else if (JAPAN_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
+                    pn.getCountryCode() == util.getCountryCodeForRegion(JAPAN_ISO_COUNTRY_CODE) &&
+                    (pn.getCountryCodeSource() ==
+                            PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
+                /**
+                 * Need to reformat Japanese phone numbers (when user is in Japan) with the national
+                 * dialing format.
+                 */
+                result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+            } else if (Flags.removeCountryCodeFromLocalSingaporeCalls() &&
+                    (SINGAPORE_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
+                            pn.getCountryCode() ==
+                                    util.getCountryCodeForRegion(SINGAPORE_ISO_COUNTRY_CODE) &&
+                            (pn.getCountryCodeSource() ==
+                                    PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN))) {
+                /*
+                 * Need to reformat Singaporean phone numbers (when the user is in Singapore)
+                 * with the country code (+65) removed to comply with Singaporean regulations.
+                 */
+                result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+            } else {
+                result = util.formatInOriginalFormat(pn, defaultCountryIso);
+            }
+        } catch (NumberParseException e) {
+        }
+        return result;
+    }
+
+    /**
+     * Format the phone number only if the given number hasn't been formatted.
+     * <p>
+     * The number which has only dailable character is treated as not being
+     * formatted.
+     *
+     * @param phoneNumber
+     *            the number to be formatted.
+     * @param phoneNumberE164
+     *            the E164 format number whose country code is used if the given
+     *            phoneNumber doesn't have the country code.
+     * @param defaultCountryIso
+     *            the ISO 3166-1 two letters country code whose convention will
+     *            be used if the phoneNumberE164 is null or invalid, or if phoneNumber
+     *            contains IDD.
+     * @return the formatted number if the given number has been formatted,
+     *            otherwise, return the given number.
+     */
+    public static String formatNumber(
+            String phoneNumber, String phoneNumberE164, String defaultCountryIso) {
+        if (defaultCountryIso != null) {
+            defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+        }
+
+        int len = phoneNumber.length();
+        for (int i = 0; i < len; i++) {
+            if (!isDialable(phoneNumber.charAt(i))) {
+                return phoneNumber;
+            }
+        }
+        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+        // Get the country code from phoneNumberE164
+        if (phoneNumberE164 != null && phoneNumberE164.length() >= 2
+                && phoneNumberE164.charAt(0) == '+') {
+            try {
+                // The number to be parsed is in E164 format, so the default region used doesn't
+                // matter.
+                PhoneNumber pn = util.parse(phoneNumberE164, "ZZ");
+                String regionCode = util.getRegionCodeForNumber(pn);
+                if (!TextUtils.isEmpty(regionCode) &&
+                    // This makes sure phoneNumber doesn't contain an IDD
+                    normalizeNumber(phoneNumber).indexOf(phoneNumberE164.substring(1)) <= 0) {
+                    defaultCountryIso = regionCode;
+                }
+            } catch (NumberParseException e) {
+            }
+        }
+        String result = formatNumber(phoneNumber, defaultCountryIso);
+        return result != null ? result : phoneNumber;
+    }
+
+    /**
+     * Normalize a phone number by removing the characters other than digits. If
+     * the given number has keypad letters, the letters will be converted to
+     * digits first.
+     *
+     * @param phoneNumber the number to be normalized.
+     * @return the normalized number.
+     */
+    public static String normalizeNumber(String phoneNumber) {
+        if (TextUtils.isEmpty(phoneNumber)) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        int len = phoneNumber.length();
+        for (int i = 0; i < len; i++) {
+            char c = phoneNumber.charAt(i);
+            // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.)
+            int digit = Character.digit(c, 10);
+            if (digit != -1) {
+                sb.append(digit);
+            } else if (sb.length() == 0 && c == '+') {
+                sb.append(c);
+            } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+                return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Replaces all unicode(e.g. Arabic, Persian) digits with their decimal digit equivalents.
+     *
+     * @param number the number to perform the replacement on.
+     * @return the replaced number.
+     */
+    public static String replaceUnicodeDigits(String number) {
+        StringBuilder normalizedDigits = new StringBuilder(number.length());
+        for (char c : number.toCharArray()) {
+            int digit = Character.digit(c, 10);
+            if (digit != -1) {
+                normalizedDigits.append(digit);
+            } else {
+                normalizedDigits.append(c);
+            }
+        }
+        return normalizedDigits.toString();
+    }
+
+    /**
+     * Checks a given number against the list of
+     * emergency numbers provided by the RIL and SIM card.
+     *
+     * @param number the number to look up.
+     * @return true if the number is in the list of emergency numbers
+     *         listed in the RIL / SIM, otherwise return false.
+     *
+     * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)} instead.
+     */
+    @Deprecated
+    public static boolean isEmergencyNumber(String number) {
+        return isEmergencyNumber(getDefaultVoiceSubId(), number);
+    }
+
+    /**
+     * Checks a given number against the list of
+     * emergency numbers provided by the RIL and SIM card.
+     *
+     * @param subId the subscription id of the SIM.
+     * @param number the number to look up.
+     * @return true if the number is in the list of emergency numbers
+     *         listed in the RIL / SIM, otherwise return false.
+     *
+     * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)}
+     *             instead.
+     *
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public static boolean isEmergencyNumber(int subId, String number) {
+        // Return true only if the specified number *exactly* matches
+        // one of the emergency numbers listed by the RIL / SIM.
+        return isEmergencyNumberInternal(subId, number);
+    }
+
+    /**
+     * Helper function for isEmergencyNumber(String, String) and.
+     *
+     * @param subId the subscription id of the SIM.
+     * @param number the number to look up.
+     * @return true if the number is an emergency number for the specified country.
+     * @hide
+     */
+    private static boolean isEmergencyNumberInternal(int subId, String number) {
+        //TODO: remove subid later. Keep it for now in case we need it later.
+        try {
+                return TelephonyManager.getDefault().isEmergencyNumber(number);
+        } catch (RuntimeException ex) {
+            Rlog.e(LOG_TAG, "isEmergencyNumberInternal: RuntimeException: " + ex);
+        }
+        return false;
+    }
+
+    /**
+     * Checks if a given number is an emergency number for the country that the user is in.
+     *
+     * @param number the number to look up.
+     * @param context the specific context which the number should be checked against
+     * @return true if the specified number is an emergency number for the country the user
+     * is currently in.
+     *
+     * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)}
+     *             instead.
+     */
+    @Deprecated
+    public static boolean isLocalEmergencyNumber(Context context, String number) {
+        return isEmergencyNumberInternal(getDefaultVoiceSubId(), number);
+    }
+
+    /**
+     * isVoiceMailNumber: checks a given number against the voicemail
+     *   number provided by the RIL and SIM card. The caller must have
+     *   the READ_PHONE_STATE credential.
+     *
+     * @param number the number to look up.
+     * @return true if the number is in the list of voicemail. False
+     * otherwise, including if the caller does not have the permission
+     * to read the VM number.
+     */
+    public static boolean isVoiceMailNumber(String number) {
+        return isVoiceMailNumber(SubscriptionManager.getDefaultSubscriptionId(), number);
+    }
+
+    /**
+     * isVoiceMailNumber: checks a given number against the voicemail
+     *   number provided by the RIL and SIM card. The caller must have
+     *   the READ_PHONE_STATE credential.
+     *
+     * @param subId the subscription id of the SIM.
+     * @param number the number to look up.
+     * @return true if the number is in the list of voicemail. False
+     * otherwise, including if the caller does not have the permission
+     * to read the VM number.
+     * @hide
+     */
+    public static boolean isVoiceMailNumber(int subId, String number) {
+        return isVoiceMailNumber(null, subId, number);
+    }
+
+    /**
+     * isVoiceMailNumber: checks a given number against the voicemail
+     *   number provided by the RIL and SIM card. The caller must have
+     *   the READ_PHONE_STATE credential.
+     *
+     * @param context {@link Context}.
+     * @param subId the subscription id of the SIM.
+     * @param number the number to look up.
+     * @return true if the number is in the list of voicemail. False
+     * otherwise, including if the caller does not have the permission
+     * to read the VM number.
+     * @hide
+     */
+    @SystemApi
+    public static boolean isVoiceMailNumber(@NonNull Context context, int subId,
+            @Nullable String number) {
+        String vmNumber, mdn;
+        try {
+            final TelephonyManager tm;
+            if (context == null) {
+                tm = TelephonyManager.getDefault();
+                if (DBG) log("isVoiceMailNumber: default tm");
+            } else {
+                tm = TelephonyManager.from(context);
+                if (DBG) log("isVoiceMailNumber: tm from context");
+            }
+            vmNumber = tm.getVoiceMailNumber(subId);
+            mdn = tm.getLine1Number(subId);
+            if (DBG) log("isVoiceMailNumber: mdn=" + mdn + ", vmNumber=" + vmNumber
+                    + ", number=" + number);
+        } catch (SecurityException ex) {
+            if (DBG) log("isVoiceMailNumber: SecurityExcpetion caught");
+            return false;
+        }
+        // Strip the separators from the number before comparing it
+        // to the list.
+        number = extractNetworkPortionAlt(number);
+        if (TextUtils.isEmpty(number)) {
+            if (DBG) log("isVoiceMailNumber: number is empty after stripping");
+            return false;
+        }
+
+        // check if the carrier considers MDN to be an additional voicemail number
+        boolean compareWithMdn = false;
+        if (context != null) {
+            CarrierConfigManager configManager = (CarrierConfigManager)
+                    context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            if (configManager != null) {
+                PersistableBundle b = configManager.getConfigForSubId(subId);
+                if (b != null) {
+                    compareWithMdn = b.getBoolean(CarrierConfigManager.
+                            KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL);
+                    if (DBG) log("isVoiceMailNumber: compareWithMdn=" + compareWithMdn);
+                }
+            }
+        }
+
+        if (compareWithMdn) {
+            if (DBG) log("isVoiceMailNumber: treating mdn as additional vm number");
+            return compare(number, vmNumber) || compare(number, mdn);
+        } else {
+            if (DBG) log("isVoiceMailNumber: returning regular compare");
+            return compare(number, vmNumber);
+        }
+    }
+
+    /**
+     * Translates any alphabetic letters (i.e. [A-Za-z]) in the
+     * specified phone number into the equivalent numeric digits,
+     * according to the phone keypad letter mapping described in
+     * ITU E.161 and ISO/IEC 9995-8.
+     *
+     * @return the input string, with alpha letters converted to numeric
+     *         digits using the phone keypad letter mapping.  For example,
+     *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
+     */
+    public static String convertKeypadLettersToDigits(String input) {
+        if (input == null) {
+            return input;
+        }
+        int len = input.length();
+        if (len == 0) {
+            return input;
+        }
+
+        char[] out = input.toCharArray();
+
+        for (int i = 0; i < len; i++) {
+            char c = out[i];
+            // If this char isn't in KEYPAD_MAP at all, just leave it alone.
+            out[i] = (char) KEYPAD_MAP.get(c, c);
+        }
+
+        return new String(out);
+    }
+
+    /**
+     * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
+     * TODO: This should come from a resource.
+     */
+    private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
+    static {
+        KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
+        KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
+
+        KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
+        KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
+
+        KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
+        KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
+
+        KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
+        KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
+
+        KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
+        KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
+
+        KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
+        KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
+
+        KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
+        KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
+
+        KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
+        KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
+    }
+
+    //================ Plus Code formatting =========================
+    private static final char PLUS_SIGN_CHAR = '+';
+    private static final String PLUS_SIGN_STRING = "+";
+    private static final String NANP_IDP_STRING = "011";
+    private static final int NANP_LENGTH = 10;
+
+    /**
+     * This function checks if there is a plus sign (+) in the passed-in dialing number.
+     * If there is, it processes the plus sign based on the default telephone
+     * numbering plan of the system when the phone is activated and the current
+     * telephone numbering plan of the system that the phone is camped on.
+     * Currently, we only support the case that the default and current telephone
+     * numbering plans are North American Numbering Plan(NANP).
+     *
+     * The passed-in dialStr should only contain the valid format as described below,
+     * 1) the 1st character in the dialStr should be one of the really dialable
+     *    characters listed below
+     *    ISO-LATIN characters 0-9, *, # , +
+     * 2) the dialStr should already strip out the separator characters,
+     *    every character in the dialStr should be one of the non separator characters
+     *    listed below
+     *    ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
+     *
+     * Otherwise, this function returns the dial string passed in
+     *
+     * @param dialStr the original dial string
+     * @return the converted dial string if the current/default countries belong to NANP,
+     * and if there is the "+" in the original dial string. Otherwise, the original dial
+     * string returns.
+     *
+     * This API is for CDMA only
+     *
+     * @hide TODO: pending API Council approval
+     */
+    @UnsupportedAppUsage
+    public static String cdmaCheckAndProcessPlusCode(String dialStr) {
+        if (!TextUtils.isEmpty(dialStr)) {
+            if (isReallyDialable(dialStr.charAt(0)) &&
+                isNonSeparator(dialStr)) {
+                String currIso = TelephonyManager.getDefault().getNetworkCountryIso();
+                String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
+                if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
+                    return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
+                            getFormatTypeFromCountryCode(currIso),
+                            getFormatTypeFromCountryCode(defaultIso));
+                }
+            }
+        }
+        return dialStr;
+    }
+
+    /**
+     * Process phone number for CDMA, converting plus code using the home network number format.
+     * This is used for outgoing SMS messages.
+     *
+     * @param dialStr the original dial string
+     * @return the converted dial string
+     * @hide for internal use
+     */
+    public static String cdmaCheckAndProcessPlusCodeForSms(String dialStr) {
+        if (!TextUtils.isEmpty(dialStr)) {
+            if (isReallyDialable(dialStr.charAt(0)) && isNonSeparator(dialStr)) {
+                String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
+                if (!TextUtils.isEmpty(defaultIso)) {
+                    int format = getFormatTypeFromCountryCode(defaultIso);
+                    return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr, format, format);
+                }
+            }
+        }
+        return dialStr;
+    }
+
+    /**
+     * This function should be called from checkAndProcessPlusCode only
+     * And it is used for test purpose also.
+     *
+     * It checks the dial string by looping through the network portion,
+     * post dial portion 1, post dial porting 2, etc. If there is any
+     * plus sign, then process the plus sign.
+     * Currently, this function supports the plus sign conversion within NANP only.
+     * Specifically, it handles the plus sign in the following ways:
+     * 1)+1NANP,remove +, e.g.
+     *   +18475797000 is converted to 18475797000,
+     * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
+     *   +8475797000 is converted to 0118475797000,
+     *   +11875767800 is converted to 01111875767800
+     * 3)+1NANP in post dial string(s), e.g.
+     *   8475797000;+18475231753 is converted to 8475797000;18475231753
+     *
+     *
+     * @param dialStr the original dial string
+     * @param currFormat the numbering system of the current country that the phone is camped on
+     * @param defaultFormat the numbering system of the country that the phone is activated on
+     * @return the converted dial string if the current/default countries belong to NANP,
+     * and if there is the "+" in the original dial string. Otherwise, the original dial
+     * string returns.
+     *
+     * @hide
+     */
+    public static String
+    cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormat) {
+        String retStr = dialStr;
+
+        boolean useNanp = (currFormat == defaultFormat) && (currFormat == FORMAT_NANP);
+
+        // Checks if the plus sign character is in the passed-in dial string
+        if (dialStr != null &&
+            dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
+
+            // Handle case where default and current telephone numbering plans are NANP.
+            String postDialStr = null;
+            String tempDialStr = dialStr;
+
+            // Sets the retStr to null since the conversion will be performed below.
+            retStr = null;
+            if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
+            // This routine is to process the plus sign in the dial string by loop through
+            // the network portion, post dial portion 1, post dial portion 2... etc. if
+            // applied
+            do {
+                String networkDialStr;
+                // Format the string based on the rules for the country the number is from,
+                // and the current country the phone is camped
+                if (useNanp) {
+                    networkDialStr = extractNetworkPortion(tempDialStr);
+                } else  {
+                    networkDialStr = extractNetworkPortionAlt(tempDialStr);
+
+                }
+
+                networkDialStr = processPlusCode(networkDialStr, useNanp);
+
+                // Concatenates the string that is converted from network portion
+                if (!TextUtils.isEmpty(networkDialStr)) {
+                    if (retStr == null) {
+                        retStr = networkDialStr;
+                    } else {
+                        retStr = retStr.concat(networkDialStr);
+                    }
+                } else {
+                    // This should never happen since we checked the if dialStr is null
+                    // and if it contains the plus sign in the beginning of this function.
+                    // The plus sign is part of the network portion.
+                    Rlog.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
+                    return dialStr;
+                }
+                postDialStr = extractPostDialPortion(tempDialStr);
+                if (!TextUtils.isEmpty(postDialStr)) {
+                    int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
+
+                    // dialableIndex should always be greater than 0
+                    if (dialableIndex >= 1) {
+                        retStr = appendPwCharBackToOrigDialStr(dialableIndex,
+                                 retStr,postDialStr);
+                        // Skips the P/W character, extracts the dialable portion
+                        tempDialStr = postDialStr.substring(dialableIndex);
+                    } else {
+                        // Non-dialable character such as P/W should not be at the end of
+                        // the dial string after P/W processing in GsmCdmaConnection.java
+                        // Set the postDialStr to "" to break out of the loop
+                        if (dialableIndex < 0) {
+                            postDialStr = "";
+                        }
+                        Rlog.e("wrong postDialStr=", postDialStr);
+                    }
+                }
+                if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
+            } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
+        }
+        return retStr;
+    }
+
+    /**
+     * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
+     * containing a phone number in its entirety.
+     *
+     * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
+     * @return A {@code CharSequence} with appropriate annotations.
+     */
+    public static CharSequence createTtsSpannable(CharSequence phoneNumber) {
+        if (phoneNumber == null) {
+            return null;
+        }
+        Spannable spannable = Spannable.Factory.getInstance().newSpannable(phoneNumber);
+        PhoneNumberUtils.addTtsSpan(spannable, 0, spannable.length());
+        return spannable;
+    }
+
+    /**
+     * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
+     * annotating that location as containing a phone number.
+     *
+     * @param s A {@code Spannable} to annotate.
+     * @param start The starting character position of the phone number in {@code s}.
+     * @param endExclusive The position after the ending character in the phone number {@code s}.
+     */
+    public static void addTtsSpan(Spannable s, int start, int endExclusive) {
+        s.setSpan(createTtsSpan(s.subSequence(start, endExclusive).toString()),
+                start,
+                endExclusive,
+                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+    }
+
+    /**
+     * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
+     * containing a phone number in its entirety.
+     *
+     * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
+     * @return A {@code CharSequence} with appropriate annotations.
+     * @deprecated Renamed {@link #createTtsSpannable}.
+     *
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    public static CharSequence ttsSpanAsPhoneNumber(CharSequence phoneNumber) {
+        return createTtsSpannable(phoneNumber);
+    }
+
+    /**
+     * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
+     * annotating that location as containing a phone number.
+     *
+     * @param s A {@code Spannable} to annotate.
+     * @param start The starting character position of the phone number in {@code s}.
+     * @param end The ending character position of the phone number in {@code s}.
+     *
+     * @deprecated Renamed {@link #addTtsSpan}.
+     *
+     * @hide
+     */
+    @Deprecated
+    public static void ttsSpanAsPhoneNumber(Spannable s, int start, int end) {
+        addTtsSpan(s, start, end);
+    }
+
+    /**
+     * Create a {@code TtsSpan} for the supplied {@code String}.
+     *
+     * @param phoneNumberString A {@code String} the entirety of which represents a phone number.
+     * @return A {@code TtsSpan} for {@param phoneNumberString}.
+     */
+    public static TtsSpan createTtsSpan(String phoneNumberString) {
+        if (phoneNumberString == null) {
+            return null;
+        }
+
+        // Parse the phone number
+        final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
+        PhoneNumber phoneNumber = null;
+        try {
+            // Don't supply a defaultRegion so this fails for non-international numbers because
+            // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already
+            // present
+            phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null);
+        } catch (NumberParseException ignored) {
+        }
+
+        // Build a telephone tts span
+        final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder();
+        if (phoneNumber == null) {
+            // Strip separators otherwise TalkBack will be silent
+            // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel)
+            builder.setNumberParts(splitAtNonNumerics(phoneNumberString));
+        } else {
+            if (phoneNumber.hasCountryCode()) {
+                builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode()));
+            }
+            builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber()));
+        }
+        return builder.build();
+    }
+
+    // Split a phone number like "+20(123)-456#" using spaces, ignoring anything that is not
+    // a digit or the characters * and #, to produce a result like "20 123 456#".
+    private static String splitAtNonNumerics(CharSequence number) {
+        StringBuilder sb = new StringBuilder(number.length());
+        for (int i = 0; i < number.length(); i++) {
+            sb.append(PhoneNumberUtils.is12Key(number.charAt(i))
+                    ? number.charAt(i)
+                    : " ");
+        }
+        // It is very important to remove extra spaces. At time of writing, any leading or trailing
+        // spaces, or any sequence of more than one space, will confuse TalkBack and cause the TTS
+        // span to be non-functional!
+        return sb.toString().replaceAll(" +", " ").trim();
+    }
+
+    private static String getCurrentIdp(boolean useNanp) {
+        String ps = null;
+        if (useNanp) {
+            ps = NANP_IDP_STRING;
+        } else {
+            // in case, there is no IDD is found, we shouldn't convert it.
+            ps = TelephonyProperties.operator_idp_string().orElse(PLUS_SIGN_STRING);
+        }
+        return ps;
+    }
+
+    private static boolean isTwoToNine (char c) {
+        if (c >= '2' && c <= '9') {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private static int getFormatTypeFromCountryCode (String country) {
+        // Check for the NANP countries
+        int length = NANP_COUNTRIES.length;
+        for (int i = 0; i < length; i++) {
+            if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
+                return FORMAT_NANP;
+            }
+        }
+        if ("jp".compareToIgnoreCase(country) == 0) {
+            return FORMAT_JAPAN;
+        }
+        return FORMAT_UNKNOWN;
+    }
+
+    /**
+     * This function checks if the passed in string conforms to the NANP format
+     * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static boolean isNanp (String dialStr) {
+        boolean retVal = false;
+        if (dialStr != null) {
+            if (dialStr.length() == NANP_LENGTH) {
+                if (isTwoToNine(dialStr.charAt(0)) &&
+                    isTwoToNine(dialStr.charAt(3))) {
+                    retVal = true;
+                    for (int i=1; i<NANP_LENGTH; i++ ) {
+                        char c=dialStr.charAt(i);
+                        if (!PhoneNumberUtils.isISODigit(c)) {
+                            retVal = false;
+                            break;
+                        }
+                    }
+                }
+            }
+        } else {
+            Rlog.e("isNanp: null dialStr passed in", dialStr);
+        }
+        return retVal;
+    }
+
+   /**
+    * This function checks if the passed in string conforms to 1-NANP format
+    */
+    private static boolean isOneNanp(String dialStr) {
+        boolean retVal = false;
+        if (dialStr != null) {
+            String newDialStr = dialStr.substring(1);
+            if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
+                retVal = true;
+            }
+        } else {
+            Rlog.e("isOneNanp: null dialStr passed in", dialStr);
+        }
+        return retVal;
+    }
+
+    /**
+     * Determines if the specified number is actually a URI
+     * (i.e. a SIP address) rather than a regular PSTN phone number,
+     * based on whether or not the number contains an "@" character.
+     *
+     * @hide
+     * @param number
+     * @return true if number contains @
+     */
+    @SystemApi
+    public static boolean isUriNumber(@Nullable String number) {
+        // Note we allow either "@" or "%40" to indicate a URI, in case
+        // the passed-in string is URI-escaped.  (Neither "@" nor "%40"
+        // will ever be found in a legal PSTN number.)
+        return number != null && (number.contains("@") || number.contains("%40"));
+    }
+
+    /**
+     * @return the "username" part of the specified SIP address,
+     *         i.e. the part before the "@" character (or "%40").
+     *
+     * @param number SIP address of the form "username@domainname"
+     *               (or the URI-escaped equivalent "username%40domainname")
+     * @see #isUriNumber
+     *
+     * @hide
+     */
+    @SystemApi
+    public static @NonNull String getUsernameFromUriNumber(@NonNull String number) {
+        // The delimiter between username and domain name can be
+        // either "@" or "%40" (the URI-escaped equivalent.)
+        int delimiterIndex = number.indexOf('@');
+        if (delimiterIndex < 0) {
+            delimiterIndex = number.indexOf("%40");
+        }
+        if (delimiterIndex < 0) {
+            Rlog.w(LOG_TAG,
+                  "getUsernameFromUriNumber: no delimiter found in SIP addr '" + number + "'");
+            delimiterIndex = number.length();
+        }
+        return number.substring(0, delimiterIndex);
+    }
+
+    /**
+     * Given a {@link Uri} with a {@code sip} scheme, attempts to build an equivalent {@code tel}
+     * scheme {@link Uri}.  If the source {@link Uri} does not contain a valid number, or is not
+     * using the {@code sip} scheme, the original {@link Uri} is returned.
+     *
+     * @param source The {@link Uri} to convert.
+     * @return The equivalent {@code tel} scheme {@link Uri}.
+     *
+     * @hide
+     */
+    public static Uri convertSipUriToTelUri(Uri source) {
+        // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
+        // Per RFC3261, the "user" can be a telephone number.
+        // For example: sip:1650555121;[email protected]
+        // In this case, the phone number is in the user field of the URI, and the parameters can be
+        // ignored.
+        //
+        // A SIP URI can also specify a phone number in a format similar to:
+        // sip:[email protected];user=phone
+        // In this case, the phone number is again in user field and the parameters can be ignored.
+        // We can get the user field in these instances by splitting the string on the @, ;, or :
+        // and looking at the first found item.
+
+        String scheme = source.getScheme();
+
+        if (!PhoneAccount.SCHEME_SIP.equals(scheme)) {
+            // Not a sip URI, bail.
+            return source;
+        }
+
+        String number = source.getSchemeSpecificPart();
+        String numberParts[] = number.split("[@;:]");
+
+        if (numberParts.length == 0) {
+            // Number not found, bail.
+            return source;
+        }
+        number = numberParts[0];
+
+        return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
+    }
+
+    /**
+     * This function handles the plus code conversion
+     * If the number format is
+     * 1)+1NANP,remove +,
+     * 2)other than +1NANP, any + numbers,replace + with the current IDP
+     */
+    private static String processPlusCode(String networkDialStr, boolean useNanp) {
+        String retStr = networkDialStr;
+
+        if (DBG) log("processPlusCode, networkDialStr = " + networkDialStr
+                + "for NANP = " + useNanp);
+        // If there is a plus sign at the beginning of the dial string,
+        // Convert the plus sign to the default IDP since it's an international number
+        if (networkDialStr != null &&
+            networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
+            networkDialStr.length() > 1) {
+            String newStr = networkDialStr.substring(1);
+            // TODO: for nonNanp, should the '+' be removed if following number is country code
+            if (useNanp && isOneNanp(newStr)) {
+                // Remove the leading plus sign
+                retStr = newStr;
+            } else {
+                // Replaces the plus sign with the default IDP
+                retStr = networkDialStr.replaceFirst("[+]", getCurrentIdp(useNanp));
+            }
+        }
+        if (DBG) log("processPlusCode, retStr=" + retStr);
+        return retStr;
+    }
+
+    // This function finds the index of the dialable character(s)
+    // in the post dial string
+    private static int findDialableIndexFromPostDialStr(String postDialStr) {
+        for (int index = 0;index < postDialStr.length();index++) {
+             char c = postDialStr.charAt(index);
+             if (isReallyDialable(c)) {
+                return index;
+             }
+        }
+        return -1;
+    }
+
+    // This function appends the non-dialable P/W character to the original
+    // dial string based on the dialable index passed in
+    private static String
+    appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
+        String retStr;
+
+        // There is only 1 P/W character before the dialable characters
+        if (dialableIndex == 1) {
+            StringBuilder ret = new StringBuilder(origStr);
+            ret = ret.append(dialStr.charAt(0));
+            retStr = ret.toString();
+        } else {
+            // It means more than 1 P/W characters in the post dial string,
+            // appends to retStr
+            String nonDigitStr = dialStr.substring(0,dialableIndex);
+            retStr = origStr.concat(nonDigitStr);
+        }
+        return retStr;
+    }
+
+    //===== Beginning of utility methods used in compareLoosely() =====
+
+    /**
+     * Phone numbers are stored in "lookup" form in the database
+     * as reversed strings to allow for caller ID lookup
+     *
+     * This method takes a phone number and makes a valid SQL "LIKE"
+     * string that will match the lookup form
+     *
+     */
+    /** all of a up to len must be an international prefix or
+     *  separators/non-dialing digits
+     */
+    private static boolean
+    matchIntlPrefix(String a, int len) {
+        /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
+        /*        0       1                           2 3 45               */
+
+        int state = 0;
+        for (int i = 0 ; i < len ; i++) {
+            char c = a.charAt(i);
+
+            switch (state) {
+                case 0:
+                    if      (c == '+') state = 1;
+                    else if (c == '0') state = 2;
+                    else if (isNonSeparator(c)) return false;
+                break;
+
+                case 2:
+                    if      (c == '0') state = 3;
+                    else if (c == '1') state = 4;
+                    else if (isNonSeparator(c)) return false;
+                break;
+
+                case 4:
+                    if      (c == '1') state = 5;
+                    else if (isNonSeparator(c)) return false;
+                break;
+
+                default:
+                    if (isNonSeparator(c)) return false;
+                break;
+
+            }
+        }
+
+        return state == 1 || state == 3 || state == 5;
+    }
+
+    /** all of 'a' up to len must be a (+|00|011)country code)
+     *  We're fast and loose with the country code. Any \d{1,3} matches */
+    private static boolean
+    matchIntlPrefixAndCC(String a, int len) {
+        /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
+        /*      0          1 2 3 45  6 7  8                 */
+
+        int state = 0;
+        for (int i = 0 ; i < len ; i++ ) {
+            char c = a.charAt(i);
+
+            switch (state) {
+                case 0:
+                    if      (c == '+') state = 1;
+                    else if (c == '0') state = 2;
+                    else if (isNonSeparator(c)) return false;
+                break;
+
+                case 2:
+                    if      (c == '0') state = 3;
+                    else if (c == '1') state = 4;
+                    else if (isNonSeparator(c)) return false;
+                break;
+
+                case 4:
+                    if      (c == '1') state = 5;
+                    else if (isNonSeparator(c)) return false;
+                break;
+
+                case 1:
+                case 3:
+                case 5:
+                    if      (isISODigit(c)) state = 6;
+                    else if (isNonSeparator(c)) return false;
+                break;
+
+                case 6:
+                case 7:
+                    if      (isISODigit(c)) state++;
+                    else if (isNonSeparator(c)) return false;
+                break;
+
+                default:
+                    if (isNonSeparator(c)) return false;
+            }
+        }
+
+        return state == 6 || state == 7 || state == 8;
+    }
+
+    /** all of 'a' up to len must match non-US trunk prefix ('0') */
+    private static boolean
+    matchTrunkPrefix(String a, int len) {
+        boolean found;
+
+        found = false;
+
+        for (int i = 0 ; i < len ; i++) {
+            char c = a.charAt(i);
+
+            if (c == '0' && !found) {
+                found = true;
+            } else if (isNonSeparator(c)) {
+                return false;
+            }
+        }
+
+        return found;
+    }
+
+    //===== End of utility methods used only in compareLoosely() =====
+
+    //===== Beginning of utility methods used only in compareStrictly() ====
+
+    /*
+     * If true, the number is country calling code.
+     */
+    private static final boolean COUNTRY_CALLING_CALL[] = {
+        true, true, false, false, false, false, false, true, false, false,
+        false, false, false, false, false, false, false, false, false, false,
+        true, false, false, false, false, false, false, true, true, false,
+        true, true, true, true, true, false, true, false, false, true,
+        true, false, false, true, true, true, true, true, true, true,
+        false, true, true, true, true, true, true, true, true, false,
+        true, true, true, true, true, true, true, false, false, false,
+        false, false, false, false, false, false, false, false, false, false,
+        false, true, true, true, true, false, true, false, false, true,
+        true, true, true, true, true, true, false, false, true, false,
+    };
+    private static final int CCC_LENGTH = COUNTRY_CALLING_CALL.length;
+
+    /**
+     * @return true when input is valid Country Calling Code.
+     */
+    private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
+        return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
+                COUNTRY_CALLING_CALL[countryCallingCodeCandidate];
+    }
+
+    /**
+     * Returns integer corresponding to the input if input "ch" is
+     * ISO-LATIN characters 0-9.
+     * Returns -1 otherwise
+     */
+    private static int tryGetISODigit(char ch) {
+        if ('0' <= ch && ch <= '9') {
+            return ch - '0';
+        } else {
+            return -1;
+        }
+    }
+
+    private static class CountryCallingCodeAndNewIndex {
+        public final int countryCallingCode;
+        public final int newIndex;
+        public CountryCallingCodeAndNewIndex(int countryCode, int newIndex) {
+            this.countryCallingCode = countryCode;
+            this.newIndex = newIndex;
+        }
+    }
+
+    /*
+     * Note that this function does not strictly care the country calling code with
+     * 3 length (like Morocco: +212), assuming it is enough to use the first two
+     * digit to compare two phone numbers.
+     */
+    private static CountryCallingCodeAndNewIndex tryGetCountryCallingCodeAndNewIndex(
+        String str, boolean acceptThailandCase) {
+        // Rough regexp:
+        //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
+        //         0        1 2 3 45  6 7  89
+        //
+        // In all the states, this function ignores separator characters.
+        // "166" is the special case for the call from Thailand to the US. Uguu!
+        int state = 0;
+        int ccc = 0;
+        final int length = str.length();
+        for (int i = 0 ; i < length ; i++ ) {
+            char ch = str.charAt(i);
+            switch (state) {
+                case 0:
+                    if      (ch == '+') state = 1;
+                    else if (ch == '0') state = 2;
+                    else if (ch == '1') {
+                        if (acceptThailandCase) {
+                            state = 8;
+                        } else {
+                            return null;
+                        }
+                    } else if (isDialable(ch)) {
+                        return null;
+                    }
+                break;
+
+                case 2:
+                    if      (ch == '0') state = 3;
+                    else if (ch == '1') state = 4;
+                    else if (isDialable(ch)) {
+                        return null;
+                    }
+                break;
+
+                case 4:
+                    if      (ch == '1') state = 5;
+                    else if (isDialable(ch)) {
+                        return null;
+                    }
+                break;
+
+                case 1:
+                case 3:
+                case 5:
+                case 6:
+                case 7:
+                    {
+                        int ret = tryGetISODigit(ch);
+                        if (ret > 0) {
+                            ccc = ccc * 10 + ret;
+                            if (ccc >= 100 || isCountryCallingCode(ccc)) {
+                                return new CountryCallingCodeAndNewIndex(ccc, i + 1);
+                            }
+                            if (state == 1 || state == 3 || state == 5) {
+                                state = 6;
+                            } else {
+                                state++;
+                            }
+                        } else if (isDialable(ch)) {
+                            return null;
+                        }
+                    }
+                    break;
+                case 8:
+                    if (ch == '6') state = 9;
+                    else if (isDialable(ch)) {
+                        return null;
+                    }
+                    break;
+                case 9:
+                    if (ch == '6') {
+                        return new CountryCallingCodeAndNewIndex(66, i + 1);
+                    } else {
+                        return null;
+                    }
+                default:
+                    return null;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Currently this function simply ignore the first digit assuming it is
+     * trunk prefix. Actually trunk prefix is different in each country.
+     *
+     * e.g.
+     * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
+     * "+33123456789" equals "0123456789" (French trunk digit is 0)
+     *
+     */
+    private static int tryGetTrunkPrefixOmittedIndex(String str, int currentIndex) {
+        int length = str.length();
+        for (int i = currentIndex ; i < length ; i++) {
+            final char ch = str.charAt(i);
+            if (tryGetISODigit(ch) >= 0) {
+                return i + 1;
+            } else if (isDialable(ch)) {
+                return -1;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
+     * that "str" has only one digit and separator characters. The one digit is
+     * assumed to be trunk prefix.
+     */
+    private static boolean checkPrefixIsIgnorable(final String str,
+            int forwardIndex, int backwardIndex) {
+        boolean trunk_prefix_was_read = false;
+        while (backwardIndex >= forwardIndex) {
+            if (tryGetISODigit(str.charAt(backwardIndex)) >= 0) {
+                if (trunk_prefix_was_read) {
+                    // More than one digit appeared, meaning that "a" and "b"
+                    // is different.
+                    return false;
+                } else {
+                    // Ignore just one digit, assuming it is trunk prefix.
+                    trunk_prefix_was_read = true;
+                }
+            } else if (isDialable(str.charAt(backwardIndex))) {
+                // Trunk prefix is a digit, not "*", "#"...
+                return false;
+            }
+            backwardIndex--;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns Default voice subscription Id.
+     */
+    private static int getDefaultVoiceSubId() {
+        return SubscriptionManager.getDefaultVoiceSubscriptionId();
+    }
+    //==== End of utility methods used only in compareStrictly() =====
+
+
+    /*
+     * The config held calling number conversion map, expected to convert to emergency number.
+     */
+    private static String[] sConvertToEmergencyMap = null;
+
+    /**
+     * Converts to emergency number based on the conversion map.
+     * The conversion map is declared as config_convert_to_emergency_number_map.
+     *
+     * @param context a context to use for accessing resources
+     * @return The converted emergency number if the number matches conversion map,
+     * otherwise original number.
+     *
+     * @hide
+     */
+    public static String convertToEmergencyNumber(Context context, String number) {
+        if (context == null || TextUtils.isEmpty(number)) {
+            return number;
+        }
+
+        String normalizedNumber = normalizeNumber(number);
+
+        // The number is already emergency number. Skip conversion.
+        if (isEmergencyNumber(normalizedNumber)) {
+            return number;
+        }
+
+        if (sConvertToEmergencyMap == null) {
+            sConvertToEmergencyMap = context.getResources().getStringArray(
+                    com.android.internal.R.array.config_convert_to_emergency_number_map);
+        }
+
+        // The conversion map is not defined (this is default). Skip conversion.
+        if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0) {
+            return number;
+        }
+
+        for (String convertMap : sConvertToEmergencyMap) {
+            if (DBG) log("convertToEmergencyNumber: " + convertMap);
+            String[] entry = null;
+            String[] filterNumbers = null;
+            String convertedNumber = null;
+            if (!TextUtils.isEmpty(convertMap)) {
+                entry = convertMap.split(":");
+            }
+            if (entry != null && entry.length == 2) {
+                convertedNumber = entry[1];
+                if (!TextUtils.isEmpty(entry[0])) {
+                    filterNumbers = entry[0].split(",");
+                }
+            }
+            // Skip if the format of entry is invalid
+            if (TextUtils.isEmpty(convertedNumber) || filterNumbers == null
+                    || filterNumbers.length == 0) {
+                continue;
+            }
+
+            for (String filterNumber : filterNumbers) {
+                if (DBG) log("convertToEmergencyNumber: filterNumber = " + filterNumber
+                        + ", convertedNumber = " + convertedNumber);
+                if (!TextUtils.isEmpty(filterNumber) && filterNumber.equals(normalizedNumber)) {
+                    if (DBG) log("convertToEmergencyNumber: Matched. Successfully converted to: "
+                            + convertedNumber);
+                    return convertedNumber;
+                }
+            }
+        }
+        return number;
+    }
+
+    /**
+     * Determines if two phone numbers are the same.
+     * <p>
+     * Matching is based on <a href="https://github.com/google/libphonenumber>libphonenumber</a>.
+     * Unlike {@link #compare(String, String)}, matching takes into account national
+     * dialing plans rather than simply matching the last 7 digits of the two phone numbers. As a
+     * result, it is expected that some numbers which would match using the previous method will no
+     * longer match using this new approach.
+     *
+     * @param number1
+     * @param number2
+     * @param defaultCountryIso The lowercase two letter ISO 3166-1 country code. Used when parsing
+     *                          the phone numbers where it is not possible to determine the country
+     *                          associated with a phone number based on the number alone. It
+     *                          is recommended to pass in
+     *                          {@link TelephonyManager#getNetworkCountryIso()}.
+     * @return True if the two given phone number are same.
+     */
+    public static boolean areSamePhoneNumber(@NonNull String number1,
+            @NonNull String number2, @NonNull String defaultCountryIso) {
+        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+        PhoneNumber n1;
+        PhoneNumber n2;
+
+        if (defaultCountryIso != null) {
+            defaultCountryIso = defaultCountryIso.toUpperCase(Locale.ROOT);
+        }
+
+        try {
+            n1 = util.parseAndKeepRawInput(number1, defaultCountryIso);
+            n2 = util.parseAndKeepRawInput(number2, defaultCountryIso);
+        } catch (NumberParseException e) {
+            return false;
+        }
+
+        PhoneNumberUtil.MatchType matchType = util.isNumberMatch(n1, n2);
+        if (matchType == PhoneNumberUtil.MatchType.EXACT_MATCH
+                || matchType == PhoneNumberUtil.MatchType.NSN_MATCH) {
+            return true;
+        } else if (matchType == PhoneNumberUtil.MatchType.SHORT_NSN_MATCH) {
+            return (n1.getNationalNumber() == n2.getNationalNumber()
+                    && n1.getCountryCode() == n2.getCountryCode());
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Check if the number is for Wireless Priority Service call.
+     * @param number  The phone number used for WPS call.
+     * @return {@code true} if number matches WPS pattern and {@code false} otherwise.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_WPS_CHECK_API_FLAG)
+    public static boolean isWpsCallNumber(@NonNull String number) {
+        return (number != null) && (number.startsWith(PREFIX_WPS)
+                || number.startsWith(PREFIX_WPS_CLIR_ACTIVATE)
+                || number.startsWith(PREFIX_WPS_CLIR_DEACTIVATE));
+    }
+}
diff --git a/android-35/android/telephony/PhoneStateListener.java b/android-35/android/telephony/PhoneStateListener.java
new file mode 100644
index 0000000..4281da1
--- /dev/null
+++ b/android-35/android/telephony/PhoneStateListener.java
@@ -0,0 +1,1697 @@
+/*
+ * Copyright (C) 2008 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;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
+import android.telephony.Annotation.DisconnectCauses;
+import android.telephony.Annotation.PreciseDisconnectCauses;
+import android.telephony.Annotation.RadioPowerState;
+import android.telephony.Annotation.SimActivationState;
+import android.telephony.Annotation.SrvccState;
+import android.telephony.TelephonyManager.DataEnabledReason;
+import android.telephony.TelephonyManager.EmergencyCallbackModeStopReason;
+import android.telephony.TelephonyManager.EmergencyCallbackModeType;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.MediaQualityStatus;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IPhoneStateListener;
+
+import dalvik.system.VMRuntime;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * A listener class for monitoring changes in specific telephony states
+ * on the device, including service state, signal strength, message
+ * waiting indicator (voicemail), and others.
+ * <p>
+ * Override the methods for the state that you wish to receive updates for, and
+ * pass your PhoneStateListener object, along with bitwise-or of the LISTEN_
+ * flags to {@link TelephonyManager#listen TelephonyManager.listen()}. Methods are
+ * called when the state changes, as well as once on initial registration.
+ * <p>
+ * Note that access to some telephony information is
+ * permission-protected. Your application won't receive updates for protected
+ * information unless it has the appropriate permissions declared in
+ * its manifest file. Where permissions apply, they are noted in the
+ * appropriate LISTEN_ flags.
+ *
+ * @deprecated Use {@link TelephonyCallback} instead.
+ */
+@Deprecated
+public class PhoneStateListener {
+    private static final String LOG_TAG = "PhoneStateListener";
+    private static final boolean DBG = false; // STOPSHIP if true
+
+    /**
+     * Stop listening for updates.
+     *
+     * The PhoneStateListener is not tied to any subscription and unregistered for any update.
+     */
+    public static final int LISTEN_NONE = 0;
+
+    /**
+     *  Listen for changes to the network service state (cellular).
+     *
+     *  @see #onServiceStateChanged
+     *  @see ServiceState
+     *  @deprecated Use {@link TelephonyCallback.ServiceStateListener} instead.
+     */
+    @Deprecated
+    public static final int LISTEN_SERVICE_STATE                            = 0x00000001;
+
+    /**
+     * Listen for changes to the network signal strength (cellular).
+     * {@more}
+     *
+     * @see #onSignalStrengthChanged
+     * @deprecated Use {@link TelephonyCallback.SignalStrengthsListener} instead.
+     */
+    @Deprecated
+    public static final int LISTEN_SIGNAL_STRENGTH                          = 0x00000002;
+
+    /**
+     * Listen for changes to the message-waiting indicator.
+     * {@more}
+     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
+     * READ_PHONE_STATE} or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     * <p>
+     * Example: The status bar uses this to determine when to display the
+     * voicemail icon.
+     *
+     * @see #onMessageWaitingIndicatorChanged
+     * @deprecated Use {@link TelephonyCallback.MessageWaitingIndicatorListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final int LISTEN_MESSAGE_WAITING_INDICATOR                = 0x00000004;
+
+    /**
+     * Listen for changes to the call-forwarding indicator.
+     * {@more}
+     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
+     * READ_PHONE_STATE} or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @see #onCallForwardingIndicatorChanged
+     * @deprecated Use {@link TelephonyCallback.CallForwardingIndicatorListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final int LISTEN_CALL_FORWARDING_INDICATOR                = 0x00000008;
+
+    /**
+     * Listen for changes to the device's cell location. Note that
+     * this will result in frequent callbacks to the listener.
+     * {@more}
+     * Requires Permission: {@link android.Manifest.permission#ACCESS_FINE_LOCATION
+     * ACCESS_FINE_LOCATION}
+     * <p>
+     * If you need regular location updates but want more control over
+     * the update interval or location precision, you can set up a listener
+     * through the {@link android.location.LocationManager location manager}
+     * instead.
+     *
+     * @see #onCellLocationChanged
+     * @deprecated Use {@link TelephonyCallback.CellLocationListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+    public static final int LISTEN_CELL_LOCATION                            = 0x00000010;
+
+    /**
+     * Listen for changes to the device call state.
+     * {@more}
+     *
+     * @see #onCallStateChanged
+     * @deprecated Use {@link TelephonyCallback.CallStateListener} instead.
+     */
+    @Deprecated
+    public static final int LISTEN_CALL_STATE                               = 0x00000020;
+
+    /**
+     * Listen for changes to the data connection state (cellular).
+     *
+     * @see #onDataConnectionStateChanged
+     * @deprecated Use {@link TelephonyCallback.DataConnectionStateListener} instead.
+     */
+    @Deprecated
+    public static final int LISTEN_DATA_CONNECTION_STATE                    = 0x00000040;
+
+    /**
+     * Listen for changes to the direction of data traffic on the data
+     * connection (cellular).
+     * {@more}
+     * Example: The status bar uses this to display the appropriate
+     * data-traffic icon.
+     *
+     * @see #onDataActivity
+     * @deprecated Use {@link TelephonyCallback.DataActivityListener} instead.
+     */
+    @Deprecated
+    public static final int LISTEN_DATA_ACTIVITY                            = 0x00000080;
+
+    /**
+     * Listen for changes to the network signal strengths (cellular).
+     * <p>
+     * Example: The status bar uses this to control the signal-strength
+     * icon.
+     *
+     * @see #onSignalStrengthsChanged
+     * @deprecated Use {@link TelephonyCallback.SignalStrengthsListener} instead.
+     */
+    @Deprecated
+    public static final int LISTEN_SIGNAL_STRENGTHS                         = 0x00000100;
+
+    /**
+     * Listen for changes to observed cell info.
+     *
+     * Listening to this event requires the {@link Manifest.permission#READ_PHONE_STATE} and
+     * {@link Manifest.permission#ACCESS_FINE_LOCATION}
+     * permission.
+     *
+     * @see #onCellInfoChanged
+     * @deprecated Use {@link TelephonyCallback.CellInfoListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
+    public static final int LISTEN_CELL_INFO = 0x00000400;
+
+    /**
+     * Listen for {@link android.telephony.Annotation.PreciseCallStates} of ringing,
+     * background and foreground calls.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.PreciseCallStateListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @SystemApi
+    public static final int LISTEN_PRECISE_CALL_STATE                       = 0x00000800;
+
+    /**
+     * Listen for {@link PreciseDataConnectionState} on the data connection (cellular).
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @see #onPreciseDataConnectionStateChanged
+     * @deprecated Use {@link TelephonyCallback.PreciseDataConnectionStateListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE            = 0x00001000;
+
+    /**
+     * Listen for real time info for all data connections (cellular)).
+     * {@more}
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE}
+     * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
+     *
+     * @deprecated Use {@link TelephonyManager#requestModemActivityInfo} instead.
+     * @hide
+     */
+    @Deprecated
+    public static final int LISTEN_DATA_CONNECTION_REAL_TIME_INFO           = 0x00002000;
+
+    /**
+     * Listen for changes to the SRVCC state of the active call.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @see #onSrvccStateChanged
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.SrvccStateListener} instead.
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int LISTEN_SRVCC_STATE_CHANGED                     = 0x00004000;
+
+    /**
+     * Listen for OEM hook raw event
+     *
+     * @see #onOemHookRawEvent
+     * @hide
+     * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
+     */
+    @Deprecated
+    public static final int LISTEN_OEM_HOOK_RAW_EVENT                       = 0x00008000;
+
+    /**
+     * Listen for carrier network changes indicated by a carrier app.
+     *
+     * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.CarrierNetworkListener} instead.
+     */
+    @Deprecated
+    public static final int LISTEN_CARRIER_NETWORK_CHANGE                   = 0x00010000;
+
+    /**
+     * Listen for changes to the sim voice activation state
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+     * {@more}
+     * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
+     * fully activated
+     *
+     * @see #onVoiceActivationStateChanged
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.VoiceActivationStateListener} instead.
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int LISTEN_VOICE_ACTIVATION_STATE                   = 0x00020000;
+
+    /**
+     * Listen for changes to the sim data activation state
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+     *
+     * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
+     * fully activated
+     *
+     * @see #onDataActivationStateChanged
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.DataActivationStateListener} instead.
+     */
+    @Deprecated
+    public static final int LISTEN_DATA_ACTIVATION_STATE                   = 0x00040000;
+
+    /**
+     *  Listen for changes to the user mobile data state
+     *
+     *  @see #onUserMobileDataStateChanged
+     *  @deprecated Use {@link TelephonyCallback.UserMobileDataStateListener} instead.
+     */
+    @Deprecated
+    public static final int LISTEN_USER_MOBILE_DATA_STATE                  = 0x00080000;
+
+    /**
+     *  Listen for display info changed event.
+     *
+     * For clients compiled on Android 11 SDK, requires permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling app has carrier
+     * privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     * For clients compiled on Android 12 SDK or newer,
+     * {@link android.Manifest.permission#READ_PHONE_STATE} or carrier privileges is not required
+     * anymore.
+     *
+     * @see #onDisplayInfoChanged
+     * @deprecated Use {@link TelephonyCallback.DisplayInfoListener} instead.
+     */
+    @Deprecated
+    public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000;
+
+    /**
+     *  Listen for changes to the phone capability.
+     *
+     *  @see #onPhoneCapabilityChanged
+     *  @hide
+     *  @deprecated Use {@link TelephonyCallback.PhoneCapabilityListener} instead.
+     */
+    @Deprecated
+    public static final int LISTEN_PHONE_CAPABILITY_CHANGE                 = 0x00200000;
+
+    /**
+     *  Listen for changes to active data subId. Active data subscription is
+     *  the current subscription used to setup Cellular Internet data. For example,
+     *  it could be the current active opportunistic subscription in use, or the
+     *  subscription user selected as default data subscription in DSDS mode.
+     *
+     *  @see #onActiveDataSubscriptionIdChanged
+     *  @deprecated Use {@link TelephonyCallback.ActiveDataSubscriptionIdListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000;
+
+    /**
+     *  Listen for changes to the radio power state.
+     *
+     *  @see #onRadioPowerStateChanged
+     *  @hide
+     *  @deprecated Use {@link TelephonyCallback.RadioPowerStateListener} instead.
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int LISTEN_RADIO_POWER_STATE_CHANGED               = 0x00800000;
+
+    /**
+     * Listen for changes to emergency number list based on all active subscriptions.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @deprecated Use {@link TelephonyCallback.EmergencyNumberListListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final int LISTEN_EMERGENCY_NUMBER_LIST                   = 0x01000000;
+
+    /**
+     * Listen for call disconnect causes which contains {@link DisconnectCause} and
+     * the precise disconnect cause.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @deprecated Use {@link TelephonyCallback.CallDisconnectCauseListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int LISTEN_CALL_DISCONNECT_CAUSES                  = 0x02000000;
+
+    /**
+     * Listen for changes to the call attributes of a currently active call.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @see #onCallAttributesChanged
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.CallAttributesListener} instead.
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int LISTEN_CALL_ATTRIBUTES_CHANGED                 = 0x04000000;
+
+    /**
+     * Listen for IMS call disconnect causes which contains
+     * {@link android.telephony.ims.ImsReasonInfo}
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo)
+     * @deprecated Use {@link TelephonyCallback.ImsCallDisconnectCauseListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES              = 0x08000000;
+
+    /**
+     * Listen for the emergency number placed from an outgoing call.
+     *
+     * @see #onOutgoingEmergencyCall
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.OutgoingEmergencyCallListener} instead.
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+    public static final int LISTEN_OUTGOING_EMERGENCY_CALL                  = 0x10000000;
+
+    /**
+     * Listen for the emergency number placed from an outgoing SMS.
+     *
+     * @see #onOutgoingEmergencySms
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.OutgoingEmergencySmsListener} instead.
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+    public static final int LISTEN_OUTGOING_EMERGENCY_SMS                   = 0x20000000;
+
+    /**
+     * Listen for Registration Failures.
+     *
+     * Listen for indications that a registration procedure has failed in either the CS or PS
+     * domain. This indication does not necessarily indicate a change of service state, which should
+     * be tracked via {@link #LISTEN_SERVICE_STATE}.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+     * of whether the calling app has carrier privileges.
+     *
+     * @see #onRegistrationFailed
+     * @deprecated Use {@link TelephonyCallback.RegistrationFailedListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
+    public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000;
+
+    /**
+     * Listen for Barring Information for the current registered / camped cell.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+     * of whether the calling app has carrier privileges.
+     *
+     * @see #onBarringInfoChanged
+     * @deprecated Use {@link TelephonyCallback.BarringInfoListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
+    public static final int LISTEN_BARRING_INFO = 0x80000000;
+
+    /*
+     * Subscription used to listen to the phone state changes
+     * @hide
+     */
+    /** @hide */
+    @UnsupportedAppUsage
+    protected Integer mSubId;
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    @UnsupportedAppUsage(
+            maxTargetSdk = Build.VERSION_CODES.R,
+            publicAlternatives = "Use {@code TelephonyManager#registerTelephonyCallback(" +
+                    "Executor, TelephonyCallback)} instead")
+    public final IPhoneStateListener callback;
+
+    /**
+     * Create a PhoneStateListener for the Phone with the default subscription.
+     * This class requires Looper.myLooper() not return null.
+     */
+    public PhoneStateListener() {
+        this(null, Looper.myLooper());
+    }
+
+    /**
+     * Create a PhoneStateListener for the Phone with the default subscription
+     * using a particular non-null Looper.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public PhoneStateListener(Looper looper) {
+        this(null, looper);
+    }
+
+    /**
+     * Create a PhoneStateListener for the Phone using the specified subscription.
+     * This class requires Looper.myLooper() not return null. To supply your
+     * own non-null Looper use PhoneStateListener(int subId, Looper looper) below.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public PhoneStateListener(Integer subId) {
+        this(subId, Looper.myLooper());
+        if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion()
+                >= Build.VERSION_CODES.Q) {
+            throw new IllegalArgumentException("PhoneStateListener with subId: "
+                    + subId + " is not supported, use default constructor");
+        }
+    }
+    /**
+     * Create a PhoneStateListener for the Phone using the specified subscription
+     * and non-null Looper.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public PhoneStateListener(Integer subId, Looper looper) {
+        this(subId, new HandlerExecutor(new Handler(looper)));
+        if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion()
+                >= Build.VERSION_CODES.Q) {
+            throw new IllegalArgumentException("PhoneStateListener with subId: "
+                    + subId + " is not supported, use default constructor");
+        }
+    }
+
+    /**
+     * Create a PhoneStateListener for the Phone using the specified Executor
+     *
+     * <p>Create a PhoneStateListener with a specified Executor for handling necessary callbacks.
+     * The Executor must not be null.
+     *
+     * @param executor a non-null Executor that will execute callbacks for the PhoneStateListener.
+     */
+    @Deprecated
+    public PhoneStateListener(@NonNull Executor executor) {
+        this(null, executor);
+    }
+
+    private PhoneStateListener(Integer subId, Executor e) {
+        if (e == null) {
+            throw new IllegalArgumentException("PhoneStateListener Executor must be non-null");
+        }
+        mSubId = subId;
+        callback = new IPhoneStateListenerStub(this, e);
+    }
+
+    /**
+     * Callback invoked when device service state changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * The instance of {@link ServiceState} passed as an argument here will have various levels of
+     * location information stripped from it depending on the location permissions that your app
+     * holds. Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will
+     * receive all the information in {@link ServiceState}, otherwise the cellIdentity will be null
+     * if apps only holding the {@link Manifest.permission#ACCESS_COARSE_LOCATION} permission.
+     * Network operator name in long/short alphanumeric format and numeric id will be null if apps
+     * holding neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * @see ServiceState#STATE_EMERGENCY_ONLY
+     * @see ServiceState#STATE_IN_SERVICE
+     * @see ServiceState#STATE_OUT_OF_SERVICE
+     * @see ServiceState#STATE_POWER_OFF
+     * @deprecated Use {@link TelephonyCallback.ServiceStateListener} instead.
+     */
+    @Deprecated
+    public void onServiceStateChanged(ServiceState serviceState) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when network signal strength changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @see ServiceState#STATE_EMERGENCY_ONLY
+     * @see ServiceState#STATE_IN_SERVICE
+     * @see ServiceState#STATE_OUT_OF_SERVICE
+     * @see ServiceState#STATE_POWER_OFF
+     * @deprecated Use {@link #onSignalStrengthsChanged(SignalStrength)}
+     */
+    @Deprecated
+    public void onSignalStrengthChanged(int asu) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when the message-waiting indicator changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @deprecated Use {@link TelephonyCallback.MessageWaitingIndicatorListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public void onMessageWaitingIndicatorChanged(boolean mwi) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when the call-forwarding indicator changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @deprecated Use {@link TelephonyCallback.CallForwardingIndicatorListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public void onCallForwardingIndicatorChanged(boolean cfi) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when device cell location changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @deprecated Use {@link TelephonyCallback.CellLocationListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+    public void onCellLocationChanged(CellLocation location) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when device call state changes.
+     * <p>
+     * Reports the state of Telephony (mobile) calls on the device for the registered subscription.
+     * <p>
+     * Note: the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     * <p>
+     * Note: The state returned here may differ from that returned by
+     * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that
+     * calling {@link TelephonyManager#getCallState()} from within this callback may return a
+     * different state than the callback reports.
+     *
+     * Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
+     * targeting API level 31+.
+     *
+     * @param state call state
+     * @param phoneNumber call phone number. If application does not have
+     * {@link android.Manifest.permission#READ_CALL_LOG READ_CALL_LOG} permission or carrier
+     * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be
+     * passed as an argument.
+     *
+     * @deprecated Use {@link TelephonyCallback.CallStateListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
+    public void onCallStateChanged(@Annotation.CallState int state, String phoneNumber) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when connection state changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @see TelephonyManager#DATA_DISCONNECTED
+     * @see TelephonyManager#DATA_CONNECTING
+     * @see TelephonyManager#DATA_CONNECTED
+     * @see TelephonyManager#DATA_SUSPENDED
+     * @see TelephonyManager#DATA_HANDOVER_IN_PROGRESS
+     * @deprecated Use {@link TelephonyCallback.DataConnectionStateListener} instead.
+     */
+    @Deprecated
+    public void onDataConnectionStateChanged(int state) {
+        // default implementation empty
+    }
+
+    /**
+     * same as above, but with the network type.  Both called.
+     *
+     * @deprecated Use {@link TelephonyCallback.DataConnectionStateListener} instead.
+     */
+    @Deprecated
+    public void onDataConnectionStateChanged(int state, int networkType) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when data activity state changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @see TelephonyManager#DATA_ACTIVITY_NONE
+     * @see TelephonyManager#DATA_ACTIVITY_IN
+     * @see TelephonyManager#DATA_ACTIVITY_OUT
+     * @see TelephonyManager#DATA_ACTIVITY_INOUT
+     * @see TelephonyManager#DATA_ACTIVITY_DORMANT
+     * @deprecated Use {@link TelephonyCallback.DataActivityListener} instead.
+     */
+    @Deprecated
+    public void onDataActivity(int direction) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when network signal strengths changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @deprecated Use {@link TelephonyCallback.SignalStrengthsListener} instead.
+     */
+    @Deprecated
+    public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when a observed cell info has changed or new cells have been added
+     * or removed on the registered subscription.
+     * Note, the registration subId s from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @param cellInfo is the list of currently visible cells.
+     * @deprecated Use {@link TelephonyCallback.CellInfoListener} instead.
+     */
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
+    @Deprecated
+    public void onCellInfoChanged(List<CellInfo> cellInfo) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when precise device call state changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @param callState {@link PreciseCallState}
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.PreciseCallStateListener} instead.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @SystemApi
+    @Deprecated
+    public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when call disconnect cause changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @param disconnectCause the disconnect cause
+     * @param preciseDisconnectCause the precise disconnect cause
+     * @deprecated Use {@link TelephonyCallback.CallDisconnectCauseListener} instead.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @Deprecated
+    public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause,
+            @PreciseDisconnectCauses int preciseDisconnectCause) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when Ims call disconnect cause changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
+     * @deprecated Use {@link TelephonyCallback.ImsCallDisconnectCauseListener} instead.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @Deprecated
+    public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback providing update about the default/internet data connection on the registered
+     * subscription.
+     *
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param dataConnectionState {@link PreciseDataConnectionState}
+     * @deprecated Use {@link TelephonyCallback.PreciseDataConnectionStateListener} instead.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @Deprecated
+    public void onPreciseDataConnectionStateChanged(
+            @NonNull PreciseDataConnectionState dataConnectionState) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when data connection real time info changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @hide
+     * @deprecated Use {@link TelephonyManager#requestModemActivityInfo}
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @Deprecated
+    public void onDataConnectionRealTimeInfoChanged(
+            DataConnectionRealTimeInfo dcRtInfo) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when there has been a change in the Single Radio Voice Call Continuity
+     * (SRVCC) state for the currently active call on the registered subscription.
+     *
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.SrvccStateListener} instead.
+     */
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void onSrvccStateChanged(@SrvccState int srvccState) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when the SIM voice activation state has changed on the registered
+     * subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @param state is the current SIM voice activation state
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.VoiceActivationStateListener} instead.
+     */
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void onVoiceActivationStateChanged(@SimActivationState int state) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when the SIM data activation state has changed on the registered
+     * subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @param state is the current SIM data activation state
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.DataActivationStateListener} instead.
+     */
+    @Deprecated
+    public void onDataActivationStateChanged(@SimActivationState int state) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when the user mobile data state has changed on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @param enabled indicates whether the current user mobile data state is enabled or disabled.
+     * @deprecated Use {@link TelephonyCallback.UserMobileDataStateListener} instead.
+     */
+    @Deprecated
+    public void onUserMobileDataStateChanged(boolean enabled) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when the display info has changed on the registered subscription.
+     * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user based on
+     * carrier policy.
+     *
+     * For clients compiled on Android 11 SDK, requires permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling app has carrier
+     * privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     * For clients compiled on Android 12 SDK or newer,
+     * {@link android.Manifest.permission#READ_PHONE_STATE} or carrier privileges is not required
+     * anymore.
+     *
+     * @param telephonyDisplayInfo The display information.
+     * @deprecated Use {@link TelephonyCallback.DisplayInfoListener} instead.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @Deprecated
+    public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when the current emergency number list has changed on the registered
+     * subscription.
+     *
+     * Note, the registered subscription is associated with {@link TelephonyManager} object
+     * on which {@link TelephonyManager#listen(PhoneStateListener, int)} was called.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * given subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @param emergencyNumberList Map associating all active subscriptions on the device with the
+     *                            list of emergency numbers originating from that subscription.
+     *                            If there are no active subscriptions, the map will contain a
+     *                            single entry with
+     *                            {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as
+     *                            the key and a list of emergency numbers as the value. If no
+     *                            emergency number information is available, the value will be null.
+     * @deprecated Use {@link TelephonyCallback.EmergencyNumberListListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public void onEmergencyNumberListChanged(
+            @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when an outgoing call is placed to an emergency number.
+     *
+     * This method will be called when an emergency call is placed on any subscription (including
+     * the no-SIM case), regardless of which subscription this listener was registered on.
+     *
+     * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was placed to.
+     * @deprecated Use {@link #onOutgoingEmergencyCall(EmergencyNumber, int)}.
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+    public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when an outgoing call is placed to an emergency number.
+     *
+     * This method will be called when an emergency call is placed on any subscription (including
+     * the no-SIM case), regardless of which subscription this listener was registered on.
+     *
+     * The default implementation of this method calls
+     * {@link #onOutgoingEmergencyCall(EmergencyNumber)} for backwards compatibility purposes. Do
+     * not call {@code super(...)} from within your implementation unless you want
+     * {@link #onOutgoingEmergencyCall(EmergencyNumber)} to be called as well.
+     *
+     * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was placed to.
+     * @param subscriptionId The subscription ID used to place the emergency call. If the
+     *                       emergency call was placed without a valid subscription (e.g. when there
+     *                       are no SIM cards in the device), this will be equal to
+     *                       {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.OutgoingEmergencyCallListener} instead.
+     */
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+    public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
+            int subscriptionId) {
+        // Default implementation for backwards compatibility
+        onOutgoingEmergencyCall(placedEmergencyNumber);
+    }
+
+    /**
+     * Callback invoked when an outgoing SMS is placed to an emergency number.
+     *
+     * This method will be called when an emergency sms is sent on any subscription.
+     * @param sentEmergencyNumber the emergency number {@link EmergencyNumber} the SMS is sent to.
+     *
+     * @deprecated Use {@link #onOutgoingEmergencySms(EmergencyNumber, int)}.
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.OutgoingEmergencySmsListener} instead.
+     */
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+    public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
+        // default implementation empty
+    }
+
+    /**
+     * Smsback invoked when an outgoing sms is sent to an emergency number.
+     *
+     * This method will be called when an emergency sms is sent on any subscription,
+     * regardless of which subscription this listener was registered on.
+     *
+     * The default implementation of this method calls
+     * {@link #onOutgoingEmergencySms(EmergencyNumber)} for backwards compatibility purposes. Do
+     * not call {@code super(...)} from within your implementation unless you want
+     * {@link #onOutgoingEmergencySms(EmergencyNumber)} to be called as well.
+     *
+     * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to.
+     * @param subscriptionId The subscription ID used to send the emergency sms.
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.OutgoingEmergencySmsListener} instead.
+     */
+    @SystemApi
+    @Deprecated
+    public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+            int subscriptionId) {
+        // Default implementation for backwards compatibility
+        onOutgoingEmergencySms(sentEmergencyNumber);
+    }
+
+    /**
+     * Callback invoked when OEM hook raw event is received on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     * @param rawData is the byte array of the OEM hook raw data.
+     * @hide
+     * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @Deprecated
+    public void onOemHookRawEvent(byte[] rawData) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when phone capability changes.
+     * Note, this callback triggers regardless of registered subscription.
+     *
+     * @param capability the new phone capability
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.PhoneCapabilityListener} instead.
+     */
+    @Deprecated
+    public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when active data subId changes.
+     * Note, this callback triggers regardless of registered subscription.
+     *
+     * Requires the READ_PHONE_STATE permission.
+     * @param subId current subscription used to setup Cellular Internet data.
+     *              For example, it could be the current active opportunistic subscription in use,
+     *              or the subscription user selected as default data subscription in DSDS mode.
+     * @deprecated Use {@link TelephonyCallback.ActiveDataSubscriptionIdListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public void onActiveDataSubscriptionIdChanged(int subId) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when the call attributes changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * Requires the READ_PRECISE_PHONE_STATE permission.
+     * @param callAttributes the call attributes
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.CallAttributesListener} instead.
+     */
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public void onCallAttributesChanged(@NonNull CallAttributes callAttributes) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when modem radio power state changes on the registered subscription.
+     * Note, the registration subId comes from {@link TelephonyManager} object which registers
+     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+     * If this TelephonyManager object was created with
+     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+     * subId. Otherwise, this callback applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @param state the modem radio power state
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.RadioPowerStateListener} instead.
+     */
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void onRadioPowerStateChanged(@RadioPowerState int state) {
+        // default implementation empty
+    }
+
+    /**
+     * Callback invoked when telephony has received notice from a carrier
+     * app that a network action that could result in connectivity loss
+     * has been requested by an app using
+     * {@link android.telephony.TelephonyManager#notifyCarrierNetworkChange(boolean)}
+     *
+     * Note, this callback is pinned to the registered subscription and will be invoked when
+     * the notifying carrier app has carrier privilege rule on the registered
+     * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges}
+     *
+     * @param active Whether the carrier network change is or shortly
+     *               will be active. This value is true to indicate
+     *               showing alternative UI and false to stop.
+     * @hide
+     * @deprecated Use {@link TelephonyCallback.CarrierNetworkListener} instead.
+     */
+    @Deprecated
+    public void onCarrierNetworkChange(boolean active) {
+        // default implementation empty
+    }
+
+    /**
+     * Report that Registration or a Location/Routing/Tracking Area update has failed.
+     *
+     * <p>Indicate whenever a registration procedure, including a location, routing, or tracking
+     * area update fails. This includes procedures that do not necessarily result in a change of
+     * the modem's registration status. If the modem's registration status changes, that is
+     * reflected in the onNetworkStateChanged() and subsequent get{Voice/Data}RegistrationState().
+     *
+     * <p>Because registration failures are ephemeral, this callback is not sticky.
+     * Registrants will not receive the most recent past value when registering.
+     *
+     * @param cellIdentity the CellIdentity, which must include the globally unique identifier
+     *        for the cell (for example, all components of the CGI or ECGI).
+     * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the
+     *         cell that was chosen for the failed registration attempt.
+     * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure.
+     * @param causeCode the primary failure cause code of the procedure.
+     *        For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
+     *        For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
+     *        For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+     *        For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
+     *        Integer.MAX_VALUE if this value is unused.
+     * @param additionalCauseCode the cause code of any secondary/combined procedure if appropriate.
+     *        For UMTS, if a combined attach succeeds for PS only, then the GMM cause code shall be
+     *        included as an additionalCauseCode. For LTE (ESM), cause codes are in
+     *        TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
+     * @deprecated Use {@link TelephonyCallback.RegistrationFailedListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
+    public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
+            int domain, int causeCode, int additionalCauseCode) {
+        // default implementation empty
+    }
+
+    /**
+     * Report updated barring information for the current camped/registered cell.
+     *
+     * <p>Barring info is provided for all services applicable to the current camped/registered
+     * cell, for the registered PLMN and current access class/access category.
+     *
+     * @param barringInfo for all services on the current cell.
+     * @see android.telephony.BarringInfo
+     * @deprecated Use {@link TelephonyCallback.BarringInfoListener} instead.
+     */
+    @Deprecated
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
+    public void onBarringInfoChanged(@NonNull BarringInfo barringInfo) {
+        // default implementation empty
+    }
+
+    /**
+     * The callback methods need to be called on the handler thread where
+     * this object was created.  If the binder did that for us it'd be nice.
+     *
+     * Using a static class and weak reference here to avoid memory leak caused by the
+     * IPhoneStateListener.Stub callback retaining references to the outside PhoneStateListeners:
+     * even caller has been destroyed and "un-registered" the PhoneStateListener, it is still not
+     * eligible for GC given the references coming from:
+     * Native Stack --> PhoneStateListener --> Context (Activity).
+     * memory of caller's context will be collected after GC from service side get triggered
+     */
+    private static class IPhoneStateListenerStub extends IPhoneStateListener.Stub {
+        private WeakReference<PhoneStateListener> mPhoneStateListenerWeakRef;
+        private Executor mExecutor;
+
+        IPhoneStateListenerStub(PhoneStateListener phoneStateListener, Executor executor) {
+            mPhoneStateListenerWeakRef = new WeakReference<PhoneStateListener>(phoneStateListener);
+            mExecutor = executor;
+        }
+
+        public void onServiceStateChanged(ServiceState serviceState) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onServiceStateChanged(serviceState)));
+        }
+
+        public void onSignalStrengthChanged(int asu) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onSignalStrengthChanged(asu)));
+        }
+
+        public void onMessageWaitingIndicatorChanged(boolean mwi) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onMessageWaitingIndicatorChanged(mwi)));
+        }
+
+        public void onCallForwardingIndicatorChanged(boolean cfi) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCallForwardingIndicatorChanged(cfi)));
+        }
+
+        public void onCellLocationChanged(CellIdentity cellIdentity) {
+            // There is no system/public API to create an CellIdentity in system server,
+            // so the server pass a null to indicate an empty initial location.
+            CellLocation location =
+                    cellIdentity == null ? CellLocation.getEmpty() : cellIdentity.asCellLocation();
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCellLocationChanged(location)));
+        }
+
+        public void onLegacyCallStateChanged(int state, String incomingNumber) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCallStateChanged(state, incomingNumber)));
+        }
+
+        public void onCallStateChanged(int state) {
+            // Only used for the new TelephonyCallback class
+        }
+
+        public void onDataConnectionStateChanged(int state, int networkType) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            if (state == TelephonyManager.DATA_DISCONNECTING
+                    && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) {
+                Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                        () -> {
+                            psl.onDataConnectionStateChanged(
+                                    TelephonyManager.DATA_CONNECTED, networkType);
+                            psl.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED);
+                        }));
+            } else {
+                Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                        () -> {
+                            psl.onDataConnectionStateChanged(state, networkType);
+                            psl.onDataConnectionStateChanged(state);
+                        }));
+            }
+        }
+
+        public void onDataActivity(int direction) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onDataActivity(direction)));
+        }
+
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onSignalStrengthsChanged(signalStrength)));
+        }
+
+        public void onCellInfoChanged(List<CellInfo> cellInfo) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCellInfoChanged(cellInfo)));
+        }
+
+        public void onPreciseCallStateChanged(PreciseCallState callState) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onPreciseCallStateChanged(callState)));
+        }
+
+        public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCallDisconnectCauseChanged(
+                            disconnectCause, preciseDisconnectCause)));
+        }
+
+        public void onPreciseDataConnectionStateChanged(
+                PreciseDataConnectionState dataConnectionState) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onPreciseDataConnectionStateChanged(dataConnectionState)));
+        }
+
+        public void onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo dcRtInfo) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onDataConnectionRealTimeInfoChanged(dcRtInfo)));
+        }
+
+        public void onSrvccStateChanged(int state) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onSrvccStateChanged(state)));
+        }
+
+        public void onVoiceActivationStateChanged(int activationState) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onVoiceActivationStateChanged(activationState)));
+        }
+
+        public void onDataActivationStateChanged(int activationState) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onDataActivationStateChanged(activationState)));
+        }
+
+        public void onUserMobileDataStateChanged(boolean enabled) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onUserMobileDataStateChanged(enabled)));
+        }
+
+        public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onDisplayInfoChanged(telephonyDisplayInfo)));
+        }
+
+        public void onOemHookRawEvent(byte[] rawData) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onOemHookRawEvent(rawData)));
+        }
+
+        public void onCarrierNetworkChange(boolean active) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCarrierNetworkChange(active)));
+        }
+
+        public void onEmergencyNumberListChanged(Map emergencyNumberList) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onEmergencyNumberListChanged(emergencyNumberList)));
+        }
+
+        public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
+                int subscriptionId) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onOutgoingEmergencyCall(placedEmergencyNumber,
+                                    subscriptionId)));
+        }
+
+        public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+                int subscriptionId) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onOutgoingEmergencySms(sentEmergencyNumber, subscriptionId)));
+        }
+
+        public void onPhoneCapabilityChanged(PhoneCapability capability) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onPhoneCapabilityChanged(capability)));
+        }
+
+        public void onRadioPowerStateChanged(@RadioPowerState int state) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onRadioPowerStateChanged(state)));
+        }
+
+        public void onCallStatesChanged(List<CallState> callStateList) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            if (callStateList == null) return;
+            CallAttributes ca;
+            if (callStateList.isEmpty()) {
+                ca = new CallAttributes(
+                        new PreciseCallState(PreciseCallState.PRECISE_CALL_STATE_IDLE,
+                                PreciseCallState.PRECISE_CALL_STATE_IDLE,
+                                PreciseCallState.PRECISE_CALL_STATE_IDLE,
+                                DisconnectCause.NOT_VALID, PreciseDisconnectCause.NOT_VALID),
+                        TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality());
+            } else {
+                int foregroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE;
+                int backgroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE;
+                int ringingCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE;
+                for (CallState cs : callStateList) {
+                    switch (cs.getCallClassification()) {
+                        case CallState.CALL_CLASSIFICATION_FOREGROUND:
+                            foregroundCallState = cs.getCallState();
+                            break;
+                        case CallState.CALL_CLASSIFICATION_BACKGROUND:
+                            backgroundCallState = cs.getCallState();
+                            break;
+                        case CallState.CALL_CLASSIFICATION_RINGING:
+                            ringingCallState = cs.getCallState();
+                            break;
+                        default:
+                            break;
+                    }
+                }
+                ca = new CallAttributes(
+                        new PreciseCallState(
+                                ringingCallState, foregroundCallState, backgroundCallState,
+                                DisconnectCause.NOT_VALID, PreciseDisconnectCause.NOT_VALID),
+                        callStateList.get(0).getNetworkType(),
+                        callStateList.get(0).getCallQuality());
+            }
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onCallAttributesChanged(ca)));
+        }
+
+        public void onActiveDataSubIdChanged(int subId) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onActiveDataSubscriptionIdChanged(subId)));
+        }
+
+        public void onImsCallDisconnectCauseChanged(ImsReasonInfo disconnectCause) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onImsCallDisconnectCauseChanged(disconnectCause)));
+        }
+
+        public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
+                @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onRegistrationFailed(
+                            cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
+            // default implementation empty
+        }
+
+        public void onBarringInfoChanged(BarringInfo barringInfo) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onBarringInfoChanged(barringInfo)));
+        }
+
+        public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) {
+            // default implementation empty
+        }
+
+        public void onDataEnabledChanged(boolean enabled, @DataEnabledReason int reason) {
+            // default implementation empty
+        }
+
+        public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) {
+            // default implementation empty
+        }
+
+        public void onLinkCapacityEstimateChanged(
+                List<LinkCapacityEstimate> linkCapacityEstimateList) {
+            // default implementation empty
+        }
+
+        public final void onMediaQualityStatusChanged(MediaQualityStatus mediaQualityStatus) {
+            // not support. Can't override. Use TelephonyCallback.
+        }
+
+        /** @hide */
+        public final void onCallBackModeStarted(
+                @TelephonyManager.EmergencyCallbackModeType int type) {
+            // not support. Can't override. Use TelephonyCallback.
+        }
+
+        /** @hide */
+        public final void onCallBackModeStopped(@EmergencyCallbackModeType int type,
+                @EmergencyCallbackModeStopReason int reason) {
+            // not support. Can't override. Use TelephonyCallback.
+        }
+
+        public final void onSimultaneousCallingStateChanged(int[] subIds) {
+            // not supported on the deprecated interface - Use TelephonyCallback instead
+        }
+
+        public final void onCarrierRoamingNtnModeChanged(boolean active) {
+            // not supported on the deprecated interface - Use TelephonyCallback instead
+        }
+    }
+
+    private void log(String s) {
+        Rlog.d(LOG_TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/PhysicalChannelConfig.java b/android-35/android/telephony/PhysicalChannelConfig.java
new file mode 100644
index 0000000..2c6d457
--- /dev/null
+++ b/android-35/android/telephony/PhysicalChannelConfig.java
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+
+public final class PhysicalChannelConfig implements Parcelable {
+    // TODO(b/72993578) consolidate these enums in a central location.
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({CONNECTION_PRIMARY_SERVING, CONNECTION_SECONDARY_SERVING, CONNECTION_UNKNOWN})
+    public @interface ConnectionStatus {}
+
+    /**
+     * UE has connection to cell for signalling and possibly data (3GPP 36.331, 25.331).
+     *
+     * @deprecated Use {@link  CellInfo#CONNECTION_PRIMARY_SERVING} instead.
+     */
+    @Deprecated
+    public static final int CONNECTION_PRIMARY_SERVING = 1;
+
+    /**
+     * UE has connection to cell for data (3GPP 36.331, 25.331).
+     *
+     * @deprecated Use {@link  CellInfo#CONNECTION_SECONDARY_SERVING} instead.
+     */
+    @Deprecated
+    public static final int CONNECTION_SECONDARY_SERVING = 2;
+
+    /**
+     * Connection status is unknown.
+     *
+     * @deprecated Use {@link  CellInfo#CONNECTION_UNKNOWN} instead.
+     */
+    @Deprecated
+    public static final int CONNECTION_UNKNOWN = -1;
+
+    /** Channel number is unknown. */
+    public static final int CHANNEL_NUMBER_UNKNOWN = Integer.MAX_VALUE;
+
+    /** Physical Cell Id is unknown. */
+    public static final int PHYSICAL_CELL_ID_UNKNOWN = -1;
+
+    /** Physical Cell Id's maximum value is 1007. */
+    public static final int PHYSICAL_CELL_ID_MAXIMUM_VALUE = 1007;
+
+    /** Cell bandwidth is unknown. */
+    public static final int CELL_BANDWIDTH_UNKNOWN = 0;
+
+    /** The frequency is unknown. */
+    public static final int FREQUENCY_UNKNOWN = -1;
+
+    /** The band is unknown. */
+    public static final int BAND_UNKNOWN = 0;
+
+    /**
+     * Connection status of the cell.
+     *
+     * <p>One of {@link #CONNECTION_PRIMARY_SERVING}, {@link #CONNECTION_SECONDARY_SERVING}.
+     */
+    @ConnectionStatus
+    private int mCellConnectionStatus;
+
+    /**
+     * Downlink cell bandwidth, in kHz.
+     */
+    private int mCellBandwidthDownlinkKhz;
+
+    /**
+     * Uplink cell bandwidth, in kHz.
+     */
+    private int mCellBandwidthUplinkKhz;
+
+    /**
+     * The radio technology for this physical channel.
+     */
+    @NetworkType
+    private int mNetworkType;
+
+    /**
+     * The rough frequency range for this physical channel.
+     */
+    @ServiceState.FrequencyRange
+    private int mFrequencyRange;
+
+    /**
+     * The frequency of Downlink.
+     */
+    private int mDownlinkFrequency;
+
+    /**
+     * The frequency of Uplink.
+     */
+    private int mUplinkFrequency;
+
+    /**
+     * Downlink Absolute Radio Frequency Channel Number
+     */
+    private int mDownlinkChannelNumber;
+
+    /**
+     * Uplink Absolute Radio Frequency Channel Number
+     */
+    private int mUplinkChannelNumber;
+
+    /**
+     * A list of data calls mapped to this physical channel. An empty list means the physical
+     * channel has no data call mapped to it.
+     */
+    private int[] mContextIds;
+
+    /**
+     * The physical cell identifier for this cell - PCI, PSC, {@link #PHYSICAL_CELL_ID_UNKNOWN}
+     * if unknown.
+     */
+    private int mPhysicalCellId;
+
+    /**
+     * This is the band which is being used.
+     */
+    private int mBand;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mCellConnectionStatus);
+        dest.writeInt(mCellBandwidthDownlinkKhz);
+        dest.writeInt(mCellBandwidthUplinkKhz);
+        dest.writeInt(mNetworkType);
+        dest.writeInt(mDownlinkChannelNumber);
+        dest.writeInt(mUplinkChannelNumber);
+        dest.writeInt(mFrequencyRange);
+        dest.writeIntArray(mContextIds);
+        dest.writeInt(mPhysicalCellId);
+        dest.writeInt(mBand);
+    }
+
+    /**
+     * @return Downlink cell bandwidth in kHz, {@link #CELL_BANDWIDTH_UNKNOWN} if unknown.
+     */
+    @IntRange(from = 1)
+    public int getCellBandwidthDownlinkKhz() {
+        return mCellBandwidthDownlinkKhz;
+    }
+
+    /**
+     * @return Uplink cell bandwidth in kHz, {@link #CELL_BANDWIDTH_UNKNOWN} if unknown.
+     */
+    @IntRange(from = 1)
+    public int getCellBandwidthUplinkKhz() {
+        return mCellBandwidthUplinkKhz;
+    }
+
+    /**
+     * Get the list of data call ids mapped to this physical channel. This list is sorted into
+     * ascending numerical order. Each id in this list must match the id in
+     * {@link com.android.internal.telephony.dataconnection.DataConnection}. An empty list means the
+     * physical channel has no data call mapped to it.
+     *
+     * @return an integer list indicates the data call ids,
+     * @hide
+     */
+    public int[] getContextIds() {
+        return mContextIds;
+    }
+
+    /**
+     * @return the rough frequency range for this physical channel,
+     * {@link ServiceState#FREQUENCY_RANGE_UNKNOWN} if unknown.
+     * @see {@link ServiceState#FREQUENCY_RANGE_LOW}
+     * @see {@link ServiceState#FREQUENCY_RANGE_MID}
+     * @see {@link ServiceState#FREQUENCY_RANGE_HIGH}
+     * @see {@link ServiceState#FREQUENCY_RANGE_MMWAVE}
+     * @hide
+     */
+    @ServiceState.FrequencyRange
+    public int getFrequencyRange() {
+        return mFrequencyRange;
+    }
+
+    /**
+     * @return Downlink Absolute Radio Frequency Channel Number,
+     * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown.
+     */
+    @IntRange(from = 0)
+    public int getDownlinkChannelNumber() {
+        return mDownlinkChannelNumber;
+    }
+
+    /**
+     * @return Uplink Absolute Radio Frequency Channel Number,
+     * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown.
+     */
+    @IntRange(from = 0)
+    public int getUplinkChannelNumber() {
+        return mUplinkChannelNumber;
+    }
+
+    /**
+     * The valid bands are {@link AccessNetworkConstants.GeranBand},
+     * {@link AccessNetworkConstants.UtranBand}, {@link AccessNetworkConstants.EutranBand} and
+     * {@link AccessNetworkConstants.NgranBands}.
+     *
+     * @return the frequency band, {@link #BAND_UNKNOWN} if unknown. */
+    @IntRange(from = 1, to = 261)
+    public int getBand() {
+        return mBand;
+    }
+
+    /**
+     * @return The downlink frequency in kHz, {@link #FREQUENCY_UNKNOWN} if unknown.
+     */
+    @IntRange(from = 0)
+    public int getDownlinkFrequencyKhz() {
+        return mDownlinkFrequency;
+    }
+
+    /**
+     * @return The uplink frequency in kHz, {@link #FREQUENCY_UNKNOWN} if unknown.
+     */
+    @IntRange(from = 0)
+    public int getUplinkFrequencyKhz() {
+        return mUplinkFrequency;
+    }
+
+    /**
+     * The physical cell ID which differentiates cells using the same radio channel.
+     *
+     * In GERAN, this value is the BSIC. The range is [0-63].
+     * Reference: 3GPP TS 3.03 section 4.2.2.
+     *
+     * In UTRAN, this value is primary scrambling code. The range is [0, 511].
+     * Reference: 3GPP TS 25.213 section 5.2.2.
+     *
+     * In EUTRAN, this value is physical layer cell identity. The range is [0, 503].
+     * Reference: 3GPP TS 36.211 section 6.11.
+     *
+     * In 5G RAN, this value is physical layer cell identity. The range is [0, 1007].
+     * Reference: 3GPP TS 38.211 section 7.4.2.1.
+     *
+     * @return the physical cell identifier for this cell, {@link #PHYSICAL_CELL_ID_UNKNOWN}
+     * if {@link android.telephony.CellInfo#UNAVAILABLE}.
+     */
+    @IntRange(from = 0, to = 1007)
+    public int getPhysicalCellId() {
+        return mPhysicalCellId;
+    }
+
+    /**
+     * @return The network type for this physical channel,
+     * {@link TelephonyManager#NETWORK_TYPE_UNKNOWN} if unknown.
+     */
+    @NetworkType
+    public int getNetworkType() {
+        return mNetworkType;
+    }
+
+    /**
+     * Gets the connection status of the cell.
+     *
+     * @see #CONNECTION_PRIMARY_SERVING
+     * @see #CONNECTION_SECONDARY_SERVING
+     * @see #CONNECTION_UNKNOWN
+     *
+     * @return Connection status of the cell, {@link #CONNECTION_UNKNOWN} if unknown.
+     */
+    @ConnectionStatus
+    public int getConnectionStatus() {
+        return mCellConnectionStatus;
+    }
+
+    /**
+     * Return a copy of this PhysicalChannelConfig object but redact all the location info.
+     * @hide
+     */
+    public PhysicalChannelConfig createLocationInfoSanitizedCopy() {
+        return new Builder(this).setPhysicalCellId(PHYSICAL_CELL_ID_UNKNOWN).build();
+    }
+
+    /**
+     * @return String representation of the connection status
+     * @hide
+     */
+    private String getConnectionStatusString() {
+        switch(mCellConnectionStatus) {
+            case CONNECTION_PRIMARY_SERVING:
+                return "PrimaryServing";
+            case CONNECTION_SECONDARY_SERVING:
+                return "SecondaryServing";
+            case CONNECTION_UNKNOWN:
+                return "Unknown";
+            default:
+                return "Invalid(" + mCellConnectionStatus + ")";
+        }
+    }
+
+    private void setDownlinkFrequency() {
+        switch (mNetworkType) {
+            case TelephonyManager.NETWORK_TYPE_NR:
+                mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromNrArfcn(
+                        mDownlinkChannelNumber);
+                break;
+            case TelephonyManager.NETWORK_TYPE_LTE:
+                mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromEarfcn(
+                        mBand, mDownlinkChannelNumber, false);
+                break;
+            case TelephonyManager.NETWORK_TYPE_HSPAP:
+            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+            case TelephonyManager.NETWORK_TYPE_UMTS:
+            case TelephonyManager.NETWORK_TYPE_HSDPA:
+            case TelephonyManager.NETWORK_TYPE_HSUPA:
+            case TelephonyManager.NETWORK_TYPE_HSPA:
+                mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromUarfcn(
+                        mBand, mDownlinkChannelNumber, false);
+                break;
+            case TelephonyManager.NETWORK_TYPE_GPRS:
+            case TelephonyManager.NETWORK_TYPE_EDGE:
+            case TelephonyManager.NETWORK_TYPE_GSM:
+                mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromArfcn(
+                        mBand, mDownlinkChannelNumber, false);
+                break;
+        }
+    }
+
+    private void setUplinkFrequency() {
+        switch (mNetworkType){
+            case TelephonyManager.NETWORK_TYPE_NR:
+                mUplinkFrequency = AccessNetworkUtils.getFrequencyFromNrArfcn(
+                        mUplinkChannelNumber);
+                break;
+            case TelephonyManager.NETWORK_TYPE_LTE:
+                mUplinkFrequency = AccessNetworkUtils.getFrequencyFromEarfcn(
+                        mBand, mUplinkChannelNumber, true);
+                break;
+            case TelephonyManager.NETWORK_TYPE_HSPAP:
+            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+            case TelephonyManager.NETWORK_TYPE_UMTS:
+            case TelephonyManager.NETWORK_TYPE_HSDPA:
+            case TelephonyManager.NETWORK_TYPE_HSUPA:
+            case TelephonyManager.NETWORK_TYPE_HSPA:
+                mUplinkFrequency = AccessNetworkUtils.getFrequencyFromUarfcn(
+                        mBand, mUplinkChannelNumber, true);
+                break;
+            case TelephonyManager.NETWORK_TYPE_GPRS:
+            case TelephonyManager.NETWORK_TYPE_EDGE:
+            case TelephonyManager.NETWORK_TYPE_GSM:
+                mUplinkFrequency = AccessNetworkUtils.getFrequencyFromArfcn(
+                        mBand, mUplinkChannelNumber, true);
+                break;
+        }
+    }
+
+    private void setFrequencyRange() {
+        if (mFrequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN) {
+            return;
+        }
+
+        switch (mNetworkType) {
+            case TelephonyManager.NETWORK_TYPE_NR:
+                mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromNrBand(mBand);
+                break;
+            case TelephonyManager.NETWORK_TYPE_LTE:
+                mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromEutranBand(mBand);
+                break;
+            case TelephonyManager.NETWORK_TYPE_HSPAP:
+            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+            case TelephonyManager.NETWORK_TYPE_UMTS:
+            case TelephonyManager.NETWORK_TYPE_HSDPA:
+            case TelephonyManager.NETWORK_TYPE_HSUPA:
+            case TelephonyManager.NETWORK_TYPE_HSPA:
+                mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromUtranBand(mBand);
+                break;
+            case TelephonyManager.NETWORK_TYPE_GPRS:
+            case TelephonyManager.NETWORK_TYPE_EDGE:
+            case TelephonyManager.NETWORK_TYPE_GSM:
+                mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromGeranBand(mBand);
+                break;
+            default:
+                mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN;
+                break;
+        }
+
+        if (mFrequencyRange == ServiceState.FREQUENCY_RANGE_UNKNOWN) {
+            mFrequencyRange = AccessNetworkUtils.getFrequencyRangeFromArfcn(
+                    mDownlinkFrequency);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof PhysicalChannelConfig)) {
+            return false;
+        }
+
+        PhysicalChannelConfig config = (PhysicalChannelConfig) o;
+        return mCellConnectionStatus == config.mCellConnectionStatus
+                && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz
+                && mCellBandwidthUplinkKhz == config.mCellBandwidthUplinkKhz
+                && mNetworkType == config.mNetworkType
+                && mFrequencyRange == config.mFrequencyRange
+                && mDownlinkChannelNumber == config.mDownlinkChannelNumber
+                && mUplinkChannelNumber == config.mUplinkChannelNumber
+                && mPhysicalCellId == config.mPhysicalCellId
+                && Arrays.equals(mContextIds, config.mContextIds)
+                && mBand == config.mBand
+                && mDownlinkFrequency == config.mDownlinkFrequency
+                && mUplinkFrequency == config.mUplinkFrequency;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mCellConnectionStatus, mCellBandwidthDownlinkKhz, mCellBandwidthUplinkKhz,
+                mNetworkType, mFrequencyRange, mDownlinkChannelNumber, mUplinkChannelNumber,
+                Arrays.hashCode(mContextIds), mPhysicalCellId, mBand, mDownlinkFrequency,
+                mUplinkFrequency);
+    }
+
+    public static final
+    @android.annotation.NonNull Parcelable.Creator<PhysicalChannelConfig> CREATOR =
+            new Parcelable.Creator<PhysicalChannelConfig>() {
+                public PhysicalChannelConfig createFromParcel(Parcel in) {
+                    return new PhysicalChannelConfig(in);
+                }
+
+                public PhysicalChannelConfig[] newArray(int size) {
+                    return new PhysicalChannelConfig[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+                .append("{mConnectionStatus=")
+                .append(getConnectionStatusString())
+                .append(",mCellBandwidthDownlinkKhz=")
+                .append(mCellBandwidthDownlinkKhz)
+                .append(",mCellBandwidthUplinkKhz=")
+                .append(mCellBandwidthUplinkKhz)
+                .append(",mNetworkType=")
+                .append(TelephonyManager.getNetworkTypeName(mNetworkType))
+                .append(",mFrequencyRange=")
+                .append(ServiceState.frequencyRangeToString(mFrequencyRange))
+                .append(",mDownlinkChannelNumber=")
+                .append(mDownlinkChannelNumber)
+                .append(",mUplinkChannelNumber=")
+                .append(mUplinkChannelNumber)
+                .append(",mContextIds=")
+                .append(Arrays.toString(mContextIds))
+                .append(",mPhysicalCellId=")
+                .append(mPhysicalCellId)
+                .append(",mBand=")
+                .append(mBand)
+                .append(",mDownlinkFrequency=")
+                .append(mDownlinkFrequency)
+                .append(",mUplinkFrequency=")
+                .append(mUplinkFrequency)
+                .append("}")
+                .toString();
+    }
+
+    private PhysicalChannelConfig(Parcel in) {
+        mCellConnectionStatus = in.readInt();
+        mCellBandwidthDownlinkKhz = in.readInt();
+        mCellBandwidthUplinkKhz = in.readInt();
+        mNetworkType = in.readInt();
+        mDownlinkChannelNumber = in.readInt();
+        mUplinkChannelNumber = in.readInt();
+        mFrequencyRange = in.readInt();
+        mContextIds = in.createIntArray();
+        mPhysicalCellId = in.readInt();
+        mBand = in.readInt();
+        if (mBand > BAND_UNKNOWN) {
+            setDownlinkFrequency();
+            setUplinkFrequency();
+            setFrequencyRange();
+        }
+    }
+
+    private PhysicalChannelConfig(Builder builder) {
+        mCellConnectionStatus = builder.mCellConnectionStatus;
+        mCellBandwidthDownlinkKhz = builder.mCellBandwidthDownlinkKhz;
+        mCellBandwidthUplinkKhz = builder.mCellBandwidthUplinkKhz;
+        mNetworkType = builder.mNetworkType;
+        mDownlinkChannelNumber = builder.mDownlinkChannelNumber;
+        mUplinkChannelNumber = builder.mUplinkChannelNumber;
+        mFrequencyRange = builder.mFrequencyRange;
+        mContextIds = builder.mContextIds;
+        mPhysicalCellId = builder.mPhysicalCellId;
+        mBand = builder.mBand;
+        if (mBand > BAND_UNKNOWN) {
+            setDownlinkFrequency();
+            setUplinkFrequency();
+            setFrequencyRange();
+        }
+    }
+
+    /**
+     * The builder of {@code PhysicalChannelConfig}.
+     * @hide
+     */
+    public static final class Builder {
+        private int mNetworkType;
+        private int mFrequencyRange;
+        private int mDownlinkChannelNumber;
+        private int mUplinkChannelNumber;
+        private int mCellBandwidthDownlinkKhz;
+        private int mCellBandwidthUplinkKhz;
+        private int mCellConnectionStatus;
+        private int[] mContextIds;
+        private int mPhysicalCellId;
+        private int mBand;
+
+        public Builder() {
+            mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+            mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN;
+            mDownlinkChannelNumber = CHANNEL_NUMBER_UNKNOWN;
+            mUplinkChannelNumber = CHANNEL_NUMBER_UNKNOWN;
+            mCellBandwidthDownlinkKhz = CELL_BANDWIDTH_UNKNOWN;
+            mCellBandwidthUplinkKhz = CELL_BANDWIDTH_UNKNOWN;
+            mCellConnectionStatus = CONNECTION_UNKNOWN;
+            mContextIds = new int[0];
+            mPhysicalCellId = PHYSICAL_CELL_ID_UNKNOWN;
+            mBand = BAND_UNKNOWN;
+        }
+
+        /**
+         * Builder object constructed from existing PhysicalChannelConfig object.
+         * @hide
+         */
+        public Builder(PhysicalChannelConfig config) {
+            mNetworkType = config.getNetworkType();
+            mFrequencyRange = config.getFrequencyRange();
+            mDownlinkChannelNumber = config.getDownlinkChannelNumber();
+            mUplinkChannelNumber = config.getUplinkChannelNumber();
+            mCellBandwidthDownlinkKhz = config.getCellBandwidthDownlinkKhz();
+            mCellBandwidthUplinkKhz = config.getCellBandwidthUplinkKhz();
+            mCellConnectionStatus = config.getConnectionStatus();
+            mContextIds = Arrays.copyOf(config.getContextIds(), config.getContextIds().length);
+            mPhysicalCellId = config.getPhysicalCellId();
+            mBand = config.getBand();
+        }
+
+        public PhysicalChannelConfig build() {
+            return new PhysicalChannelConfig(this);
+        }
+
+        public @NonNull Builder setNetworkType(@NetworkType int networkType) {
+            if (!TelephonyManager.isNetworkTypeValid(networkType)) {
+                throw new IllegalArgumentException("Network type " + networkType + " is invalid.");
+            }
+            mNetworkType = networkType;
+            return this;
+        }
+
+        public @NonNull Builder setFrequencyRange(int frequencyRange) {
+            if (!ServiceState.isFrequencyRangeValid(frequencyRange)
+                    && frequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN) {
+                throw new IllegalArgumentException("Frequency range " + frequencyRange
+                        + " is invalid.");
+            }
+            mFrequencyRange = frequencyRange;
+            return this;
+        }
+
+        public @NonNull Builder setDownlinkChannelNumber(int downlinkChannelNumber) {
+            mDownlinkChannelNumber = downlinkChannelNumber;
+            return this;
+        }
+
+        public @NonNull Builder setUplinkChannelNumber(int uplinkChannelNumber) {
+            mUplinkChannelNumber = uplinkChannelNumber;
+            return this;
+        }
+
+        public @NonNull Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) {
+            if (cellBandwidthDownlinkKhz < CELL_BANDWIDTH_UNKNOWN) {
+                throw new IllegalArgumentException("Cell downlink bandwidth(kHz) "
+                        + cellBandwidthDownlinkKhz + " is invalid.");
+            }
+            mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz;
+            return this;
+        }
+
+        public @NonNull Builder setCellBandwidthUplinkKhz(int cellBandwidthUplinkKhz) {
+            if (cellBandwidthUplinkKhz < CELL_BANDWIDTH_UNKNOWN) {
+                throw new IllegalArgumentException("Cell uplink bandwidth(kHz) "
+                        + cellBandwidthUplinkKhz + " is invalid.");
+            }
+            mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz;
+            return this;
+        }
+
+        public @NonNull Builder setCellConnectionStatus(int connectionStatus) {
+            mCellConnectionStatus = connectionStatus;
+            return this;
+        }
+
+        public @NonNull Builder setContextIds(int[] contextIds) {
+            if (contextIds != null) Arrays.sort(contextIds);
+            mContextIds = contextIds;
+            return this;
+        }
+
+        public @NonNull Builder setPhysicalCellId(int physicalCellId) {
+            if (physicalCellId > PHYSICAL_CELL_ID_MAXIMUM_VALUE) {
+                throw new IllegalArgumentException("Physical cell ID " + physicalCellId
+                        + " is over limit.");
+            }
+            mPhysicalCellId = physicalCellId;
+            return this;
+        }
+
+        public @NonNull Builder setBand(int band) {
+            if (band <= BAND_UNKNOWN) {
+                throw new IllegalArgumentException("Band " + band + " is invalid.");
+            }
+            mBand = band;
+            return this;
+        }
+    }
+}
diff --git a/android-35/android/telephony/PinResult.java b/android-35/android/telephony/PinResult.java
new file mode 100644
index 0000000..14713c7
--- /dev/null
+++ b/android-35/android/telephony/PinResult.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.PhoneConstants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Holds the result from a PIN attempt.
+ *
+ * @see TelephonyManager#supplyIccLockPin
+ * @see TelephonyManager#supplyIccLockPuk
+ * @see TelephonyManager#setIccLockEnabled
+ * @see TelephonyManager#changeIccLockPin
+ *
+ * @hide
+ */
+@SystemApi
+public final class PinResult implements Parcelable {
+    /** @hide */
+    @IntDef({
+            PIN_RESULT_TYPE_SUCCESS,
+            PIN_RESULT_TYPE_INCORRECT,
+            PIN_RESULT_TYPE_FAILURE,
+            PIN_RESULT_TYPE_ABORTED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PinResultType {}
+
+    /**
+     * Indicates that the pin attempt was a success.
+     */
+    public static final int PIN_RESULT_TYPE_SUCCESS = PhoneConstants.PIN_RESULT_SUCCESS;
+
+    /**
+     * Indicates that the pin attempt was incorrect.
+     */
+    public static final int PIN_RESULT_TYPE_INCORRECT = PhoneConstants.PIN_PASSWORD_INCORRECT;
+
+    /**
+     * Indicates that the pin attempt was a failure.
+     */
+    public static final int PIN_RESULT_TYPE_FAILURE = PhoneConstants.PIN_GENERAL_FAILURE;
+
+    /**
+     * Indicates that the pin attempt was aborted.
+     */
+    public static final int PIN_RESULT_TYPE_ABORTED = PhoneConstants.PIN_OPERATION_ABORTED;
+
+    private static final PinResult sFailedResult =
+            new PinResult(PinResult.PIN_RESULT_TYPE_FAILURE, -1);
+
+    private final @PinResultType int mResult;
+
+    private final int mAttemptsRemaining;
+
+    /**
+     * Returns the result of the PIN attempt.
+     *
+     * @return The result of the PIN attempt.
+     */
+    public @PinResultType int getResult() {
+        return mResult;
+    }
+
+    /**
+     * Returns the number of PIN attempts remaining.
+     * This will be set when {@link #getResult} is {@link #PIN_RESULT_TYPE_INCORRECT}.
+     * Indicates the number of attempts at entering the PIN before the SIM will be locked and
+     * require a PUK unlock to be performed.
+     *
+     * @return Number of attempts remaining.
+     */
+    public int getAttemptsRemaining() {
+        return mAttemptsRemaining;
+    }
+
+    /**
+     * Used to indicate a failed PIN attempt result.
+     *
+     * @return default PinResult for failure.
+     *
+     * @hide
+     */
+    @NonNull
+    public static PinResult getDefaultFailedResult() {
+        return sFailedResult;
+    }
+
+    /**
+     * PinResult constructor.
+     *
+     * @param result The pin result value.
+     * @see #PIN_RESULT_TYPE_SUCCESS
+     * @see #PIN_RESULT_TYPE_INCORRECT
+     * @see #PIN_RESULT_TYPE_FAILURE
+     * @see #PIN_RESULT_TYPE_ABORTED
+     * @param attemptsRemaining Number of pin attempts remaining.
+     *
+     * @hide
+     */
+    public PinResult(@PinResultType int result, int attemptsRemaining) {
+        mResult = result;
+        mAttemptsRemaining = attemptsRemaining;
+    }
+
+    /**
+     * Construct a PinResult object from the given parcel.
+     *
+     * @hide
+     */
+    private PinResult(Parcel in) {
+        mResult = in.readInt();
+        mAttemptsRemaining = in.readInt();
+    }
+
+    /**
+     * String representation of the Pin Result.
+     */
+    @NonNull
+    @Override
+    public String toString() {
+        return "result: " + getResult() + ", attempts remaining: " + getAttemptsRemaining();
+    }
+
+    /**
+     * Describe the contents of this object.
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Write this object to a Parcel.
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mResult);
+        out.writeInt(mAttemptsRemaining);
+    }
+
+    /**
+     * Parcel creator class.
+     */
+    public static final @NonNull Parcelable.Creator<PinResult> CREATOR = new Creator<PinResult>() {
+        public PinResult createFromParcel(Parcel in) {
+            return new PinResult(in);
+        }
+        public PinResult[] newArray(int size) {
+            return new PinResult[size];
+        }
+    };
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAttemptsRemaining, mResult);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        PinResult other = (PinResult) obj;
+        return (mResult == other.mResult
+                && mAttemptsRemaining == other.mAttemptsRemaining);
+    }
+}
diff --git a/android-35/android/telephony/PreciseCallState.java b/android-35/android/telephony/PreciseCallState.java
new file mode 100644
index 0000000..f433609
--- /dev/null
+++ b/android-35/android/telephony/PreciseCallState.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.DisconnectCauses;
+import android.telephony.Annotation.PreciseCallStates;
+import android.telephony.Annotation.PreciseDisconnectCauses;
+
+import java.util.Objects;
+
+/**
+ * Contains precise call states.
+ *
+ * The following call information is included in returned PreciseCallState:
+ *
+ * <ul>
+ *   <li>Precise ringing call state.
+ *   <li>Precise foreground call state.
+ *   <li>Precise background call state.
+ * </ul>
+ *
+ * @see android.telephony.Annotation.CallState which contains generic call states.
+ *
+ * @hide
+ */
+@SystemApi
+public final class PreciseCallState implements Parcelable {
+
+    /** Call state is not valid (Not received a call state). */
+    public static final int PRECISE_CALL_STATE_NOT_VALID =      -1;
+    /** Call state: No activity. */
+    public static final int PRECISE_CALL_STATE_IDLE =           0;
+    /** Call state: Active. */
+    public static final int PRECISE_CALL_STATE_ACTIVE =         1;
+    /** Call state: On hold. */
+    public static final int PRECISE_CALL_STATE_HOLDING =        2;
+    /** Call state: Dialing. */
+    public static final int PRECISE_CALL_STATE_DIALING =        3;
+    /** Call state: Alerting. */
+    public static final int PRECISE_CALL_STATE_ALERTING =       4;
+    /** Call state: Incoming. */
+    public static final int PRECISE_CALL_STATE_INCOMING =       5;
+    /** Call state: Waiting. */
+    public static final int PRECISE_CALL_STATE_WAITING =        6;
+    /** Call state: Disconnected. */
+    public static final int PRECISE_CALL_STATE_DISCONNECTED =   7;
+    /** Call state: Disconnecting. */
+    public static final int PRECISE_CALL_STATE_DISCONNECTING =  8;
+    /**
+     * Call state: Incoming in pre-alerting state i.e. prior to entering
+     * {@link #PRECISE_CALL_STATE_INCOMING}.
+     */
+    public static final int PRECISE_CALL_STATE_INCOMING_SETUP = 9;
+
+    private @PreciseCallStates int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
+    private @PreciseCallStates int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+    private @PreciseCallStates int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+    private @DisconnectCauses int mDisconnectCause = DisconnectCause.NOT_VALID;
+    private @PreciseDisconnectCauses int mPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
+
+    /**
+     * Construct PreciseCallState with parameters
+     *
+     * @param ringingCall ring call state
+     * @param foregroundCall foreground call state
+     * @param backgroundCall background call state
+     * @param disconnectCause disconnect cause
+     * @param preciseDisconnectCause precise disconnect cause
+     *
+     * @hide
+     */
+    @SystemApi
+    public PreciseCallState(@PreciseCallStates int ringingCall,
+                            @PreciseCallStates int foregroundCall,
+                            @PreciseCallStates int backgroundCall,
+                            @DisconnectCauses int disconnectCause,
+                            @PreciseDisconnectCauses int preciseDisconnectCause) {
+        mRingingCallState = ringingCall;
+        mForegroundCallState = foregroundCall;
+        mBackgroundCallState = backgroundCall;
+        mDisconnectCause = disconnectCause;
+        mPreciseDisconnectCause = preciseDisconnectCause;
+    }
+
+    /**
+     * Empty Constructor
+     *
+     * @hide
+     */
+    public PreciseCallState() {
+    }
+
+    /**
+     * Construct a PreciseCallState object from the given parcel.
+     *
+     * @hide
+     */
+    private PreciseCallState(Parcel in) {
+        mRingingCallState = in.readInt();
+        mForegroundCallState = in.readInt();
+        mBackgroundCallState = in.readInt();
+        mDisconnectCause = in.readInt();
+        mPreciseDisconnectCause = in.readInt();
+    }
+
+    /**
+     * Returns the precise ringing call state.
+     */
+    public @PreciseCallStates int getRingingCallState() {
+        return mRingingCallState;
+    }
+
+    /**
+     * Returns the precise foreground call state.
+     */
+    public @PreciseCallStates int getForegroundCallState() {
+        return mForegroundCallState;
+    }
+
+    /**
+     * Returns the precise background call state.
+     */
+    public @PreciseCallStates int getBackgroundCallState() {
+        return mBackgroundCallState;
+    }
+
+    /**
+     * Get disconnect cause generated by the framework
+     *
+     * @see DisconnectCause#NOT_VALID
+     * @see DisconnectCause#NOT_DISCONNECTED
+     * @see DisconnectCause#INCOMING_MISSED
+     * @see DisconnectCause#NORMAL
+     * @see DisconnectCause#LOCAL
+     * @see DisconnectCause#BUSY
+     * @see DisconnectCause#CONGESTION
+     * @see DisconnectCause#MMI
+     * @see DisconnectCause#INVALID_NUMBER
+     * @see DisconnectCause#NUMBER_UNREACHABLE
+     * @see DisconnectCause#SERVER_UNREACHABLE
+     * @see DisconnectCause#INVALID_CREDENTIALS
+     * @see DisconnectCause#OUT_OF_NETWORK
+     * @see DisconnectCause#SERVER_ERROR
+     * @see DisconnectCause#TIMED_OUT
+     * @see DisconnectCause#LOST_SIGNAL
+     * @see DisconnectCause#LIMIT_EXCEEDED
+     * @see DisconnectCause#INCOMING_REJECTED
+     * @see DisconnectCause#POWER_OFF
+     * @see DisconnectCause#OUT_OF_SERVICE
+     * @see DisconnectCause#ICC_ERROR
+     * @see DisconnectCause#CALL_BARRED
+     * @see DisconnectCause#FDN_BLOCKED
+     * @see DisconnectCause#CS_RESTRICTED
+     * @see DisconnectCause#CS_RESTRICTED_NORMAL
+     * @see DisconnectCause#CS_RESTRICTED_EMERGENCY
+     * @see DisconnectCause#UNOBTAINABLE_NUMBER
+     * @see DisconnectCause#CDMA_LOCKED_UNTIL_POWER_CYCLE
+     * @see DisconnectCause#CDMA_DROP
+     * @see DisconnectCause#CDMA_INTERCEPT
+     * @see DisconnectCause#CDMA_REORDER
+     * @see DisconnectCause#CDMA_SO_REJECT
+     * @see DisconnectCause#CDMA_RETRY_ORDER
+     * @see DisconnectCause#CDMA_ACCESS_FAILURE
+     * @see DisconnectCause#CDMA_PREEMPTED
+     * @see DisconnectCause#CDMA_NOT_EMERGENCY
+     * @see DisconnectCause#CDMA_ACCESS_BLOCKED
+     * @see DisconnectCause#ERROR_UNSPECIFIED
+     *
+     * TODO: remove disconnect cause from preciseCallState as there is no link between random
+     * connection disconnect cause with foreground, background or ringing call.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getDisconnectCause() {
+        return mDisconnectCause;
+    }
+
+    /**
+     * Get disconnect cause generated by the RIL
+     *
+     * @see PreciseDisconnectCause#NOT_VALID
+     * @see PreciseDisconnectCause#NO_DISCONNECT_CAUSE_AVAILABLE
+     * @see PreciseDisconnectCause#UNOBTAINABLE_NUMBER
+     * @see PreciseDisconnectCause#NORMAL
+     * @see PreciseDisconnectCause#BUSY
+     * @see PreciseDisconnectCause#NUMBER_CHANGED
+     * @see PreciseDisconnectCause#STATUS_ENQUIRY
+     * @see PreciseDisconnectCause#NORMAL_UNSPECIFIED
+     * @see PreciseDisconnectCause#NO_CIRCUIT_AVAIL
+     * @see PreciseDisconnectCause#TEMPORARY_FAILURE
+     * @see PreciseDisconnectCause#SWITCHING_CONGESTION
+     * @see PreciseDisconnectCause#CHANNEL_NOT_AVAIL
+     * @see PreciseDisconnectCause#QOS_NOT_AVAIL
+     * @see PreciseDisconnectCause#BEARER_NOT_AVAIL
+     * @see PreciseDisconnectCause#ACM_LIMIT_EXCEEDED
+     * @see PreciseDisconnectCause#CALL_BARRED
+     * @see PreciseDisconnectCause#FDN_BLOCKED
+     * @see PreciseDisconnectCause#IMSI_UNKNOWN_IN_VLR
+     * @see PreciseDisconnectCause#IMEI_NOT_ACCEPTED
+     * @see PreciseDisconnectCause#CDMA_LOCKED_UNTIL_POWER_CYCLE
+     * @see PreciseDisconnectCause#CDMA_DROP
+     * @see PreciseDisconnectCause#CDMA_INTERCEPT
+     * @see PreciseDisconnectCause#CDMA_REORDER
+     * @see PreciseDisconnectCause#CDMA_SO_REJECT
+     * @see PreciseDisconnectCause#CDMA_RETRY_ORDER
+     * @see PreciseDisconnectCause#CDMA_ACCESS_FAILURE
+     * @see PreciseDisconnectCause#CDMA_PREEMPTED
+     * @see PreciseDisconnectCause#CDMA_NOT_EMERGENCY
+     * @see PreciseDisconnectCause#CDMA_ACCESS_BLOCKED
+     * @see PreciseDisconnectCause#ERROR_UNSPECIFIED
+     *
+     * TODO: remove precise disconnect cause from preciseCallState as there is no link between
+     * random connection disconnect cause with foreground, background or ringing call.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getPreciseDisconnectCause() {
+        return mPreciseDisconnectCause;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mRingingCallState);
+        out.writeInt(mForegroundCallState);
+        out.writeInt(mBackgroundCallState);
+        out.writeInt(mDisconnectCause);
+        out.writeInt(mPreciseDisconnectCause);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<PreciseCallState> CREATOR
+            = new Parcelable.Creator<PreciseCallState>() {
+
+        public PreciseCallState createFromParcel(Parcel in) {
+            return new PreciseCallState(in);
+        }
+
+        public PreciseCallState[] newArray(int size) {
+            return new PreciseCallState[size];
+        }
+    };
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRingingCallState, mForegroundCallState, mForegroundCallState,
+                mDisconnectCause, mPreciseDisconnectCause);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        PreciseCallState other = (PreciseCallState) obj;
+        return (mRingingCallState == other.mRingingCallState
+                && mForegroundCallState == other.mForegroundCallState
+                && mBackgroundCallState == other.mBackgroundCallState
+                && mDisconnectCause == other.mDisconnectCause
+                && mPreciseDisconnectCause == other.mPreciseDisconnectCause);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("Ringing call state: " + mRingingCallState);
+        sb.append(", Foreground call state: " + mForegroundCallState);
+        sb.append(", Background call state: " + mBackgroundCallState);
+        sb.append(", Disconnect cause: " + mDisconnectCause);
+        sb.append(", Precise disconnect cause: " + mPreciseDisconnectCause);
+
+        return sb.toString();
+    }
+}
diff --git a/android-35/android/telephony/PreciseDataConnectionState.java b/android-35/android/telephony/PreciseDataConnectionState.java
new file mode 100644
index 0000000..e6515f1
--- /dev/null
+++ b/android-35/android/telephony/PreciseDataConnectionState.java
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.net.LinkProperties;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.DataFailureCause;
+import android.telephony.Annotation.DataState;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.DataCallResponse;
+import android.telephony.data.Qos;
+
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+
+/**
+ * Contains precise data connection state.
+ *
+ * The following data connection information is included in returned PreciseDataConnectionState:
+ *
+ * <ul>
+ *   <li>Data connection state.
+ *   <li>Network type of the connection.
+ *   <li>APN types.
+ *   <li>APN.
+ *   <li>The properties of the network link.
+ *   <li>Data connection fail cause.
+ * </ul>
+ *
+ */
+public final class PreciseDataConnectionState implements Parcelable {
+    private final @TransportType int mTransportType;
+    private final int mId;
+    private final int mNetId;
+    private final @DataState int mState;
+    private final @NetworkType int mNetworkType;
+    private final @DataFailureCause int mFailCause;
+    private final LinkProperties mLinkProperties;
+    private final ApnSetting mApnSetting;
+    private final Qos mDefaultQos;
+    private final @NetworkValidationStatus int mNetworkValidationStatus;
+
+    /** @hide */
+    @IntDef(prefix = "NETWORK_VALIDATION_", value = {
+            NETWORK_VALIDATION_UNSUPPORTED,
+            NETWORK_VALIDATION_NOT_REQUESTED,
+            NETWORK_VALIDATION_IN_PROGRESS,
+            NETWORK_VALIDATION_SUCCESS,
+            NETWORK_VALIDATION_FAILURE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NetworkValidationStatus {}
+
+    /**
+     * Unsupported. The unsupported state is used when the data network cannot support the network
+     * validation function for the current data connection state.
+     */
+    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+    public static final int NETWORK_VALIDATION_UNSUPPORTED = 0;
+
+    /**
+     * Not Requested. The not requested status is used when the data network supports the network
+     * validation function, but no network validation is being performed yet.
+     */
+    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+    public static final int NETWORK_VALIDATION_NOT_REQUESTED = 1;
+
+    /**
+     * In progress. The in progress state is used when the network validation process for the data
+     * network is in progress. This state is followed by either success or failure.
+     */
+    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+    public static final int NETWORK_VALIDATION_IN_PROGRESS = 2;
+
+    /**
+     * Success. The Success status is used when network validation has been completed for the data
+     * network and the result is successful.
+     */
+    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+    public static final int NETWORK_VALIDATION_SUCCESS = 3;
+
+    /**
+     * Failure. The Failure status is used when network validation has been completed for the data
+     * network and the result is failure.
+     */
+    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+    public static final int NETWORK_VALIDATION_FAILURE = 4;
+
+    /**
+     * Constructor
+     *
+     * @deprecated this constructor has been superseded and should not be used.
+     * @hide
+     */
+    @TestApi
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) // (maxTargetSdk = Build.VERSION_CODES.Q)
+    // FIXME: figure out how to remove the UnsupportedAppUsage and delete this constructor
+    public PreciseDataConnectionState(@DataState int state,
+                                      @NetworkType int networkType,
+                                      @ApnType int apnTypes, @NonNull String apn,
+                                      @Nullable LinkProperties linkProperties,
+                                      @DataFailureCause int failCause) {
+        this(AccessNetworkConstants.TRANSPORT_TYPE_INVALID, -1, -1, state, networkType,
+                linkProperties, failCause, new ApnSetting.Builder()
+                        .setApnTypeBitmask(apnTypes)
+                        .setApnName(apn)
+                        .setEntryName(apn)
+                        .build(), null, NETWORK_VALIDATION_UNSUPPORTED);
+    }
+
+
+    /**
+     * Constructor of PreciseDataConnectionState
+     *
+     * @param transportType The transport of the data connection
+     * @param id The id of the data connection
+     * @param state The state of the data connection
+     * @param networkType The access network that is/would carry this data connection
+     * @param linkProperties If the data connection is connected, the properties of the connection
+     * @param failCause In case a procedure related to this data connection fails, a non-zero error
+     *        code indicating the cause of the failure.
+     * @param apnSetting If there is a valid APN for this Data Connection, then the APN Settings;
+     *        if there is no valid APN setting for the specific type, then this will be null
+     * @param defaultQos If there is a valid QoS for the default bearer supporting this data call,
+     *        (supported for LTE and NR), then this is specified. Otherwise it should be null.
+     */
+    private PreciseDataConnectionState(@TransportType int transportType, int id, int netId,
+            @DataState int state, @NetworkType int networkType,
+            @Nullable LinkProperties linkProperties, @DataFailureCause int failCause,
+            @Nullable ApnSetting apnSetting, @Nullable Qos defaultQos,
+            @NetworkValidationStatus int networkValidationStatus) {
+        mTransportType = transportType;
+        mId = id;
+        mNetId = netId;
+        mState = state;
+        mNetworkType = networkType;
+        mLinkProperties = linkProperties;
+        mFailCause = failCause;
+        mApnSetting = apnSetting;
+        mDefaultQos = defaultQos;
+        mNetworkValidationStatus = networkValidationStatus;
+    }
+
+    /**
+     * Construct a PreciseDataConnectionState object from the given parcel.
+     *
+     * @hide
+     */
+    private PreciseDataConnectionState(Parcel in) {
+        mTransportType = in.readInt();
+        mId = in.readInt();
+        mNetId = in.readInt();
+        mState = in.readInt();
+        mNetworkType = in.readInt();
+        mLinkProperties = in.readParcelable(
+                LinkProperties.class.getClassLoader(),
+                android.net.LinkProperties.class);
+        mFailCause = in.readInt();
+        mApnSetting = in.readParcelable(
+                ApnSetting.class.getClassLoader(),
+                android.telephony.data.ApnSetting.class);
+        mDefaultQos = in.readParcelable(
+                Qos.class.getClassLoader(),
+                android.telephony.data.Qos.class);
+        mNetworkValidationStatus = in.readInt();
+    }
+
+    /**
+     * Used for checking if the SDK version for
+     * {@code PreciseDataConnectionState#getDataConnectionState} is above Q.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    private static final long GET_DATA_CONNECTION_STATE_R_VERSION = 148535736L;
+
+    /**
+     * Returns the state of data connection that supported the apn types returned by
+     * {@link #getDataConnectionApnTypeBitMask()}
+     *
+     * @deprecated use {@link #getState()}
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    public @DataState int getDataConnectionState() {
+        if (mState == TelephonyManager.DATA_DISCONNECTING
+                && !Compatibility.isChangeEnabled(GET_DATA_CONNECTION_STATE_R_VERSION)) {
+            return TelephonyManager.DATA_CONNECTED;
+        }
+
+        return mState;
+    }
+
+    /**
+     * @return The transport type of this data connection.
+     */
+    public @TransportType int getTransportType() {
+        return mTransportType;
+    }
+
+    /**
+     * @return The unique id of the data connection
+     *
+     * Note this is the id assigned by the data service.
+     * The id remains the same for data connection handover between
+     * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN} and
+     * {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN}
+     *
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * @return the current TelephonyNetworkAgent ID. {@code -1} if no network agent.
+     * @hide
+     */
+    public int getNetId() {
+        return mNetId;
+    }
+
+    /**
+     * @return The high-level state of this data connection.
+     */
+    public @DataState int getState() {
+        return mState;
+    }
+
+    /**
+     * Get the network type associated with this data connection.
+     *
+     * @return The current/latest (radio) bearer technology that carries this data connection.
+     * For a variety of reasons, the network type can change during the life of the data
+     * connection, and this information is not reliable unless the physical link is currently
+     * active; (there is currently no mechanism to know whether the physical link is active at
+     * any given moment). Thus, this value is generally correct but may not be relied-upon to
+     * represent the status of the radio bearer at any given moment.
+     */
+    public @NetworkType int getNetworkType() {
+        return mNetworkType;
+    }
+
+    /**
+     * Returns the APN types mapped to this data connection.
+     *
+     * @deprecated use {@link #getApnSetting()}
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    public @ApnType int getDataConnectionApnTypeBitMask() {
+        return (mApnSetting != null) ? mApnSetting.getApnTypeBitmask() : ApnSetting.TYPE_NONE;
+    }
+
+    /**
+     * Returns APN of this data connection.
+     *
+     * @deprecated use {@link #getApnSetting()}
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @Deprecated
+    public String getDataConnectionApn() {
+        return (mApnSetting != null) ? mApnSetting.getApnName() : "";
+    }
+
+    /**
+     * Get the properties of the network link {@link LinkProperties}.
+     */
+    @Nullable
+    public LinkProperties getLinkProperties() {
+        return mLinkProperties;
+    }
+
+    /**
+     * Returns the cause code generated by the most recent state change.
+     *
+     * @deprecated use {@link #getLastCauseCode()}
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    public int getDataConnectionFailCause() {
+        return mFailCause;
+    }
+
+    /**
+     * Returns the cause code generated by the most recent state change.
+     *
+     * Return the cause code for the most recent change in {@link #getState}. In the event of an
+     * error, this cause code will be non-zero.
+     */
+    public @DataFailureCause int getLastCauseCode() {
+        return mFailCause;
+    }
+
+    /**
+     * Return the APN Settings for this data connection.
+     *
+     * @return the ApnSetting that was used to configure this data connection. Note that a data
+     * connection cannot be established without a valid {@link ApnSetting}. The return value would
+     * never be {@code null} even though it has {@link Nullable} annotation.
+     */
+    public @Nullable ApnSetting getApnSetting() {
+        return mApnSetting;
+    }
+
+    /**
+     * Return the QoS for the default bearer of this data connection.
+     *
+     * @return the default QoS if known or {@code null} if it is unknown. If the value is reported
+     * for LTE, then it will be an {@link android.telephony.data.EpsQos EpsQos}. If the value is
+     * reported for 5G, then it will be an {@link android.telehpony.data.NrQos NrQos}. Otherwise it
+     * shall always be {@code null}.
+     *
+     * @hide
+     */
+    public @Nullable Qos getDefaultQos() {
+        return mDefaultQos;
+    }
+
+    /**
+     * Returns the network validation state.
+     *
+     * @return the network validation status of the data call
+     */
+    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+    public @NetworkValidationStatus int getNetworkValidationStatus() {
+        return mNetworkValidationStatus;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mTransportType);
+        out.writeInt(mId);
+        out.writeInt(mNetId);
+        out.writeInt(mState);
+        out.writeInt(mNetworkType);
+        out.writeParcelable(mLinkProperties, flags);
+        out.writeInt(mFailCause);
+        out.writeParcelable(mApnSetting, flags);
+        out.writeParcelable(mDefaultQos, flags);
+        out.writeInt(mNetworkValidationStatus);
+    }
+
+    public static final @NonNull Parcelable.Creator<PreciseDataConnectionState> CREATOR
+            = new Parcelable.Creator<PreciseDataConnectionState>() {
+
+        public PreciseDataConnectionState createFromParcel(Parcel in) {
+            return new PreciseDataConnectionState(in);
+        }
+
+        public PreciseDataConnectionState[] newArray(int size) {
+            return new PreciseDataConnectionState[size];
+        }
+    };
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTransportType, mId, mNetId, mState, mNetworkType, mFailCause,
+                mLinkProperties, mApnSetting, mDefaultQos, mNetworkValidationStatus);
+    }
+
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PreciseDataConnectionState that = (PreciseDataConnectionState) o;
+        return mTransportType == that.mTransportType
+                && mId == that.mId
+                && mNetId == that.mNetId
+                && mState == that.mState
+                && mNetworkType == that.mNetworkType
+                && mFailCause == that.mFailCause
+                && Objects.equals(mLinkProperties, that.mLinkProperties)
+                && Objects.equals(mApnSetting, that.mApnSetting)
+                && Objects.equals(mDefaultQos, that.mDefaultQos)
+                && mNetworkValidationStatus == that.mNetworkValidationStatus;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(" state: ").append(TelephonyUtils.dataStateToString(mState));
+        sb.append(", transport: ").append(
+                AccessNetworkConstants.transportTypeToString(mTransportType));
+        sb.append(", id: ").append(mId);
+        sb.append(", netId: ").append(mNetId);
+        sb.append(", network type: ").append(TelephonyManager.getNetworkTypeName(mNetworkType));
+        sb.append(", APN Setting: ").append(mApnSetting);
+        sb.append(", link properties: ").append(mLinkProperties);
+        sb.append(", default QoS: ").append(mDefaultQos);
+        sb.append(", fail cause: ").append(DataFailCause.toString(mFailCause));
+        sb.append(", network validation status: ").append(
+                networkValidationStatusToString(mNetworkValidationStatus));
+
+        return sb.toString();
+    }
+
+    /**
+     * Convert a network validation status to string.
+     *
+     * @param networkValidationStatus network validation status.
+     * @return string of validation status.
+     *
+     * @hide
+     */
+    @NonNull
+    public static String networkValidationStatusToString(
+            @NetworkValidationStatus int networkValidationStatus) {
+        switch (networkValidationStatus) {
+            case NETWORK_VALIDATION_UNSUPPORTED: return "unsupported";
+            case NETWORK_VALIDATION_NOT_REQUESTED: return "not requested";
+            case NETWORK_VALIDATION_IN_PROGRESS: return "in progress";
+            case NETWORK_VALIDATION_SUCCESS: return "success";
+            case NETWORK_VALIDATION_FAILURE: return "failure";
+            default: return Integer.toString(networkValidationStatus);
+        }
+    }
+
+    /**
+     * {@link PreciseDataConnectionState} builder
+     *
+     * @hide
+     */
+    public static final class Builder {
+        /** The transport type of the data connection */
+        private @TransportType int mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+
+        /**
+         * The unique ID of the data connection. This is the id assigned in
+         * {@link DataCallResponse)}.
+         */
+        private int mId = -1;
+
+        /**
+         * The current TelephonyNetworkAgent ID. {@code -1} if no network agent.
+         */
+        private int mNetworkAgentId = -1;
+
+        /** The state of the data connection */
+        private @DataState int mState = TelephonyManager.DATA_UNKNOWN;
+
+        /** The network type associated with this data connection */
+        private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
+        /** If the data connection is connected, the properties of the connection */
+        private @Nullable LinkProperties mLinkProperties;
+
+        /**
+         * In case a procedure related to this data connection fails, a non-zero error code
+         * indicating the cause of the failure.
+         */
+        private @DataFailureCause int mFailCause = DataFailCause.NONE;
+
+        /** The APN Setting for this data connection */
+        private @Nullable ApnSetting mApnSetting;
+
+        /** The Default QoS for this EPS/5GS bearer or null otherwise */
+        private @Nullable Qos mDefaultQos;
+
+        /** The network validation status for the data connection. */
+        private @NetworkValidationStatus int mNetworkValidationStatus =
+                NETWORK_VALIDATION_UNSUPPORTED;
+
+        /**
+         * Set the transport type of the data connection.
+         *
+         * @param transportType The transport type of the data connection
+         * @return The builder
+         */
+        public @NonNull Builder setTransportType(@TransportType int transportType) {
+            mTransportType = transportType;
+            return this;
+        }
+
+        /**
+         * Set the id of the data connection.
+         *
+         * @param id The id of the data connection
+         * @return The builder
+         */
+        public @NonNull Builder setId(int id) {
+            mId = id;
+            return this;
+        }
+
+        /**
+         * Set the id of the data connection.
+         *
+         * @param agentId The id of the data connection
+         * @return The builder
+         */
+        public @NonNull Builder setNetworkAgentId(int agentId) {
+            mNetworkAgentId = agentId;
+            return this;
+        }
+
+        /**
+         * Set the state of the data connection.
+         *
+         * @param state The state of the data connection
+         * @return The builder
+         */
+        public @NonNull Builder setState(@DataState int state) {
+            mState = state;
+            return this;
+        }
+
+        /**
+         * Set the network type associated with this data connection.
+         *
+         * @param networkType The network type
+         * @return The builder
+         */
+        public @NonNull Builder setNetworkType(@NetworkType int networkType) {
+            mNetworkType = networkType;
+            return this;
+        }
+
+        /**
+         * Set the link properties of the connection.
+         *
+         * @param linkProperties Link properties
+         * @return The builder
+         */
+        public @NonNull Builder setLinkProperties(LinkProperties linkProperties) {
+            mLinkProperties = linkProperties;
+            return this;
+        }
+
+        /**
+         * Set the fail cause of the data connection.
+         *
+         * @param failCause In case a procedure related to this data connection fails, a non-zero
+         * error code indicating the cause of the failure.
+         * @return The builder
+         */
+        public @NonNull Builder setFailCause(@DataFailureCause int failCause) {
+            mFailCause = failCause;
+            return this;
+        }
+
+        /**
+         * Set the APN Setting for this data connection.
+         *
+         * @param apnSetting APN setting
+         * @return This builder
+         */
+        public @NonNull Builder setApnSetting(@NonNull ApnSetting apnSetting) {
+            mApnSetting = apnSetting;
+            return this;
+        }
+
+        /**
+         * Set the default QoS for this data connection.
+         *
+         * @param qos The qos information, if any, associated with the default bearer of the
+         * data connection.
+         * @return The builder
+         * @hide
+         */
+        public @NonNull Builder setDefaultQos(@Nullable Qos qos) {
+            mDefaultQos = qos;
+            return this;
+        }
+
+        /**
+         * Set the network validation state for the data connection.
+         *
+         * @param networkValidationStatus the network validation status of the data call
+         * @return The builder
+         */
+        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+        public @NonNull Builder setNetworkValidationStatus(
+                @NetworkValidationStatus int networkValidationStatus) {
+            mNetworkValidationStatus = networkValidationStatus;
+            return this;
+        }
+
+        /**
+         * Build the {@link PreciseDataConnectionState} instance.
+         *
+         * @return The {@link PreciseDataConnectionState} instance
+         */
+        public PreciseDataConnectionState build() {
+            return new PreciseDataConnectionState(mTransportType, mId, mNetworkAgentId, mState,
+                    mNetworkType, mLinkProperties, mFailCause, mApnSetting, mDefaultQos,
+                    mNetworkValidationStatus);
+        }
+    }
+}
diff --git a/android-35/android/telephony/PreciseDisconnectCause.java b/android-35/android/telephony/PreciseDisconnectCause.java
new file mode 100644
index 0000000..d9437ab
--- /dev/null
+++ b/android-35/android/telephony/PreciseDisconnectCause.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * Contains precise disconnect call causes generated by the framework and the RIL.
+ * @hide
+ */
+@SystemApi
+public final class PreciseDisconnectCause {
+
+    /** The disconnect cause is not valid (Not received a disconnect cause).*/
+    public static final int NOT_VALID                                        = -1;
+    /** No disconnect cause provided. Generally a local disconnect or an incoming missed call. */
+    public static final int NO_DISCONNECT_CAUSE_AVAILABLE                    = 0;
+    /**
+     * The destination cannot be reached because the number, although valid,
+     * is not currently assigned.
+     */
+    public static final int UNOBTAINABLE_NUMBER                              = 1;
+    /**
+     * The user cannot be reached because the network through which the call has been routed does
+     * not serve the destination desired.
+     */
+    public static final int NO_ROUTE_TO_DESTINATION                          = 3;
+    /**
+     * The channel most recently identified is not acceptable to the sending entity for use in this
+     * call.
+     */
+    public static final int CHANNEL_UNACCEPTABLE                             = 6;
+    /**
+     * The mobile station (MS) has tried to access a service that the MS's network operator or
+     * service provider is not prepared to allow.
+     */
+    public static final int OPERATOR_DETERMINED_BARRING                      = 8;
+    /** One of the users involved in the call has requested that the call is cleared. */
+    public static final int NORMAL                                           = 16;
+    /** The called user is unable to accept another call. */
+    public static final int BUSY                                             = 17;
+    /**
+     * The user does not respond to a call establishment message with either an alerting or connect
+     * indication within the prescribed period of time allocated.
+     */
+    public static final int NO_USER_RESPONDING                               = 18;
+    /**
+     * The user has provided an alerting indication but has not provided a connect indication
+     * within a prescribed period of time.
+     */
+    public static final int NO_ANSWER_FROM_USER                              = 19;
+    /** The equipment sending this cause does not wish to accept this call. */
+    public static final int CALL_REJECTED                                    = 21;
+    /** The called number is no longer assigned. */
+    public static final int NUMBER_CHANGED                                   = 22;
+    /**
+     * This cause is returned to the network when a mobile station clears an active call which is
+     * being pre-empted by another call with higher precedence.
+     */
+    public static final int PREEMPTION                                       = 25;
+    /**
+     * The destination indicated by the mobile station cannot be reached because the interface to
+     * the destination is not functioning correctly.
+     */
+    public static final int DESTINATION_OUT_OF_ORDER                         = 27;
+    /** The called party number is not a valid format or is not complete. */
+    public static final int INVALID_NUMBER_FORMAT                            = 28;
+    /** The facility requested by user can not be provided by the network. */
+    public static final int FACILITY_REJECTED                                = 29;
+    /** Provided in response to a STATUS ENQUIRY message. */
+    public static final int STATUS_ENQUIRY                                   = 30;
+    /** Reports a normal disconnect only when no other normal cause applies. */
+    public static final int NORMAL_UNSPECIFIED                               = 31;
+    /** There is no channel presently available to handle the call. */
+    public static final int NO_CIRCUIT_AVAIL                                 = 34;
+    /**
+     * The network is not functioning correctly and that the condition is likely to last a
+     * relatively long period of time.
+     */
+    public static final int NETWORK_OUT_OF_ORDER                             = 38;
+    /**
+     * The network is not functioning correctly and the condition is not likely to last a long
+     * period of time.
+     */
+    public static final int TEMPORARY_FAILURE                                = 41;
+    /** The switching equipment is experiencing a period of high traffic. */
+    public static final int SWITCHING_CONGESTION                             = 42;
+    /** The network could not deliver access information to the remote user as requested. */
+    public static final int ACCESS_INFORMATION_DISCARDED                     = 43;
+    /** The channel cannot be provided. */
+    public static final int CHANNEL_NOT_AVAIL                                = 44;
+    /**
+     * This cause is used to report a resource unavailable event only when no other cause in the
+     * resource unavailable class applies.
+     */
+    public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED             = 47;
+    /** The requested quality of service (ITU-T X.213) cannot be provided. */
+    public static final int QOS_NOT_AVAIL                                    = 49;
+    /**
+     * The facility could not be provided by the network because the user has no complete
+     * subscription.
+     */
+    public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED                = 50;
+    /** Incoming calls are not allowed within this calling user group (CUG). */
+    public static final int INCOMING_CALLS_BARRED_WITHIN_CUG                 = 55;
+    /** The mobile station is not authorized to use bearer capability requested. */
+    public static final int BEARER_CAPABILITY_NOT_AUTHORIZED                 = 57;
+    /** The requested bearer capability is not available at this time. */
+    public static final int BEARER_NOT_AVAIL                                 = 58;
+    /** The service option is not available at this time. */
+    public static final int SERVICE_OPTION_NOT_AVAILABLE                     = 63;
+    /** The equipment sending this cause does not support the bearer capability requested. */
+    public static final int BEARER_SERVICE_NOT_IMPLEMENTED                   = 65;
+    /** The call clearing is due to ACM being greater than or equal to ACMmax. */
+    public static final int ACM_LIMIT_EXCEEDED                               = 68;
+    /** The equipment sending this cause does not support the requested facility. */
+    public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED               = 69;
+    /**
+     * The equipment sending this cause only supports the restricted version of the requested bearer
+     * capability.
+     */
+    public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE        = 70;
+    /** The service requested is not implemented at network. */
+    public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED                = 79;
+    /**
+     * The equipment sending this cause has received a message with a transaction identifier
+     * which is not currently in use on the mobile station network interface.
+     */
+    public static final int INVALID_TRANSACTION_IDENTIFIER                   = 81;
+    /**
+     * The called user for the incoming CUG call is not a member of the specified calling user
+     * group (CUG).
+     */
+    public static final int USER_NOT_MEMBER_OF_CUG                           = 87;
+    /** The equipment sending this cause has received a request which can't be accomodated. */
+    public static final int INCOMPATIBLE_DESTINATION                         = 88;
+    /** This cause is used to report receipt of a message with semantically incorrect contents. */
+    public static final int SEMANTICALLY_INCORRECT_MESSAGE                   = 95;
+    /**
+     * The equipment sending this cause has received a message with a non-semantical mandatory
+     * information element (IE) error.
+     */
+    public static final int INVALID_MANDATORY_INFORMATION                    = 96;
+    /**
+     * This is sent in response to a message which is not defined, or defined but not implemented
+     * by the equipment sending this cause.
+     */
+    public static final int MESSAGE_TYPE_NON_IMPLEMENTED                     = 97;
+    /**
+     * The equipment sending this cause has received a message not compatible with the protocol
+     * state.
+     */
+    public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE  = 98;
+    /**
+     * The equipment sending this cause has received a message which includes information
+     * elements not recognized because its identifier is not defined or it is defined but not
+     * implemented by the equipment sending the cause.
+     */
+    public static final int INFORMATION_ELEMENT_NON_EXISTENT                 = 99;
+    /** The equipment sending this cause has received a message with conditional IE errors. */
+    public static final int CONDITIONAL_IE_ERROR                             = 100;
+    /** The message has been received which is incompatible with the protocol state. */
+    public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE       = 101;
+    /**
+     * The procedure has been initiated by the expiry of a timer in association with
+     * 3GPP TS 24.008 error handling procedures.
+     */
+    public static final int RECOVERY_ON_TIMER_EXPIRED                        = 102;
+    /**
+     * This protocol error event is reported only when no other cause in the protocol error class
+     * applies.
+     */
+    public static final int PROTOCOL_ERROR_UNSPECIFIED                       = 111;
+    /**
+     * Interworking with a network which does not provide causes for actions it takes thus, the
+     * precise cause for a message which is being sent cannot be ascertained.
+     */
+    public static final int INTERWORKING_UNSPECIFIED                         = 127;
+    /** The call is restricted. */
+    public static final int CALL_BARRED                                      = 240;
+    /** The call is blocked by the Fixed Dialing Number list. */
+    public static final int FDN_BLOCKED                                      = 241;
+    /** The given IMSI is not known at the Visitor Location Register (VLR) TS 24.008 cause . */
+    public static final int IMSI_UNKNOWN_IN_VLR                              = 242;
+    /**
+     * The network does not accept emergency call establishment using an IMEI or not accept attach
+     * procedure for emergency services using an IMEI.
+     */
+    public static final int IMEI_NOT_ACCEPTED                                = 243;
+    /** The call cannot be established because RADIO is OFF. */
+    public static final int RADIO_OFF                                        = 247;
+    /** The call cannot be established because of no cell coverage. */
+    public static final int OUT_OF_SRV                                       = 248;
+    /** The call cannot be established because of no valid SIM. */
+    public static final int NO_VALID_SIM                                     = 249;
+    /** The call is dropped or failed internally by modem. */
+    public static final int RADIO_INTERNAL_ERROR                             = 250;
+    /** Call failed because of UE timer expired while waiting for a response from network. */
+    public static final int NETWORK_RESP_TIMEOUT                             = 251;
+    /** Call failed because of a network reject. */
+    public static final int NETWORK_REJECT                                   = 252;
+    /** Call failed because of radio access failure. ex. RACH failure. */
+    public static final int RADIO_ACCESS_FAILURE                             = 253;
+    /** Call failed/dropped because of a Radio Link Failure (RLF). */
+    public static final int RADIO_LINK_FAILURE                               = 254;
+    /** Call failed/dropped because of radio link lost. */
+    public static final int RADIO_LINK_LOST                                  = 255;
+    /** Call failed because of a radio uplink issue. */
+    public static final int RADIO_UPLINK_FAILURE                             = 256;
+    /** Call failed because of a RRC (Radio Resource Control) connection setup failure. */
+    public static final int RADIO_SETUP_FAILURE                              = 257;
+    /** Call failed/dropped because of RRC (Radio Resource Control) connection release from NW. */
+    public static final int RADIO_RELEASE_NORMAL                             = 258;
+    /**
+     * Call failed/dropped because of RRC (Radio Resource Control) abnormally released by
+     * modem/network.
+     */
+    public static final int RADIO_RELEASE_ABNORMAL                           = 259;
+    /** Call setup failed because of access class barring. */
+    public static final int ACCESS_CLASS_BLOCKED                             = 260;
+    /** Call failed/dropped because of a network detach. */
+    public static final int NETWORK_DETACH                                   = 261;
+
+    /**
+     * Dialing emergency calls is currently unavailable.
+     * The call should be redialed on the other subscription silently.
+     * If there are no other subscriptions available then the call may be redialed
+     * on this subscription again.
+     */
+    @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+    public static final int EMERGENCY_TEMP_FAILURE                           = 325;
+    /**
+     * Dialing emergency calls is currently unavailable.
+     * The call should be redialed on the other subscription silently.
+     * If there are no other subscriptions available then the call should not
+     * be redialed on this subscription again.
+     */
+    @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+    public static final int EMERGENCY_PERM_FAILURE                           = 326;
+
+    /** Mobile station (MS) is locked until next power cycle. */
+    public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE                    = 1000;
+    /** Drop call. */
+    public static final int CDMA_DROP                                        = 1001;
+    /** INTERCEPT order received, Mobile station (MS) state idle entered. */
+    public static final int CDMA_INTERCEPT                                   = 1002;
+    /** Mobile station (MS) has been redirected, call is cancelled. */
+    public static final int CDMA_REORDER                                     = 1003;
+    /** Service option rejection. */
+    public static final int CDMA_SO_REJECT                                   = 1004;
+    /** Requested service is rejected, retry delay is set. */
+    public static final int CDMA_RETRY_ORDER                                 = 1005;
+    /** Unable to obtain access to the CDMA system. */
+    public static final int CDMA_ACCESS_FAILURE                              = 1006;
+    /** Not a preempted call. */
+    public static final int CDMA_PREEMPTED                                   = 1007;
+    /** Not an emergency call. */
+    public static final int CDMA_NOT_EMERGENCY                               = 1008;
+    /** Access Blocked by CDMA network. */
+    public static final int CDMA_ACCESS_BLOCKED                              = 1009;
+
+    /* OEM specific error codes. To be used by OEMs when they don't want to
+       reveal error code which would be replaced by ERROR_UNSPECIFIED */
+    public static final int OEM_CAUSE_1                                      = 0xf001;
+    public static final int OEM_CAUSE_2                                      = 0xf002;
+    public static final int OEM_CAUSE_3                                      = 0xf003;
+    public static final int OEM_CAUSE_4                                      = 0xf004;
+    public static final int OEM_CAUSE_5                                      = 0xf005;
+    public static final int OEM_CAUSE_6                                      = 0xf006;
+    public static final int OEM_CAUSE_7                                      = 0xf007;
+    public static final int OEM_CAUSE_8                                      = 0xf008;
+    public static final int OEM_CAUSE_9                                      = 0xf009;
+    public static final int OEM_CAUSE_10                                     = 0xf00a;
+    public static final int OEM_CAUSE_11                                     = 0xf00b;
+    public static final int OEM_CAUSE_12                                     = 0xf00c;
+    public static final int OEM_CAUSE_13                                     = 0xf00d;
+    public static final int OEM_CAUSE_14                                     = 0xf00e;
+    public static final int OEM_CAUSE_15                                     = 0xf00f;
+
+    /** Disconnected due to unspecified reasons. */
+    public static final int ERROR_UNSPECIFIED                                = 0xffff;
+
+    /** Private constructor to avoid class instantiation. */
+    private PreciseDisconnectCause() {
+        // Do nothing.
+    }
+}
diff --git a/android-35/android/telephony/RadioAccessFamily.java b/android-35/android/telephony/RadioAccessFamily.java
new file mode 100644
index 0000000..90d6f89
--- /dev/null
+++ b/android-35/android/telephony/RadioAccessFamily.java
@@ -0,0 +1,412 @@
+/*
+* Copyright (C) 2014 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;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.TelephonyManager.PrefNetworkMode;
+
+import com.android.internal.telephony.RILConstants;
+
+import java.util.Locale;
+
+
+/**
+ * Object to indicate the phone radio type and access technology.
+ *
+ * @hide
+ */
+public class RadioAccessFamily implements Parcelable {
+
+    /**
+     * TODO: get rid of RAF definition in RadioAccessFamily and
+     * use {@link TelephonyManager.NetworkTypeBitMask}
+     * TODO: public definition {@link TelephonyManager.NetworkTypeBitMask} is long.
+     * TODO: Convert from int to long everywhere including HAL definitions.
+     */
+    // 2G
+    public static final int RAF_UNKNOWN = (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN;
+    public static final int RAF_GSM = (int) TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
+    public static final int RAF_GPRS = (int) TelephonyManager.NETWORK_TYPE_BITMASK_GPRS;
+    public static final int RAF_EDGE = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EDGE;
+    public static final int RAF_IS95A = (int) TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
+    public static final int RAF_IS95B = (int) TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
+    public static final int RAF_1xRTT = (int) TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;
+    // 3G
+    public static final int RAF_EVDO_0 = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0;
+    public static final int RAF_EVDO_A = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A;
+    public static final int RAF_EVDO_B = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B;
+    public static final int RAF_EHRPD = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD;
+    public static final int RAF_HSUPA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA;
+    public static final int RAF_HSDPA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA;
+    public static final int RAF_HSPA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSPA;
+    public static final int RAF_HSPAP = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP;
+    public static final int RAF_UMTS = (int) TelephonyManager.NETWORK_TYPE_BITMASK_UMTS;
+    public static final int RAF_TD_SCDMA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA;
+    // 4G
+    public static final int RAF_LTE = (int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
+    public static final int RAF_LTE_CA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
+
+    // 5G
+    public static final int RAF_NR = (int) TelephonyManager.NETWORK_TYPE_BITMASK_NR;
+
+    // Grouping of RAFs
+    // 2G
+    private static final int GSM = RAF_GSM | RAF_GPRS | RAF_EDGE;
+    private static final int CDMA = RAF_IS95A | RAF_IS95B | RAF_1xRTT;
+    // 3G
+    private static final int EVDO = RAF_EVDO_0 | RAF_EVDO_A | RAF_EVDO_B | RAF_EHRPD;
+    private static final int HS = RAF_HSUPA | RAF_HSDPA | RAF_HSPA | RAF_HSPAP;
+    private static final int WCDMA = HS | RAF_UMTS;
+    // 4G
+    private static final int LTE = RAF_LTE | RAF_LTE_CA;
+
+    // 5G
+    private static final int NR = RAF_NR;
+
+    /* Phone ID of phone */
+    private int mPhoneId;
+
+    /* Radio Access Family */
+    private int mRadioAccessFamily;
+
+    /**
+     * Constructor.
+     *
+     * @param phoneId the phone ID
+     * @param radioAccessFamily the phone radio access family bitmask based on
+     * {@link TelephonyManager.NetworkTypeBitMask}. It's a bit mask value to represent the support
+     *                          type.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public RadioAccessFamily(int phoneId, int radioAccessFamily) {
+        mPhoneId = phoneId;
+        mRadioAccessFamily = radioAccessFamily;
+    }
+
+    /**
+     * Get phone ID.
+     *
+     * @return phone ID
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int getPhoneId() {
+        return mPhoneId;
+    }
+
+    /**
+     * get radio access family.
+     *
+     * @return radio access family
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public @TelephonyManager.NetworkTypeBitMask int getRadioAccessFamily() {
+        return mRadioAccessFamily;
+    }
+
+    @Override
+    public String toString() {
+        String ret = "{ mPhoneId = " + mPhoneId
+                + ", mRadioAccessFamily = " + mRadioAccessFamily
+                + "}";
+        return ret;
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     *
+     * @return describe content
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     *
+     * @param outParcel The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written.
+     */
+    @Override
+    public void writeToParcel(Parcel outParcel, int flags) {
+        outParcel.writeInt(mPhoneId);
+        outParcel.writeInt(mRadioAccessFamily);
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     */
+    public static final @android.annotation.NonNull Creator<android.telephony.RadioAccessFamily> CREATOR =
+            new Creator<android.telephony.RadioAccessFamily>() {
+
+        @Override
+        public android.telephony.RadioAccessFamily createFromParcel(Parcel in) {
+            int phoneId = in.readInt();
+            int radioAccessFamily = in.readInt();
+
+            return new android.telephony.RadioAccessFamily(phoneId, radioAccessFamily);
+        }
+
+        @Override
+        public android.telephony.RadioAccessFamily[] newArray(int size) {
+            return new android.telephony.RadioAccessFamily[size];
+        }
+    };
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @TelephonyManager.NetworkTypeBitMask
+    public static int getRafFromNetworkType(@PrefNetworkMode int type) {
+        switch (type) {
+            case RILConstants.NETWORK_MODE_WCDMA_PREF:
+                return GSM | WCDMA;
+            case RILConstants.NETWORK_MODE_GSM_ONLY:
+                return GSM;
+            case RILConstants.NETWORK_MODE_WCDMA_ONLY:
+                return WCDMA;
+            case RILConstants.NETWORK_MODE_GSM_UMTS:
+                return GSM | WCDMA;
+            case RILConstants.NETWORK_MODE_CDMA:
+                return CDMA | EVDO;
+            case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
+                return LTE | CDMA | EVDO;
+            case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA:
+                return LTE | GSM | WCDMA;
+            case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
+                return LTE | CDMA | EVDO | GSM | WCDMA;
+            case RILConstants.NETWORK_MODE_LTE_ONLY:
+                return LTE;
+            case RILConstants.NETWORK_MODE_LTE_WCDMA:
+                return LTE | WCDMA;
+            case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
+                return CDMA;
+            case RILConstants.NETWORK_MODE_EVDO_NO_CDMA:
+                return EVDO;
+            case RILConstants.NETWORK_MODE_GLOBAL:
+                return GSM | WCDMA | CDMA | EVDO;
+            case RILConstants.NETWORK_MODE_TDSCDMA_ONLY:
+                return RAF_TD_SCDMA;
+            case RILConstants.NETWORK_MODE_TDSCDMA_WCDMA:
+                return RAF_TD_SCDMA | WCDMA;
+            case RILConstants.NETWORK_MODE_LTE_TDSCDMA:
+                return LTE | RAF_TD_SCDMA;
+            case RILConstants.NETWORK_MODE_TDSCDMA_GSM:
+                return RAF_TD_SCDMA | GSM;
+            case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM:
+                return LTE | RAF_TD_SCDMA | GSM;
+            case RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA:
+                return RAF_TD_SCDMA | GSM | WCDMA;
+            case RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA:
+                return LTE | RAF_TD_SCDMA | WCDMA;
+            case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA:
+                return LTE | RAF_TD_SCDMA | GSM | WCDMA;
+            case RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+                return RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
+            case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+                return LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
+            case (RILConstants.NETWORK_MODE_NR_ONLY):
+                return NR;
+            case (RILConstants.NETWORK_MODE_NR_LTE):
+                return NR | LTE;
+            case (RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO):
+                return NR | LTE | CDMA | EVDO;
+            case (RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA):
+                return NR | LTE | GSM | WCDMA;
+            case (RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA):
+                return NR | LTE | CDMA | EVDO | GSM | WCDMA;
+            case (RILConstants.NETWORK_MODE_NR_LTE_WCDMA):
+                return NR | LTE | WCDMA;
+            case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA):
+                return NR | LTE | RAF_TD_SCDMA;
+            case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM):
+                return NR | LTE | RAF_TD_SCDMA | GSM;
+            case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA):
+                return NR | LTE | RAF_TD_SCDMA | WCDMA;
+            case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA):
+                return NR | LTE | RAF_TD_SCDMA | GSM | WCDMA;
+            case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA):
+                return NR | LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
+            default:
+                return RAF_UNKNOWN;
+        }
+    }
+
+    /**
+     * if the raf includes ANY bit set for a group
+     * adjust it to contain ALL the bits for that group
+     */
+    private static int getAdjustedRaf(int raf) {
+        raf = ((GSM & raf) > 0) ? (GSM | raf) : raf;
+        raf = ((WCDMA & raf) > 0) ? (WCDMA | raf) : raf;
+        raf = ((CDMA & raf) > 0) ? (CDMA | raf) : raf;
+        raf = ((EVDO & raf) > 0) ? (EVDO | raf) : raf;
+        raf = ((LTE & raf) > 0) ? (LTE | raf) : raf;
+        raf = ((NR & raf) > 0) ? (NR | raf) : raf;
+
+        return raf;
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @PrefNetworkMode
+    public static int getNetworkTypeFromRaf(int raf) {
+        raf = getAdjustedRaf(raf);
+
+        switch (raf) {
+            case (GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_WCDMA_PREF;
+            case GSM:
+                return RILConstants.NETWORK_MODE_GSM_ONLY;
+            case WCDMA:
+                return RILConstants.NETWORK_MODE_WCDMA_ONLY;
+            case (CDMA | EVDO):
+                return RILConstants.NETWORK_MODE_CDMA;
+            case (LTE | CDMA | EVDO):
+                return RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
+            case (LTE | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
+            case (LTE | CDMA | EVDO | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
+            case LTE:
+                return RILConstants.NETWORK_MODE_LTE_ONLY;
+            case (LTE | WCDMA):
+                return RILConstants.NETWORK_MODE_LTE_WCDMA;
+            case CDMA:
+                return RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
+            case EVDO:
+                return RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
+            case (GSM | WCDMA | CDMA | EVDO):
+                return RILConstants.NETWORK_MODE_GLOBAL;
+            case RAF_TD_SCDMA:
+                return RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
+            case (RAF_TD_SCDMA | WCDMA):
+                return RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
+            case (LTE | RAF_TD_SCDMA):
+                return RILConstants.NETWORK_MODE_LTE_TDSCDMA;
+            case (RAF_TD_SCDMA | GSM):
+                return RILConstants.NETWORK_MODE_TDSCDMA_GSM;
+            case (LTE | RAF_TD_SCDMA | GSM):
+                return RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
+            case (RAF_TD_SCDMA | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
+            case (LTE | RAF_TD_SCDMA | WCDMA):
+                return RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
+            case (LTE | RAF_TD_SCDMA | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
+            case (RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+            case (LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+            case (NR):
+                return RILConstants.NETWORK_MODE_NR_ONLY;
+            case (NR | LTE):
+                return RILConstants.NETWORK_MODE_NR_LTE;
+            case (NR | LTE | CDMA | EVDO):
+                return RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO;
+            case (NR | LTE | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA;
+            case (NR | LTE | CDMA | EVDO | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA;
+            case (NR | LTE | WCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_WCDMA;
+            case (NR | LTE | RAF_TD_SCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA;
+            case (NR | LTE | RAF_TD_SCDMA | GSM):
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM;
+            case (NR | LTE | RAF_TD_SCDMA | WCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA;
+            case (NR | LTE | RAF_TD_SCDMA | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA;
+            case (NR | LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+            default:
+                return RILConstants.PREFERRED_NETWORK_MODE;
+        }
+    }
+
+    public static int singleRafTypeFromString(String rafString) {
+        switch (rafString) {
+            case "GPRS":    return RAF_GPRS;
+            case "EDGE":    return RAF_EDGE;
+            case "UMTS":    return RAF_UMTS;
+            case "IS95A":   return RAF_IS95A;
+            case "IS95B":   return RAF_IS95B;
+            case "1XRTT":   return RAF_1xRTT;
+            case "EVDO_0":  return RAF_EVDO_0;
+            case "EVDO_A":  return RAF_EVDO_A;
+            case "HSDPA":   return RAF_HSDPA;
+            case "HSUPA":   return RAF_HSUPA;
+            case "HSPA":    return RAF_HSPA;
+            case "EVDO_B":  return RAF_EVDO_B;
+            case "EHRPD":   return RAF_EHRPD;
+            case "LTE":     return RAF_LTE;
+            case "HSPAP":   return RAF_HSPAP;
+            case "GSM":     return RAF_GSM;
+            case "TD_SCDMA":return RAF_TD_SCDMA;
+            case "HS":      return HS;
+            case "CDMA":    return CDMA;
+            case "EVDO":    return EVDO;
+            case "WCDMA":   return WCDMA;
+            case "LTE_CA":  return RAF_LTE_CA;
+            case "NR":      return RAF_NR;
+            default:        return RAF_UNKNOWN;
+        }
+    }
+
+    public static int rafTypeFromString(String rafList) {
+        rafList = rafList.toUpperCase(Locale.ROOT);
+        String[] rafs = rafList.split("\\|");
+        int result = 0;
+        for(String raf : rafs) {
+            int rafType = singleRafTypeFromString(raf.trim());
+            if (rafType == RAF_UNKNOWN) return rafType;
+            result |= rafType;
+        }
+        return result;
+    }
+
+    /**
+     * Compare two sets of network types to see which is more capable.
+     *
+     * This algorithm first tries to see see if a set has a strict superset of RAT support for
+     * each generation, from newest to oldest; if that results in a tie, then it returns the set
+     * that supports the most RAT types.
+     */
+    public static int compare(long networkTypeBitmaskL, long networkTypeBitmaskR) {
+        final long[] prioritizedNetworkClassBitmasks = new long[] {
+            TelephonyManager.NETWORK_CLASS_BITMASK_5G,
+            TelephonyManager.NETWORK_CLASS_BITMASK_4G,
+            TelephonyManager.NETWORK_CLASS_BITMASK_3G,
+            TelephonyManager.NETWORK_CLASS_BITMASK_2G,
+        };
+
+        long lhsUnique = networkTypeBitmaskL & ~networkTypeBitmaskR;
+        long rhsUnique = networkTypeBitmaskR & ~networkTypeBitmaskL;
+
+        // See if one has a strict super-set of capabilities, generation by generation.
+        for (long classBitmask : prioritizedNetworkClassBitmasks) {
+            int result = 0;
+            if ((lhsUnique & classBitmask) != 0) ++result;
+            if ((rhsUnique & classBitmask) != 0) --result;
+            if (result != 0) return result;
+        }
+
+        // Without a clear winner, return the one that supports the most types.
+        return Long.bitCount(networkTypeBitmaskL) - Long.bitCount(networkTypeBitmaskR);
+    }
+}
diff --git a/android-35/android/telephony/RadioAccessSpecifier.java b/android-35/android/telephony/RadioAccessSpecifier.java
new file mode 100644
index 0000000..9511db6
--- /dev/null
+++ b/android-35/android/telephony/RadioAccessSpecifier.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Describes a particular radio access network to be scanned.
+ *
+ * The scan can be performed on either bands or channels for a specific radio access network type.
+ */
+public final class RadioAccessSpecifier implements Parcelable {
+
+    /**
+     * The radio access network that needs to be scanned
+     *
+     * This parameter must be provided or else the scan will be rejected.
+     *
+     * See {@link AccessNetworkConstants.AccessNetworkType} for details.
+     */
+    private int mRadioAccessNetwork;
+
+    /**
+     * The frequency bands that need to be scanned
+     *
+     * When no specific bands are specified (empty array or null), all the frequency bands
+     * supported by the modem will be scanned.
+     *
+     * See {@link AccessNetworkConstants} for details.
+     */
+    private int[] mBands;
+
+    /**
+     * The frequency channels that need to be scanned
+     *
+     * When any specific channels are provided for scan, the corresponding frequency bands that
+     * contains those channels must also be provided, or else the channels will be ignored.
+     *
+     * When no specific channels are specified (empty array or null), all the frequency channels
+     * supported by the modem will be scanned.
+     *
+     * See {@link AccessNetworkConstants} for details.
+     */
+    private int[] mChannels;
+
+    /**
+    * Creates a new RadioAccessSpecifier with radio network, bands and channels
+    *
+    * The user must specify the radio network type, and at least specify either of frequency
+    * bands or channels.
+    *
+    * @param ran The type of the radio access network
+    * @param bands the frequency bands to be scanned
+    * @param channels the frequency bands to be scanned
+    */
+    public RadioAccessSpecifier(int ran, int[] bands, int[] channels) {
+        this.mRadioAccessNetwork = ran;
+        if (bands != null) {
+            this.mBands = bands.clone();
+        } else {
+            this.mBands = null;
+        }
+        if (channels != null) {
+            this.mChannels = channels.clone();
+        } else {
+            this.mChannels = null;
+        }
+    }
+
+    /**
+     * Returns the radio access network that needs to be scanned.
+     *
+     * The returned value is define in {@link AccessNetworkConstants.AccessNetworkType};
+     */
+    public int getRadioAccessNetwork() {
+        return mRadioAccessNetwork;
+    }
+
+    /**
+     * Returns the frequency bands that need to be scanned.
+     *
+     * The returned value is defined in either of {@link AccessNetworkConstants.GeranBand},
+     * {@link AccessNetworkConstants.UtranBand}, {@link AccessNetworkConstants.EutranBand},
+     * and {@link AccessNetworkConstants.NgranBands}, and it depends on
+     * the returned value of {@link #getRadioAccessNetwork()}.
+     */
+    public int[] getBands() {
+        return mBands == null ? null : mBands.clone();
+    }
+
+    /** Returns the frequency channels that need to be scanned. */
+    public int[] getChannels() {
+        return mChannels == null ? null : mChannels.clone();
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<RadioAccessSpecifier> CREATOR =
+            new Parcelable.Creator<RadioAccessSpecifier> (){
+                @Override
+                public RadioAccessSpecifier createFromParcel(Parcel in) {
+                    return new RadioAccessSpecifier(in);
+                }
+
+                @Override
+                public RadioAccessSpecifier[] newArray(int size) {
+                    return new RadioAccessSpecifier[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRadioAccessNetwork);
+        dest.writeIntArray(mBands);
+        dest.writeIntArray(mChannels);
+    }
+
+    private RadioAccessSpecifier(Parcel in) {
+        mRadioAccessNetwork = in.readInt();
+        mBands = in.createIntArray();
+        mChannels = in.createIntArray();
+    }
+
+    @Override
+    public boolean equals (Object o) {
+        RadioAccessSpecifier ras;
+
+        try {
+            ras = (RadioAccessSpecifier) o;
+        } catch (ClassCastException ex) {
+            return false;
+        }
+
+        if (o == null) {
+            return false;
+        }
+
+        return (mRadioAccessNetwork == ras.mRadioAccessNetwork
+                && Arrays.equals(mBands, ras.mBands)
+                && Arrays.equals(mChannels, ras.mChannels));
+    }
+
+    @Override
+    public int hashCode() {
+        return ((mRadioAccessNetwork * 31)
+                + (Arrays.hashCode(mBands) * 37)
+                + (Arrays.hashCode(mChannels)) * 39);
+    }
+
+    @Override
+    public String toString() {
+        return "RadioAccessSpecifier[mRadioAccessNetwork="
+                + AccessNetworkConstants.AccessNetworkType.toString(mRadioAccessNetwork)
+                + ", mBands=" + Arrays.toString(mBands)
+                + ", mChannels=" + Arrays.toString(mChannels) + "]";
+    }
+}
diff --git a/android-35/android/telephony/Rlog.java b/android-35/android/telephony/Rlog.java
new file mode 100644
index 0000000..a1c74e6
--- /dev/null
+++ b/android-35/android/telephony/Rlog.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+
+/**
+ * A class to log strings to the RADIO LOG.
+ *
+ * @hide
+ */
+public final class Rlog {
+
+    private static final boolean USER_BUILD = Build.IS_USER;
+
+    private Rlog() {
+    }
+
+    @UnsupportedAppUsage
+    public static int v(String tag, String msg) {
+        return Log.println_native(Log.LOG_ID_RADIO, Log.VERBOSE, tag, msg);
+    }
+
+    public static int v(String tag, String msg, Throwable tr) {
+        return Log.println_native(Log.LOG_ID_RADIO, Log.VERBOSE, tag,
+                msg + '\n' + Log.getStackTraceString(tr));
+    }
+
+    @UnsupportedAppUsage
+    public static int d(String tag, String msg) {
+        return Log.println_native(Log.LOG_ID_RADIO, Log.DEBUG, tag, msg);
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static int d(String tag, String msg, Throwable tr) {
+        return Log.println_native(Log.LOG_ID_RADIO, Log.DEBUG, tag,
+                msg + '\n' + Log.getStackTraceString(tr));
+    }
+
+    @UnsupportedAppUsage
+    public static int i(String tag, String msg) {
+        return Log.println_native(Log.LOG_ID_RADIO, Log.INFO, tag, msg);
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public static int i(String tag, String msg, Throwable tr) {
+        return Log.println_native(Log.LOG_ID_RADIO, Log.INFO, tag,
+                msg + '\n' + Log.getStackTraceString(tr));
+    }
+
+    @UnsupportedAppUsage
+    public static int w(String tag, String msg) {
+        return Log.println_native(Log.LOG_ID_RADIO, Log.WARN, tag, msg);
+    }
+
+    @UnsupportedAppUsage
+    public static int w(String tag, String msg, Throwable tr) {
+        return Log.println_native(Log.LOG_ID_RADIO, Log.WARN, tag,
+                msg + '\n' + Log.getStackTraceString(tr));
+    }
+
+    public static int w(String tag, Throwable tr) {
+        return Log.println_native(Log.LOG_ID_RADIO, Log.WARN, tag, Log.getStackTraceString(tr));
+    }
+
+    @UnsupportedAppUsage
+    public static int e(String tag, String msg) {
+        return Log.println_native(Log.LOG_ID_RADIO, Log.ERROR, tag, msg);
+    }
+
+    @UnsupportedAppUsage
+    public static int e(String tag, String msg, Throwable tr) {
+        return Log.println_native(Log.LOG_ID_RADIO, Log.ERROR, tag,
+                msg + '\n' + Log.getStackTraceString(tr));
+    }
+
+    public static int println(int priority, String tag, String msg) {
+        return Log.println_native(Log.LOG_ID_RADIO, priority, tag, msg);
+    }
+
+    public static boolean isLoggable(String tag, int level) {
+        return Log.isLoggable(tag, level);
+    }
+
+    /**
+     * Redact personally identifiable information for production users.
+     * @param tag used to identify the source of a log message
+     * @param pii the personally identifiable information we want to apply secure hash on.
+     * @return If tag is loggable in verbose mode or pii is null, return the original input.
+     * otherwise return a secure Hash of input pii
+     */
+    public static String pii(String tag, Object pii) {
+        String val = String.valueOf(pii);
+        if (pii == null || TextUtils.isEmpty(val) || isLoggable(tag, Log.VERBOSE)) {
+            return val;
+        }
+        return "[" + secureHash(val.getBytes()) + "]";
+    }
+
+    /**
+     * Redact personally identifiable information for production users.
+     * @param enablePiiLogging set when caller explicitly want to enable sensitive logging.
+     * @param pii the personally identifiable information we want to apply secure hash on.
+     * @return If enablePiiLogging is set to true or pii is null, return the original input.
+     * otherwise return a secure Hash of input pii
+     */
+    public static String pii(boolean enablePiiLogging, Object pii) {
+        String val = String.valueOf(pii);
+        if (pii == null || TextUtils.isEmpty(val) || enablePiiLogging) {
+            return val;
+        }
+        return "[" + secureHash(val.getBytes()) + "]";
+    }
+
+    /**
+     * Returns a secure hash (using the SHA1 algorithm) of the provided input.
+     *
+     * @return "****" if the build type is user, otherwise the hash
+     * @param input the bytes for which the secure hash should be computed.
+     */
+    private static String secureHash(byte[] input) {
+        // Refrain from logging user personal information in user build.
+        if (USER_BUILD) {
+            return "****";
+        }
+
+        MessageDigest messageDigest;
+
+        try {
+            messageDigest = MessageDigest.getInstance("SHA-1");
+        } catch (NoSuchAlgorithmException e) {
+            return "####";
+        }
+
+        byte[] result = messageDigest.digest(input);
+        return Base64.encodeToString(
+                result, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
+    }
+}
+
diff --git a/android-35/android/telephony/SecurityAlgorithmUpdate.java b/android-35/android/telephony/SecurityAlgorithmUpdate.java
new file mode 100644
index 0000000..57209eb
--- /dev/null
+++ b/android-35/android/telephony/SecurityAlgorithmUpdate.java
@@ -0,0 +1,223 @@
+/*
+ * 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.telephony;
+
+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;
+
+/**
+ * A single occurrence capturing a notable change to previously reported
+ * cryptography algorithms for a given network and network event.
+ *
+ * @hide
+ */
+public final class SecurityAlgorithmUpdate implements Parcelable {
+    private static final String TAG = "SecurityAlgorithmUpdate";
+
+    private @ConnectionEvent int mConnectionEvent;
+    private @SecurityAlgorithm int mEncryption;
+    private @SecurityAlgorithm int mIntegrity;
+    private boolean mIsUnprotectedEmergency;
+
+    public SecurityAlgorithmUpdate(@ConnectionEvent int connectionEvent,
+            @SecurityAlgorithm int encryption, @SecurityAlgorithm int integrity,
+            boolean isUnprotectedEmergency) {
+        mConnectionEvent = connectionEvent;
+        mEncryption = encryption;
+        mIntegrity = integrity;
+        mIsUnprotectedEmergency = isUnprotectedEmergency;
+    }
+
+    private SecurityAlgorithmUpdate(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public @ConnectionEvent int getConnectionEvent() {
+        return mConnectionEvent;
+    }
+
+    public @SecurityAlgorithm int getEncryption() {
+        return mEncryption;
+    }
+
+    public @SecurityAlgorithm int getIntegrity() {
+        return mIntegrity;
+    }
+
+    public boolean isUnprotectedEmergency() {
+        return mIsUnprotectedEmergency;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mConnectionEvent);
+        out.writeInt(mEncryption);
+        out.writeInt(mIntegrity);
+        out.writeBoolean(mIsUnprotectedEmergency);
+    }
+
+    private void readFromParcel(@NonNull Parcel in) {
+        mConnectionEvent = in.readInt();
+        mEncryption = in.readInt();
+        mIntegrity = in.readInt();
+        mIsUnprotectedEmergency = in.readBoolean();
+    }
+
+    public static final Parcelable.Creator<SecurityAlgorithmUpdate> CREATOR =
+            new Parcelable.Creator<SecurityAlgorithmUpdate>() {
+                public SecurityAlgorithmUpdate createFromParcel(Parcel in) {
+                    return new SecurityAlgorithmUpdate(in);
+                }
+
+                public SecurityAlgorithmUpdate[] newArray(int size) {
+                    return new SecurityAlgorithmUpdate[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return TAG + ":{ mConnectionEvent = " + mConnectionEvent + " mEncryption = " + mEncryption
+                + " mIntegrity = " + mIntegrity + " mIsUnprotectedEmergency = "
+                + mIsUnprotectedEmergency;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SecurityAlgorithmUpdate)) return false;
+        SecurityAlgorithmUpdate that = (SecurityAlgorithmUpdate) o;
+        return mConnectionEvent == that.mConnectionEvent
+                && mEncryption == that.mEncryption
+                && mIntegrity == that.mIntegrity
+                && mIsUnprotectedEmergency == that.mIsUnprotectedEmergency;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mConnectionEvent, mEncryption, mIntegrity, mIsUnprotectedEmergency);
+    }
+
+    public static final int CONNECTION_EVENT_CS_SIGNALLING_GSM = 0;
+    public static final int CONNECTION_EVENT_PS_SIGNALLING_GPRS = 1;
+    public static final int CONNECTION_EVENT_CS_SIGNALLING_3G = 2;
+    public static final int CONNECTION_EVENT_PS_SIGNALLING_3G = 3;
+    public static final int CONNECTION_EVENT_NAS_SIGNALLING_LTE = 4;
+    public static final int CONNECTION_EVENT_AS_SIGNALLING_LTE = 5;
+    public static final int CONNECTION_EVENT_VOLTE_SIP = 6;
+    public static final int CONNECTION_EVENT_VOLTE_SIP_SOS = 7;
+    public static final int CONNECTION_EVENT_VOLTE_RTP = 8;
+    public static final int CONNECTION_EVENT_VOLTE_RTP_SOS = 9;
+    public static final int CONNECTION_EVENT_NAS_SIGNALLING_5G = 10;
+    public static final int CONNECTION_EVENT_AS_SIGNALLING_5G = 11;
+    public static final int CONNECTION_EVENT_VONR_SIP = 12;
+    public static final int CONNECTION_EVENT_VONR_SIP_SOS = 13;
+    public static final int CONNECTION_EVENT_VONR_RTP = 14;
+    public static final int CONNECTION_EVENT_VONR_RTP_SOS = 15;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CONNECTION_EVENT_"}, value = {CONNECTION_EVENT_CS_SIGNALLING_GSM,
+            CONNECTION_EVENT_PS_SIGNALLING_GPRS, CONNECTION_EVENT_CS_SIGNALLING_3G,
+            CONNECTION_EVENT_PS_SIGNALLING_3G, CONNECTION_EVENT_NAS_SIGNALLING_LTE,
+            CONNECTION_EVENT_AS_SIGNALLING_LTE, CONNECTION_EVENT_VOLTE_SIP,
+            CONNECTION_EVENT_VOLTE_SIP_SOS, CONNECTION_EVENT_VOLTE_RTP,
+            CONNECTION_EVENT_VOLTE_RTP_SOS, CONNECTION_EVENT_NAS_SIGNALLING_5G,
+            CONNECTION_EVENT_AS_SIGNALLING_5G, CONNECTION_EVENT_VONR_SIP,
+            CONNECTION_EVENT_VONR_SIP_SOS, CONNECTION_EVENT_VONR_RTP,
+            CONNECTION_EVENT_VONR_RTP_SOS})
+    public @interface ConnectionEvent {
+    }
+
+    public static final int SECURITY_ALGORITHM_A50 = 0;
+    public static final int SECURITY_ALGORITHM_A51 = 1;
+    public static final int SECURITY_ALGORITHM_A52 = 2;
+    public static final int SECURITY_ALGORITHM_A53 = 3;
+    public static final int SECURITY_ALGORITHM_A54 = 4;
+    public static final int SECURITY_ALGORITHM_GEA0 = 14;
+    public static final int SECURITY_ALGORITHM_GEA1 = 15;
+    public static final int SECURITY_ALGORITHM_GEA2 = 16;
+    public static final int SECURITY_ALGORITHM_GEA3 = 17;
+    public static final int SECURITY_ALGORITHM_GEA4 = 18;
+    public static final int SECURITY_ALGORITHM_GEA5 = 19;
+    public static final int SECURITY_ALGORITHM_UEA0 = 29;
+    public static final int SECURITY_ALGORITHM_UEA1 = 30;
+    public static final int SECURITY_ALGORITHM_UEA2 = 31;
+    public static final int SECURITY_ALGORITHM_EEA0 = 41;
+    public static final int SECURITY_ALGORITHM_EEA1 = 42;
+    public static final int SECURITY_ALGORITHM_EEA2 = 43;
+    public static final int SECURITY_ALGORITHM_EEA3 = 44;
+    public static final int SECURITY_ALGORITHM_NEA0 = 55;
+    public static final int SECURITY_ALGORITHM_NEA1 = 56;
+    public static final int SECURITY_ALGORITHM_NEA2 = 57;
+    public static final int SECURITY_ALGORITHM_NEA3 = 58;
+    public static final int SECURITY_ALGORITHM_SIP_NO_IPSEC_CONFIG = 66;
+    public static final int SECURITY_ALGORITHM_IMS_NULL = 67;
+    public static final int SECURITY_ALGORITHM_SIP_NULL = 68;
+    public static final int SECURITY_ALGORITHM_AES_GCM = 69;
+    public static final int SECURITY_ALGORITHM_AES_GMAC = 70;
+    public static final int SECURITY_ALGORITHM_AES_CBC = 71;
+    public static final int SECURITY_ALGORITHM_DES_EDE3_CBC = 72;
+    public static final int SECURITY_ALGORITHM_AES_EDE3_CBC = 73;
+    public static final int SECURITY_ALGORITHM_HMAC_SHA1_96 = 74;
+    public static final int SECURITY_ALGORITHM_HMAC_MD5_96 = 75;
+    public static final int SECURITY_ALGORITHM_RTP = 85;
+    public static final int SECURITY_ALGORITHM_SRTP_NULL = 86;
+    public static final int SECURITY_ALGORITHM_SRTP_AES_COUNTER = 87;
+    public static final int SECURITY_ALGORITHM_SRTP_AES_F8 = 88;
+    public static final int SECURITY_ALGORITHM_SRTP_HMAC_SHA1 = 89;
+    public static final int SECURITY_ALGORITHM_ENCR_AES_GCM_16 = 99;
+    public static final int SECURITY_ALGORITHM_ENCR_AES_CBC = 100;
+    public static final int SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128 = 101;
+    public static final int SECURITY_ALGORITHM_UNKNOWN = 113;
+    public static final int SECURITY_ALGORITHM_OTHER = 114;
+    public static final int SECURITY_ALGORITHM_ORYX = 124;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CONNECTION_EVENT_"}, value = {SECURITY_ALGORITHM_A50, SECURITY_ALGORITHM_A51,
+            SECURITY_ALGORITHM_A52, SECURITY_ALGORITHM_A53,
+            SECURITY_ALGORITHM_A54, SECURITY_ALGORITHM_GEA0, SECURITY_ALGORITHM_GEA1,
+            SECURITY_ALGORITHM_GEA2, SECURITY_ALGORITHM_GEA3, SECURITY_ALGORITHM_GEA4,
+            SECURITY_ALGORITHM_GEA5, SECURITY_ALGORITHM_UEA0, SECURITY_ALGORITHM_UEA1,
+            SECURITY_ALGORITHM_UEA2, SECURITY_ALGORITHM_EEA0, SECURITY_ALGORITHM_EEA1,
+            SECURITY_ALGORITHM_EEA2, SECURITY_ALGORITHM_EEA3, SECURITY_ALGORITHM_NEA0,
+            SECURITY_ALGORITHM_NEA1, SECURITY_ALGORITHM_NEA2, SECURITY_ALGORITHM_NEA3,
+            SECURITY_ALGORITHM_SIP_NO_IPSEC_CONFIG, SECURITY_ALGORITHM_IMS_NULL,
+            SECURITY_ALGORITHM_SIP_NULL, SECURITY_ALGORITHM_AES_GCM,
+            SECURITY_ALGORITHM_AES_GMAC, SECURITY_ALGORITHM_AES_CBC,
+            SECURITY_ALGORITHM_DES_EDE3_CBC, SECURITY_ALGORITHM_AES_EDE3_CBC,
+            SECURITY_ALGORITHM_HMAC_SHA1_96, SECURITY_ALGORITHM_HMAC_MD5_96,
+            SECURITY_ALGORITHM_RTP, SECURITY_ALGORITHM_SRTP_NULL,
+            SECURITY_ALGORITHM_SRTP_AES_COUNTER, SECURITY_ALGORITHM_SRTP_AES_F8,
+            SECURITY_ALGORITHM_SRTP_HMAC_SHA1, SECURITY_ALGORITHM_ENCR_AES_GCM_16,
+            SECURITY_ALGORITHM_ENCR_AES_CBC, SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128,
+            SECURITY_ALGORITHM_UNKNOWN, SECURITY_ALGORITHM_OTHER, SECURITY_ALGORITHM_ORYX})
+    public @interface SecurityAlgorithm {
+    }
+
+}
diff --git a/android-35/android/telephony/ServiceState.java b/android-35/android/telephony/ServiceState.java
new file mode 100644
index 0000000..db167c0
--- /dev/null
+++ b/android-35/android/telephony/ServiceState.java
@@ -0,0 +1,2272 @@
+/*
+ * Copyright (C) 2006 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;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.NetworkRegistrationInfo.Domain;
+import android.telephony.NetworkRegistrationInfo.NRState;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.flags.Flags;
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * Contains phone state and service related information.
+ *
+ * The following phone information is included in returned ServiceState:
+ *
+ * <ul>
+ *   <li>Service state: IN_SERVICE, OUT_OF_SERVICE, EMERGENCY_ONLY, POWER_OFF
+ *   <li>Duplex mode: UNKNOWN, FDD, TDD
+ *   <li>Roaming indicator
+ *   <li>Operator name, short name and numeric id
+ *   <li>Network selection mode
+ * </ul>
+ *
+ * For historical reasons this class is not declared as final; however,
+ * it should be treated as though it were final.
+ */
[email protected]
+public class ServiceState implements Parcelable {
+
+    static final String LOG_TAG = "PHONE";
+    static final boolean DBG = false;
+    static final boolean VDBG = false;  // STOPSHIP if true
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "STATE_",
+            value = {STATE_IN_SERVICE, STATE_OUT_OF_SERVICE, STATE_EMERGENCY_ONLY,
+                    STATE_POWER_OFF})
+    public @interface RegState {}
+
+    /**
+     * Normal operation condition, the phone is registered
+     * with an operator either in home network or in roaming.
+     */
+    public static final int STATE_IN_SERVICE = TelephonyProtoEnums.SERVICE_STATE_IN_SERVICE; // 0
+
+    /**
+     * Phone is not registered with any operator, the phone
+     * can be currently searching a new operator to register to, or not
+     * searching to registration at all, or registration is denied, or radio
+     * signal is not available.
+     */
+    public static final int STATE_OUT_OF_SERVICE =
+            TelephonyProtoEnums.SERVICE_STATE_OUT_OF_SERVICE;  // 1
+
+    /**
+     * The phone is registered and locked.  Only emergency numbers are allowed. {@more}
+     */
+    //TODO: This state is not used anymore. It should be deprecated in a future release.
+    public static final int STATE_EMERGENCY_ONLY =
+            TelephonyProtoEnums.SERVICE_STATE_EMERGENCY_ONLY;  // 2
+
+    /**
+     * Radio of telephony is explicitly powered off.
+     */
+    public static final int STATE_POWER_OFF = TelephonyProtoEnums.SERVICE_STATE_POWER_OFF;  // 3
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "FREQUENCY_RANGE_",
+            value = {FREQUENCY_RANGE_UNKNOWN, FREQUENCY_RANGE_LOW, FREQUENCY_RANGE_MID,
+                    FREQUENCY_RANGE_HIGH, FREQUENCY_RANGE_MMWAVE})
+    public @interface FrequencyRange {}
+
+    /**
+     * Indicates frequency range is unknown.
+     * @hide
+     */
+    public static final int FREQUENCY_RANGE_UNKNOWN = 0;
+
+    /**
+     * Indicates the frequency range is below 1GHz.
+     * @hide
+     */
+    public static final int FREQUENCY_RANGE_LOW = 1;
+
+    /**
+     * Indicates the frequency range is between 1GHz to 3GHz.
+     * @hide
+     */
+    public static final int FREQUENCY_RANGE_MID = 2;
+
+    /**
+     * Indicates the frequency range is between 3GHz and 6GHz.
+     * @hide
+     */
+    public static final int FREQUENCY_RANGE_HIGH = 3;
+
+    /**
+     * Indicates the frequency range is above 6GHz (millimeter wave frequency).
+     * @hide
+     */
+    public static final int FREQUENCY_RANGE_MMWAVE = 4;
+
+    /**
+     * Number of frequency ranges.
+     * @hide
+     */
+    public static final int FREQUENCY_RANGE_COUNT = 5;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "DUPLEX_MODE_",
+            value = {DUPLEX_MODE_UNKNOWN, DUPLEX_MODE_FDD, DUPLEX_MODE_TDD})
+    public @interface DuplexMode {}
+
+    /**
+     * Duplex mode for the phone is unknown.
+     */
+    public static final int DUPLEX_MODE_UNKNOWN = 0;
+
+    /**
+     * Duplex mode for the phone is frequency-division duplexing.
+     */
+    public static final int DUPLEX_MODE_FDD = 1;
+
+    /**
+     * Duplex mode for the phone is time-division duplexing.
+     */
+    public static final int DUPLEX_MODE_TDD = 2;
+
+    /**
+     * Available radio technologies for GSM, UMTS and CDMA.
+     * Duplicates the constants from hardware/radio/include/ril.h
+     * This should only be used by agents working with the ril.  Others
+     * should use the equivalent TelephonyManager.NETWORK_TYPE_*
+     */
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_UNKNOWN = 0;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_GPRS = 1;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_EDGE = 2;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_UMTS = 3;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_IS95A = 4;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_IS95B = 5;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_1xRTT = 6;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_EVDO_0 = 7;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_EVDO_A = 8;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_HSDPA = 9;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_HSUPA = 10;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_HSPA = 11;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_EVDO_B = 12;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_EHRPD = 13;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_LTE = 14;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_HSPAP = 15;
+    /**
+     * GSM radio technology only supports voice. It does not support data.
+     * @hide
+     */
+    public static final int RIL_RADIO_TECHNOLOGY_GSM = 16;
+    /** @hide */
+    public static final int RIL_RADIO_TECHNOLOGY_TD_SCDMA = 17;
+    /**
+     * IWLAN
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static final int RIL_RADIO_TECHNOLOGY_IWLAN = 18;
+
+    /**
+     * LTE_CA
+     * @hide
+     */
+    public static final int RIL_RADIO_TECHNOLOGY_LTE_CA = 19;
+
+    /**
+     * NR(New Radio) 5G.
+     * @hide
+     */
+    public static final int  RIL_RADIO_TECHNOLOGY_NR = 20;
+
+    /**
+     * RIL Radio Annotation
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"RIL_RADIO_TECHNOLOGY_" }, value = {
+        ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN,
+        ServiceState.RIL_RADIO_TECHNOLOGY_GPRS,
+        ServiceState.RIL_RADIO_TECHNOLOGY_EDGE,
+        ServiceState.RIL_RADIO_TECHNOLOGY_UMTS,
+        ServiceState.RIL_RADIO_TECHNOLOGY_IS95A,
+        ServiceState.RIL_RADIO_TECHNOLOGY_IS95B,
+        ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT,
+        ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0,
+        ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A,
+        ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA,
+        ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA,
+        ServiceState.RIL_RADIO_TECHNOLOGY_HSPA,
+        ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B,
+        ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD,
+        ServiceState.RIL_RADIO_TECHNOLOGY_LTE,
+        ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP,
+        ServiceState.RIL_RADIO_TECHNOLOGY_GSM,
+        ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA,
+        ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN,
+        ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA,
+        ServiceState.RIL_RADIO_TECHNOLOGY_NR})
+    public @interface RilRadioTechnology {}
+
+
+    /**
+     * The number of the radio technologies.
+     */
+    private static final int NEXT_RIL_RADIO_TECHNOLOGY = 21;
+
+    /** @hide */
+    public static final int RIL_RADIO_CDMA_TECHNOLOGY_BITMASK =
+            (1 << (RIL_RADIO_TECHNOLOGY_IS95A - 1))
+                    | (1 << (RIL_RADIO_TECHNOLOGY_IS95B - 1))
+                    | (1 << (RIL_RADIO_TECHNOLOGY_1xRTT - 1))
+                    | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_0 - 1))
+                    | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_A - 1))
+                    | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_B - 1))
+                    | (1 << (RIL_RADIO_TECHNOLOGY_EHRPD - 1));
+
+    private int mVoiceRegState = STATE_OUT_OF_SERVICE;
+    private int mDataRegState = STATE_OUT_OF_SERVICE;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "ROAMING_TYPE_" }, value = {
+            ROAMING_TYPE_NOT_ROAMING,
+            ROAMING_TYPE_UNKNOWN,
+            ROAMING_TYPE_DOMESTIC,
+            ROAMING_TYPE_INTERNATIONAL
+    })
+    public @interface RoamingType {}
+
+    /**
+     * Not roaming, registered in home network.
+     * @hide
+     */
+    @SystemApi
+    public static final int ROAMING_TYPE_NOT_ROAMING = 0;
+    /**
+     * registered in a roaming network, but can not tell if it's domestic or international.
+     * @hide
+     */
+    @SystemApi
+    public static final int ROAMING_TYPE_UNKNOWN = 1;
+    /**
+     * registered in a domestic roaming network
+     * @hide
+     */
+    @SystemApi
+    public static final int ROAMING_TYPE_DOMESTIC = 2;
+    /**
+     * registered in an international roaming network
+     * @hide
+     */
+    @SystemApi
+    public static final int ROAMING_TYPE_INTERNATIONAL = 3;
+
+    /**
+     * Unknown ID. Could be returned by {@link #getCdmaNetworkId()} or {@link #getCdmaSystemId()}
+     */
+    public static final int UNKNOWN_ID = -1;
+
+    /**
+     * A parcelable extra used with {@link Intent#ACTION_SERVICE_STATE} representing the service
+     * state.
+     * @hide
+     */
+    private static final String EXTRA_SERVICE_STATE = "android.intent.extra.SERVICE_STATE";
+
+
+    private String mOperatorAlphaLong;
+    private String mOperatorAlphaShort;
+    private String mOperatorNumeric;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private boolean mIsManualNetworkSelection;
+
+    private boolean mIsEmergencyOnly;
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private boolean mCssIndicator;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private int mNetworkId;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private int mSystemId;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mCdmaRoamingIndicator;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mCdmaDefaultRoamingIndicator;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mCdmaEriIconIndex;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mCdmaEriIconMode;
+
+    @FrequencyRange
+    private int mNrFrequencyRange;
+    private int mChannelNumber;
+    private int[] mCellBandwidths = new int[0];
+
+    /**
+     *  ARFCN stands for Absolute Radio Frequency Channel Number. This field is current used for
+     *  LTE where it represents the boost for EARFCN (Reference: 3GPP TS 36.104 5.4.3) and for NR
+     *  where it's for NR ARFCN (Reference: 3GPP TS 36.108) */
+    private int mArfcnRsrpBoost = 0;
+
+    private final List<NetworkRegistrationInfo> mNetworkRegistrationInfos = new ArrayList<>();
+
+    private String mOperatorAlphaLongRaw;
+    private String mOperatorAlphaShortRaw;
+    private boolean mIsDataRoamingFromRegistration;
+    private boolean mIsIwlanPreferred;
+
+    /**
+     * get String description of roaming type
+     * @hide
+     */
+    public static final String getRoamingLogString(int roamingType) {
+        switch (roamingType) {
+            case ROAMING_TYPE_NOT_ROAMING:
+                return "home";
+
+            case ROAMING_TYPE_UNKNOWN:
+                return "roaming";
+
+            case ROAMING_TYPE_DOMESTIC:
+                return "Domestic Roaming";
+
+            case ROAMING_TYPE_INTERNATIONAL:
+                return "International Roaming";
+
+            default:
+                return "UNKNOWN";
+        }
+    }
+
+    /**
+     * Create a new ServiceState from a intent notifier Bundle
+     *
+     * This method is used to get ServiceState object from extras upon receiving
+     * {@link Intent#ACTION_SERVICE_STATE}.
+     *
+     * @param m Bundle from intent notifier
+     * @return newly created ServiceState
+     * @hide
+     */
+    @NonNull
+    @UnsupportedAppUsage
+    public static ServiceState newFromBundle(@NonNull Bundle m) {
+        ServiceState ret;
+        ret = new ServiceState();
+        ret.setFromNotifierBundle(m);
+        return ret;
+    }
+
+    /**
+     * Empty constructor
+     */
+    public ServiceState() {
+    }
+
+    /**
+     * Copy constructors
+     *
+     * @param s Source service state
+     */
+    public ServiceState(ServiceState s) {
+        copyFrom(s);
+    }
+
+    protected void copyFrom(ServiceState s) {
+        mVoiceRegState = s.mVoiceRegState;
+        mDataRegState = s.mDataRegState;
+        mOperatorAlphaLong = s.mOperatorAlphaLong;
+        mOperatorAlphaShort = s.mOperatorAlphaShort;
+        mOperatorNumeric = s.mOperatorNumeric;
+        mIsManualNetworkSelection = s.mIsManualNetworkSelection;
+        mCssIndicator = s.mCssIndicator;
+        mNetworkId = s.mNetworkId;
+        mSystemId = s.mSystemId;
+        mCdmaRoamingIndicator = s.mCdmaRoamingIndicator;
+        mCdmaDefaultRoamingIndicator = s.mCdmaDefaultRoamingIndicator;
+        mCdmaEriIconIndex = s.mCdmaEriIconIndex;
+        mCdmaEriIconMode = s.mCdmaEriIconMode;
+        mIsEmergencyOnly = s.mIsEmergencyOnly;
+        mChannelNumber = s.mChannelNumber;
+        mCellBandwidths = s.mCellBandwidths == null ? null :
+                Arrays.copyOf(s.mCellBandwidths, s.mCellBandwidths.length);
+        mArfcnRsrpBoost = s.mArfcnRsrpBoost;
+        synchronized (mNetworkRegistrationInfos) {
+            mNetworkRegistrationInfos.clear();
+            for (NetworkRegistrationInfo nri : s.getNetworkRegistrationInfoList()) {
+                mNetworkRegistrationInfos.add(new NetworkRegistrationInfo(nri));
+            }
+        }
+        mNrFrequencyRange = s.mNrFrequencyRange;
+        mOperatorAlphaLongRaw = s.mOperatorAlphaLongRaw;
+        mOperatorAlphaShortRaw = s.mOperatorAlphaShortRaw;
+        mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration;
+        mIsIwlanPreferred = s.mIsIwlanPreferred;
+    }
+
+    /**
+     * Construct a ServiceState object from the given parcel.
+     *
+     * @deprecated The constructor takes parcel should not be public at the beginning. Use
+     * {@link #ServiceState()} instead.
+     */
+    @Deprecated
+    public ServiceState(Parcel in) {
+        mVoiceRegState = in.readInt();
+        mDataRegState = in.readInt();
+        mOperatorAlphaLong = in.readString();
+        mOperatorAlphaShort = in.readString();
+        mOperatorNumeric = in.readString();
+        mIsManualNetworkSelection = in.readInt() != 0;
+        mCssIndicator = (in.readInt() != 0);
+        mNetworkId = in.readInt();
+        mSystemId = in.readInt();
+        mCdmaRoamingIndicator = in.readInt();
+        mCdmaDefaultRoamingIndicator = in.readInt();
+        mCdmaEriIconIndex = in.readInt();
+        mCdmaEriIconMode = in.readInt();
+        mIsEmergencyOnly = in.readInt() != 0;
+        mArfcnRsrpBoost = in.readInt();
+        synchronized (mNetworkRegistrationInfos) {
+            in.readList(mNetworkRegistrationInfos, NetworkRegistrationInfo.class.getClassLoader(), android.telephony.NetworkRegistrationInfo.class);
+        }
+        mChannelNumber = in.readInt();
+        mCellBandwidths = in.createIntArray();
+        mNrFrequencyRange = in.readInt();
+        mOperatorAlphaLongRaw = in.readString();
+        mOperatorAlphaShortRaw = in.readString();
+        mIsDataRoamingFromRegistration = in.readBoolean();
+        mIsIwlanPreferred = in.readBoolean();
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mVoiceRegState);
+        out.writeInt(mDataRegState);
+        out.writeString(mOperatorAlphaLong);
+        out.writeString(mOperatorAlphaShort);
+        out.writeString(mOperatorNumeric);
+        out.writeInt(mIsManualNetworkSelection ? 1 : 0);
+        out.writeInt(mCssIndicator ? 1 : 0);
+        out.writeInt(mNetworkId);
+        out.writeInt(mSystemId);
+        out.writeInt(mCdmaRoamingIndicator);
+        out.writeInt(mCdmaDefaultRoamingIndicator);
+        out.writeInt(mCdmaEriIconIndex);
+        out.writeInt(mCdmaEriIconMode);
+        out.writeInt(mIsEmergencyOnly ? 1 : 0);
+        out.writeInt(mArfcnRsrpBoost);
+        synchronized (mNetworkRegistrationInfos) {
+            out.writeList(mNetworkRegistrationInfos);
+        }
+        out.writeInt(mChannelNumber);
+        out.writeIntArray(mCellBandwidths);
+        out.writeInt(mNrFrequencyRange);
+        out.writeString(mOperatorAlphaLongRaw);
+        out.writeString(mOperatorAlphaShortRaw);
+        out.writeBoolean(mIsDataRoamingFromRegistration);
+        out.writeBoolean(mIsIwlanPreferred);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ServiceState> CREATOR =
+            new Parcelable.Creator<ServiceState>() {
+        public ServiceState createFromParcel(Parcel in) {
+            return new ServiceState(in);
+        }
+
+        public ServiceState[] newArray(int size) {
+            return new ServiceState[size];
+        }
+    };
+
+    /**
+     * Get current voice service state
+     */
+    public int getState() {
+        return getVoiceRegState();
+    }
+
+    /**
+     * Get current voice service state
+     *
+     * @see #STATE_IN_SERVICE
+     * @see #STATE_OUT_OF_SERVICE
+     * @see #STATE_EMERGENCY_ONLY
+     * @see #STATE_POWER_OFF
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getVoiceRegState() {
+        return mVoiceRegState;
+    }
+
+    /**
+     * Get current data registration state.
+     *
+     * @see #STATE_IN_SERVICE
+     * @see #STATE_OUT_OF_SERVICE
+     * @see #STATE_EMERGENCY_ONLY
+     * @see #STATE_POWER_OFF
+     *
+     * @return current data registration state
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public int getDataRegState() {
+        return mDataRegState;
+    }
+
+    /**
+     * Get current data registration state.
+     *
+     * @see #STATE_IN_SERVICE
+     * @see #STATE_OUT_OF_SERVICE
+     * @see #STATE_EMERGENCY_ONLY
+     * @see #STATE_POWER_OFF
+     *
+     * @return current data registration state
+     *
+     * @hide
+     */
+    public @RegState int getDataRegistrationState() {
+        return getDataRegState();
+    }
+
+    /**
+     * Get the current duplex mode
+     *
+     * @see #DUPLEX_MODE_UNKNOWN
+     * @see #DUPLEX_MODE_FDD
+     * @see #DUPLEX_MODE_TDD
+     *
+     * @return Current {@code DuplexMode} for the phone
+     */
+    @DuplexMode
+    public int getDuplexMode() {
+        // support LTE/NR duplex mode
+        if (!isPsOnlyTech(getRilDataRadioTechnology())) {
+            return DUPLEX_MODE_UNKNOWN;
+        }
+
+        int band = AccessNetworkUtils.getOperatingBandForEarfcn(mChannelNumber);
+        return AccessNetworkUtils.getDuplexModeForEutranBand(band);
+    }
+
+    /**
+     * Get the channel number of the current primary serving cell, or -1 if unknown
+     *
+     * <p>This is NRARFCN for NR, EARFCN for LTE, UARFCN for UMTS, and ARFCN for GSM.
+     *
+     * @return Channel number of primary serving cell
+     */
+    public int getChannelNumber() {
+        return mChannelNumber;
+    }
+
+    /**
+     * Get an array of cell bandwidths (kHz) for the current serving cells
+     *
+     * @return Current serving cell bandwidths
+     */
+    public int[] getCellBandwidths() {
+        return mCellBandwidths == null ? new int[0] : mCellBandwidths;
+    }
+
+    /**
+     * Get current roaming indicator of phone. This roaming state could be overridden by the carrier
+     * config.
+     * (note: not just decoding from TS 27.007 7.2)
+     * @see TelephonyDisplayInfo#isRoaming() for visualization purpose.
+     * @return true if TS 27.007 7.2 roaming is true
+     *              and ONS is different from SPN
+     * @see CarrierConfigManager#KEY_FORCE_HOME_NETWORK_BOOL
+     * @see CarrierConfigManager#KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+     * @see CarrierConfigManager#KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+     * @see CarrierConfigManager#KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY
+     * @see CarrierConfigManager#KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY
+     */
+    public boolean getRoaming() {
+        return getVoiceRoaming() || getDataRoaming();
+    }
+
+    /**
+     * Get current voice network roaming status
+     * @return roaming status
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public boolean getVoiceRoaming() {
+        return getVoiceRoamingType() != ROAMING_TYPE_NOT_ROAMING;
+    }
+
+    /**
+     * Get current voice roaming type. This roaming type could be overridden by the carrier config.
+     * @return roaming type
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public @RoamingType int getVoiceRoamingType() {
+        final NetworkRegistrationInfo regState = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (regState != null) {
+            return regState.getRoamingType();
+        }
+        return ROAMING_TYPE_NOT_ROAMING;
+    }
+
+    /**
+     * Get whether the current data network is roaming.
+     * This value may be overwritten by resource overlay or carrier configuration.
+     * @see #getDataRoamingFromRegistration() to get the value from the network registration.
+     * @return roaming type
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public boolean getDataRoaming() {
+        return getDataRoamingType() != ROAMING_TYPE_NOT_ROAMING;
+    }
+
+    /**
+     * Set whether the data network registration state is roaming.
+     * This should only be set to the roaming value received
+     * once the data registration phase has completed.
+     * @hide
+     */
+    public void setDataRoamingFromRegistration(boolean dataRoaming) {
+        mIsDataRoamingFromRegistration = dataRoaming;
+    }
+
+    /**
+     * Get whether data network registration state is roaming.
+     * This value is set directly from the modem and will not be overwritten
+     * by resource overlay or carrier configuration.
+     * @return true if registration indicates roaming, false otherwise
+     * @hide
+     */
+    public boolean getDataRoamingFromRegistration() {
+        // TODO: all callers should refactor to get roaming state directly from modem
+        // this should not be exposed as a public API
+        return mIsDataRoamingFromRegistration;
+    }
+
+    /**
+     * Get current data roaming type. This roaming type could be overridden by the carrier config.
+     * @return roaming type
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public @RoamingType int getDataRoamingType() {
+        final NetworkRegistrationInfo regState = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (regState != null) {
+            return regState.getRoamingType();
+        }
+        return ROAMING_TYPE_NOT_ROAMING;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean isEmergencyOnly() {
+        return mIsEmergencyOnly;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getCdmaRoamingIndicator(){
+        return this.mCdmaRoamingIndicator;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getCdmaDefaultRoamingIndicator(){
+        return this.mCdmaDefaultRoamingIndicator;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getCdmaEriIconIndex() {
+        return this.mCdmaEriIconIndex;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getCdmaEriIconMode() {
+        return this.mCdmaEriIconMode;
+    }
+
+    /**
+     * Get current registered operator name in long alphanumeric format.
+     *
+     * In GSM/UMTS, long format can be up to 16 characters long.
+     * In CDMA, returns the ERI text, if set. Otherwise, returns the ONS.
+     *
+     * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the
+     * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * @return long name of operator, null if unregistered or unknown
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.ACCESS_FINE_LOCATION,
+            android.Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    public String getOperatorAlphaLong() {
+        return mOperatorAlphaLong;
+    }
+
+    /**
+     * Get current registered voice network operator name in long alphanumeric format.
+     *
+     * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the
+     * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * @return long name of operator
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.ACCESS_FINE_LOCATION,
+            android.Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #getOperatorAlphaLong} instead.")
+    public String getVoiceOperatorAlphaLong() {
+        return mOperatorAlphaLong;
+    }
+
+    /**
+     * Get current registered operator name in short alphanumeric format.
+     *
+     * In GSM/UMTS, short format can be up to 8 characters long.
+     *
+     * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the
+     * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * @return short name of operator, null if unregistered or unknown
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.ACCESS_FINE_LOCATION,
+            android.Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    public String getOperatorAlphaShort() {
+        return mOperatorAlphaShort;
+    }
+
+    /**
+     * Get current registered voice network operator name in short alphanumeric format.
+     *
+     * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the
+     * caller does not have neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * @return short name of operator, null if unregistered or unknown
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.ACCESS_FINE_LOCATION,
+            android.Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #getOperatorAlphaShort} instead.")
+    public String getVoiceOperatorAlphaShort() {
+        return mOperatorAlphaShort;
+    }
+
+    /**
+     * Get current registered data network operator name in short alphanumeric format.
+     *
+     * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the
+     * caller does not have neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * @return short name of operator, null if unregistered or unknown
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.ACCESS_FINE_LOCATION,
+            android.Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #getOperatorAlphaShort} instead.")
+    public String getDataOperatorAlphaShort() {
+        return mOperatorAlphaShort;
+    }
+
+    /**
+     * Get current registered operator name in long alphanumeric format if
+     * available or short otherwise.
+     *
+     * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the
+     * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * @see #getOperatorAlphaLong
+     * @see #getOperatorAlphaShort
+     *
+     * @return name of operator, null if unregistered or unknown
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.ACCESS_FINE_LOCATION,
+            android.Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    public String getOperatorAlpha() {
+        if (TextUtils.isEmpty(mOperatorAlphaLong)) {
+            return mOperatorAlphaShort;
+        }
+
+        return mOperatorAlphaLong;
+    }
+
+    /**
+     * Get current registered operator numeric id.
+     *
+     * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit
+     * network code.
+     *
+     * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the
+     * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * @return numeric format of operator, null if unregistered or unknown
+     */
+    /*
+     * The country code can be decoded using
+     * {@link com.android.internal.telephony.MccTable#countryCodeForMcc(int)}.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.ACCESS_FINE_LOCATION,
+            android.Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    public String getOperatorNumeric() {
+        return mOperatorNumeric;
+    }
+
+    /**
+     * Get current registered voice network operator numeric id.
+     *
+     * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the
+     * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * @return numeric format of operator, null if unregistered or unknown
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.ACCESS_FINE_LOCATION,
+            android.Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public String getVoiceOperatorNumeric() {
+        return mOperatorNumeric;
+    }
+
+    /**
+     * Get current registered data network operator numeric id.
+     *
+     * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the
+     * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * @return numeric format of operator, null if unregistered or unknown
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.ACCESS_FINE_LOCATION,
+            android.Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #getOperatorNumeric} instead.")
+    public String getDataOperatorNumeric() {
+        return mOperatorNumeric;
+    }
+
+    /**
+     * Get current network selection mode.
+     *
+     * @return true if manual mode, false if automatic mode
+     */
+    public boolean getIsManualSelection() {
+        return mIsManualNetworkSelection;
+    }
+
+    @Override
+    public int hashCode() {
+        synchronized (mNetworkRegistrationInfos) {
+            return Objects.hash(
+                    mVoiceRegState,
+                    mDataRegState,
+                    mChannelNumber,
+                    Arrays.hashCode(mCellBandwidths),
+                    mOperatorAlphaLong,
+                    mOperatorAlphaShort,
+                    mOperatorNumeric,
+                    mIsManualNetworkSelection,
+                    mCssIndicator,
+                    mNetworkId,
+                    mSystemId,
+                    mCdmaRoamingIndicator,
+                    mCdmaDefaultRoamingIndicator,
+                    mCdmaEriIconIndex,
+                    mCdmaEriIconMode,
+                    mIsEmergencyOnly,
+                    mArfcnRsrpBoost,
+                    mNetworkRegistrationInfos,
+                    mNrFrequencyRange,
+                    mOperatorAlphaLongRaw,
+                    mOperatorAlphaShortRaw,
+                    mIsDataRoamingFromRegistration,
+                    mIsIwlanPreferred);
+        }
+    }
+
+    @Override
+    public boolean equals (Object o) {
+        if (!(o instanceof ServiceState)) return false;
+        ServiceState s = (ServiceState) o;
+
+        synchronized (mNetworkRegistrationInfos) {
+            return mVoiceRegState == s.mVoiceRegState
+                    && mDataRegState == s.mDataRegState
+                    && mIsManualNetworkSelection == s.mIsManualNetworkSelection
+                    && mChannelNumber == s.mChannelNumber
+                    && Arrays.equals(mCellBandwidths, s.mCellBandwidths)
+                    && equalsHandlesNulls(mOperatorAlphaLong, s.mOperatorAlphaLong)
+                    && equalsHandlesNulls(mOperatorAlphaShort, s.mOperatorAlphaShort)
+                    && equalsHandlesNulls(mOperatorNumeric, s.mOperatorNumeric)
+                    && equalsHandlesNulls(mCssIndicator, s.mCssIndicator)
+                    && equalsHandlesNulls(mNetworkId, s.mNetworkId)
+                    && equalsHandlesNulls(mSystemId, s.mSystemId)
+                    && equalsHandlesNulls(mCdmaRoamingIndicator, s.mCdmaRoamingIndicator)
+                    && equalsHandlesNulls(mCdmaDefaultRoamingIndicator,
+                    s.mCdmaDefaultRoamingIndicator)
+                    && mIsEmergencyOnly == s.mIsEmergencyOnly
+                    && equalsHandlesNulls(mOperatorAlphaLongRaw, s.mOperatorAlphaLongRaw)
+                    && equalsHandlesNulls(mOperatorAlphaShortRaw, s.mOperatorAlphaShortRaw)
+                    && mNetworkRegistrationInfos.size() == s.mNetworkRegistrationInfos.size()
+                    && mNetworkRegistrationInfos.containsAll(s.mNetworkRegistrationInfos)
+                    && mNrFrequencyRange == s.mNrFrequencyRange
+                    && mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration
+                    && mIsIwlanPreferred == s.mIsIwlanPreferred;
+        }
+    }
+
+    /**
+     * Convert roaming type to string
+     *
+     * @param roamingType roaming type
+     * @return The roaming type in string format
+     *
+     * @hide
+     */
+    public static String roamingTypeToString(@RoamingType int roamingType) {
+        switch (roamingType) {
+            case ROAMING_TYPE_NOT_ROAMING: return "NOT_ROAMING";
+            case ROAMING_TYPE_UNKNOWN: return "UNKNOWN";
+            case ROAMING_TYPE_DOMESTIC: return "DOMESTIC";
+            case ROAMING_TYPE_INTERNATIONAL: return "INTERNATIONAL";
+        }
+        return "Unknown roaming type " + roamingType;
+    }
+
+    /**
+     * Convert radio technology to String
+     *
+     * @param rt radioTechnology
+     * @return String representation of the RAT
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static String rilRadioTechnologyToString(int rt) {
+        String rtString;
+
+        switch(rt) {
+            case RIL_RADIO_TECHNOLOGY_UNKNOWN:
+                rtString = "Unknown";
+                break;
+            case RIL_RADIO_TECHNOLOGY_GPRS:
+                rtString = "GPRS";
+                break;
+            case RIL_RADIO_TECHNOLOGY_EDGE:
+                rtString = "EDGE";
+                break;
+            case RIL_RADIO_TECHNOLOGY_UMTS:
+                rtString = "UMTS";
+                break;
+            case RIL_RADIO_TECHNOLOGY_IS95A:
+                rtString = "CDMA-IS95A";
+                break;
+            case RIL_RADIO_TECHNOLOGY_IS95B:
+                rtString = "CDMA-IS95B";
+                break;
+            case RIL_RADIO_TECHNOLOGY_1xRTT:
+                rtString = "1xRTT";
+                break;
+            case RIL_RADIO_TECHNOLOGY_EVDO_0:
+                rtString = "EvDo-rev.0";
+                break;
+            case RIL_RADIO_TECHNOLOGY_EVDO_A:
+                rtString = "EvDo-rev.A";
+                break;
+            case RIL_RADIO_TECHNOLOGY_HSDPA:
+                rtString = "HSDPA";
+                break;
+            case RIL_RADIO_TECHNOLOGY_HSUPA:
+                rtString = "HSUPA";
+                break;
+            case RIL_RADIO_TECHNOLOGY_HSPA:
+                rtString = "HSPA";
+                break;
+            case RIL_RADIO_TECHNOLOGY_EVDO_B:
+                rtString = "EvDo-rev.B";
+                break;
+            case RIL_RADIO_TECHNOLOGY_EHRPD:
+                rtString = "eHRPD";
+                break;
+            case RIL_RADIO_TECHNOLOGY_LTE:
+                rtString = "LTE";
+                break;
+            case RIL_RADIO_TECHNOLOGY_HSPAP:
+                rtString = "HSPAP";
+                break;
+            case RIL_RADIO_TECHNOLOGY_GSM:
+                rtString = "GSM";
+                break;
+            case RIL_RADIO_TECHNOLOGY_IWLAN:
+                rtString = "IWLAN";
+                break;
+            case RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+                rtString = "TD-SCDMA";
+                break;
+            case RIL_RADIO_TECHNOLOGY_LTE_CA:
+                rtString = "LTE_CA";
+                break;
+            case RIL_RADIO_TECHNOLOGY_NR:
+                rtString = "NR_SA";
+                break;
+            default:
+                rtString = "Unexpected";
+                Rlog.w(LOG_TAG, "Unexpected radioTechnology=" + rt);
+                break;
+        }
+        return rtString;
+    }
+
+    /**
+     * Convert frequency range into string
+     *
+     * @param range The cellular frequency range
+     * @return Frequency range in string format
+     *
+     * @hide
+     */
+    @android.ravenwood.annotation.RavenwoodKeep
+    public static @NonNull String frequencyRangeToString(@FrequencyRange int range) {
+        switch (range) {
+            case FREQUENCY_RANGE_UNKNOWN: return "UNKNOWN";
+            case FREQUENCY_RANGE_LOW: return "LOW";
+            case FREQUENCY_RANGE_MID: return "MID";
+            case FREQUENCY_RANGE_HIGH: return "HIGH";
+            case FREQUENCY_RANGE_MMWAVE: return "MMWAVE";
+            default:
+                return Integer.toString(range);
+        }
+    }
+
+    /**
+     * Convert RIL Service State to String
+     *
+     * @param serviceState
+     * @return String representation of the ServiceState
+     *
+     * @hide
+     */
+    public static String rilServiceStateToString(int serviceState) {
+        switch(serviceState) {
+            case STATE_IN_SERVICE:
+                return "IN_SERVICE";
+            case STATE_OUT_OF_SERVICE:
+                return "OUT_OF_SERVICE";
+            case STATE_EMERGENCY_ONLY:
+                return "EMERGENCY_ONLY";
+            case STATE_POWER_OFF:
+                return "POWER_OFF";
+            default:
+                return "UNKNOWN";
+        }
+    }
+
+    @Override
+    public String toString() {
+        synchronized (mNetworkRegistrationInfos) {
+            return new StringBuilder().append("{mVoiceRegState=").append(mVoiceRegState)
+                    .append("(" + rilServiceStateToString(mVoiceRegState) + ")")
+                    .append(", mDataRegState=").append(mDataRegState)
+                    .append("(" + rilServiceStateToString(mDataRegState) + ")")
+                    .append(", mChannelNumber=").append(mChannelNumber)
+                    .append(", duplexMode()=").append(getDuplexMode())
+                    .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths))
+                    .append(", mOperatorAlphaLong=").append(mOperatorAlphaLong)
+                    .append(", mOperatorAlphaShort=").append(mOperatorAlphaShort)
+                    .append(", isManualNetworkSelection=").append(mIsManualNetworkSelection)
+                    .append(mIsManualNetworkSelection ? "(manual)" : "(automatic)")
+                    .append(", getRilVoiceRadioTechnology=").append(getRilVoiceRadioTechnology())
+                    .append("(" + rilRadioTechnologyToString(getRilVoiceRadioTechnology()) + ")")
+                    .append(", getRilDataRadioTechnology=").append(getRilDataRadioTechnology())
+                    .append("(" + rilRadioTechnologyToString(getRilDataRadioTechnology()) + ")")
+                    .append(", mCssIndicator=").append(mCssIndicator ? "supported" : "unsupported")
+                    .append(", mNetworkId=").append(mNetworkId)
+                    .append(", mSystemId=").append(mSystemId)
+                    .append(", mCdmaRoamingIndicator=").append(mCdmaRoamingIndicator)
+                    .append(", mCdmaDefaultRoamingIndicator=").append(mCdmaDefaultRoamingIndicator)
+                    .append(", mIsEmergencyOnly=").append(mIsEmergencyOnly)
+                    .append(", isUsingCarrierAggregation=").append(isUsingCarrierAggregation())
+                    .append(", mArfcnRsrpBoost=").append(mArfcnRsrpBoost)
+                    .append(", mNetworkRegistrationInfos=").append(mNetworkRegistrationInfos)
+                    .append(", mNrFrequencyRange=").append(Build.IS_DEBUGGABLE
+                            ? mNrFrequencyRange : FREQUENCY_RANGE_UNKNOWN)
+                    .append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw)
+                    .append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw)
+                    .append(", mIsDataRoamingFromRegistration=")
+                    .append(mIsDataRoamingFromRegistration)
+                    .append(", mIsIwlanPreferred=").append(mIsIwlanPreferred)
+                    .append("}").toString();
+        }
+    }
+
+    /**
+     * Initialize the service state. Set everything to the default value.
+     */
+    private void init() {
+        if (DBG) Rlog.d(LOG_TAG, "init");
+        mVoiceRegState = STATE_OUT_OF_SERVICE;
+        mDataRegState = STATE_OUT_OF_SERVICE;
+        mChannelNumber = -1;
+        mCellBandwidths = new int[0];
+        mOperatorAlphaLong = null;
+        mOperatorAlphaShort = null;
+        mOperatorNumeric = null;
+        mIsManualNetworkSelection = false;
+        mCssIndicator = false;
+        mNetworkId = -1;
+        mSystemId = -1;
+        mCdmaRoamingIndicator = -1;
+        mCdmaDefaultRoamingIndicator = -1;
+        mCdmaEriIconIndex = -1;
+        mCdmaEriIconMode = -1;
+        mIsEmergencyOnly = false;
+        mArfcnRsrpBoost = 0;
+        mNrFrequencyRange = FREQUENCY_RANGE_UNKNOWN;
+        synchronized (mNetworkRegistrationInfos) {
+            mNetworkRegistrationInfos.clear();
+            addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+                    .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
+                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                    .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
+                    .build());
+            addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+                    .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                    .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
+                    .build());
+            addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+                    .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+                    .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN)
+                    .build());
+        }
+        mOperatorAlphaLongRaw = null;
+        mOperatorAlphaShortRaw = null;
+        mIsDataRoamingFromRegistration = false;
+        mIsIwlanPreferred = false;
+    }
+
+    public void setStateOutOfService() {
+        init();
+    }
+
+    public void setStateOff() {
+        init();
+        mVoiceRegState = STATE_POWER_OFF;
+        mDataRegState = STATE_POWER_OFF;
+    }
+
+    /**
+     * Set the service state to out-of-service
+     *
+     * @param powerOff {@code true} if this is a power off case (i.e. Airplane mode on).
+     * @hide
+     */
+    public void setOutOfService(boolean powerOff) {
+        init();
+        if (powerOff) {
+            mVoiceRegState = STATE_POWER_OFF;
+            mDataRegState = STATE_POWER_OFF;
+        }
+    }
+
+    public void setState(int state) {
+        setVoiceRegState(state);
+        if (DBG) Rlog.e(LOG_TAG, "[ServiceState] setState deprecated use setVoiceRegState()");
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void setVoiceRegState(int state) {
+        mVoiceRegState = state;
+        if (DBG) Rlog.d(LOG_TAG, "[ServiceState] setVoiceRegState=" + mVoiceRegState);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setDataRegState(int state) {
+        mDataRegState = state;
+        if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRegState=" + mDataRegState);
+    }
+
+    /** @hide */
+    @TestApi
+    public void setCellBandwidths(int[] bandwidths) {
+        mCellBandwidths = bandwidths;
+    }
+
+    /** @hide */
+    @TestApi
+    public void setChannelNumber(int channelNumber) {
+        mChannelNumber = channelNumber;
+    }
+
+    public void setRoaming(boolean roaming) {
+        setVoiceRoaming(roaming);
+        setDataRoaming(roaming);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void setVoiceRoaming(boolean roaming) {
+        setVoiceRoamingType(roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING);
+    }
+
+    /** @hide */
+    @TestApi
+    public void setVoiceRoamingType(@RoamingType int type) {
+        NetworkRegistrationInfo regInfo = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (regInfo == null) {
+            regInfo = new NetworkRegistrationInfo.Builder()
+                    .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
+                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                    .build();
+        }
+        regInfo.setRoamingType(type);
+        addNetworkRegistrationInfo(regInfo);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void setDataRoaming(boolean dataRoaming) {
+        setDataRoamingType(dataRoaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING);
+    }
+
+    /** @hide */
+    @TestApi
+    public void setDataRoamingType(@RoamingType int type) {
+        NetworkRegistrationInfo regInfo = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (regInfo == null) {
+            regInfo = new NetworkRegistrationInfo.Builder()
+                    .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                    .build();
+        }
+        regInfo.setRoamingType(type);
+        addNetworkRegistrationInfo(regInfo);
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setEmergencyOnly(boolean emergencyOnly) {
+        mIsEmergencyOnly = emergencyOnly;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setCdmaRoamingIndicator(int roaming) {
+        this.mCdmaRoamingIndicator = roaming;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setCdmaDefaultRoamingIndicator (int roaming) {
+        this.mCdmaDefaultRoamingIndicator = roaming;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setCdmaEriIconIndex(int index) {
+        this.mCdmaEriIconIndex = index;
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setCdmaEriIconMode(int mode) {
+        this.mCdmaEriIconMode = mode;
+    }
+
+    public void setOperatorName(String longName, String shortName, String numeric) {
+        mOperatorAlphaLong = longName;
+        mOperatorAlphaShort = shortName;
+        mOperatorNumeric = numeric;
+    }
+
+    /**
+     * In CDMA, mOperatorAlphaLong can be set from the ERI text.
+     * This is done from the GsmCdmaPhone and not from the ServiceStateTracker.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void setOperatorAlphaLong(@Nullable String longName) {
+        mOperatorAlphaLong = longName;
+    }
+
+    public void setIsManualSelection(boolean isManual) {
+        mIsManualNetworkSelection = isManual;
+    }
+
+    /**
+     * Test whether two objects hold the same data values or both are null.
+     *
+     * @param a first obj
+     * @param b second obj
+     * @return true if two objects equal or both are null
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private static boolean equalsHandlesNulls (Object a, Object b) {
+        return (a == null) ? (b == null) : a.equals (b);
+    }
+
+    /**
+     * Set ServiceState based on intent notifier map.
+     *
+     * @param m intent notifier map
+     * @hide
+     */
+    @UnsupportedAppUsage
+    private void setFromNotifierBundle(Bundle m) {
+        ServiceState ssFromBundle = m.getParcelable(EXTRA_SERVICE_STATE, android.telephony.ServiceState.class);
+        if (ssFromBundle != null) {
+            copyFrom(ssFromBundle);
+        }
+    }
+
+    /**
+     * Set intent notifier Bundle based on service state.
+     *
+     * Put ServiceState object and its fields into bundle which is used by TelephonyRegistry
+     * to broadcast {@link Intent#ACTION_SERVICE_STATE}.
+     *
+     * @param m intent notifier Bundle
+     * @hide
+     *
+     */
+    @UnsupportedAppUsage
+    public void fillInNotifierBundle(@NonNull Bundle m) {
+        m.putParcelable(EXTRA_SERVICE_STATE, this);
+        // serviceState already consists of below entries.
+        // for backward compatibility, we continue fill in below entries.
+        m.putInt("voiceRegState", mVoiceRegState);
+        m.putInt("dataRegState", mDataRegState);
+        m.putInt("dataRoamingType", getDataRoamingType());
+        m.putInt("voiceRoamingType", getVoiceRoamingType());
+        m.putString("operator-alpha-long", mOperatorAlphaLong);
+        m.putString("operator-alpha-short", mOperatorAlphaShort);
+        m.putString("operator-numeric", mOperatorNumeric);
+        m.putString("data-operator-alpha-long", mOperatorAlphaLong);
+        m.putString("data-operator-alpha-short", mOperatorAlphaShort);
+        m.putString("data-operator-numeric", mOperatorNumeric);
+        m.putBoolean("manual", mIsManualNetworkSelection);
+        m.putInt("radioTechnology", getRilVoiceRadioTechnology());
+        m.putInt("dataRadioTechnology", getRilDataRadioTechnology());
+        m.putBoolean("cssIndicator", mCssIndicator);
+        m.putInt("networkId", mNetworkId);
+        m.putInt("systemId", mSystemId);
+        m.putInt("cdmaRoamingIndicator", mCdmaRoamingIndicator);
+        m.putInt("cdmaDefaultRoamingIndicator", mCdmaDefaultRoamingIndicator);
+        m.putBoolean("emergencyOnly", mIsEmergencyOnly);
+        m.putBoolean("isDataRoamingFromRegistration", getDataRoamingFromRegistration());
+        m.putBoolean("isUsingCarrierAggregation", isUsingCarrierAggregation());
+        m.putInt("ArfcnRsrpBoost", mArfcnRsrpBoost);
+        m.putInt("ChannelNumber", mChannelNumber);
+        m.putIntArray("CellBandwidths", mCellBandwidths);
+        m.putInt("mNrFrequencyRange", mNrFrequencyRange);
+        m.putString("operator-alpha-long-raw", mOperatorAlphaLongRaw);
+        m.putString("operator-alpha-short-raw", mOperatorAlphaShortRaw);
+    }
+
+    /** @hide */
+    @TestApi
+    public void setRilVoiceRadioTechnology(@RilRadioTechnology int rt) {
+        Rlog.e(LOG_TAG, "ServiceState.setRilVoiceRadioTechnology() called. It's encouraged to "
+                + "use addNetworkRegistrationInfo() instead *******");
+        // Sync to network registration state
+        NetworkRegistrationInfo regInfo = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (regInfo == null) {
+            regInfo = new NetworkRegistrationInfo.Builder()
+                    .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
+                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                    .build();
+        }
+        regInfo.setAccessNetworkTechnology(rilRadioTechnologyToNetworkType(rt));
+        addNetworkRegistrationInfo(regInfo);
+    }
+
+
+    /** @hide */
+    @TestApi
+    public void setRilDataRadioTechnology(@RilRadioTechnology int rt) {
+        Rlog.e(LOG_TAG, "ServiceState.setRilDataRadioTechnology() called. It's encouraged to "
+                + "use addNetworkRegistrationInfo() instead *******");
+        // Sync to network registration state. Always write down the WWAN transport. For AP-assisted
+        // mode device, use addNetworkRegistrationInfo() to set the correct transport if RAT
+        // is IWLAN.
+        NetworkRegistrationInfo regInfo = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+        if (regInfo == null) {
+            regInfo = new NetworkRegistrationInfo.Builder()
+                    .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                    .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                    .build();
+        }
+        regInfo.setAccessNetworkTechnology(rilRadioTechnologyToNetworkType(rt));
+        addNetworkRegistrationInfo(regInfo);
+    }
+
+    /** @hide */
+    public boolean isUsingCarrierAggregation() {
+        if (getCellBandwidths().length > 1) return true;
+
+        synchronized (mNetworkRegistrationInfos) {
+            for (NetworkRegistrationInfo nri : mNetworkRegistrationInfos) {
+                if (nri.isUsingCarrierAggregation()) return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Get the 5G NR frequency range the device is currently registered.
+     *
+     * @return the frequency range of 5G NR.
+     * @hide
+     */
+    public @FrequencyRange int getNrFrequencyRange() {
+        return mNrFrequencyRange;
+    }
+
+    /**
+     * Get the NR 5G state of the mobile data network.
+     * @return the NR 5G state.
+     * @hide
+     */
+    public @NRState int getNrState() {
+        final NetworkRegistrationInfo regInfo = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (regInfo == null) return NetworkRegistrationInfo.NR_STATE_NONE;
+        return regInfo.getNrState();
+    }
+
+    /**
+     * @param nrFrequencyRange the frequency range of 5G NR.
+     * @hide
+     */
+    public void setNrFrequencyRange(@FrequencyRange int nrFrequencyRange) {
+        mNrFrequencyRange = nrFrequencyRange;
+    }
+
+    /** @hide */
+    public int getArfcnRsrpBoost() {
+        return mArfcnRsrpBoost;
+    }
+
+    /** @hide */
+    public void setArfcnRsrpBoost(int arfcnRsrpBoost) {
+        mArfcnRsrpBoost = arfcnRsrpBoost;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setCssIndicator(int css) {
+        this.mCssIndicator = (css != 0);
+    }
+
+    /** @hide */
+    @TestApi
+    public void setCdmaSystemAndNetworkId(int systemId, int networkId) {
+        this.mSystemId = systemId;
+        this.mNetworkId = networkId;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int getRilVoiceRadioTechnology() {
+        NetworkRegistrationInfo wwanRegInfo = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (wwanRegInfo != null) {
+            return networkTypeToRilRadioTechnology(wwanRegInfo.getAccessNetworkTechnology());
+        }
+        return RIL_RADIO_TECHNOLOGY_UNKNOWN;
+    }
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int getRilDataRadioTechnology() {
+        return networkTypeToRilRadioTechnology(getDataNetworkType());
+    }
+
+    /**
+     * Transform RIL radio technology {@link RilRadioTechnology} value to Network
+     * type {@link NetworkType}.
+     *
+     * @param rat The RIL radio technology {@link RilRadioTechnology}.
+     * @return The network type {@link NetworkType}.
+     *
+     * @hide
+     */
+    public static int rilRadioTechnologyToNetworkType(@RilRadioTechnology int rat) {
+        switch(rat) {
+            case RIL_RADIO_TECHNOLOGY_GPRS:
+                return TelephonyManager.NETWORK_TYPE_GPRS;
+            case RIL_RADIO_TECHNOLOGY_EDGE:
+                return TelephonyManager.NETWORK_TYPE_EDGE;
+            case RIL_RADIO_TECHNOLOGY_UMTS:
+                return TelephonyManager.NETWORK_TYPE_UMTS;
+            case RIL_RADIO_TECHNOLOGY_HSDPA:
+                return TelephonyManager.NETWORK_TYPE_HSDPA;
+            case RIL_RADIO_TECHNOLOGY_HSUPA:
+                return TelephonyManager.NETWORK_TYPE_HSUPA;
+            case RIL_RADIO_TECHNOLOGY_HSPA:
+                return TelephonyManager.NETWORK_TYPE_HSPA;
+            case RIL_RADIO_TECHNOLOGY_IS95A:
+            case RIL_RADIO_TECHNOLOGY_IS95B:
+                return TelephonyManager.NETWORK_TYPE_CDMA;
+            case RIL_RADIO_TECHNOLOGY_1xRTT:
+                return TelephonyManager.NETWORK_TYPE_1xRTT;
+            case RIL_RADIO_TECHNOLOGY_EVDO_0:
+                return TelephonyManager.NETWORK_TYPE_EVDO_0;
+            case RIL_RADIO_TECHNOLOGY_EVDO_A:
+                return TelephonyManager.NETWORK_TYPE_EVDO_A;
+            case RIL_RADIO_TECHNOLOGY_EVDO_B:
+                return TelephonyManager.NETWORK_TYPE_EVDO_B;
+            case RIL_RADIO_TECHNOLOGY_EHRPD:
+                return TelephonyManager.NETWORK_TYPE_EHRPD;
+            case RIL_RADIO_TECHNOLOGY_LTE:
+                return TelephonyManager.NETWORK_TYPE_LTE;
+            case RIL_RADIO_TECHNOLOGY_HSPAP:
+                return TelephonyManager.NETWORK_TYPE_HSPAP;
+            case RIL_RADIO_TECHNOLOGY_GSM:
+                return TelephonyManager.NETWORK_TYPE_GSM;
+            case RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+                return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
+            case RIL_RADIO_TECHNOLOGY_IWLAN:
+                return TelephonyManager.NETWORK_TYPE_IWLAN;
+            case RIL_RADIO_TECHNOLOGY_LTE_CA:
+                return TelephonyManager.NETWORK_TYPE_LTE_CA;
+            case RIL_RADIO_TECHNOLOGY_NR:
+                return TelephonyManager.NETWORK_TYPE_NR;
+            default:
+                return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        }
+    }
+
+    /** @hide */
+    public static int rilRadioTechnologyToAccessNetworkType(@RilRadioTechnology int rt) {
+        switch(rt) {
+            case RIL_RADIO_TECHNOLOGY_GPRS:
+            case RIL_RADIO_TECHNOLOGY_EDGE:
+            case RIL_RADIO_TECHNOLOGY_GSM:
+                return AccessNetworkType.GERAN;
+            case RIL_RADIO_TECHNOLOGY_UMTS:
+            case RIL_RADIO_TECHNOLOGY_HSDPA:
+            case RIL_RADIO_TECHNOLOGY_HSPAP:
+            case RIL_RADIO_TECHNOLOGY_HSUPA:
+            case RIL_RADIO_TECHNOLOGY_HSPA:
+            case RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+                return AccessNetworkType.UTRAN;
+            case RIL_RADIO_TECHNOLOGY_IS95A:
+            case RIL_RADIO_TECHNOLOGY_IS95B:
+            case RIL_RADIO_TECHNOLOGY_1xRTT:
+            case RIL_RADIO_TECHNOLOGY_EVDO_0:
+            case RIL_RADIO_TECHNOLOGY_EVDO_A:
+            case RIL_RADIO_TECHNOLOGY_EVDO_B:
+            case RIL_RADIO_TECHNOLOGY_EHRPD:
+                return AccessNetworkType.CDMA2000;
+            case RIL_RADIO_TECHNOLOGY_LTE:
+            case RIL_RADIO_TECHNOLOGY_LTE_CA:
+                return AccessNetworkType.EUTRAN;
+            case RIL_RADIO_TECHNOLOGY_NR:
+                return AccessNetworkType.NGRAN;
+            case RIL_RADIO_TECHNOLOGY_IWLAN:
+                return AccessNetworkType.IWLAN;
+            case RIL_RADIO_TECHNOLOGY_UNKNOWN:
+            default:
+                return AccessNetworkType.UNKNOWN;
+        }
+    }
+
+    /**
+     * Transform network type {@link NetworkType} value to RIL radio technology
+     * {@link RilRadioTechnology}.
+     *
+     * @param networkType The network type {@link NetworkType}.
+     * @return The RIL radio technology {@link RilRadioTechnology}.
+     *
+     * @hide
+     */
+    public static int networkTypeToRilRadioTechnology(int networkType) {
+        switch(networkType) {
+            case TelephonyManager.NETWORK_TYPE_GPRS:
+                return RIL_RADIO_TECHNOLOGY_GPRS;
+            case TelephonyManager.NETWORK_TYPE_EDGE:
+                return RIL_RADIO_TECHNOLOGY_EDGE;
+            case TelephonyManager.NETWORK_TYPE_UMTS:
+                return RIL_RADIO_TECHNOLOGY_UMTS;
+            case TelephonyManager.NETWORK_TYPE_HSDPA:
+                return RIL_RADIO_TECHNOLOGY_HSDPA;
+            case TelephonyManager.NETWORK_TYPE_HSUPA:
+                return RIL_RADIO_TECHNOLOGY_HSUPA;
+            case TelephonyManager.NETWORK_TYPE_HSPA:
+                return RIL_RADIO_TECHNOLOGY_HSPA;
+            case TelephonyManager.NETWORK_TYPE_CDMA:
+                return RIL_RADIO_TECHNOLOGY_IS95A;
+            case TelephonyManager.NETWORK_TYPE_1xRTT:
+                return RIL_RADIO_TECHNOLOGY_1xRTT;
+            case TelephonyManager.NETWORK_TYPE_EVDO_0:
+                return RIL_RADIO_TECHNOLOGY_EVDO_0;
+            case TelephonyManager.NETWORK_TYPE_EVDO_A:
+                return RIL_RADIO_TECHNOLOGY_EVDO_A;
+            case TelephonyManager.NETWORK_TYPE_EVDO_B:
+                return RIL_RADIO_TECHNOLOGY_EVDO_B;
+            case TelephonyManager.NETWORK_TYPE_EHRPD:
+                return RIL_RADIO_TECHNOLOGY_EHRPD;
+            case TelephonyManager.NETWORK_TYPE_LTE:
+                return RIL_RADIO_TECHNOLOGY_LTE;
+            case TelephonyManager.NETWORK_TYPE_HSPAP:
+                return RIL_RADIO_TECHNOLOGY_HSPAP;
+            case TelephonyManager.NETWORK_TYPE_GSM:
+                return RIL_RADIO_TECHNOLOGY_GSM;
+            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+                return RIL_RADIO_TECHNOLOGY_TD_SCDMA;
+            case TelephonyManager.NETWORK_TYPE_IWLAN:
+                return RIL_RADIO_TECHNOLOGY_IWLAN;
+            case TelephonyManager.NETWORK_TYPE_LTE_CA:
+                return RIL_RADIO_TECHNOLOGY_LTE_CA;
+            case TelephonyManager.NETWORK_TYPE_NR:
+                return RIL_RADIO_TECHNOLOGY_NR;
+            default:
+                return RIL_RADIO_TECHNOLOGY_UNKNOWN;
+        }
+    }
+
+    /**
+     * Get current data network type.
+     *
+     * Note that for IWLAN AP-assisted mode device, which is reporting both camped access networks
+     * (cellular RAT and IWLAN)at the same time, this API is simulating the old legacy mode device
+     * behavior,
+     *
+     * @return Current data network type
+     * @hide
+     */
+    @TestApi
+    public @NetworkType int getDataNetworkType() {
+        final NetworkRegistrationInfo iwlanRegInfo = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+        final NetworkRegistrationInfo wwanRegInfo = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+        // For legacy mode device, or AP-assisted mode device but IWLAN is out of service, use
+        // the RAT from cellular.
+        if (iwlanRegInfo == null || !iwlanRegInfo.isInService()) {
+            return (wwanRegInfo != null) ? wwanRegInfo.getAccessNetworkTechnology()
+                    : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        }
+
+        // At this point, it must be an AP-assisted mode device and IWLAN is in service. We should
+        // use the RAT from IWLAN service is cellular is out of service, or when both are in service
+        // and any APN type of data is preferred on IWLAN.
+        if (!wwanRegInfo.isInService() || mIsIwlanPreferred) {
+            return iwlanRegInfo.getAccessNetworkTechnology();
+        }
+
+        // If both cellular and IWLAN are in service, but no APN is preferred on IWLAN, still use
+        // the RAT from cellular.
+        return wwanRegInfo.getAccessNetworkTechnology();
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public @NetworkType int getVoiceNetworkType() {
+        final NetworkRegistrationInfo regState = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (regState != null) {
+            return regState.getAccessNetworkTechnology();
+        }
+        return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public int getCssIndicator() {
+        return this.mCssIndicator ? 1 : 0;
+    }
+
+    /**
+     * Get the CDMA NID (Network Identification Number), a number uniquely identifying a network
+     * within a wireless system. (Defined in 3GPP2 C.S0023 3.4.8)
+     *
+     * <p>Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return
+     * {@link #UNKNOWN_ID}.
+     *
+     * @return The CDMA NID or {@link #UNKNOWN_ID} if not available.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.ACCESS_FINE_LOCATION,
+            android.Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    public int getCdmaNetworkId() {
+        return this.mNetworkId;
+    }
+
+    /**
+     * Get the CDMA SID (System Identification Number), a number uniquely identifying a wireless
+     * system. (Defined in 3GPP2 C.S0023 3.4.8)
+     *
+     * <p>Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return
+     * {@link #UNKNOWN_ID}.
+     *
+     * @return The CDMA SID or {@link #UNKNOWN_ID} if not available.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.ACCESS_FINE_LOCATION,
+            android.Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    public int getCdmaSystemId() {
+        return this.mSystemId;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static boolean isGsm(int radioTechnology) {
+        return radioTechnology == RIL_RADIO_TECHNOLOGY_GPRS
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_EDGE
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_UMTS
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_HSDPA
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_HSUPA
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_HSPA
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_LTE
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_HSPAP
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_GSM
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_TD_SCDMA
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_IWLAN
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_LTE_CA
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_NR;
+
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public static boolean isCdma(int radioTechnology) {
+        return radioTechnology == RIL_RADIO_TECHNOLOGY_IS95A
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_IS95B
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_1xRTT
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_EVDO_0
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_EVDO_A
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_EVDO_B
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_EHRPD;
+    }
+
+    /** @hide */
+    public static boolean isPsOnlyTech(int radioTechnology) {
+        return radioTechnology == RIL_RADIO_TECHNOLOGY_LTE
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_LTE_CA
+                || radioTechnology == RIL_RADIO_TECHNOLOGY_NR;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public static boolean bearerBitmapHasCdma(int networkTypeBitmask) {
+        return (RIL_RADIO_CDMA_TECHNOLOGY_BITMASK
+                & convertNetworkTypeBitmaskToBearerBitmask(networkTypeBitmask)) != 0;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static boolean bitmaskHasTech(int bearerBitmask, int radioTech) {
+        if (bearerBitmask == 0) {
+            return true;
+        } else if (radioTech >= 1) {
+            return ((bearerBitmask & (1 << (radioTech - 1))) != 0);
+        }
+        return false;
+    }
+
+    /** @hide */
+    public static int getBitmaskForTech(int radioTech) {
+        if (radioTech >= 1) {
+            return (1 << (radioTech - 1));
+        }
+        return 0;
+    }
+
+    /** @hide */
+    public static int getBitmaskFromString(String bearerList) {
+        String[] bearers = bearerList.split("\\|");
+        int bearerBitmask = 0;
+        for (String bearer : bearers) {
+            int bearerInt = 0;
+            try {
+                bearerInt = Integer.parseInt(bearer.trim());
+            } catch (NumberFormatException nfe) {
+                return 0;
+            }
+
+            if (bearerInt == 0) {
+                return 0;
+            }
+
+            bearerBitmask |= getBitmaskForTech(bearerInt);
+        }
+        return bearerBitmask;
+    }
+
+    /**
+     * Convert network type bitmask to bearer bitmask.
+     *
+     * @param networkTypeBitmask The network type bitmask value
+     * @return The bearer bitmask value.
+     *
+     * @hide
+     */
+    public static int convertNetworkTypeBitmaskToBearerBitmask(int networkTypeBitmask) {
+        if (networkTypeBitmask == 0) {
+            return 0;
+        }
+        int bearerBitmask = 0;
+        for (int bearerInt = 0; bearerInt < NEXT_RIL_RADIO_TECHNOLOGY; bearerInt++) {
+            if (bitmaskHasTech(networkTypeBitmask, rilRadioTechnologyToNetworkType(bearerInt))) {
+                bearerBitmask |= getBitmaskForTech(bearerInt);
+            }
+        }
+        return bearerBitmask;
+    }
+
+    /**
+     * Convert bearer bitmask to network type bitmask.
+     *
+     * @param bearerBitmask The bearer bitmask value.
+     * @return The network type bitmask value.
+     *
+     * @hide
+     */
+    public static int convertBearerBitmaskToNetworkTypeBitmask(int bearerBitmask) {
+        if (bearerBitmask == 0) {
+            return 0;
+        }
+        int networkTypeBitmask = 0;
+        for (int bearerInt = 0; bearerInt < NEXT_RIL_RADIO_TECHNOLOGY; bearerInt++) {
+            if (bitmaskHasTech(bearerBitmask, bearerInt)) {
+                networkTypeBitmask |= getBitmaskForTech(rilRadioTechnologyToNetworkType(bearerInt));
+            }
+        }
+        return networkTypeBitmask;
+    }
+
+    /**
+     * Returns a merged ServiceState consisting of the base SS with voice settings from the
+     * voice SS. The voice SS is only used if it is IN_SERVICE (otherwise the base SS is returned).
+     * @hide
+     * */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static ServiceState mergeServiceStates(ServiceState baseSs, ServiceState voiceSs) {
+        if (voiceSs.mVoiceRegState != STATE_IN_SERVICE) {
+            return baseSs;
+        }
+
+        ServiceState newSs = new ServiceState(baseSs);
+
+        // voice overrides
+        newSs.mVoiceRegState = voiceSs.mVoiceRegState;
+        newSs.mIsEmergencyOnly = false; // only get here if voice is IN_SERVICE
+
+        return newSs;
+    }
+
+    /**
+     * Get all of the available network registration info.
+     *
+     * @return List of {@link NetworkRegistrationInfo}
+     */
+    @NonNull
+    public List<NetworkRegistrationInfo> getNetworkRegistrationInfoList() {
+        synchronized (mNetworkRegistrationInfos) {
+            List<NetworkRegistrationInfo> newList = new ArrayList<>();
+            for (NetworkRegistrationInfo nri : mNetworkRegistrationInfos) {
+                newList.add(new NetworkRegistrationInfo(nri));
+            }
+            return newList;
+        }
+    }
+
+    /**
+     * Get the network registration info list for the transport type.
+     *
+     * @param transportType The transport type
+     * @return List of {@link NetworkRegistrationInfo}
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public List<NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(
+            @TransportType int transportType) {
+        List<NetworkRegistrationInfo> list = new ArrayList<>();
+
+        synchronized (mNetworkRegistrationInfos) {
+            for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) {
+                if (networkRegistrationInfo.getTransportType() == transportType) {
+                    list.add(new NetworkRegistrationInfo(networkRegistrationInfo));
+                }
+            }
+        }
+
+        return list;
+    }
+
+    /**
+     * Get the network registration info list for the network domain.
+     *
+     * @param domain The network {@link NetworkRegistrationInfo.Domain domain}
+     * @return List of {@link NetworkRegistrationInfo}
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public List<NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(
+            @Domain int domain) {
+        List<NetworkRegistrationInfo> list = new ArrayList<>();
+
+        synchronized (mNetworkRegistrationInfos) {
+            for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) {
+                if ((networkRegistrationInfo.getDomain() & domain) != 0) {
+                    list.add(new NetworkRegistrationInfo(networkRegistrationInfo));
+                }
+            }
+        }
+
+        return list;
+    }
+
+    /**
+     * Get the network registration state for the transport type and network domain.
+     * If multiple domains are in the input bitmask, only the first one from
+     * networkRegistrationInfo.getDomain() will be returned.
+     *
+     * @param domain The network {@link NetworkRegistrationInfo.Domain domain}
+     * @param transportType The transport type
+     * @return The matching {@link NetworkRegistrationInfo}
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    public NetworkRegistrationInfo getNetworkRegistrationInfo(@Domain int domain,
+                                                              @TransportType int transportType) {
+        synchronized (mNetworkRegistrationInfos) {
+            for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) {
+                if (networkRegistrationInfo.getTransportType() == transportType
+                        && (networkRegistrationInfo.getDomain() & domain) != 0) {
+                    return new NetworkRegistrationInfo(networkRegistrationInfo);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public void addNetworkRegistrationInfo(NetworkRegistrationInfo nri) {
+        if (nri == null) return;
+
+        synchronized (mNetworkRegistrationInfos) {
+            for (int i = 0; i < mNetworkRegistrationInfos.size(); i++) {
+                NetworkRegistrationInfo curRegState = mNetworkRegistrationInfos.get(i);
+                if (curRegState.getTransportType() == nri.getTransportType()
+                        && curRegState.getDomain() == nri.getDomain()) {
+                    mNetworkRegistrationInfos.remove(i);
+                    break;
+                }
+            }
+
+            mNetworkRegistrationInfos.add(new NetworkRegistrationInfo(nri));
+        }
+    }
+
+    /**
+     * Returns a copy of self with location-identifying information removed.
+     * Always clears the NetworkRegistrationInfo's CellIdentity fields, but if removeCoarseLocation
+     * is true, clears other info as well.
+     *
+     * @param removeCoarseLocation Whether to also remove coarse location information.
+     *                             if false, it only clears fine location information such as
+     *                             NetworkRegistrationInfo's CellIdentity fields; If true, it will
+     *                             also remove other location information such as operator's MCC
+     *                             and MNC.
+     * @return the copied ServiceState with location info sanitized.
+     * @hide
+     */
+    @NonNull
+    public ServiceState createLocationInfoSanitizedCopy(boolean removeCoarseLocation) {
+        ServiceState state = new ServiceState(this);
+        synchronized (state.mNetworkRegistrationInfos) {
+            List<NetworkRegistrationInfo> networkRegistrationInfos =
+                    state.mNetworkRegistrationInfos.stream()
+                            .map(NetworkRegistrationInfo::sanitizeLocationInfo)
+                            .collect(Collectors.toList());
+            state.mNetworkRegistrationInfos.clear();
+            state.mNetworkRegistrationInfos.addAll(networkRegistrationInfos);
+        }
+        if (!removeCoarseLocation) return state;
+
+        state.mOperatorAlphaLong = null;
+        state.mOperatorAlphaShort = null;
+        state.mOperatorNumeric = null;
+        state.mSystemId = UNKNOWN_ID;
+        state.mNetworkId = UNKNOWN_ID;
+
+        return state;
+    }
+
+    /**
+     * @hide
+     */
+    public void setOperatorAlphaLongRaw(String operatorAlphaLong) {
+        mOperatorAlphaLongRaw = operatorAlphaLong;
+    }
+
+    /**
+     * The current registered raw data network operator name in long alphanumeric format.
+     *
+     * The long format can be up to 16 characters long.
+     *
+     * @return long raw name of operator, null if unregistered or unknown
+     * @hide
+     */
+    @Nullable
+    public String getOperatorAlphaLongRaw() {
+        return mOperatorAlphaLongRaw;
+    }
+
+    /**
+     * @hide
+     */
+    public void setOperatorAlphaShortRaw(String operatorAlphaShort) {
+        mOperatorAlphaShortRaw = operatorAlphaShort;
+    }
+
+    /**
+     * The current registered raw data network operator name in short alphanumeric format.
+     *
+     * The short format can be up to 8 characters long.
+     *
+     * @return short raw name of operator, null if unregistered or unknown
+     * @hide
+     */
+    @Nullable
+    public String getOperatorAlphaShortRaw() {
+        return mOperatorAlphaShortRaw;
+    }
+
+    /**
+     * Set to {@code true} if any data network is preferred on IWLAN.
+     *
+     * @param isIwlanPreferred {@code true} if IWLAN is preferred.
+     * @hide
+     */
+    public void setIwlanPreferred(boolean isIwlanPreferred) {
+        mIsIwlanPreferred = isIwlanPreferred;
+    }
+
+    /**
+     * @return {@code true} if any data network is preferred on IWLAN.
+     *
+     * Note only when this value is true, {@link #getDataNetworkType()} will return
+     * {@link TelephonyManager#NETWORK_TYPE_IWLAN} when AP-assisted mode device camps on both
+     * cellular and IWLAN. This value does not affect legacy mode devices as the data network
+     * type is directly reported by the modem.
+     *
+     * @hide
+     */
+    public boolean isIwlanPreferred() {
+        return mIsIwlanPreferred;
+    }
+
+    /**
+     * This indicates whether the device is searching for service.
+     *
+     * This API reports the modem searching status for
+     * {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN} (cellular) service in either
+     * {@link NetworkRegistrationInfo#DOMAIN_CS} or {@link NetworkRegistrationInfo#DOMAIN_PS}.
+     * This API will not report searching status for
+     * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}.
+     *
+     * @return {@code true} whenever the modem is searching for service.
+     */
+    public boolean isSearching() {
+        NetworkRegistrationInfo psRegState = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+        if (psRegState != null && psRegState.getRegistrationState()
+                == NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING) {
+            return true;
+        }
+
+        NetworkRegistrationInfo csRegState = getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+        if (csRegState != null && csRegState.getRegistrationState()
+                == NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * The frequency range is valid or not.
+     *
+     * @param frequencyRange The frequency range {@link FrequencyRange}.
+     * @return {@code true} if valid, {@code false} otherwise.
+     *
+     * @hide
+     */
+    public static boolean isFrequencyRangeValid(int frequencyRange) {
+        if (frequencyRange == FREQUENCY_RANGE_LOW
+                || frequencyRange == FREQUENCY_RANGE_MID
+                || frequencyRange == FREQUENCY_RANGE_HIGH
+                || frequencyRange == FREQUENCY_RANGE_MMWAVE) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Get whether device is connected to a non-terrestrial network.
+     *
+     * @return {@code true} if device is connected to a non-terrestrial network else {@code false}.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public boolean isUsingNonTerrestrialNetwork() {
+        synchronized (mNetworkRegistrationInfos) {
+            for (NetworkRegistrationInfo nri : mNetworkRegistrationInfos) {
+                if (nri.isNonTerrestrialNetwork()) return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/android-35/android/telephony/SignalStrength.java b/android-35/android/telephony/SignalStrength.java
new file mode 100644
index 0000000..53f347e
--- /dev/null
+++ b/android-35/android/telephony/SignalStrength.java
@@ -0,0 +1,904 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.os.SystemClock;
+
+import com.android.telephony.Rlog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Contains phone signal strength related information.
+ */
+public class SignalStrength implements Parcelable {
+
+    private static final String LOG_TAG = "SignalStrength";
+    private static final boolean DBG = false;
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN =
+            CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; // = 0
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static final int SIGNAL_STRENGTH_POOR =
+            CellSignalStrength.SIGNAL_STRENGTH_POOR; // = 1
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static final int SIGNAL_STRENGTH_MODERATE =
+            CellSignalStrength.SIGNAL_STRENGTH_MODERATE; // = 2
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static final int SIGNAL_STRENGTH_GOOD =
+            CellSignalStrength.SIGNAL_STRENGTH_GOOD; // = 3
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static final int SIGNAL_STRENGTH_GREAT =
+            CellSignalStrength.SIGNAL_STRENGTH_GREAT; // = 4
+    /** @hide */
+    @UnsupportedAppUsage
+    public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
+
+    /**
+     * Indicates the invalid measures of signal strength.
+     *
+     * For example, this can be returned by {@link #getEvdoDbm()} or {@link #getCdmaDbm()}
+     */
+    public static final int INVALID = Integer.MAX_VALUE;
+
+    // Timestamp of SignalStrength since boot
+    // Effectively final. Timestamp is set during construction of SignalStrength
+    private long mTimestampMillis;
+
+    private boolean mLteAsPrimaryInNrNsa = true;
+
+    CellSignalStrengthCdma mCdma;
+    CellSignalStrengthGsm mGsm;
+    CellSignalStrengthWcdma mWcdma;
+    CellSignalStrengthTdscdma mTdscdma;
+    CellSignalStrengthLte mLte;
+    CellSignalStrengthNr mNr;
+
+    /**
+     * This constructor is used to create SignalStrength with default
+     * values.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public SignalStrength() {
+        this(new CellSignalStrengthCdma(), new CellSignalStrengthGsm(),
+                new CellSignalStrengthWcdma(), new CellSignalStrengthTdscdma(),
+                new CellSignalStrengthLte(), new CellSignalStrengthNr());
+    }
+
+    /**
+     * Constructor with all fields present
+     *
+     * @hide
+     */
+    public SignalStrength(
+            @NonNull CellSignalStrengthCdma cdma,
+            @NonNull CellSignalStrengthGsm gsm,
+            @NonNull CellSignalStrengthWcdma wcdma,
+            @NonNull CellSignalStrengthTdscdma tdscdma,
+            @NonNull CellSignalStrengthLte lte,
+            @NonNull CellSignalStrengthNr nr) {
+        mCdma = cdma;
+        mGsm = gsm;
+        mWcdma = wcdma;
+        mTdscdma = tdscdma;
+        mLte = lte;
+        mNr = nr;
+        mTimestampMillis = SystemClock.elapsedRealtime();
+    }
+
+    private CellSignalStrength getPrimary() {
+        // This behavior is intended to replicate the legacy behavior of getLevel() by prioritizing
+        // newer faster RATs for default/for display purposes.
+
+        if (mLteAsPrimaryInNrNsa) {
+            if (mLte.isValid()) return mLte;
+        }
+        if (mNr.isValid()) return mNr;
+        if (mLte.isValid()) return mLte;
+        if (mCdma.isValid()) return mCdma;
+        if (mTdscdma.isValid()) return mTdscdma;
+        if (mWcdma.isValid()) return mWcdma;
+        if (mGsm.isValid()) return mGsm;
+        return mLte;
+    }
+
+    /**
+     * Returns a List of CellSignalStrength Components of this SignalStrength Report.
+     *
+     * Use this API to access underlying
+     * {@link android.telephony.CellSignalStrength CellSignalStrength} objects that provide more
+     * granular information about the SignalStrength report. Only valid (non-empty)
+     * CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
+     * and the list may contain more than one instance of a CellSignalStrength type.
+     *
+     * @return a List of CellSignalStrength or an empty List if there are no valid measurements.
+     *
+     * @see android.telephony.CellSignalStrength
+     * @see android.telephony.CellSignalStrengthNr
+     * @see android.telephony.CellSignalStrengthLte
+     * @see android.telephony.CellSignalStrengthTdscdma
+     * @see android.telephony.CellSignalStrengthWcdma
+     * @see android.telephony.CellSignalStrengthCdma
+     * @see android.telephony.CellSignalStrengthGsm
+     */
+    @NonNull public List<CellSignalStrength> getCellSignalStrengths() {
+        return getCellSignalStrengths(CellSignalStrength.class);
+    }
+
+    /**
+     * Returns a List of CellSignalStrength Components of this SignalStrength Report.
+     *
+     * Use this API to access underlying
+     * {@link android.telephony.CellSignalStrength CellSignalStrength} objects that provide more
+     * granular information about the SignalStrength report. Only valid (non-empty)
+     * CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
+     * and the list may contain more than one instance of a CellSignalStrength type.
+     *
+     * @param clazz a class type that extends
+     *        {@link android.telephony.CellSignalStrength CellSignalStrength} to filter possible
+     *        return values.
+     * @return a List of CellSignalStrength or an empty List if there are no valid measurements.
+     *
+     * @see android.telephony.CellSignalStrength
+     * @see android.telephony.CellSignalStrengthNr
+     * @see android.telephony.CellSignalStrengthLte
+     * @see android.telephony.CellSignalStrengthTdscdma
+     * @see android.telephony.CellSignalStrengthWcdma
+     * @see android.telephony.CellSignalStrengthCdma
+     * @see android.telephony.CellSignalStrengthGsm
+     */
+    @NonNull public <T extends CellSignalStrength> List<T> getCellSignalStrengths(
+            @NonNull Class<T> clazz) {
+        List<T> cssList = new ArrayList<>(2); // Usually have 2 or fewer elems
+        if (mLte.isValid() && clazz.isAssignableFrom(CellSignalStrengthLte.class)) {
+            cssList.add((T) mLte);
+        }
+        if (mCdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthCdma.class)) {
+            cssList.add((T) mCdma);
+        }
+        if (mTdscdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthTdscdma.class)) {
+            cssList.add((T) mTdscdma);
+        }
+        if (mWcdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthWcdma.class)) {
+            cssList.add((T) mWcdma);
+        }
+        if (mGsm.isValid() && clazz.isAssignableFrom(CellSignalStrengthGsm.class)) {
+            cssList.add((T) mGsm);
+        }
+        if (mNr.isValid() && clazz.isAssignableFrom(CellSignalStrengthNr.class)) {
+            cssList.add((T) mNr);
+        }
+        return cssList;
+    }
+
+    /** @hide */
+    public void updateLevel(PersistableBundle cc, ServiceState ss) {
+        if (cc != null) {
+            mLteAsPrimaryInNrNsa = cc.getBoolean(
+                    CarrierConfigManager.KEY_SIGNAL_STRENGTH_NR_NSA_USE_LTE_AS_PRIMARY_BOOL, true);
+        }
+        mCdma.updateLevel(cc, ss);
+        mGsm.updateLevel(cc, ss);
+        mWcdma.updateLevel(cc, ss);
+        mTdscdma.updateLevel(cc, ss);
+        mLte.updateLevel(cc, ss);
+        mNr.updateLevel(cc, ss);
+    }
+
+    /**
+     * This constructor is used to create a copy of an existing SignalStrength object.
+     *
+     * @param s Source SignalStrength
+     */
+    public SignalStrength(@NonNull SignalStrength s) {
+        copyFrom(s);
+    }
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    protected void copyFrom(SignalStrength s) {
+        mCdma = new CellSignalStrengthCdma(s.mCdma);
+        mGsm = new CellSignalStrengthGsm(s.mGsm);
+        mWcdma = new CellSignalStrengthWcdma(s.mWcdma);
+        mTdscdma = new CellSignalStrengthTdscdma(s.mTdscdma);
+        mLte = new CellSignalStrengthLte(s.mLte);
+        mNr = new CellSignalStrengthNr(s.mNr);
+        mTimestampMillis = s.getTimestampMillis();
+    }
+
+    /**
+     * Construct a SignalStrength object from the given parcel.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public SignalStrength(Parcel in) {
+        if (DBG) log("Size of signalstrength parcel:" + in.dataSize());
+
+        mCdma = in.readParcelable(CellSignalStrengthCdma.class.getClassLoader(), android.telephony.CellSignalStrengthCdma.class);
+        mGsm = in.readParcelable(CellSignalStrengthGsm.class.getClassLoader(), android.telephony.CellSignalStrengthGsm.class);
+        mWcdma = in.readParcelable(CellSignalStrengthWcdma.class.getClassLoader(), android.telephony.CellSignalStrengthWcdma.class);
+        mTdscdma = in.readParcelable(CellSignalStrengthTdscdma.class.getClassLoader(), android.telephony.CellSignalStrengthTdscdma.class);
+        mLte = in.readParcelable(CellSignalStrengthLte.class.getClassLoader(), android.telephony.CellSignalStrengthLte.class);
+        mNr = in.readParcelable(CellSignalStrengthLte.class.getClassLoader(), android.telephony.CellSignalStrengthNr.class);
+        mTimestampMillis = in.readLong();
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(mCdma, flags);
+        out.writeParcelable(mGsm, flags);
+        out.writeParcelable(mWcdma, flags);
+        out.writeParcelable(mTdscdma, flags);
+        out.writeParcelable(mLte, flags);
+        out.writeParcelable(mNr, flags);
+        out.writeLong(mTimestampMillis);
+    }
+
+    /**
+     * @return timestamp in milliseconds since boot for {@link SignalStrength}.
+     * This timestamp reports the approximate time that the signal was measured and reported
+     * by the modem. It can be used to compare the recency of {@link SignalStrength} instances.
+     */
+    @ElapsedRealtimeLong
+    public long getTimestampMillis() {
+        return mTimestampMillis;
+    }
+
+   /**
+     * {@link Parcelable#describeContents}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable.Creator}
+     *
+     */
+    public static final @android.annotation.NonNull Parcelable.Creator<SignalStrength> CREATOR =
+            new Parcelable.Creator<>() {
+                public SignalStrength createFromParcel(Parcel in) {
+                    return new SignalStrength(in);
+                }
+
+                public SignalStrength[] newArray(int size) {
+                    return new SignalStrength[size];
+                }
+            };
+
+    /**
+     * Get the GSM RSSI in ASU.
+     *
+     * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+     *
+     * @return RSSI in ASU 0..31, 99, or UNAVAILABLE
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthGsm#getAsuLevel}.
+     * @see android.telephony.CellSignalStrengthGsm
+     * @see android.telephony.SignalStrength#getCellSignalStrengths
+     */
+    @Deprecated
+    public int getGsmSignalStrength() {
+        return mGsm.getAsuLevel();
+    }
+
+    /**
+     * Get the GSM bit error rate (0-7, 99) as defined in TS 27.007 8.5
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthGsm#getBitErrorRate}.
+     *
+     * @see android.telephony.CellSignalStrengthGsm
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     */
+    @Deprecated
+    public int getGsmBitErrorRate() {
+        return mGsm.getBitErrorRate();
+    }
+
+    /**
+     * Get the CDMA RSSI value in dBm
+     *
+     * @return the CDMA RSSI value or {@link #INVALID} if invalid
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthCdma#getCdmaDbm}.
+     *
+     * @see android.telephony.CellSignalStrengthCdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     */
+    @Deprecated
+    public int getCdmaDbm() {
+        return mCdma.getCdmaDbm();
+    }
+
+    /**
+     * Get the CDMA Ec/Io value in dB*10
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthCdma#getCdmaEcio}.
+     *
+     * @see android.telephony.CellSignalStrengthCdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     */
+    @Deprecated
+    public int getCdmaEcio() {
+        return mCdma.getCdmaEcio();
+    }
+
+    /**
+     * Get the EVDO RSSI value in dBm
+     *
+     * @return the EVDO RSSI value or {@link #INVALID} if invalid
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthCdma#getEvdoDbm}.
+     *
+     * @see android.telephony.CellSignalStrengthCdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     */
+    @Deprecated
+    public int getEvdoDbm() {
+        return mCdma.getEvdoDbm();
+    }
+
+    /**
+     * Get the EVDO Ec/Io value in dB*10
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthCdma#getEvdoEcio}.
+     *
+     * @see android.telephony.CellSignalStrengthCdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     */
+    @Deprecated
+    public int getEvdoEcio() {
+        return mCdma.getEvdoEcio();
+    }
+
+    /**
+     * Get the signal to noise ratio. Valid values are 0-8. 8 is the highest.
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthCdma#getEvdoSnr}.
+     *
+     * @see android.telephony.CellSignalStrengthCdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     */
+    @Deprecated
+    public int getEvdoSnr() {
+        return mCdma.getEvdoSnr();
+    }
+
+    /**
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthLte#getRssi}.
+     *
+     * @see android.telephony.CellSignalStrengthLte
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getLteSignalStrength() {
+        return mLte.getRssi();
+    }
+
+    /**
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthLte#getRsrp}.
+     *
+     * @see android.telephony.CellSignalStrengthLte
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getLteRsrp() {
+        return mLte.getRsrp();
+    }
+
+    /**
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthLte#getRsrq}.
+     *
+     * @see android.telephony.CellSignalStrengthLte
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getLteRsrq() {
+        return mLte.getRsrq();
+    }
+
+    /**
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthLte#getRssnr}.
+     *
+     * @see android.telephony.CellSignalStrengthLte
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getLteRssnr() {
+        return mLte.getRssnr();
+    }
+
+    /**
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthLte#getCqi}.
+     *
+     * @see android.telephony.CellSignalStrengthLte
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getLteCqi() {
+        return mLte.getCqi();
+    }
+
+    /**
+     * Retrieve an abstract level value for the overall signal strength.
+     *
+     * @return a single integer from 0 to 4 representing the general signal quality.
+     *     This may take into account many different radio technology inputs.
+     *     0 represents very poor signal strength
+     *     while 4 represents a very strong signal strength.
+     */
+    public int getLevel() {
+        int level = getPrimary().getLevel();
+        if (level < SIGNAL_STRENGTH_NONE_OR_UNKNOWN || level > SIGNAL_STRENGTH_GREAT) {
+            loge("Invalid Level " + level + ", this=" + this);
+            return SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        }
+        return getPrimary().getLevel();
+    }
+
+    /**
+     * Get the signal level as an asu value with a range dependent on the underlying technology.
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrength#getAsuLevel}. Because the levels vary by technology,
+     *             this method is misleading and should not be used.
+     * @see android.telephony.CellSignalStrength
+     * @see android.telephony.SignalStrength#getCellSignalStrengths
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getAsuLevel() {
+        return getPrimary().getAsuLevel();
+    }
+
+    /**
+     * Get the signal strength as dBm
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrength#getDbm()}. Because the levels vary by technology,
+     *             this method is misleading and should not be used.
+     * @see android.telephony.CellSignalStrength
+     * @see android.telephony.SignalStrength#getCellSignalStrengths
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getDbm() {
+        return getPrimary().getDbm();
+    }
+
+    /**
+     * Get Gsm signal strength as dBm
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthGsm#getDbm}.
+     *
+     * @see android.telephony.CellSignalStrengthGsm
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getGsmDbm() {
+        return mGsm.getDbm();
+    }
+
+    /**
+     * Get gsm as level 0..4
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthGsm#getLevel}.
+     *
+     * @see android.telephony.CellSignalStrengthGsm
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getGsmLevel() {
+        return mGsm.getLevel();
+    }
+
+    /**
+     * Get the gsm signal level as an asu value between 0..31, 99 is unknown
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthGsm#getAsuLevel}.
+     *
+     * @see android.telephony.CellSignalStrengthGsm
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getGsmAsuLevel() {
+        return mGsm.getAsuLevel();
+    }
+
+    /**
+     * Get cdma as level 0..4
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthCdma#getLevel}.
+     *
+     * @see android.telephony.CellSignalStrengthCdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getCdmaLevel() {
+        return mCdma.getLevel();
+    }
+
+    /**
+     * Get the cdma signal level as an asu value between 0..31, 99 is unknown
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthCdma#getAsuLevel}. Since there is no definition of
+     *             ASU for CDMA, the resultant value is Android-specific and is not recommended
+     *             for use.
+     *
+     * @see android.telephony.CellSignalStrengthCdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getCdmaAsuLevel() {
+        return mCdma.getAsuLevel();
+    }
+
+    /**
+     * Get Evdo as level 0..4
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthCdma#getEvdoLevel}.
+     *
+     * @see android.telephony.CellSignalStrengthCdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getEvdoLevel() {
+        return mCdma.getEvdoLevel();
+    }
+
+    /**
+     * Get the evdo signal level as an asu value between 0..31, 99 is unknown
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthCdma#getEvdoAsuLevel}. Since there is no definition of
+     *             ASU for EvDO, the resultant value is Android-specific and is not recommended
+     *             for use.
+     *
+     * @see android.telephony.CellSignalStrengthCdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getEvdoAsuLevel() {
+        return mCdma.getEvdoAsuLevel();
+    }
+
+    /**
+     * Get LTE as dBm
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthLte#getDbm}.
+     *
+     * @see android.telephony.CellSignalStrengthLte
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getLteDbm() {
+        return mLte.getRsrp();
+    }
+
+    /**
+     * Get LTE as level 0..4
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthLte#getLevel}.
+     *
+     * @see android.telephony.CellSignalStrengthLte
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getLteLevel() {
+        return mLte.getLevel();
+    }
+
+    /**
+     * Get the LTE signal level as an asu value between 0..97, 99 is unknown
+     * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthLte#getAsuLevel}.
+     *
+     * @see android.telephony.CellSignalStrengthLte
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getLteAsuLevel() {
+        return mLte.getAsuLevel();
+    }
+
+    /**
+     * @return true if this is for GSM
+     *
+     * @deprecated This method returns true if there are any 3gpp type SignalStrength elements in
+     *             this SignalStrength report or if the report contains no valid SignalStrength
+     *             information. Instead callers should use
+     *             {@link android.telephony.SignalStrength#getCellSignalStrengths
+     *             getCellSignalStrengths()} to determine which types of information are contained
+     *             in the SignalStrength report.
+     */
+    @Deprecated
+    public boolean isGsm() {
+        return !(getPrimary() instanceof CellSignalStrengthCdma);
+    }
+
+    /**
+     * @return get TD-SCDMA dBm
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthTdscdma#getDbm}.
+     *
+     * @see android.telephony.CellSignalStrengthTdscdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getTdScdmaDbm() {
+        return mTdscdma.getRscp();
+    }
+
+    /**
+     * Get TD-SCDMA as level 0..4
+     * Range : 25 to 120
+     * INT_MAX: 0x7FFFFFFF denotes invalid value
+     * Reference: 3GPP TS 25.123, section 9.1.1.1
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthTdscdma#getLevel}.
+     *
+     * @see android.telephony.CellSignalStrengthTdscdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getTdScdmaLevel() {
+        return mTdscdma.getLevel();
+     }
+
+    /**
+     * Get the TD-SCDMA signal level as an asu value.
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthTdscdma#getAsuLevel}.
+     *
+     * @see android.telephony.CellSignalStrengthTdscdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getTdScdmaAsuLevel() {
+        return mTdscdma.getAsuLevel();
+    }
+
+    /**
+     * Gets WCDMA RSCP as a dBm value between -120 and -24, as defined in TS 27.007 8.69.
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthWcdma#getRscp}.
+     *
+     * @see android.telephony.CellSignalStrengthWcdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    public int getWcdmaRscp() {
+        return mWcdma.getRscp();
+    }
+
+    /**
+     * Get the WCDMA signal level as an ASU value between 0-96, 255 is unknown
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthWcdma#getAsuLevel}.
+     *
+     * @see android.telephony.CellSignalStrengthWcdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    public int getWcdmaAsuLevel() {
+        /*
+         * 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+         * 0      -120 dBm or less
+         * 1      -119 dBm
+         * 2...95 -118... -25 dBm
+         * 96     -24 dBm or greater
+         * 255    not known or not detectable
+         */
+        return mWcdma.getAsuLevel();
+    }
+
+    /**
+     * Gets WCDMA signal strength as a dBm value between -120 and -24, as defined in TS 27.007 8.69.
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthWcdma#getDbm}.
+     *
+     * @see android.telephony.CellSignalStrengthWcdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    public int getWcdmaDbm() {
+        return mWcdma.getDbm();
+    }
+
+    /**
+     * Get WCDMA as level 0..4
+     *
+     * @deprecated this information should be retrieved from
+     *             {@link CellSignalStrengthWcdma#getDbm}.
+     *
+     * @see android.telephony.CellSignalStrengthWcdma
+     * @see android.telephony.SignalStrength#getCellSignalStrengths()
+     * @hide
+     */
+    @Deprecated
+    public int getWcdmaLevel() {
+        return mWcdma.getLevel();
+    }
+
+    /**
+     * @return hash code
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCdma, mGsm, mWcdma, mTdscdma, mLte, mNr);
+    }
+
+    /**
+     * @return true if the signal strengths are the same
+     */
+    @Override
+    public boolean equals (Object o) {
+        if (!(o instanceof SignalStrength)) return false;
+
+        SignalStrength s = (SignalStrength) o;
+
+        return mCdma.equals(s.mCdma)
+            && mGsm.equals(s.mGsm)
+            && mWcdma.equals(s.mWcdma)
+            && mTdscdma.equals(s.mTdscdma)
+            && mLte.equals(s.mLte)
+            && mNr.equals(s.mNr);
+    }
+
+    /**
+     * @return string representation.
+     */
+    @Override
+    public String toString() {
+        return new StringBuilder().append("SignalStrength:{")
+            .append("mCdma=").append(mCdma)
+            .append(",mGsm=").append(mGsm)
+            .append(",mWcdma=").append(mWcdma)
+            .append(",mTdscdma=").append(mTdscdma)
+            .append(",mLte=").append(mLte)
+            .append(",mNr=").append(mNr)
+            .append(",primary=").append(getPrimary().getClass().getSimpleName())
+            .append("}")
+            .toString();
+    }
+
+    /**
+     * Set intent notifier Bundle based on SignalStrength
+     *
+     * @param m intent notifier Bundle
+     *
+     * @deprecated this method relies on non-stable implementation details, and full access to
+     *             internal storage is available via {@link #getCellSignalStrengths()}.
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public void fillInNotifierBundle(Bundle m) {
+        m.putParcelable("Cdma", mCdma);
+        m.putParcelable("Gsm", mGsm);
+        m.putParcelable("Wcdma", mWcdma);
+        m.putParcelable("Tdscdma", mTdscdma);
+        m.putParcelable("Lte", mLte);
+        m.putParcelable("Nr", mNr);
+    }
+
+    /**
+     * log warning
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+
+    /**
+     * log error
+     */
+    private static void loge(String s) {
+        Rlog.e(LOG_TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/SignalStrengthUpdateRequest.java b/android-35/android/telephony/SignalStrengthUpdateRequest.java
new file mode 100644
index 0000000..4884d54
--- /dev/null
+++ b/android-35/android/telephony/SignalStrengthUpdateRequest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Request used to register {@link SignalThresholdInfo} to be notified when the signal strength
+ * breach the specified thresholds.
+ */
+public final class SignalStrengthUpdateRequest implements Parcelable {
+    /**
+     * List of SignalThresholdInfo for the request.
+     */
+    private final List<SignalThresholdInfo> mSignalThresholdInfos;
+
+    /**
+     * Whether the reporting is required for thresholds in the request while device is idle.
+     */
+    private final boolean mIsReportingRequestedWhileIdle;
+
+    /**
+     * Whether the reporting requested for system thresholds while device is idle.
+     *
+     * System signal thresholds are loaded from carrier config items and mainly used for UI
+     * displaying. By default, they are ignored when device is idle. When setting the value to true,
+     * modem will continue reporting signal strength changes over the system signal thresholds even
+     * device is idle.
+     *
+     * This should only set to true by the system caller.
+     */
+    private final boolean mIsSystemThresholdReportingRequestedWhileIdle;
+
+    /**
+     * A IBinder object as a token for server side to check if the request client is still living.
+     */
+    private final IBinder mLiveToken;
+
+    private SignalStrengthUpdateRequest(
+            @Nullable List<SignalThresholdInfo> signalThresholdInfos,
+            boolean isReportingRequestedWhileIdle,
+            boolean isSystemThresholdReportingRequestedWhileIdle) {
+        validate(signalThresholdInfos, isSystemThresholdReportingRequestedWhileIdle);
+
+        mSignalThresholdInfos = signalThresholdInfos;
+        mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle;
+        mIsSystemThresholdReportingRequestedWhileIdle =
+                isSystemThresholdReportingRequestedWhileIdle;
+        mLiveToken = new Binder();
+    }
+
+    /**
+     * Builder class to create {@link SignalStrengthUpdateRequest} object.
+     */
+    public static final class Builder {
+        private List<SignalThresholdInfo> mSignalThresholdInfos = null;
+        private boolean mIsReportingRequestedWhileIdle = false;
+        private boolean mIsSystemThresholdReportingRequestedWhileIdle = false;
+
+        /**
+         * Set the collection of SignalThresholdInfo for the builder object
+         *
+         * @param signalThresholdInfos the collection of SignalThresholdInfo
+         *
+         * @return the builder to facilitate the chaining
+         */
+        public @NonNull Builder setSignalThresholdInfos(
+                @NonNull Collection<SignalThresholdInfo> signalThresholdInfos) {
+            Objects.requireNonNull(signalThresholdInfos,
+                    "SignalThresholdInfo collection must not be null");
+            for (SignalThresholdInfo info : signalThresholdInfos) {
+                Objects.requireNonNull(info,
+                        "SignalThresholdInfo in the collection must not be null");
+            }
+
+            mSignalThresholdInfos = new ArrayList<>(signalThresholdInfos);
+            // Sort the collection with RAN and then SignalMeasurementType ascending order, make the
+            // ordering not matter for equals
+            mSignalThresholdInfos.sort(
+                    Comparator.comparingInt(SignalThresholdInfo::getRadioAccessNetworkType)
+                            .thenComparing(SignalThresholdInfo::getSignalMeasurementType));
+            return this;
+        }
+
+        /**
+         * Set the builder object if require reporting on thresholds in this request when device is
+         * idle.
+         *
+         * @param isReportingRequestedWhileIdle true if request reporting when device is idle
+         *
+         * @return the builder to facilitate the chaining
+         */
+        public @NonNull Builder setReportingRequestedWhileIdle(
+                boolean isReportingRequestedWhileIdle) {
+            mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle;
+            return this;
+        }
+
+        /**
+         * Set the builder object if require reporting on the system thresholds when device is idle.
+         *
+         * <p>This is intended to be used by the system privileged caller only. When setting to
+         * {@code true}, signal strength update request through
+         * {@link TelephonyManager#setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}
+         * will require permission
+         * {@link android.Manifest.permission#LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH}.
+         *
+         * @param isSystemThresholdReportingRequestedWhileIdle true if request reporting on the
+         *                                                     system thresholds when device is idle
+         * @return the builder to facilitate the chaining
+         * @hide
+         */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
+        public @NonNull Builder setSystemThresholdReportingRequestedWhileIdle(
+                boolean isSystemThresholdReportingRequestedWhileIdle) {
+            mIsSystemThresholdReportingRequestedWhileIdle =
+                    isSystemThresholdReportingRequestedWhileIdle;
+            return this;
+        }
+
+        /**
+         * Build a {@link SignalStrengthUpdateRequest} object.
+         *
+         * @return the SignalStrengthUpdateRequest object
+         *
+         * @throws IllegalArgumentException if the SignalThresholdInfo collection is empty size, the
+         * signal measurement type for the same RAN in the collection is not unique
+         */
+        public @NonNull SignalStrengthUpdateRequest build() {
+            return new SignalStrengthUpdateRequest(mSignalThresholdInfos,
+                    mIsReportingRequestedWhileIdle, mIsSystemThresholdReportingRequestedWhileIdle);
+        }
+    }
+
+    private SignalStrengthUpdateRequest(Parcel in) {
+        mSignalThresholdInfos = in.createTypedArrayList(SignalThresholdInfo.CREATOR);
+        mIsReportingRequestedWhileIdle = in.readBoolean();
+        mIsSystemThresholdReportingRequestedWhileIdle = in.readBoolean();
+        mLiveToken = in.readStrongBinder();
+    }
+
+    /**
+     * Get the collection of SignalThresholdInfo in the request.
+     *
+     * @return the collection of SignalThresholdInfo
+     */
+    @NonNull
+    public Collection<SignalThresholdInfo> getSignalThresholdInfos() {
+        return Collections.unmodifiableList(mSignalThresholdInfos);
+    }
+
+    /**
+     * Get whether reporting is requested for the threshold in the request while device is idle.
+     *
+     * @return true if reporting requested while device is idle
+     */
+    public boolean isReportingRequestedWhileIdle() {
+        return mIsReportingRequestedWhileIdle;
+    }
+
+    /**
+     * @return true if reporting requested for system thresholds while device is idle
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isSystemThresholdReportingRequestedWhileIdle() {
+        return mIsSystemThresholdReportingRequestedWhileIdle;
+    }
+
+    /**
+     * @return the live token of the request
+     *
+     * @hide
+     */
+    public @NonNull IBinder getLiveToken() {
+        return mLiveToken;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedList(mSignalThresholdInfos);
+        dest.writeBoolean(mIsReportingRequestedWhileIdle);
+        dest.writeBoolean(mIsSystemThresholdReportingRequestedWhileIdle);
+        dest.writeStrongBinder(mLiveToken);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) return true;
+
+        if (!(other instanceof SignalStrengthUpdateRequest)) {
+            return false;
+        }
+
+        SignalStrengthUpdateRequest request = (SignalStrengthUpdateRequest) other;
+        return mSignalThresholdInfos.equals(request.mSignalThresholdInfos)
+                && mIsReportingRequestedWhileIdle == request.mIsReportingRequestedWhileIdle
+                && mIsSystemThresholdReportingRequestedWhileIdle
+                    == request.mIsSystemThresholdReportingRequestedWhileIdle;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSignalThresholdInfos, mIsReportingRequestedWhileIdle,
+                mIsSystemThresholdReportingRequestedWhileIdle);
+    }
+
+    public static final @NonNull Parcelable.Creator<SignalStrengthUpdateRequest> CREATOR =
+            new Parcelable.Creator<SignalStrengthUpdateRequest>() {
+                @Override
+                public SignalStrengthUpdateRequest createFromParcel(Parcel source) {
+                    return new SignalStrengthUpdateRequest(source);
+                }
+
+                @Override
+                public SignalStrengthUpdateRequest[] newArray(int size) {
+                    return new SignalStrengthUpdateRequest[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return new StringBuilder("SignalStrengthUpdateRequest{")
+                .append("mSignalThresholdInfos=")
+                .append(mSignalThresholdInfos)
+                .append(" mIsReportingRequestedWhileIdle=")
+                .append(mIsReportingRequestedWhileIdle)
+                .append(" mIsSystemThresholdReportingRequestedWhileIdle=")
+                .append(mIsSystemThresholdReportingRequestedWhileIdle)
+                .append(" mLiveToken")
+                .append(mLiveToken)
+                .append("}").toString();
+    }
+
+    /**
+     * Throw IAE if SignalThresholdInfo collection is null or empty,
+     * or the SignalMeasurementType for the same RAN in the collection is not unique.
+     */
+    private static void validate(Collection<SignalThresholdInfo> infos,
+            boolean isSystemThresholdReportingRequestedWhileIdle) {
+        // System app (like Bluetooth) can specify the request to report system thresholds while
+        // device is idle (with permission protection). In this case, the request doesn't need to
+        // provide a non-empty list of SignalThresholdInfo which is only asked for public apps.
+        if (infos == null || (infos.isEmpty() && !isSystemThresholdReportingRequestedWhileIdle)) {
+            throw new IllegalArgumentException("SignalThresholdInfo collection is null or empty");
+        }
+
+        // Map from RAN to set of SignalMeasurementTypes
+        Map<Integer, Set<Integer>> ranToTypes = new HashMap<>(infos.size());
+        for (SignalThresholdInfo info : infos) {
+            final int ran = info.getRadioAccessNetworkType();
+            final int type = info.getSignalMeasurementType();
+            ranToTypes.putIfAbsent(ran, new HashSet<>());
+            if (!ranToTypes.get(ran).add(type)) {
+                throw new IllegalArgumentException(
+                        "SignalMeasurementType " + type + " for RAN " + ran + " is not unique");
+            }
+        }
+    }
+}
diff --git a/android-35/android/telephony/SignalThresholdInfo.java b/android-35/android/telephony/SignalThresholdInfo.java
new file mode 100644
index 0000000..80f1de1
--- /dev/null
+++ b/android-35/android/telephony/SignalThresholdInfo.java
@@ -0,0 +1,757 @@
+/*
+ * Copyright (C) 2019 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;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+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.Arrays;
+import java.util.Objects;
+
+/**
+ * Defines the threshold value of the signal strength.
+ */
+public final class SignalThresholdInfo implements Parcelable {
+
+    /**
+     * Unknown signal measurement type.
+     */
+    public static final int SIGNAL_MEASUREMENT_TYPE_UNKNOWN = 0;
+
+    /**
+     * Received Signal Strength Indication.
+     * Range: -113 dBm and -51 dBm
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#GERAN},
+     *           {@link AccessNetworkConstants.AccessNetworkType#CDMA2000}
+     * Reference: 3GPP TS 27.007 section 8.5.
+     */
+    public static final int SIGNAL_MEASUREMENT_TYPE_RSSI = 1;
+
+    /**
+     * Received Signal Code Power.
+     * Range: -120 dBm to -25 dBm;
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#UTRAN}
+     * Reference: 3GPP TS 25.123, section 9.1.1.1
+     */
+    public static final int SIGNAL_MEASUREMENT_TYPE_RSCP = 2;
+
+    /**
+     * Reference Signal Received Power.
+     * Range: -140 dBm to -44 dBm;
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
+     * Reference: 3GPP TS 36.133 9.1.4
+     */
+    public static final int SIGNAL_MEASUREMENT_TYPE_RSRP = 3;
+
+    /**
+     * Reference Signal Received Quality
+     * Range: -34 dB to 3 dB;
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
+     * Reference: 3GPP TS 36.133 9.1.7
+     */
+    public static final int SIGNAL_MEASUREMENT_TYPE_RSRQ = 4;
+
+    /**
+     * Reference Signal Signal to Noise Ratio
+     * Range: -20 dB to 30 dB;
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN}
+     */
+    public static final int SIGNAL_MEASUREMENT_TYPE_RSSNR = 5;
+
+    /**
+     * 5G SS reference signal received power.
+     * Range: -140 dBm to -44 dBm.
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+     * Reference: 3GPP TS 38.215.
+     */
+    public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRP = 6;
+
+    /**
+     * 5G SS reference signal received quality.
+     * Range: -43 dB to 20 dB.
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+     * Reference: 3GPP TS 38.133 section 10.1.11.1.
+     */
+    public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRQ = 7;
+
+    /**
+     * 5G SS signal-to-noise and interference ratio.
+     * Range: -23 dB to 40 dB
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN}
+     * Reference: 3GPP TS 38.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1.
+     */
+    public static final int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8;
+
+    /**
+     * The ratio between the received energy from the pilot signal CPICH per chip (Ec) to the
+     * noise density (No).
+     * Range: -24 dBm to 1 dBm.
+     * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#UTRAN}
+     * Reference: 3GPP TS 25.215 5.1.5
+     */
+    public static final int SIGNAL_MEASUREMENT_TYPE_ECNO = 9;
+
+    /** @hide */
+    @IntDef(
+            prefix = {"SIGNAL_MEASUREMENT_TYPE_"},
+            value = {
+                SIGNAL_MEASUREMENT_TYPE_UNKNOWN,
+                SIGNAL_MEASUREMENT_TYPE_RSSI,
+                SIGNAL_MEASUREMENT_TYPE_RSCP,
+                SIGNAL_MEASUREMENT_TYPE_RSRP,
+                SIGNAL_MEASUREMENT_TYPE_RSRQ,
+                SIGNAL_MEASUREMENT_TYPE_RSSNR,
+                SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+                SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+                SIGNAL_MEASUREMENT_TYPE_SSSINR,
+                SIGNAL_MEASUREMENT_TYPE_ECNO
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SignalMeasurementType {}
+
+    @SignalMeasurementType private final int mSignalMeasurementType;
+
+    /**
+     * A hysteresis time in milliseconds to prevent flapping.
+     * A value of 0 disables hysteresis
+     */
+    private final int mHysteresisMs;
+
+    /**
+     * An interval in dB defining the required magnitude change between reports.
+     * hysteresisDb must be smaller than the smallest threshold delta.
+     * An interval value of 0 disables hysteresis.
+     */
+    private final int mHysteresisDb;
+
+    /**
+     * List of threshold values.
+     * Range and unit must reference specific SignalMeasurementType
+     * The threshold values for which to apply criteria.
+     * A vector size of 0 disables the use of thresholds for reporting.
+     */
+    private final int[] mThresholds;
+
+    /**
+     * {@code true} means modem must trigger the report based on the criteria;
+     * {@code false} means modem must not trigger the report based on the criteria.
+     */
+    private final boolean mIsEnabled;
+
+    /**
+     * The radio access network type associated with the signal thresholds.
+     */
+    @AccessNetworkConstants.RadioAccessNetworkType private final int mRan;
+
+    /**
+     * Indicates the hysteresisMs is disabled.
+     *
+     * @hide
+     */
+    public static final int HYSTERESIS_MS_DISABLED = 0;
+
+    /**
+     * Indicates the default hysteresis value in dB.
+     *
+     * @hide
+     */
+    private static final int HYSTERESIS_DB_DEFAULT = 2;
+
+    /**
+     * Indicates the hysteresisDb value is not set and to be initialised to default value.
+     *
+     * @hide
+     */
+    public static final int HYSTERESIS_DB_MINIMUM = 0;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSI}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSSI_MIN_VALUE = -113;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSI}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSSI_MAX_VALUE = -51;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSCP}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSCP_MIN_VALUE = -120;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSCP}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSCP_MAX_VALUE = -25;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRP}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSRP_MIN_VALUE = -140;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRP}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSRP_MAX_VALUE = -44;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRQ}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSRQ_MIN_VALUE = -34;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRQ}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSRQ_MAX_VALUE = 3;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSNR}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSSNR_MIN_VALUE = -20;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSNR}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_RSSNR_MAX_VALUE = 30;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRP}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_SSRSRP_MIN_VALUE = -140;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRP}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_SSRSRP_MAX_VALUE = -44;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRQ}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_SSRSRQ_MIN_VALUE = -43;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRQ}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_SSRSRQ_MAX_VALUE = 20;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSSINR}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_SSSINR_MIN_VALUE = -23;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSSINR}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_SSSINR_MAX_VALUE = 40;
+
+    /**
+     * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_ECNO}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_ECNO_MIN_VALUE = -24;
+
+    /**
+     * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_ECNO}.
+     *
+     * @hide
+     */
+    public static final int SIGNAL_ECNO_MAX_VALUE = 1;
+
+    /**
+     * The minimum number of thresholds allowed in each SignalThresholdInfo.
+     *
+     * @hide
+     */
+    public static final int MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED = 1;
+
+    /**
+     * The maximum number of thresholds allowed in each SignalThresholdInfo.
+     *
+     * @hide
+     */
+    public static final int MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED = 4;
+
+    /**
+     * Constructor
+     *
+     * @param ran               Radio Access Network type
+     * @param signalMeasurementType Signal Measurement Type
+     * @param hysteresisMs      hysteresisMs
+     * @param hysteresisDb      hysteresisDb
+     * @param thresholds        threshold value
+     * @param isEnabled         isEnabled
+     */
+    private SignalThresholdInfo(
+            @AccessNetworkConstants.RadioAccessNetworkType int ran,
+            @SignalMeasurementType int signalMeasurementType,
+            int hysteresisMs,
+            int hysteresisDb,
+            @NonNull int[] thresholds,
+            boolean isEnabled) {
+        Objects.requireNonNull(thresholds, "thresholds must not be null");
+        validateRanWithMeasurementType(ran, signalMeasurementType);
+        validateThresholdRange(signalMeasurementType, thresholds);
+
+        mRan = ran;
+        mSignalMeasurementType = signalMeasurementType;
+        mHysteresisMs = hysteresisMs < 0 ? HYSTERESIS_MS_DISABLED : hysteresisMs;
+        mHysteresisDb = hysteresisDb;
+        mThresholds = thresholds;
+        mIsEnabled = isEnabled;
+    }
+
+    /**
+     * Builder class to create {@link SignalThresholdInfo} objects.
+     */
+    public static final class Builder {
+        private int mRan = AccessNetworkConstants.AccessNetworkType.UNKNOWN;
+        private int mSignalMeasurementType = SIGNAL_MEASUREMENT_TYPE_UNKNOWN;
+        private int mHysteresisMs = HYSTERESIS_MS_DISABLED;
+        private int mHysteresisDb = HYSTERESIS_DB_DEFAULT;
+        private int[] mThresholds = null;
+        private boolean mIsEnabled = false;
+
+        /**
+         * Set the radio access network type for the builder instance.
+         *
+         * @param ran The radio access network type
+         * @return the builder to facilitate the chaining
+         */
+        @NonNull
+        public Builder setRadioAccessNetworkType(
+                @AccessNetworkConstants.RadioAccessNetworkType int ran) {
+            mRan = ran;
+            return this;
+        }
+
+        /**
+         * Set the signal measurement type for the builder instance.
+         *
+         * @param signalMeasurementType The signal measurement type
+         * @return the builder to facilitate the chaining
+         */
+        @NonNull
+        public Builder setSignalMeasurementType(
+                @SignalMeasurementType int signalMeasurementType) {
+            mSignalMeasurementType = signalMeasurementType;
+            return this;
+        }
+
+        /**
+         * Set the hysteresis time in milliseconds to prevent flapping. A value of 0 disables
+         * hysteresis.
+         *
+         * @param hysteresisMs the hysteresis time in milliseconds
+         * @return the builder to facilitate the chaining
+         * @hide
+         */
+        @NonNull
+        public Builder setHysteresisMs(int hysteresisMs) {
+            mHysteresisMs = hysteresisMs;
+            return this;
+        }
+
+        /**
+         * Set the interval in dB defining the required minimum magnitude change to report a
+         * signal strength change. A value of zero disables dB-based hysteresis restrictions.
+         * Note:
+         * <p>Default hysteresis db value is 2. Minimum hysteresis db value allowed to set is 0.
+         * If hysteresis db value is not set, default hysteresis db value of 2 will be used.
+         *
+         * @param hysteresisDb the interval in dB
+         * @return the builder to facilitate the chaining
+         */
+        @NonNull
+        public Builder setHysteresisDb(@IntRange(from = 0) int hysteresisDb) {
+            if (hysteresisDb < 0) {
+                throw new IllegalArgumentException("hysteresis db value should not be less than 0");
+            }
+            mHysteresisDb = hysteresisDb;
+            return this;
+        }
+
+        /**
+         * Set the signal strength thresholds of the corresponding signal measurement type.
+         *
+         * The range and unit must reference specific SignalMeasurementType. The length of the
+         * thresholds should between the numbers return from
+         * {@link #getMinimumNumberOfThresholdsAllowed()} and
+         * {@link #getMaximumNumberOfThresholdsAllowed()}. An IllegalArgumentException will throw
+         * otherwise.
+         *
+         * @param thresholds array of integer as the signal threshold values
+         * @return the builder to facilitate the chaining
+         *
+         * @see #SIGNAL_MEASUREMENT_TYPE_RSSI
+         * @see #SIGNAL_MEASUREMENT_TYPE_RSCP
+         * @see #SIGNAL_MEASUREMENT_TYPE_RSRP
+         * @see #SIGNAL_MEASUREMENT_TYPE_RSRQ
+         * @see #SIGNAL_MEASUREMENT_TYPE_RSSNR
+         * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP
+         * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ
+         * @see #SIGNAL_MEASUREMENT_TYPE_SSSINR
+         * @see #SIGNAL_MEASUREMENT_TYPE_ECNO
+         * @see #getThresholds() for more details on signal strength thresholds
+         */
+        @NonNull
+        public Builder setThresholds(@NonNull int[] thresholds) {
+            return setThresholds(thresholds, false /*isSystem*/);
+        }
+
+        /**
+         * Set the signal strength thresholds for the corresponding signal measurement type.
+         *
+         * @param thresholds array of integer as the signal threshold values
+         * @param isSystem true is the caller is system which does not have restrictions on
+         *        the length of thresholds array.
+         * @return the builder to facilitate the chaining
+         *
+         * @hide
+         */
+        @NonNull
+        public Builder setThresholds(@NonNull int[] thresholds, boolean isSystem) {
+            Objects.requireNonNull(thresholds, "thresholds must not be null");
+            if (!isSystem
+                    && (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+                            || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED)) {
+                throw new IllegalArgumentException(
+                        "thresholds length must between "
+                                + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+                                + " and "
+                                + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
+            }
+            mThresholds = thresholds.clone();
+            Arrays.sort(mThresholds);
+            return this;
+        }
+
+        /**
+         * Set if the modem should trigger the report based on the criteria.
+         *
+         * @param isEnabled true if the modem should trigger the report based on the criteria
+         * @return the builder to facilitate the chaining
+         * @hide
+         */
+        @NonNull
+        public Builder setIsEnabled(boolean isEnabled) {
+            mIsEnabled = isEnabled;
+            return this;
+        }
+
+        /**
+         * Build {@link SignalThresholdInfo} object.
+         *
+         * @return the SignalThresholdInfo object build out
+         *
+         * @throws IllegalArgumentException if the signal measurement type is invalid, any value in
+         * the thresholds is out of range, or the RAN is not allowed to set with the signal
+         * measurement type
+         */
+        @NonNull
+        public SignalThresholdInfo build() {
+            return new SignalThresholdInfo(
+                    mRan,
+                    mSignalMeasurementType,
+                    mHysteresisMs,
+                    mHysteresisDb,
+                    mThresholds,
+                    mIsEnabled);
+        }
+    }
+
+    /**
+     * Get the radio access network type.
+     *
+     * @return radio access network type
+     */
+    @AccessNetworkConstants.RadioAccessNetworkType
+    public int getRadioAccessNetworkType() {
+        return mRan;
+    }
+
+    /**
+     * Get the signal measurement type.
+     *
+     * @return the SignalMeasurementType value
+     */
+    @SignalMeasurementType
+    public int getSignalMeasurementType() {
+        return mSignalMeasurementType;
+    }
+
+    /** @hide */
+    public int getHysteresisMs() {
+        return mHysteresisMs;
+    }
+
+    /**
+     * Get measurement hysteresis db.
+     *
+     * @return hysteresis db value
+     */
+    public int getHysteresisDb() {
+        return mHysteresisDb;
+    }
+
+    /** @hide */
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    /**
+     * Get the signal strength thresholds.
+     *
+     * Signal strength thresholds are a list of integer used for suggesting signal level and signal
+     * reporting criteria. The range and unit must reference specific SignalMeasurementType.
+     *
+     * Please refer to https://source.android.com/devices/tech/connect/signal-strength on how signal
+     * strength thresholds are used for signal strength reporting.
+     *
+     * @return array of integer of the signal thresholds
+     *
+     * @see #SIGNAL_MEASUREMENT_TYPE_RSSI
+     * @see #SIGNAL_MEASUREMENT_TYPE_RSCP
+     * @see #SIGNAL_MEASUREMENT_TYPE_RSRP
+     * @see #SIGNAL_MEASUREMENT_TYPE_RSRQ
+     * @see #SIGNAL_MEASUREMENT_TYPE_RSSNR
+     * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP
+     * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ
+     * @see #SIGNAL_MEASUREMENT_TYPE_SSSINR
+     * @see #SIGNAL_MEASUREMENT_TYPE_ECNO
+     */
+    @NonNull
+    public int[] getThresholds() {
+        return mThresholds.clone();
+    }
+
+    /**
+     * Get the minimum number of thresholds allowed in each SignalThresholdInfo.
+     *
+     * @return the minimum number of thresholds allowed
+     */
+    public static int getMinimumNumberOfThresholdsAllowed() {
+        return MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED;
+    }
+
+    /**
+     * Get the maximum number of threshold allowed in each SignalThresholdInfo.
+     *
+     * @return the maximum number of thresholds allowed
+     */
+    public static int getMaximumNumberOfThresholdsAllowed() {
+        return MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mRan);
+        out.writeInt(mSignalMeasurementType);
+        out.writeInt(mHysteresisMs);
+        out.writeInt(mHysteresisDb);
+        out.writeIntArray(mThresholds);
+        out.writeBoolean(mIsEnabled);
+    }
+
+    private SignalThresholdInfo(Parcel in) {
+        mRan = in.readInt();
+        mSignalMeasurementType = in.readInt();
+        mHysteresisMs = in.readInt();
+        mHysteresisDb = in.readInt();
+        mThresholds = in.createIntArray();
+        mIsEnabled = in.readBoolean();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (!(o instanceof SignalThresholdInfo)) {
+            return false;
+        }
+
+        SignalThresholdInfo other = (SignalThresholdInfo) o;
+        return mRan == other.mRan
+                && mSignalMeasurementType == other.mSignalMeasurementType
+                && mHysteresisMs == other.mHysteresisMs
+                && mHysteresisDb == other.mHysteresisDb
+                && Arrays.equals(mThresholds, other.mThresholds)
+                && mIsEnabled == other.mIsEnabled;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mRan,
+                mSignalMeasurementType,
+                mHysteresisMs,
+                mHysteresisDb,
+                Arrays.hashCode(mThresholds),
+                mIsEnabled);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<SignalThresholdInfo> CREATOR =
+            new Parcelable.Creator<SignalThresholdInfo>() {
+                @Override
+                public SignalThresholdInfo createFromParcel(Parcel in) {
+                    return new SignalThresholdInfo(in);
+                }
+
+                @Override
+                public SignalThresholdInfo[] newArray(int size) {
+                    return new SignalThresholdInfo[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return new StringBuilder("SignalThresholdInfo{")
+                .append("mRan=")
+                .append(mRan)
+                .append(" mSignalMeasurementType=")
+                .append(mSignalMeasurementType)
+                .append(" mHysteresisMs=")
+                .append(mHysteresisMs)
+                .append(" mHysteresisDb=")
+                .append(mHysteresisDb)
+                .append(" mThresholds=")
+                .append(Arrays.toString(mThresholds))
+                .append(" mIsEnabled=")
+                .append(mIsEnabled)
+                .append("}")
+                .toString();
+    }
+
+    /**
+     * Return true if signal measurement type is valid and the threshold value is in range.
+     */
+    private static boolean isValidThreshold(@SignalMeasurementType int type, int threshold) {
+        switch (type) {
+            case SIGNAL_MEASUREMENT_TYPE_RSSI:
+                return threshold >= SIGNAL_RSSI_MIN_VALUE && threshold <= SIGNAL_RSSI_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_RSCP:
+                return threshold >= SIGNAL_RSCP_MIN_VALUE && threshold <= SIGNAL_RSCP_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_RSRP:
+                return threshold >= SIGNAL_RSRP_MIN_VALUE && threshold <= SIGNAL_RSRP_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_RSRQ:
+                return threshold >= SIGNAL_RSRQ_MIN_VALUE && threshold <= SIGNAL_RSRQ_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_RSSNR:
+                return threshold >= SIGNAL_RSSNR_MIN_VALUE && threshold <= SIGNAL_RSSNR_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_SSRSRP:
+                return threshold >= SIGNAL_SSRSRP_MIN_VALUE && threshold <= SIGNAL_SSRSRP_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_SSRSRQ:
+                return threshold >= SIGNAL_SSRSRQ_MIN_VALUE && threshold <= SIGNAL_SSRSRQ_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_SSSINR:
+                return threshold >= SIGNAL_SSSINR_MIN_VALUE && threshold <= SIGNAL_SSSINR_MAX_VALUE;
+            case SIGNAL_MEASUREMENT_TYPE_ECNO:
+                return threshold >= SIGNAL_ECNO_MIN_VALUE && threshold <= SIGNAL_ECNO_MAX_VALUE;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Return true if the radio access type is allowed to set with the measurement type.
+     */
+    private static boolean isValidRanWithMeasurementType(
+            @AccessNetworkConstants.RadioAccessNetworkType int ran,
+            @SignalMeasurementType int type) {
+        switch (type) {
+            case SIGNAL_MEASUREMENT_TYPE_RSSI:
+                return ran == AccessNetworkConstants.AccessNetworkType.GERAN
+                        || ran == AccessNetworkConstants.AccessNetworkType.CDMA2000;
+            case SIGNAL_MEASUREMENT_TYPE_RSCP:
+            case SIGNAL_MEASUREMENT_TYPE_ECNO:
+                return ran == AccessNetworkConstants.AccessNetworkType.UTRAN;
+            case SIGNAL_MEASUREMENT_TYPE_RSRP:
+            case SIGNAL_MEASUREMENT_TYPE_RSRQ:
+            case SIGNAL_MEASUREMENT_TYPE_RSSNR:
+                return ran == AccessNetworkConstants.AccessNetworkType.EUTRAN;
+            case SIGNAL_MEASUREMENT_TYPE_SSRSRP:
+            case SIGNAL_MEASUREMENT_TYPE_SSRSRQ:
+            case SIGNAL_MEASUREMENT_TYPE_SSSINR:
+                return ran == AccessNetworkConstants.AccessNetworkType.NGRAN;
+            default:
+                return false;
+        }
+    }
+
+    private void validateRanWithMeasurementType(
+            @AccessNetworkConstants.RadioAccessNetworkType int ran,
+            @SignalMeasurementType int signalMeasurement) {
+        if (!isValidRanWithMeasurementType(ran, signalMeasurement)) {
+            throw new IllegalArgumentException(
+                    "invalid RAN: " + ran + " with signal measurement type: " + signalMeasurement);
+        }
+    }
+
+    private void validateThresholdRange(
+            @SignalMeasurementType int signalMeasurement, int[] thresholds) {
+        for (int threshold : thresholds) {
+            if (!isValidThreshold(signalMeasurement, threshold)) {
+                throw new IllegalArgumentException(
+                        "invalid signal measurement type: "
+                                + signalMeasurement
+                                + " with threshold: "
+                                + threshold);
+            }
+        }
+    }
+}
diff --git a/android-35/android/telephony/SmsCbCmasInfo.java b/android-35/android/telephony/SmsCbCmasInfo.java
new file mode 100644
index 0000000..2c10a09
--- /dev/null
+++ b/android-35/android/telephony/SmsCbCmasInfo.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Contains CMAS (Commercial Mobile Alert System) warning notification Type 1 elements for a
+ * {@link SmsCbMessage}.
+ * Supported values for each element are defined in TIA-1149-0-1 (CMAS over CDMA) and
+ * 3GPP TS 23.041 (for GSM/UMTS).
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class SmsCbCmasInfo implements Parcelable {
+
+    // CMAS message class (in GSM/UMTS message identifier or CDMA service category).
+
+    /** Presidential-level alert (Korean Public Alert System Class 0 message). */
+    public static final int CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT = 0x00;
+
+    /** Extreme threat to life and property (Korean Public Alert System Class 1 message). */
+    public static final int CMAS_CLASS_EXTREME_THREAT = 0x01;
+
+    /** Severe threat to life and property (Korean Public Alert System Class 1 message). */
+    public static final int CMAS_CLASS_SEVERE_THREAT = 0x02;
+
+    /** Child abduction emergency (AMBER Alert). */
+    public static final int CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY = 0x03;
+
+    /** CMAS test message. */
+    public static final int CMAS_CLASS_REQUIRED_MONTHLY_TEST = 0x04;
+
+    /** CMAS exercise. */
+    public static final int CMAS_CLASS_CMAS_EXERCISE = 0x05;
+
+    /** CMAS category for operator defined use. */
+    public static final int CMAS_CLASS_OPERATOR_DEFINED_USE = 0x06;
+
+    /** CMAS category for warning types that are reserved for future extension. */
+    public static final int CMAS_CLASS_UNKNOWN = -1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CMAS_CLASS_"},
+            value = {
+                    CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT,
+                    CMAS_CLASS_EXTREME_THREAT,
+                    CMAS_CLASS_SEVERE_THREAT,
+                    CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY,
+                    CMAS_CLASS_REQUIRED_MONTHLY_TEST,
+                    CMAS_CLASS_CMAS_EXERCISE,
+                    CMAS_CLASS_OPERATOR_DEFINED_USE,
+                    CMAS_CLASS_UNKNOWN,
+            })
+    public @interface Class {}
+
+    // CMAS alert category (in CDMA type 1 elements record).
+
+    /** CMAS alert category: Geophysical including landslide. */
+    public static final int CMAS_CATEGORY_GEO = 0x00;
+
+    /** CMAS alert category: Meteorological including flood. */
+    public static final int CMAS_CATEGORY_MET = 0x01;
+
+    /** CMAS alert category: General emergency and public safety. */
+    public static final int CMAS_CATEGORY_SAFETY = 0x02;
+
+    /** CMAS alert category: Law enforcement, military, homeland/local/private security. */
+    public static final int CMAS_CATEGORY_SECURITY = 0x03;
+
+    /** CMAS alert category: Rescue and recovery. */
+    public static final int CMAS_CATEGORY_RESCUE = 0x04;
+
+    /** CMAS alert category: Fire suppression and rescue. */
+    public static final int CMAS_CATEGORY_FIRE = 0x05;
+
+    /** CMAS alert category: Medical and public health. */
+    public static final int CMAS_CATEGORY_HEALTH = 0x06;
+
+    /** CMAS alert category: Pollution and other environmental. */
+    public static final int CMAS_CATEGORY_ENV = 0x07;
+
+    /** CMAS alert category: Public and private transportation. */
+    public static final int CMAS_CATEGORY_TRANSPORT = 0x08;
+
+    /** CMAS alert category: Utility, telecom, other non-transport infrastructure. */
+    public static final int CMAS_CATEGORY_INFRA = 0x09;
+
+    /** CMAS alert category: Chem, bio, radiological, nuclear, high explosive threat or attack. */
+    public static final int CMAS_CATEGORY_CBRNE = 0x0a;
+
+    /** CMAS alert category: Other events. */
+    public static final int CMAS_CATEGORY_OTHER = 0x0b;
+
+    /**
+     * CMAS alert category is unknown. The category is only available for CDMA broadcasts
+     * containing a type 1 elements record, so GSM and UMTS broadcasts always return unknown.
+     */
+    public static final int CMAS_CATEGORY_UNKNOWN = -1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CMAS_CATEORY_"},
+            value = {
+                    CMAS_CATEGORY_GEO,
+                    CMAS_CATEGORY_MET,
+                    CMAS_CATEGORY_SAFETY,
+                    CMAS_CATEGORY_SECURITY,
+                    CMAS_CATEGORY_RESCUE,
+                    CMAS_CATEGORY_FIRE,
+                    CMAS_CATEGORY_HEALTH,
+                    CMAS_CATEGORY_ENV,
+                    CMAS_CATEGORY_TRANSPORT,
+                    CMAS_CATEGORY_INFRA,
+                    CMAS_CATEGORY_CBRNE,
+                    CMAS_CATEGORY_OTHER,
+                    CMAS_CATEGORY_UNKNOWN,
+            })
+    public @interface Category {}
+
+    // CMAS response type (in CDMA type 1 elements record).
+
+    /** CMAS response type: Take shelter in place. */
+    public static final int CMAS_RESPONSE_TYPE_SHELTER = 0x00;
+
+    /** CMAS response type: Evacuate (Relocate). */
+    public static final int CMAS_RESPONSE_TYPE_EVACUATE = 0x01;
+
+    /** CMAS response type: Make preparations. */
+    public static final int CMAS_RESPONSE_TYPE_PREPARE = 0x02;
+
+    /** CMAS response type: Execute a pre-planned activity. */
+    public static final int CMAS_RESPONSE_TYPE_EXECUTE = 0x03;
+
+    /** CMAS response type: Attend to information sources. */
+    public static final int CMAS_RESPONSE_TYPE_MONITOR = 0x04;
+
+    /** CMAS response type: Avoid hazard. */
+    public static final int CMAS_RESPONSE_TYPE_AVOID = 0x05;
+
+    /** CMAS response type: Evaluate the information in this message (not for public warnings). */
+    public static final int CMAS_RESPONSE_TYPE_ASSESS = 0x06;
+
+    /** CMAS response type: No action recommended. */
+    public static final int CMAS_RESPONSE_TYPE_NONE = 0x07;
+
+    /**
+     * CMAS response type is unknown. The response type is only available for CDMA broadcasts
+     * containing a type 1 elements record, so GSM and UMTS broadcasts always return unknown.
+     */
+    public static final int CMAS_RESPONSE_TYPE_UNKNOWN = -1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CMAS_RESPONSE_TYPE_"},
+            value = {
+                    CMAS_RESPONSE_TYPE_SHELTER,
+                    CMAS_RESPONSE_TYPE_EVACUATE,
+                    CMAS_RESPONSE_TYPE_PREPARE,
+                    CMAS_RESPONSE_TYPE_EXECUTE,
+                    CMAS_RESPONSE_TYPE_MONITOR,
+                    CMAS_RESPONSE_TYPE_AVOID,
+                    CMAS_RESPONSE_TYPE_ASSESS,
+                    CMAS_RESPONSE_TYPE_NONE,
+                    CMAS_RESPONSE_TYPE_UNKNOWN,
+    })
+    public @interface ResponseType {}
+
+    // 4-bit CMAS severity (in GSM/UMTS message identifier or CDMA type 1 elements record).
+
+    /** CMAS severity type: Extraordinary threat to life or property. */
+    public static final int CMAS_SEVERITY_EXTREME = 0x0;
+
+    /** CMAS severity type: Significant threat to life or property. */
+    public static final int CMAS_SEVERITY_SEVERE = 0x1;
+
+    /**
+     * CMAS alert severity is unknown. The severity is available for CDMA warning alerts
+     * containing a type 1 elements record and for all GSM and UMTS alerts except for the
+     * Presidential-level alert class (Korean Public Alert System Class 0).
+     */
+    public static final int CMAS_SEVERITY_UNKNOWN = -1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CMAS_SEVERITY_"},
+            value = {
+                    CMAS_SEVERITY_EXTREME,
+                    CMAS_SEVERITY_SEVERE,
+                    CMAS_SEVERITY_UNKNOWN,
+            })
+    public @interface Severity {}
+
+    // CMAS urgency (in GSM/UMTS message identifier or CDMA type 1 elements record).
+
+    /** CMAS urgency type: Responsive action should be taken immediately. */
+    public static final int CMAS_URGENCY_IMMEDIATE = 0x0;
+
+    /** CMAS urgency type: Responsive action should be taken within the next hour. */
+    public static final int CMAS_URGENCY_EXPECTED = 0x1;
+
+    /**
+     * CMAS alert urgency is unknown. The urgency is available for CDMA warning alerts
+     * containing a type 1 elements record and for all GSM and UMTS alerts except for the
+     * Presidential-level alert class (Korean Public Alert System Class 0).
+     */
+    public static final int CMAS_URGENCY_UNKNOWN = -1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CMAS_URGENCY_"},
+            value = {
+                    CMAS_URGENCY_IMMEDIATE,
+                    CMAS_URGENCY_EXPECTED,
+                    CMAS_URGENCY_UNKNOWN,
+            })
+    public @interface Urgency {}
+
+    // CMAS certainty (in GSM/UMTS message identifier or CDMA type 1 elements record).
+
+    /** CMAS certainty type: Determined to have occurred or to be ongoing. */
+    public static final int CMAS_CERTAINTY_OBSERVED = 0x0;
+
+    /** CMAS certainty type: Likely (probability > ~50%). */
+    public static final int CMAS_CERTAINTY_LIKELY = 0x1;
+
+    /**
+     * CMAS alert certainty is unknown. The certainty is available for CDMA warning alerts
+     * containing a type 1 elements record and for all GSM and UMTS alerts except for the
+     * Presidential-level alert class (Korean Public Alert System Class 0).
+     */
+    public static final int CMAS_CERTAINTY_UNKNOWN = -1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CMAS_CERTAINTY_"},
+            value = {
+                    CMAS_CERTAINTY_OBSERVED,
+                    CMAS_CERTAINTY_LIKELY,
+                    CMAS_CERTAINTY_UNKNOWN,
+            })
+    public @interface Certainty {}
+
+    /** CMAS message class. */
+    private final @Class int mMessageClass;
+
+    /** CMAS category. */
+    private final @Category int mCategory;
+
+    /** CMAS response type. */
+    private final @ResponseType int mResponseType;
+
+    /** CMAS severity. */
+    private final @Severity int mSeverity;
+
+    /** CMAS urgency. */
+    private final @Urgency int mUrgency;
+
+    /** CMAS certainty. */
+    private final @Certainty int mCertainty;
+
+    /** Create a new SmsCbCmasInfo object with the specified values. */
+    public SmsCbCmasInfo(@Class int messageClass, @Category int category,
+            @ResponseType int responseType,
+            @Severity int severity, @Urgency int urgency, @Certainty int certainty) {
+        mMessageClass = messageClass;
+        mCategory = category;
+        mResponseType = responseType;
+        mSeverity = severity;
+        mUrgency = urgency;
+        mCertainty = certainty;
+    }
+
+    /** Create a new SmsCbCmasInfo object from a Parcel. */
+    SmsCbCmasInfo(Parcel in) {
+        mMessageClass = in.readInt();
+        mCategory = in.readInt();
+        mResponseType = in.readInt();
+        mSeverity = in.readInt();
+        mUrgency = in.readInt();
+        mCertainty = in.readInt();
+    }
+
+    /**
+     * Flatten this object into a Parcel.
+     *
+     * @param dest  The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written (ignored).
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mMessageClass);
+        dest.writeInt(mCategory);
+        dest.writeInt(mResponseType);
+        dest.writeInt(mSeverity);
+        dest.writeInt(mUrgency);
+        dest.writeInt(mCertainty);
+    }
+
+    /**
+     * Returns the CMAS message class, e.g. {@link #CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT}.
+     * @return one of the {@code CMAS_CLASS} values
+     */
+    public @Class int getMessageClass() {
+        return mMessageClass;
+    }
+
+    /**
+     * Returns the CMAS category, e.g. {@link #CMAS_CATEGORY_GEO}.
+     * @return one of the {@code CMAS_CATEGORY} values
+     */
+    public @Category int getCategory() {
+        return mCategory;
+    }
+
+    /**
+     * Returns the CMAS response type, e.g. {@link #CMAS_RESPONSE_TYPE_SHELTER}.
+     * @return one of the {@code CMAS_RESPONSE_TYPE} values
+     */
+    public @ResponseType int getResponseType() {
+        return mResponseType;
+    }
+
+    /**
+     * Returns the CMAS severity, e.g. {@link #CMAS_SEVERITY_EXTREME}.
+     * @return one of the {@code CMAS_SEVERITY} values
+     */
+    public @Severity int getSeverity() {
+        return mSeverity;
+    }
+
+    /**
+     * Returns the CMAS urgency, e.g. {@link #CMAS_URGENCY_IMMEDIATE}.
+     * @return one of the {@code CMAS_URGENCY} values
+     */
+    public @Urgency int getUrgency() {
+        return mUrgency;
+    }
+
+    /**
+     * Returns the CMAS certainty, e.g. {@link #CMAS_CERTAINTY_OBSERVED}.
+     *
+     * @return one of the {@code CMAS_CERTAINTY} values
+     */
+    public @Certainty int getCertainty() {
+        return mCertainty;
+    }
+
+    @Override
+    public String toString() {
+        return "SmsCbCmasInfo{messageClass=" + mMessageClass + ", category=" + mCategory
+                + ", responseType=" + mResponseType + ", severity=" + mSeverity
+                + ", urgency=" + mUrgency + ", certainty=" + mCertainty + '}';
+    }
+
+    /**
+     * Describe the kinds of special objects contained in the marshalled representation.
+     *
+     * @return a bitmask indicating this Parcelable contains no special objects
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Creator for unparcelling objects. */
+    @NonNull
+    public static final Parcelable.Creator<SmsCbCmasInfo> CREATOR =
+            new Parcelable.Creator<SmsCbCmasInfo>() {
+        @Override
+        public SmsCbCmasInfo createFromParcel(Parcel in) {
+            return new SmsCbCmasInfo(in);
+        }
+
+        @Override
+        public SmsCbCmasInfo[] newArray(int size) {
+            return new SmsCbCmasInfo[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/SmsCbEtwsInfo.java b/android-35/android/telephony/SmsCbEtwsInfo.java
new file mode 100644
index 0000000..a98916d
--- /dev/null
+++ b/android-35/android/telephony/SmsCbEtwsInfo.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.uicc.IccUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.DateTimeException;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Arrays;
+
+/**
+ * Contains information elements for a GSM or UMTS ETWS (Earthquake and Tsunami Warning
+ * System) warning notification. Supported values for each element are defined in 3GPP TS 23.041.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class SmsCbEtwsInfo implements Parcelable {
+
+    /** ETWS warning type for earthquake. */
+    public static final int ETWS_WARNING_TYPE_EARTHQUAKE = 0x00;
+
+    /** ETWS warning type for tsunami. */
+    public static final int ETWS_WARNING_TYPE_TSUNAMI = 0x01;
+
+    /** ETWS warning type for earthquake and tsunami. */
+    public static final int ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI = 0x02;
+
+    /** ETWS warning type for test messages. */
+    public static final int ETWS_WARNING_TYPE_TEST_MESSAGE = 0x03;
+
+    /** ETWS warning type for other emergency types. */
+    public static final int ETWS_WARNING_TYPE_OTHER_EMERGENCY = 0x04;
+
+    /** Unknown ETWS warning type. */
+    public static final int ETWS_WARNING_TYPE_UNKNOWN = -1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"ETWS_WARNING_TYPE_"},
+            value = {
+                    ETWS_WARNING_TYPE_EARTHQUAKE,
+                    ETWS_WARNING_TYPE_TSUNAMI,
+                    ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI,
+                    ETWS_WARNING_TYPE_TEST_MESSAGE,
+                    ETWS_WARNING_TYPE_OTHER_EMERGENCY,
+                    ETWS_WARNING_TYPE_UNKNOWN,
+            })
+    public @interface WarningType {}
+
+    /** One of the ETWS warning type constants defined in this class. */
+    private final @WarningType int mWarningType;
+
+    /** Whether or not to activate the emergency user alert tone and vibration. */
+    private final boolean mIsEmergencyUserAlert;
+
+    /** Whether or not to activate a popup alert. */
+    private final boolean mIsPopupAlert;
+
+    /** Whether ETWS primary message or not/ */
+    private final boolean mIsPrimary;
+
+    /**
+     * 50-byte security information (ETWS primary notification for GSM only). As of Release 10,
+     * 3GPP TS 23.041 states that the UE shall ignore the ETWS primary notification timestamp
+     * and digital signature if received. Therefore it is treated as a raw byte array and
+     * parceled with the broadcast intent if present, but the timestamp is only computed if an
+     * application asks for the individual components.
+     */
+    @Nullable
+    private final byte[] mWarningSecurityInformation;
+
+    /**
+     * Create a new SmsCbEtwsInfo object with the specified values.
+     * @param warningType the type of ETWS warning
+     * @param isEmergencyUserAlert whether the warning is an emergency alert, which will activate
+     *                             the user alert tone and vibration
+     * @param isPopupAlert whether the warning will activate a popup alert
+     * @param isPrimary whether this is an ETWS primary message
+     * @param warningSecurityInformation 50-byte security information (for primary notifications
+     *                                   on GSM only).
+     */
+    public SmsCbEtwsInfo(@WarningType int warningType, boolean isEmergencyUserAlert,
+            boolean isPopupAlert,
+            boolean isPrimary, @Nullable byte[] warningSecurityInformation) {
+        mWarningType = warningType;
+        mIsEmergencyUserAlert = isEmergencyUserAlert;
+        mIsPopupAlert = isPopupAlert;
+        mIsPrimary = isPrimary;
+        mWarningSecurityInformation = warningSecurityInformation;
+    }
+
+    /** Create a new SmsCbEtwsInfo object from a Parcel. */
+    SmsCbEtwsInfo(Parcel in) {
+        mWarningType = in.readInt();
+        mIsEmergencyUserAlert = (in.readInt() != 0);
+        mIsPopupAlert = (in.readInt() != 0);
+        mIsPrimary = (in.readInt() != 0);
+        mWarningSecurityInformation = in.createByteArray();
+    }
+
+    /**
+     * Flatten this object into a Parcel.
+     *
+     * @param dest  The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written (ignored).
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mWarningType);
+        dest.writeInt(mIsEmergencyUserAlert ? 1 : 0);
+        dest.writeInt(mIsPopupAlert ? 1 : 0);
+        dest.writeInt(mIsPrimary ? 1 : 0);
+        dest.writeByteArray(mWarningSecurityInformation);
+    }
+
+    /**
+     * Returns the ETWS warning type.
+     * @return a warning type such as {@link #ETWS_WARNING_TYPE_EARTHQUAKE}
+     */
+    public @WarningType int getWarningType() {
+        return mWarningType;
+    }
+
+    /**
+     * Returns the ETWS emergency user alert flag. If the ETWS message is an emergency alert, it
+     * will activate an alert tone and vibration.
+     * @return true to notify terminal to activate emergency user alert; false otherwise
+     */
+    public boolean isEmergencyUserAlert() {
+        return mIsEmergencyUserAlert;
+    }
+
+    /**
+     * Returns the ETWS activate popup flag.
+     * @return true to notify terminal to activate display popup; false otherwise
+     */
+    public boolean isPopupAlert() {
+        return mIsPopupAlert;
+    }
+
+    /**
+     * Returns the ETWS format flag.
+     * @return true if the message is primary message, otherwise secondary message
+     */
+    public boolean isPrimary() {
+        return mIsPrimary;
+    }
+
+    /**
+     * Returns the Warning-Security-Information timestamp (GSM primary notifications only).
+     * As of Release 10, 3GPP TS 23.041 states that the UE shall ignore this value if received.
+     * @return a UTC timestamp in System.currentTimeMillis() format, or 0 if not present or invalid.
+     */
+    public long getPrimaryNotificationTimestamp() {
+        if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 7) {
+            return 0;
+        }
+
+        int year = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[0]);
+        int month = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[1]);
+        int day = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[2]);
+        int hour = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[3]);
+        int minute = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[4]);
+        int second = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[5]);
+
+        // For the timezone, the most significant bit of the
+        // least significant nibble is the sign byte
+        // (meaning the max range of this field is 79 quarter-hours,
+        // which is more than enough)
+
+        byte tzByte = mWarningSecurityInformation[6];
+
+        // Mask out sign bit.
+        int timezoneOffset = IccUtils.gsmBcdByteToInt((byte) (tzByte & (~0x08)));
+
+        timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset;
+        // timezoneOffset is in quarter hours.
+        int timeZoneOffsetSeconds = timezoneOffset * 15 * 60;
+
+        try {
+            LocalDateTime localDateTime = LocalDateTime.of(
+                    // We only need to support years above 2000.
+                    year + 2000,
+                    month /* 1-12 */,
+                    day,
+                    hour,
+                    minute,
+                    second);
+
+            long epochSeconds = localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds;
+            // Convert to milliseconds, ignore overflow.
+            return epochSeconds * 1000;
+        } catch (DateTimeException ex) {
+            // No-op
+        }
+        return 0;
+    }
+
+    /**
+     * Returns the digital signature (GSM primary notifications only). As of Release 10,
+     * 3GPP TS 23.041 states that the UE shall ignore this value if received.
+     * @return a byte array containing a copy of the primary notification digital signature
+     */
+    @Nullable
+    public byte[] getPrimaryNotificationSignature() {
+        if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 50) {
+            return null;
+        }
+        return Arrays.copyOfRange(mWarningSecurityInformation, 7, 50);
+    }
+
+    @Override
+    public String toString() {
+        return "SmsCbEtwsInfo{warningType=" + mWarningType + ", emergencyUserAlert="
+                + mIsEmergencyUserAlert + ", activatePopup=" + mIsPopupAlert + '}';
+    }
+
+    /**
+     * Describe the kinds of special objects contained in the marshalled representation.
+     * @return a bitmask indicating this Parcelable contains no special objects
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Creator for unparcelling objects. */
+    @NonNull
+    public static final Creator<SmsCbEtwsInfo> CREATOR = new Creator<SmsCbEtwsInfo>() {
+        @Override
+        public SmsCbEtwsInfo createFromParcel(Parcel in) {
+            return new SmsCbEtwsInfo(in);
+        }
+
+        @Override
+        public SmsCbEtwsInfo[] newArray(int size) {
+            return new SmsCbEtwsInfo[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/SmsCbLocation.java b/android-35/android/telephony/SmsCbLocation.java
new file mode 100644
index 0000000..663e8e2
--- /dev/null
+++ b/android-35/android/telephony/SmsCbLocation.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2012 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents the location and geographical scope of a cell broadcast message.
+ * For GSM/UMTS, the Location Area and Cell ID are set when the broadcast
+ * geographical scope is cell wide or Location Area wide. For CDMA, the
+ * broadcast geographical scope is always PLMN wide.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmsCbLocation implements Parcelable {
+
+    /** The PLMN. Note that this field may be an empty string. */
+    @NonNull
+    private final String mPlmn;
+
+    private final int mLac;
+    private final int mCid;
+
+    /**
+     * Construct an empty location object. This is used for some test cases, and for
+     * cell broadcasts saved in older versions of the database without location info.
+     * @hide
+     */
+    public SmsCbLocation() {
+        mPlmn = "";
+        mLac = -1;
+        mCid = -1;
+    }
+
+    /**
+     * Construct a location object for the PLMN. This class is immutable, so
+     * the same object can be reused for multiple broadcasts.
+     * @hide
+     */
+    public SmsCbLocation(String plmn) {
+        mPlmn = plmn;
+        mLac = -1;
+        mCid = -1;
+    }
+
+    /**
+     * Construct a location object for the PLMN, LAC, and Cell ID. This class is immutable, so
+     * the same object can be reused for multiple broadcasts.
+     *
+     * @param plmn the MCC/MNC of the network
+     * @param lac the GSM location area code, or UMTS service area code
+     * @param cid the GSM or UMTS cell ID
+     */
+    public SmsCbLocation(@NonNull String plmn, int lac, int cid) {
+        mPlmn = plmn;
+        mLac = lac;
+        mCid = cid;
+    }
+
+    /**
+     * Initialize the object from a Parcel.
+     * @hide
+     */
+    public SmsCbLocation(Parcel in) {
+        mPlmn = in.readString();
+        mLac = in.readInt();
+        mCid = in.readInt();
+    }
+
+    /**
+     * Returns the MCC/MNC of the network as a String.
+     * @return the PLMN identifier (MCC+MNC) as a String
+     */
+    @NonNull
+    public String getPlmn() {
+        return mPlmn;
+    }
+
+    /**
+     * Returns the GSM location area code, or UMTS service area code.
+     * @return location area code, -1 if unknown, 0xffff max legal value
+     */
+    public int getLac() {
+        return mLac;
+    }
+
+    /**
+     * Returns the GSM or UMTS cell ID.
+     * @return gsm cell id, -1 if unknown, 0xffff max legal value
+     */
+    public int getCid() {
+        return mCid;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = mPlmn.hashCode();
+        hash = hash * 31 + mLac;
+        hash = hash * 31 + mCid;
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (o == null || !(o instanceof SmsCbLocation)) {
+            return false;
+        }
+        SmsCbLocation other = (SmsCbLocation) o;
+        return mPlmn.equals(other.mPlmn) && mLac == other.mLac && mCid == other.mCid;
+    }
+
+    @Override
+    public String toString() {
+        return '[' + mPlmn + ',' + mLac + ',' + mCid + ']';
+    }
+
+    /**
+     * Test whether this location is within the location area of the specified object.
+     *
+     * @param area the location area to compare with this location
+     * @return true if this location is contained within the specified location area
+     */
+    public boolean isInLocationArea(@NonNull SmsCbLocation area) {
+        if (mCid != -1 && mCid != area.mCid) {
+            return false;
+        }
+        if (mLac != -1 && mLac != area.mLac) {
+            return false;
+        }
+        return mPlmn.equals(area.mPlmn);
+    }
+
+    /**
+     * Test whether this location is within the location area of the CellLocation.
+     *
+     * @param plmn the PLMN to use for comparison
+     * @param lac the Location Area (GSM) or Service Area (UMTS) to compare with
+     * @param cid the Cell ID to compare with
+     * @return true if this location is contained within the specified PLMN, LAC, and Cell ID
+     */
+    public boolean isInLocationArea(@Nullable String plmn, int lac, int cid) {
+        if (!mPlmn.equals(plmn)) {
+            return false;
+        }
+
+        if (mLac != -1 && mLac != lac) {
+            return false;
+        }
+
+        if (mCid != -1 && mCid != cid) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Flatten this object into a Parcel.
+     *
+     * @param dest  The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written (ignored).
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mPlmn);
+        dest.writeInt(mLac);
+        dest.writeInt(mCid);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<SmsCbLocation> CREATOR =
+            new Parcelable.Creator<SmsCbLocation>() {
+        @Override
+        public SmsCbLocation createFromParcel(Parcel in) {
+            return new SmsCbLocation(in);
+        }
+
+        @Override
+        public SmsCbLocation[] newArray(int size) {
+            return new SmsCbLocation[size];
+        }
+    };
+
+    /**
+     * Describe the kinds of special objects contained in the marshalled representation.
+     * @return a bitmask indicating this Parcelable contains no special objects
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/android-35/android/telephony/SmsCbMessage.java b/android-35/android/telephony/SmsCbMessage.java
new file mode 100644
index 0000000..d366efe
--- /dev/null
+++ b/android-35/android/telephony/SmsCbMessage.java
@@ -0,0 +1,751 @@
+/*
+ * Copyright (C) 2010 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.Telephony.CellBroadcasts;
+import android.telephony.CbGeoUtils.Geometry;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parcelable object containing a received cell broadcast message. There are four different types
+ * of Cell Broadcast messages:
+ *
+ * <ul>
+ * <li>opt-in informational broadcasts, e.g. news, weather, stock quotes, sports scores</li>
+ * <li>cell information messages, broadcast on channel 50, indicating the current cell name for
+ *  roaming purposes (required to display on the idle screen in Brazil)</li>
+ * <li>emergency broadcasts for the Japanese Earthquake and Tsunami Warning System (ETWS)</li>
+ * <li>emergency broadcasts for the American Commercial Mobile Alert Service (CMAS)</li>
+ * </ul>
+ *
+ * <p>There are also four different CB message formats: GSM, ETWS Primary Notification (GSM only),
+ * UMTS, and CDMA. Some fields are only applicable for some message formats. Other fields were
+ * unified under a common name, avoiding some names, such as "Message Identifier", that refer to
+ * two completely different concepts in 3GPP and CDMA.
+ *
+ * <p>The GSM/UMTS Message Identifier field is available via {@link #getServiceCategory}, the name
+ * of the equivalent field in CDMA. In both cases the service category is a 16-bit value, but 3GPP
+ * and 3GPP2 have completely different meanings for the respective values. For ETWS and CMAS, the
+ * application should
+ *
+ * <p>The CDMA Message Identifier field is available via {@link #getSerialNumber}, which is used
+ * to detect the receipt of a duplicate message to be discarded. In CDMA, the message ID is
+ * unique to the current PLMN. In GSM/UMTS, there is a 16-bit serial number containing a 2-bit
+ * Geographical Scope field which indicates whether the 10-bit message code and 4-bit update number
+ * are considered unique to the PLMN, to the current cell, or to the current Location Area (or
+ * Service Area in UMTS). The relevant values are concatenated into a single String which will be
+ * unique if the messages are not duplicates.
+ *
+ * <p>The SMS dispatcher does not detect duplicate messages. However, it does concatenate the
+ * pages of a GSM multi-page cell broadcast into a single SmsCbMessage object.
+ *
+ * <p>Interested applications with {@code RECEIVE_SMS_PERMISSION} can register to receive
+ * {@code SMS_CB_RECEIVED_ACTION} broadcast intents for incoming non-emergency broadcasts.
+ * Only system applications such as the CellBroadcastReceiver may receive notifications for
+ * emergency broadcasts (ETWS and CMAS). This is intended to prevent any potential for delays or
+ * interference with the immediate display of the alert message and playing of the alert sound and
+ * vibration pattern, which could be caused by poorly written or malicious non-system code.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmsCbMessage implements Parcelable {
+
+    /** @hide */
+    public static final String LOG_TAG = "SMSCB";
+
+    /** Cell wide geographical scope with immediate display (GSM/UMTS only). */
+    public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0;
+
+    /** PLMN wide geographical scope (GSM/UMTS and all CDMA broadcasts). */
+    public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1;
+
+    /** Location / service area wide geographical scope (GSM/UMTS only). */
+    public static final int GEOGRAPHICAL_SCOPE_LOCATION_AREA_WIDE = 2;
+
+    /** Cell wide geographical scope (GSM/UMTS only). */
+    public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "GEOGRAPHICAL_SCOPE_" }, value = {
+            GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE,
+            GEOGRAPHICAL_SCOPE_PLMN_WIDE,
+            GEOGRAPHICAL_SCOPE_LOCATION_AREA_WIDE,
+            GEOGRAPHICAL_SCOPE_CELL_WIDE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GeographicalScope {}
+
+    /** GSM or UMTS format cell broadcast. */
+    public static final int MESSAGE_FORMAT_3GPP = 1;
+
+    /** CDMA format cell broadcast. */
+    public static final int MESSAGE_FORMAT_3GPP2 = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "MESSAGE_FORMAT_" }, value = {
+            MESSAGE_FORMAT_3GPP,
+            MESSAGE_FORMAT_3GPP2
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MessageFormat {}
+
+    /** Normal message priority. */
+    public static final int MESSAGE_PRIORITY_NORMAL = 0;
+
+    /** Interactive message priority. */
+    public static final int MESSAGE_PRIORITY_INTERACTIVE = 1;
+
+    /** Urgent message priority. */
+    public static final int MESSAGE_PRIORITY_URGENT = 2;
+
+    /** Emergency message priority. */
+    public static final int MESSAGE_PRIORITY_EMERGENCY = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "MESSAGE_PRIORITY_" }, value = {
+            MESSAGE_PRIORITY_NORMAL,
+            MESSAGE_PRIORITY_INTERACTIVE,
+            MESSAGE_PRIORITY_URGENT,
+            MESSAGE_PRIORITY_EMERGENCY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MessagePriority {}
+
+    /**
+     * Integer indicating that the maximum wait time is not set.
+     * Based on ATIS-0700041 Section 5.2.8 WAC Geo-Fencing Maximum Wait Time Table 12.
+     */
+    public static final int MAXIMUM_WAIT_TIME_NOT_SET = 255;
+
+    /** Format of this message (for interpretation of service category values). */
+    private final int mMessageFormat;
+
+    /** Geographical scope of broadcast. */
+    private final int mGeographicalScope;
+
+    /**
+     * Serial number of broadcast (message identifier for CDMA, geographical scope + message code +
+     * update number for GSM/UMTS). The serial number plus the location code uniquely identify
+     * a cell broadcast for duplicate detection.
+     */
+    private final int mSerialNumber;
+
+    /**
+     * Location identifier for this message. It consists of the current operator MCC/MNC as a
+     * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the
+     * message is not binary 01, the Location Area is included for comparison. If the GS is
+     * 00 or 11, the Cell ID is also included. LAC and Cell ID are -1 if not specified.
+     */
+    @NonNull
+    private final SmsCbLocation mLocation;
+
+    /**
+     * 16-bit CDMA service category or GSM/UMTS message identifier. For ETWS and CMAS warnings,
+     * the information provided by the category is also available via {@link #getEtwsWarningInfo()}
+     * or {@link #getCmasWarningInfo()}.
+     */
+    private final int mServiceCategory;
+
+    /** Message language, as a two-character string, e.g. "en". */
+    @Nullable
+    private final String mLanguage;
+
+    /** The 8-bit data coding scheme defined in 3GPP TS 23.038 section 4. */
+    private final int mDataCodingScheme;
+
+    /** Message body, as a String. */
+    @Nullable
+    private final String mBody;
+
+    /** Message priority (including emergency priority). */
+    private final int mPriority;
+
+    /** ETWS warning notification information (ETWS warnings only). */
+    @Nullable
+    private final SmsCbEtwsInfo mEtwsWarningInfo;
+
+    /** CMAS warning notification information (CMAS warnings only). */
+    @Nullable
+    private final SmsCbCmasInfo mCmasWarningInfo;
+
+    /**
+     * Geo-Fencing Maximum Wait Time in second, a device shall allow to determine its position
+     * meeting operator policy. If the device is unable to determine its position meeting operator
+     * policy within the GeoFencing Maximum Wait Time, it shall present the alert to the user and
+     * discontinue further positioning determination for the alert.
+     */
+    private final int mMaximumWaitTimeSec;
+
+    /** UNIX timestamp of when the message was received. */
+    private final long mReceivedTimeMillis;
+
+    /** CMAS warning area coordinates. */
+    private final List<Geometry> mGeometries;
+
+    private final int mSlotIndex;
+
+    private final int mSubId;
+
+    /**
+     * Create a new SmsCbMessage with the specified data.
+     * @hide
+     */
+    public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
+            @NonNull SmsCbLocation location, int serviceCategory, @Nullable String language,
+            @Nullable String body, int priority, @Nullable SmsCbEtwsInfo etwsWarningInfo,
+            @Nullable SmsCbCmasInfo cmasWarningInfo, int slotIndex, int subId) {
+
+        this(messageFormat, geographicalScope, serialNumber, location, serviceCategory, language,
+                0, body, priority, etwsWarningInfo, cmasWarningInfo, 0 /* maximumWaitingTime */,
+                null /* geometries */, System.currentTimeMillis(), slotIndex, subId);
+    }
+
+    /**
+     * Create a new {@link SmsCbMessage} with the specified data, including warning area
+     * coordinates information.
+     */
+    public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
+                        @NonNull SmsCbLocation location, int serviceCategory,
+                        @Nullable String language, int dataCodingScheme, @Nullable String body,
+                        int priority, @Nullable SmsCbEtwsInfo etwsWarningInfo,
+                        @Nullable SmsCbCmasInfo cmasWarningInfo, int maximumWaitTimeSec,
+                        @Nullable List<Geometry> geometries, long receivedTimeMillis, int slotIndex,
+                        int subId) {
+        mMessageFormat = messageFormat;
+        mGeographicalScope = geographicalScope;
+        mSerialNumber = serialNumber;
+        mLocation = location;
+        mServiceCategory = serviceCategory;
+        mLanguage = language;
+        mDataCodingScheme = dataCodingScheme;
+        mBody = body;
+        mPriority = priority;
+        mEtwsWarningInfo = etwsWarningInfo;
+        mCmasWarningInfo = cmasWarningInfo;
+        mReceivedTimeMillis = receivedTimeMillis;
+        mGeometries = geometries;
+        mMaximumWaitTimeSec = maximumWaitTimeSec;
+        mSlotIndex = slotIndex;
+        mSubId = subId;
+    }
+
+    /**
+     * Create a new SmsCbMessage object from a Parcel.
+     * @hide
+     */
+    public SmsCbMessage(@NonNull Parcel in) {
+        mMessageFormat = in.readInt();
+        mGeographicalScope = in.readInt();
+        mSerialNumber = in.readInt();
+        mLocation = new SmsCbLocation(in);
+        mServiceCategory = in.readInt();
+        mLanguage = in.readString();
+        mDataCodingScheme = in.readInt();
+        mBody = in.readString();
+        mPriority = in.readInt();
+        int type = in.readInt();
+        switch (type) {
+            case 'E':
+                // unparcel ETWS warning information
+                mEtwsWarningInfo = new SmsCbEtwsInfo(in);
+                mCmasWarningInfo = null;
+                break;
+
+            case 'C':
+                // unparcel CMAS warning information
+                mEtwsWarningInfo = null;
+                mCmasWarningInfo = new SmsCbCmasInfo(in);
+                break;
+
+            default:
+                mEtwsWarningInfo = null;
+                mCmasWarningInfo = null;
+        }
+        mReceivedTimeMillis = in.readLong();
+        String geoStr = in.readString();
+        mGeometries = geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null;
+        mMaximumWaitTimeSec = in.readInt();
+        mSlotIndex = in.readInt();
+        mSubId = in.readInt();
+    }
+
+    /**
+     * Flatten this object into a Parcel.
+     *
+     * @param dest  The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written (ignored).
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mMessageFormat);
+        dest.writeInt(mGeographicalScope);
+        dest.writeInt(mSerialNumber);
+        mLocation.writeToParcel(dest, flags);
+        dest.writeInt(mServiceCategory);
+        dest.writeString(mLanguage);
+        dest.writeInt(mDataCodingScheme);
+        dest.writeString(mBody);
+        dest.writeInt(mPriority);
+        if (mEtwsWarningInfo != null) {
+            // parcel ETWS warning information
+            dest.writeInt('E');
+            mEtwsWarningInfo.writeToParcel(dest, flags);
+        } else if (mCmasWarningInfo != null) {
+            // parcel CMAS warning information
+            dest.writeInt('C');
+            mCmasWarningInfo.writeToParcel(dest, flags);
+        } else {
+            // no ETWS or CMAS warning information
+            dest.writeInt('0');
+        }
+        dest.writeLong(mReceivedTimeMillis);
+        dest.writeString(
+                mGeometries != null ? CbGeoUtils.encodeGeometriesToString(mGeometries) : null);
+        dest.writeInt(mMaximumWaitTimeSec);
+        dest.writeInt(mSlotIndex);
+        dest.writeInt(mSubId);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<SmsCbMessage> CREATOR =
+            new Parcelable.Creator<SmsCbMessage>() {
+        @Override
+        public SmsCbMessage createFromParcel(Parcel in) {
+            return new SmsCbMessage(in);
+        }
+
+        @Override
+        public SmsCbMessage[] newArray(int size) {
+            return new SmsCbMessage[size];
+        }
+    };
+
+    /**
+     * Return the geographical scope of this message (GSM/UMTS only).
+     *
+     * @return Geographical scope
+     */
+    public @GeographicalScope int getGeographicalScope() {
+        return mGeographicalScope;
+    }
+
+    /**
+     * Return the broadcast serial number of broadcast (message identifier for CDMA, or
+     * geographical scope + message code + update number for GSM/UMTS). The serial number plus
+     * the location code uniquely identify a cell broadcast for duplicate detection.
+     *
+     * @return the 16-bit CDMA message identifier or GSM/UMTS serial number
+     */
+    public int getSerialNumber() {
+        return mSerialNumber;
+    }
+
+    /**
+     * Return the location identifier for this message, consisting of the MCC/MNC as a
+     * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the
+     * message is not binary 01, the Location Area is included. If the GS is 00 or 11, the
+     * cell ID is also included. The {@link SmsCbLocation} object includes a method to test
+     * if the location is included within another location area or within a PLMN and CellLocation.
+     *
+     * @return the geographical location code for duplicate message detection
+     */
+    @NonNull
+    public android.telephony.SmsCbLocation getLocation() {
+        return mLocation;
+    }
+
+    /**
+     * Return the 16-bit CDMA service category or GSM/UMTS message identifier. The interpretation
+     * of the category is radio technology specific. For ETWS and CMAS warnings, the information
+     * provided by the category is available via {@link #getEtwsWarningInfo()} or
+     * {@link #getCmasWarningInfo()} in a radio technology independent format.
+     *
+     * @return the radio technology specific service category
+     */
+    public int getServiceCategory() {
+        return mServiceCategory;
+    }
+
+    /**
+     * Get the ISO-639-1 language code for this message, or null if unspecified
+     *
+     * @return Language code
+     */
+    @Nullable
+    public String getLanguageCode() {
+        return mLanguage;
+    }
+
+    /**
+     * Get data coding scheme of the message
+     *
+     * @return The 8-bit data coding scheme defined in 3GPP TS 23.038 section 4.
+     */
+    public int getDataCodingScheme() {
+        return mDataCodingScheme;
+    }
+
+    /**
+     * Get the body of this message, or null if no body available
+     *
+     * @return Body, or null
+     */
+    @Nullable
+    public String getMessageBody() {
+        return mBody;
+    }
+
+    /**
+     * Get the warning area coordinates information represented by polygons and circles.
+     * @return a list of geometries, or an empty list if there is no coordinate information
+     * associated with this message.
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public List<Geometry> getGeometries() {
+        if (mGeometries == null) {
+            return new ArrayList<>();
+        }
+        return mGeometries;
+    }
+
+    /**
+     * Get the Geo-Fencing Maximum Wait Time.
+     * @return the time in second.
+     */
+    public int getMaximumWaitingDuration() {
+        return mMaximumWaitTimeSec;
+    }
+
+    /**
+     * Get the time when this message was received.
+     * @return the time in millisecond
+     */
+    public long getReceivedTime() {
+        return mReceivedTimeMillis;
+    }
+
+    /**
+     * Get the slot index associated with this message.
+     * @return the slot index associated with this message
+     */
+    public int getSlotIndex() {
+        return mSlotIndex;
+    }
+
+    /**
+     * Get the subscription id associated with this message.
+     * @return the subscription id associated with this message
+     */
+    public int getSubscriptionId() {
+        return mSubId;
+    }
+
+    /**
+     * Get the message format ({@link #MESSAGE_FORMAT_3GPP} or {@link #MESSAGE_FORMAT_3GPP2}).
+     * @return an integer representing 3GPP or 3GPP2 message format
+     */
+    public @MessageFormat int getMessageFormat() {
+        return mMessageFormat;
+    }
+
+    /**
+     * Get the message priority. Normal broadcasts return {@link #MESSAGE_PRIORITY_NORMAL}
+     * and emergency broadcasts return {@link #MESSAGE_PRIORITY_EMERGENCY}. CDMA also may return
+     * {@link #MESSAGE_PRIORITY_INTERACTIVE} or {@link #MESSAGE_PRIORITY_URGENT}.
+     * @return an integer representing the message priority
+     */
+    public @MessagePriority int getMessagePriority() {
+        return mPriority;
+    }
+
+    /**
+     * If this is an ETWS warning notification then this method will return an object containing
+     * the ETWS warning type, the emergency user alert flag, and the popup flag. If this is an
+     * ETWS primary notification (GSM only), there will also be a 7-byte timestamp and 43-byte
+     * digital signature. As of Release 10, 3GPP TS 23.041 states that the UE shall ignore the
+     * ETWS primary notification timestamp and digital signature if received.
+     *
+     * @return an SmsCbEtwsInfo object, or null if this is not an ETWS warning notification
+     */
+    @Nullable
+    public SmsCbEtwsInfo getEtwsWarningInfo() {
+        return mEtwsWarningInfo;
+    }
+
+    /**
+     * If this is a CMAS warning notification then this method will return an object containing
+     * the CMAS message class, category, response type, severity, urgency and certainty.
+     * The message class is always present. Severity, urgency and certainty are present for CDMA
+     * warning notifications containing a type 1 elements record and for GSM and UMTS warnings
+     * except for the Presidential-level alert category. Category and response type are only
+     * available for CDMA notifications containing a type 1 elements record.
+     *
+     * @return an SmsCbCmasInfo object, or null if this is not a CMAS warning notification
+     */
+    @Nullable
+    public SmsCbCmasInfo getCmasWarningInfo() {
+        return mCmasWarningInfo;
+    }
+
+    /**
+     * Return whether this message is an emergency (PWS) message type.
+     * @return true if the message is an emergency notification; false otherwise
+     */
+    public boolean isEmergencyMessage() {
+        return mPriority == MESSAGE_PRIORITY_EMERGENCY;
+    }
+
+    /**
+     * Return whether this message is an ETWS warning alert.
+     * @return true if the message is an ETWS warning notification; false otherwise
+     */
+    public boolean isEtwsMessage() {
+        return mEtwsWarningInfo != null;
+    }
+
+    /**
+     * Return whether this message is a CMAS warning alert.
+     * @return true if the message is a CMAS warning notification; false otherwise
+     */
+    public boolean isCmasMessage() {
+        return mCmasWarningInfo != null;
+    }
+
+    @Override
+    public String toString() {
+        return "SmsCbMessage{geographicalScope=" + mGeographicalScope + ", serialNumber="
+                + mSerialNumber + ", location=" + mLocation + ", serviceCategory="
+                + mServiceCategory + ", language=" + mLanguage + ", body=" + mBody
+                + ", priority=" + mPriority
+                + (mEtwsWarningInfo != null ? (", " + mEtwsWarningInfo.toString()) : "")
+                + (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "")
+                + ", maximumWaitingTime=" + mMaximumWaitTimeSec
+                + ", received time=" + mReceivedTimeMillis
+                + ", slotIndex = " + mSlotIndex
+                + ", geo=" + (mGeometries != null
+                ? CbGeoUtils.encodeGeometriesToString(mGeometries) : "null")
+                + '}';
+    }
+
+    /**
+     * Describe the kinds of special objects contained in the marshalled representation.
+     * @return a bitmask indicating this Parcelable contains no special objects
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * @return the {@link ContentValues} instance that includes the cell broadcast data.
+     */
+    @NonNull
+    public ContentValues getContentValues() {
+        ContentValues cv = new ContentValues(16);
+        cv.put(CellBroadcasts.SLOT_INDEX, mSlotIndex);
+        cv.put(CellBroadcasts.SUBSCRIPTION_ID, mSubId);
+        cv.put(CellBroadcasts.GEOGRAPHICAL_SCOPE, mGeographicalScope);
+        if (mLocation.getPlmn() != null) {
+            cv.put(CellBroadcasts.PLMN, mLocation.getPlmn());
+        }
+        if (mLocation.getLac() != -1) {
+            cv.put(CellBroadcasts.LAC, mLocation.getLac());
+        }
+        if (mLocation.getCid() != -1) {
+            cv.put(CellBroadcasts.CID, mLocation.getCid());
+        }
+        cv.put(CellBroadcasts.SERIAL_NUMBER, getSerialNumber());
+        cv.put(CellBroadcasts.SERVICE_CATEGORY, getServiceCategory());
+        cv.put(CellBroadcasts.LANGUAGE_CODE, getLanguageCode());
+        cv.put(CellBroadcasts.DATA_CODING_SCHEME, getDataCodingScheme());
+        cv.put(CellBroadcasts.MESSAGE_BODY, getMessageBody());
+        cv.put(CellBroadcasts.MESSAGE_FORMAT, getMessageFormat());
+        cv.put(CellBroadcasts.MESSAGE_PRIORITY, getMessagePriority());
+
+        SmsCbEtwsInfo etwsInfo = getEtwsWarningInfo();
+        if (etwsInfo != null) {
+            cv.put(CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType());
+            cv.put(CellBroadcasts.ETWS_IS_PRIMARY, etwsInfo.isPrimary());
+        }
+
+        SmsCbCmasInfo cmasInfo = getCmasWarningInfo();
+        if (cmasInfo != null) {
+            cv.put(CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass());
+            cv.put(CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory());
+            cv.put(CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType());
+            cv.put(CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity());
+            cv.put(CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency());
+            cv.put(CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty());
+        }
+
+        cv.put(CellBroadcasts.RECEIVED_TIME, mReceivedTimeMillis);
+
+        if (mGeometries != null) {
+            cv.put(CellBroadcasts.GEOMETRIES, CbGeoUtils.encodeGeometriesToString(mGeometries));
+        } else {
+            cv.put(CellBroadcasts.GEOMETRIES, (String) null);
+        }
+
+        cv.put(CellBroadcasts.MAXIMUM_WAIT_TIME, mMaximumWaitTimeSec);
+
+        return cv;
+    }
+
+    /**
+     * Create a {@link SmsCbMessage} instance from a row in the cell broadcast database.
+     * @param cursor an open SQLite cursor pointing to the row to read
+     * @return a {@link SmsCbMessage} instance.
+     * @throws IllegalArgumentException if one of the required columns is missing
+     */
+    @NonNull
+    public static SmsCbMessage createFromCursor(@NonNull Cursor cursor) {
+        int geoScope = cursor.getInt(
+                cursor.getColumnIndexOrThrow(CellBroadcasts.GEOGRAPHICAL_SCOPE));
+        int serialNum = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERIAL_NUMBER));
+        int category = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERVICE_CATEGORY));
+        String language = cursor.getString(
+                cursor.getColumnIndexOrThrow(CellBroadcasts.LANGUAGE_CODE));
+        String body = cursor.getString(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_BODY));
+        int format = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_FORMAT));
+        int priority = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_PRIORITY));
+        int slotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SLOT_INDEX));
+        int subId = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SUBSCRIPTION_ID));
+
+        String plmn;
+        int plmnColumn = cursor.getColumnIndex(CellBroadcasts.PLMN);
+        if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) {
+            plmn = cursor.getString(plmnColumn);
+        } else {
+            plmn = null;
+        }
+
+        int lac;
+        int lacColumn = cursor.getColumnIndex(CellBroadcasts.LAC);
+        if (lacColumn != -1 && !cursor.isNull(lacColumn)) {
+            lac = cursor.getInt(lacColumn);
+        } else {
+            lac = -1;
+        }
+
+        int cid;
+        int cidColumn = cursor.getColumnIndex(CellBroadcasts.CID);
+        if (cidColumn != -1 && !cursor.isNull(cidColumn)) {
+            cid = cursor.getInt(cidColumn);
+        } else {
+            cid = -1;
+        }
+
+        SmsCbLocation location = new SmsCbLocation(plmn, lac, cid);
+
+        SmsCbEtwsInfo etwsInfo;
+        int etwsWarningTypeColumn = cursor.getColumnIndex(CellBroadcasts.ETWS_WARNING_TYPE);
+        int etwsIsPrimaryColumn = cursor.getColumnIndex(CellBroadcasts.ETWS_IS_PRIMARY);
+        if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)
+                && etwsIsPrimaryColumn != -1 && !cursor.isNull(etwsIsPrimaryColumn)) {
+            int warningType = cursor.getInt(etwsWarningTypeColumn);
+            boolean isPrimary = cursor.getInt(etwsIsPrimaryColumn) != 0;
+            etwsInfo = new SmsCbEtwsInfo(warningType, false, false, isPrimary, null);
+        } else {
+            etwsInfo = null;
+        }
+
+        SmsCbCmasInfo cmasInfo = null;
+        int cmasMessageClassColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_MESSAGE_CLASS);
+        if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) {
+            int messageClass = cursor.getInt(cmasMessageClassColumn);
+
+            int cmasCategory;
+            int cmasCategoryColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_CATEGORY);
+            if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) {
+                cmasCategory = cursor.getInt(cmasCategoryColumn);
+            } else {
+                cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
+            }
+
+            int responseType;
+            int cmasResponseTypeColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_RESPONSE_TYPE);
+            if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) {
+                responseType = cursor.getInt(cmasResponseTypeColumn);
+            } else {
+                responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
+            }
+
+            int severity;
+            int cmasSeverityColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_SEVERITY);
+            if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) {
+                severity = cursor.getInt(cmasSeverityColumn);
+            } else {
+                severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
+            }
+
+            int urgency;
+            int cmasUrgencyColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_URGENCY);
+            if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) {
+                urgency = cursor.getInt(cmasUrgencyColumn);
+            } else {
+                urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
+            }
+
+            int certainty;
+            int cmasCertaintyColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_CERTAINTY);
+            if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) {
+                certainty = cursor.getInt(cmasCertaintyColumn);
+            } else {
+                certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
+            }
+
+            cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity,
+                    urgency, certainty);
+        }
+
+        String geoStr = cursor.getString(cursor.getColumnIndex(CellBroadcasts.GEOMETRIES));
+        List<Geometry> geometries =
+                geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null;
+
+        long receivedTimeMillis = cursor.getLong(
+                cursor.getColumnIndexOrThrow(CellBroadcasts.RECEIVED_TIME));
+
+        int maximumWaitTimeSec = cursor.getInt(
+                cursor.getColumnIndexOrThrow(CellBroadcasts.MAXIMUM_WAIT_TIME));
+
+        return new SmsCbMessage(format, geoScope, serialNum, location, category,
+                language, 0, body, priority, etwsInfo, cmasInfo, maximumWaitTimeSec, geometries,
+                receivedTimeMillis, slotIndex, subId);
+    }
+
+    /**
+     * @return {@code True} if this message needs geo-fencing check.
+     */
+    public boolean needGeoFencingCheck() {
+        return mMaximumWaitTimeSec > 0 && mGeometries != null && !mGeometries.isEmpty();
+    }
+}
diff --git a/android-35/android/telephony/SmsManager.java b/android-35/android/telephony/SmsManager.java
new file mode 100644
index 0000000..b7baabf
--- /dev/null
+++ b/android-35/android/telephony/SmsManager.java
@@ -0,0 +1,3688 @@
+/*
+ * Copyright (C) 2008 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;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressAutoDoc;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.PendingIntent;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.database.CursorWindow;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.IPhoneSubInfo;
+import com.android.internal.telephony.ISms;
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.SmsRawData;
+import com.android.internal.telephony.flags.Flags;
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/*
+ * TODO(code review): Curious question... Why are a lot of these
+ * methods not declared as static, since they do not seem to require
+ * any local object state?  Presumably this cannot be changed without
+ * interfering with the API...
+ */
+
+/**
+ * Manages SMS operations such as sending data, text, and pdu SMS messages.
+ * Get this object by calling the static method {@link #getDefault()}. To create an instance of
+ * {@link SmsManager} associated with a specific subscription ID, call
+ * {@link #getSmsManagerForSubscriptionId(int)}. This is typically used for devices that support
+ * multiple active subscriptions at once.
+ *
+ * <p>For information about how to behave as the default SMS app on Android 4.4 (API level 19)
+ * and higher, see {@link android.provider.Telephony}.
+ *
+ * @see SubscriptionManager#getActiveSubscriptionInfoList()
+ */
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+public final class SmsManager {
+    private static final String TAG = "SmsManager";
+
+    private static final Object sLockObject = new Object();
+
+    @GuardedBy("sLockObject")
+    private static final Map<Pair<Context, Integer>, SmsManager> sSubInstances =
+            new ArrayMap<>();
+
+    /** Singleton object constructed during class initialization. */
+    private static final SmsManager DEFAULT_INSTANCE = getSmsManagerForContextAndSubscriptionId(
+            null, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+
+    /** SMS record length from TS 51.011 10.5.3
+     * @hide
+     */
+    public static final int SMS_RECORD_LENGTH = 176;
+
+    /** SMS record length from C.S0023 3.4.27
+     * @hide
+     */
+    public static final int CDMA_SMS_RECORD_LENGTH = 255;
+
+    /** A concrete subscription id, or the pseudo DEFAULT_SUBSCRIPTION_ID */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mSubId;
+
+    /**
+     * Context this SmsManager is for. Can be {@code null} in the case the manager was created via
+     * legacy APIs
+     */
+    private final @Nullable Context mContext;
+
+    /*
+     * Key for the various carrier-dependent configuration values.
+     * Some of the values are used by the system in processing SMS or MMS messages. Others
+     * are provided for the convenience of SMS applications.
+     */
+
+    /**
+     * Whether to append transaction id to MMS WAP Push M-Notification.ind's content location URI
+     * when constructing the download URL of a new MMS (boolean type)
+     */
+    public static final String MMS_CONFIG_APPEND_TRANSACTION_ID =
+            CarrierConfigManager.KEY_MMS_APPEND_TRANSACTION_ID_BOOL;
+    /**
+     * Whether MMS is enabled for the current carrier (boolean type)
+     */
+    public static final String
+            MMS_CONFIG_MMS_ENABLED = CarrierConfigManager.KEY_MMS_MMS_ENABLED_BOOL;
+    /**
+     * Whether group MMS is enabled for the current carrier (boolean type)
+     */
+    public static final String
+            MMS_CONFIG_GROUP_MMS_ENABLED = CarrierConfigManager.KEY_MMS_GROUP_MMS_ENABLED_BOOL;
+    /**
+     * If this is enabled, M-NotifyResp.ind should be sent to the WAP Push content location instead
+     * of the default MMSC (boolean type)
+     */
+    public static final String MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED =
+            CarrierConfigManager.KEY_MMS_NOTIFY_WAP_MMSC_ENABLED_BOOL;
+    /**
+     * Whether alias is enabled (boolean type)
+     */
+    public static final String
+            MMS_CONFIG_ALIAS_ENABLED = CarrierConfigManager.KEY_MMS_ALIAS_ENABLED_BOOL;
+    /**
+     * Whether audio is allowed to be attached for MMS messages (boolean type)
+     */
+    public static final String
+            MMS_CONFIG_ALLOW_ATTACH_AUDIO = CarrierConfigManager.KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL;
+    /**
+     * Whether multipart SMS is enabled (boolean type)
+     */
+    public static final String MMS_CONFIG_MULTIPART_SMS_ENABLED =
+            CarrierConfigManager.KEY_MMS_MULTIPART_SMS_ENABLED_BOOL;
+    /**
+     * Whether SMS delivery report is enabled (boolean type)
+     */
+    public static final String MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED =
+            CarrierConfigManager.KEY_MMS_SMS_DELIVERY_REPORT_ENABLED_BOOL;
+    /**
+     * Whether content-disposition field should be expected in an MMS PDU (boolean type)
+     */
+    public static final String MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION =
+            CarrierConfigManager.KEY_MMS_SUPPORT_MMS_CONTENT_DISPOSITION_BOOL;
+    /**
+     * Whether multipart SMS should be sent as separate messages
+     */
+    public static final String MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES =
+            CarrierConfigManager.KEY_MMS_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES_BOOL;
+    /**
+     * Whether MMS read report is enabled (boolean type)
+     */
+    public static final String MMS_CONFIG_MMS_READ_REPORT_ENABLED =
+            CarrierConfigManager.KEY_MMS_MMS_READ_REPORT_ENABLED_BOOL;
+    /**
+     * Whether MMS delivery report is enabled (boolean type)
+     */
+    public static final String MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED =
+            CarrierConfigManager.KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL;
+    /**
+     * Max MMS message size in bytes (int type)
+     */
+    public static final String
+            MMS_CONFIG_MAX_MESSAGE_SIZE = CarrierConfigManager.KEY_MMS_MAX_MESSAGE_SIZE_INT;
+    /**
+     * Max MMS image width (int type)
+     */
+    public static final String
+            MMS_CONFIG_MAX_IMAGE_WIDTH = CarrierConfigManager.KEY_MMS_MAX_IMAGE_WIDTH_INT;
+    /**
+     * Max MMS image height (int type)
+     */
+    public static final String
+            MMS_CONFIG_MAX_IMAGE_HEIGHT = CarrierConfigManager.KEY_MMS_MAX_IMAGE_HEIGHT_INT;
+    /**
+     * Limit of recipients of MMS messages (int type)
+     */
+    public static final String
+            MMS_CONFIG_RECIPIENT_LIMIT = CarrierConfigManager.KEY_MMS_RECIPIENT_LIMIT_INT;
+    /**
+     * Min alias character count (int type)
+     */
+    public static final String
+            MMS_CONFIG_ALIAS_MIN_CHARS = CarrierConfigManager.KEY_MMS_ALIAS_MIN_CHARS_INT;
+    /**
+     * Max alias character count (int type)
+     */
+    public static final String
+            MMS_CONFIG_ALIAS_MAX_CHARS = CarrierConfigManager.KEY_MMS_ALIAS_MAX_CHARS_INT;
+    /**
+     * When the number of parts of a multipart SMS reaches this threshold, it should be converted
+     * into an MMS (int type)
+     */
+    public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD =
+            CarrierConfigManager.KEY_MMS_SMS_TO_MMS_TEXT_THRESHOLD_INT;
+    /**
+     * Some carriers require SMS to be converted into MMS when text length reaches this threshold
+     * (int type)
+     */
+    public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD =
+            CarrierConfigManager.KEY_MMS_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD_INT;
+    /**
+     * Max message text size (int type)
+     */
+    public static final String MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE =
+            CarrierConfigManager.KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT;
+    /**
+     * Max message subject length (int type)
+     */
+    public static final String
+            MMS_CONFIG_SUBJECT_MAX_LENGTH = CarrierConfigManager.KEY_MMS_SUBJECT_MAX_LENGTH_INT;
+    /**
+     * MMS HTTP socket timeout in milliseconds (int type)
+     */
+    public static final String
+            MMS_CONFIG_HTTP_SOCKET_TIMEOUT = CarrierConfigManager.KEY_MMS_HTTP_SOCKET_TIMEOUT_INT;
+    /**
+     * The name of the UA Prof URL HTTP header for MMS HTTP request (String type)
+     */
+    public static final String
+            MMS_CONFIG_UA_PROF_TAG_NAME = CarrierConfigManager.KEY_MMS_UA_PROF_TAG_NAME_STRING;
+    /**
+     * The User-Agent header value for MMS HTTP request (String type)
+     */
+    public static final String
+            MMS_CONFIG_USER_AGENT = CarrierConfigManager.KEY_MMS_USER_AGENT_STRING;
+    /**
+     * The UA Profile URL header value for MMS HTTP request (String type)
+     */
+    public static final String
+            MMS_CONFIG_UA_PROF_URL = CarrierConfigManager.KEY_MMS_UA_PROF_URL_STRING;
+    /**
+     * A list of HTTP headers to add to MMS HTTP request, separated by "|" (String type)
+     */
+    public static final String
+            MMS_CONFIG_HTTP_PARAMS = CarrierConfigManager.KEY_MMS_HTTP_PARAMS_STRING;
+    /**
+     * Email gateway number (String type)
+     */
+    public static final String MMS_CONFIG_EMAIL_GATEWAY_NUMBER =
+            CarrierConfigManager.KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING;
+    /**
+     * The suffix to append to the NAI header value for MMS HTTP request (String type)
+     */
+    public static final String
+            MMS_CONFIG_NAI_SUFFIX = CarrierConfigManager.KEY_MMS_NAI_SUFFIX_STRING;
+    /**
+     * If true, show the cell broadcast (amber alert) in the SMS settings. Some carriers don't want
+     * this shown. (Boolean type)
+     */
+    public static final String MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS =
+            CarrierConfigManager.KEY_MMS_SHOW_CELL_BROADCAST_APP_LINKS_BOOL;
+    /**
+     * Whether the carrier MMSC supports charset field in Content-Type header. If this is false,
+     * then we don't add "charset" to "Content-Type"
+     */
+    public static final String MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER =
+            CarrierConfigManager.KEY_MMS_SUPPORT_HTTP_CHARSET_HEADER_BOOL;
+    /**
+     * If true, add "Connection: close" header to MMS HTTP requests so the connection
+     * is immediately closed (disabling keep-alive). (Boolean type)
+     * @hide
+     */
+    public static final String MMS_CONFIG_CLOSE_CONNECTION =
+            CarrierConfigManager.KEY_MMS_CLOSE_CONNECTION_BOOL;
+
+    /**
+     * 3gpp2 SMS priority is not specified
+     * @hide
+     */
+    public static final int SMS_MESSAGE_PRIORITY_NOT_SPECIFIED = -1;
+    /**
+     * 3gpp SMS period is not specified
+     * @hide
+     */
+    public static final int SMS_MESSAGE_PERIOD_NOT_SPECIFIED = -1;
+
+    // RP-Cause Values For MO SMS as per TS 124 011, table 8.4.
+
+    /** @hide */
+    @IntDef(prefix = { "SMS_RP_CAUSE" }, value = {
+        SmsManager.SMS_RP_CAUSE_UNALLOCATED_NUMBER,
+        SmsManager.SMS_RP_CAUSE_OPERATOR_DETERMINED_BARRING,
+        SmsManager.SMS_RP_CAUSE_CALL_BARRING,
+        SmsManager.SMS_RP_CAUSE_RESERVED,
+        SmsManager.SMS_RP_CAUSE_SHORT_MESSAGE_TRANSFER_REJECTED,
+        SmsManager.SMS_RP_CAUSE_DESTINATION_OUT_OF_ORDER,
+        SmsManager.SMS_RP_CAUSE_UNIDENTIFIED_SUBSCRIBER,
+        SmsManager.SMS_RP_CAUSE_FACILITY_REJECTED,
+        SmsManager.SMS_RP_CAUSE_UNKNOWN_SUBSCRIBER,
+        SmsManager.SMS_RP_CAUSE_NETWORK_OUT_OF_ORDER,
+        SmsManager.SMS_RP_CAUSE_TEMPORARY_FAILURE,
+        SmsManager.SMS_RP_CAUSE_CONGESTION,
+        SmsManager.SMS_RP_CAUSE_RESOURCES_UNAVAILABLE,
+        SmsManager.SMS_RP_CAUSE_FACILITY_NOT_SUBSCRIBED,
+        SmsManager.SMS_RP_CAUSE_FACILITY_NOT_IMPLEMENTED,
+        SmsManager.SMS_RP_CAUSE_INVALID_MESSAGE_REFERENCE_VALUE,
+        SmsManager.SMS_RP_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE,
+        SmsManager.SMS_RP_CAUSE_INVALID_MANDATORY_INFORMATION,
+        SmsManager.SMS_RP_CAUSE_MESSAGE_TYPE_NON_EXISTENT,
+        SmsManager.SMS_RP_CAUSE_MESSAGE_INCOMPATIBLE_WITH_PROTOCOL_STATE,
+        SmsManager.SMS_RP_CAUSE_INFORMATION_ELEMENT_NON_EXISTENT,
+        SmsManager.SMS_RP_CAUSE_PROTOCOL_ERROR,
+        SmsManager.SMS_RP_CAUSE_INTERWORKING_UNSPECIFIED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SMS_RP_CAUSE {}
+
+    /** Unallocated Number Cause */
+    public static final int SMS_RP_CAUSE_UNALLOCATED_NUMBER = 1;
+
+    /** RP-Cause for Operator Barring */
+    public static final int SMS_RP_CAUSE_OPERATOR_DETERMINED_BARRING = 8;
+
+    /** RP-Cause Value for Call Barring */
+    public static final int SMS_RP_CAUSE_CALL_BARRING = 10;
+
+    /** RP-Cause value for Reserved Number */
+    public static final int SMS_RP_CAUSE_RESERVED = 11;
+
+    /** RP-Cause Value for Message Transfer Rejected by Network */
+    public static final int SMS_RP_CAUSE_SHORT_MESSAGE_TRANSFER_REJECTED = 21;
+
+    /** RP-Cause Value for Destination is Out of Order */
+    public static final int SMS_RP_CAUSE_DESTINATION_OUT_OF_ORDER = 27;
+
+    /** RP-Cause Value when Subscriber is not Identified */
+    public static final int SMS_RP_CAUSE_UNIDENTIFIED_SUBSCRIBER = 28;
+
+    /** RP-Cause Value when SMS Facility if Rejected by Operator */
+    public static final int SMS_RP_CAUSE_FACILITY_REJECTED = 29;
+
+    /** RP-Cause Value when Subscriber is not Identified */
+    public static final int SMS_RP_CAUSE_UNKNOWN_SUBSCRIBER = 30;
+
+    /** RP-Cause Value when network is out of order*/
+    public static final int SMS_RP_CAUSE_NETWORK_OUT_OF_ORDER = 38;
+
+    /** RP-Cause Value For Temporary failure*/
+    public static final int SMS_RP_CAUSE_TEMPORARY_FAILURE = 41;
+
+    /** RP-Cause Value for SMS Failure due to Congestion in network*/
+    public static final int SMS_RP_CAUSE_CONGESTION = 42;
+
+    /** RP-Cause Value when Network Resources are unavailable */
+    public static final int SMS_RP_CAUSE_RESOURCES_UNAVAILABLE = 47;
+
+    /** RP-Cause Value when SMS Facilty is not subscribed by Reote device */
+    public static final int SMS_RP_CAUSE_FACILITY_NOT_SUBSCRIBED = 50;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_FACILITY_NOT_IMPLEMENTED = 69;
+
+    /** RP-Cause Value when RP-MessageRefere */
+    public static final int SMS_RP_CAUSE_INVALID_MESSAGE_REFERENCE_VALUE = 81;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE = 95;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_INVALID_MANDATORY_INFORMATION = 96;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_MESSAGE_TYPE_NON_EXISTENT = 97;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_MESSAGE_INCOMPATIBLE_WITH_PROTOCOL_STATE = 98;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_INFORMATION_ELEMENT_NON_EXISTENT  = 99;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_PROTOCOL_ERROR = 111;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_INTERWORKING_UNSPECIFIED = 127;
+
+    /** @hide */
+    @IntDef(prefix = { "PREMIUM_SMS_CONSENT" }, value = {
+        SmsManager.PREMIUM_SMS_CONSENT_UNKNOWN,
+        SmsManager.PREMIUM_SMS_CONSENT_ASK_USER,
+        SmsManager.PREMIUM_SMS_CONSENT_NEVER_ALLOW,
+        SmsManager.PREMIUM_SMS_CONSENT_ALWAYS_ALLOW
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PremiumSmsConsent {}
+
+    /** Premium SMS Consent for the package is unknown. This indicates that the user
+     *  has not set a permission for this package, because this package has never tried
+     *  to send a premium SMS.
+     * @hide
+     */
+    @SystemApi
+    public static final int PREMIUM_SMS_CONSENT_UNKNOWN = 0;
+
+    /** Default premium SMS Consent (ask user for each premium SMS sent).
+     * @hide
+     */
+    @SystemApi
+    public static final int PREMIUM_SMS_CONSENT_ASK_USER = 1;
+
+    /** Premium SMS Consent when the owner has denied the app from sending premium SMS.
+     * @hide
+     */
+    @SystemApi
+    public static final int PREMIUM_SMS_CONSENT_NEVER_ALLOW = 2;
+
+    /** Premium SMS Consent when the owner has allowed the app to send premium SMS.
+     * @hide
+     */
+    @SystemApi
+    public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3;
+
+    // result of asking the user for a subscription to perform an operation.
+    private interface SubscriptionResolverResult {
+        void onSuccess(int subId);
+        void onFailure();
+    }
+
+    /**
+     * Get {@link Context#getOpPackageName()} if this manager has a context, otherwise a placeholder
+     * value.
+     *
+     * @return The package name to be used for app-ops checks
+     */
+    private @Nullable String getOpPackageName() {
+        if (mContext == null) {
+            return null;
+        } else {
+            return mContext.getOpPackageName();
+        }
+    }
+
+    /**
+     * Get {@link Context#getAttributionTag()} ()} if this manager has a context, otherwise get the
+     * default attribution tag.
+     *
+     * @return The attribution tag to be used for app-ops checks
+     */
+    private @Nullable String getAttributionTag() {
+        if (mContext == null) {
+            return null;
+        } else {
+            return mContext.getAttributionTag();
+        }
+    }
+
+    /**
+     * Send a text based SMS.
+     *
+     * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
+     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
+     *
+     * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
+     * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
+     * writes messages sent using this method to the SMS Provider (the default SMS app is always
+     * responsible for writing its sent messages to the SMS Provider). For information about
+     * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
+     *
+     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+     * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+     * suitable default subscription could be found. In this case, if {@code sentIntent} is
+     * non-null, then the {@link PendingIntent} will be sent with an error code
+     * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+     * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+     * where this operation may fail.
+     * </p>
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *  the current default SMSC
+     * @param text the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK</code> for success,
+     *  or one of these errors:<br>
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>RESULT_ERROR_NULL_PDU</code><br>
+     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
+     *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+     *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+     *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+     *  <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+     *  <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>RESULT_NETWORK_REJECT</code><br>
+     *  <code>RESULT_INVALID_ARGUMENTS</code><br>
+     *  <code>RESULT_INVALID_STATE</code><br>
+     *  <code>RESULT_NO_MEMORY</code><br>
+     *  <code>RESULT_INVALID_SMS_FORMAT</code><br>
+     *  <code>RESULT_SYSTEM_ERROR</code><br>
+     *  <code>RESULT_MODEM_ERROR</code><br>
+     *  <code>RESULT_NETWORK_ERROR</code><br>
+     *  <code>RESULT_ENCODING_ERROR</code><br>
+     *  <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>RESULT_INTERNAL_ERROR</code><br>
+     *  <code>RESULT_NO_RESOURCES</code><br>
+     *  <code>RESULT_CANCELLED</code><br>
+     *  <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
+     *  <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+     *  <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
+     *  <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+     *  <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+     *  <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
+     *  <code>RESULT_REMOTE_EXCEPTION</code><br>
+     *  <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
+     *  <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+     *  <code>RESULT_RIL_NETWORK_REJECT</code><br>
+     *  <code>RESULT_RIL_INVALID_STATE</code><br>
+     *  <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
+     *  <code>RESULT_RIL_NO_MEMORY</code><br>
+     *  <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+     *  <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+     *  <code>RESULT_RIL_SYSTEM_ERR</code><br>
+     *  <code>RESULT_RIL_ENCODING_ERR</code><br>
+     *  <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>RESULT_RIL_MODEM_ERR</code><br>
+     *  <code>RESULT_RIL_NETWORK_ERR</code><br>
+     *  <code>RESULT_RIL_INTERNAL_ERR</code><br>
+     *  <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
+     *  <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
+     *  <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_NO_RESOURCES</code><br>
+     *  <code>RESULT_RIL_CANCELLED</code><br>
+     *  <code>RESULT_RIL_SIM_ABSENT</code><br>
+     *  <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_ACCESS_BARRED</code><br>
+     *  <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+     *  the sentIntent may include the extra "errorCode" containing a radio technology specific
+     *  value, generally only useful for troubleshooting.<br>
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     *
+     * @throws IllegalArgumentException if destinationAddress or text are empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void sendTextMessage(
+            String destinationAddress, String scAddress, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+                true /* persistMessage*/, getOpPackageName(), getAttributionTag(),
+                0L /* messageId */);
+    }
+
+
+    /**
+     * Send a text based SMS. Same as {@link #sendTextMessage( String destinationAddress,
+     * String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent)}, but
+     * adds an optional messageId.
+     * @param messageId An id that uniquely identifies the message requested to be sent.
+     * Used for logging and diagnostics purposes. The id may be 0.
+     *
+     * @throws IllegalArgumentException if destinationAddress or text are empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     *
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void sendTextMessage(
+            @NonNull String destinationAddress, @Nullable String scAddress, @NonNull String text,
+            @Nullable PendingIntent sentIntent, @Nullable PendingIntent deliveryIntent,
+            long messageId) {
+        sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+                true /* persistMessage*/, getOpPackageName(), getAttributionTag(),
+                messageId);
+    }
+
+    /**
+     * Send a text based SMS with messaging options.
+     *
+     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+     * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+     * suitable default subscription could be found. In this case, if {@code sentIntent} is
+     * non-null, then the {@link PendingIntent} will be sent with an error code
+     * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+     * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+     * where this operation may fail.
+     * </p>
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *  the current default SMSC
+     * @param text the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK</code> for success,
+     *  or one of these errors:<br>
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>RESULT_ERROR_NULL_PDU</code><br>
+     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
+     *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+     *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+     *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+     *  <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+     *  <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>RESULT_NETWORK_REJECT</code><br>
+     *  <code>RESULT_INVALID_ARGUMENTS</code><br>
+     *  <code>RESULT_INVALID_STATE</code><br>
+     *  <code>RESULT_NO_MEMORY</code><br>
+     *  <code>RESULT_INVALID_SMS_FORMAT</code><br>
+     *  <code>RESULT_SYSTEM_ERROR</code><br>
+     *  <code>RESULT_MODEM_ERROR</code><br>
+     *  <code>RESULT_NETWORK_ERROR</code><br>
+     *  <code>RESULT_ENCODING_ERROR</code><br>
+     *  <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>RESULT_INTERNAL_ERROR</code><br>
+     *  <code>RESULT_NO_RESOURCES</code><br>
+     *  <code>RESULT_CANCELLED</code><br>
+     *  <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
+     *  <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+     *  <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
+     *  <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+     *  <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+     *  <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
+     *  <code>RESULT_REMOTE_EXCEPTION</code><br>
+     *  <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
+     *  <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+     *  <code>RESULT_RIL_NETWORK_REJECT</code><br>
+     *  <code>RESULT_RIL_INVALID_STATE</code><br>
+     *  <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
+     *  <code>RESULT_RIL_NO_MEMORY</code><br>
+     *  <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+     *  <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+     *  <code>RESULT_RIL_SYSTEM_ERR</code><br>
+     *  <code>RESULT_RIL_ENCODING_ERR</code><br>
+     *  <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>RESULT_RIL_MODEM_ERR</code><br>
+     *  <code>RESULT_RIL_NETWORK_ERR</code><br>
+     *  <code>RESULT_RIL_INTERNAL_ERR</code><br>
+     *  <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
+     *  <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
+     *  <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_NO_RESOURCES</code><br>
+     *  <code>RESULT_RIL_CANCELLED</code><br>
+     *  <code>RESULT_RIL_SIM_ABSENT</code><br>
+     *  <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_ACCESS_BARRED</code><br>
+     *  <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+     *  the sentIntent may include the extra "errorCode" containing a radio technology specific
+     *  value, generally only useful for troubleshooting.<br>
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     * @param priority Priority level of the message
+     *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+     *  ---------------------------------
+     *  PRIORITY      | Level of Priority
+     *  ---------------------------------
+     *      '00'      |     Normal
+     *      '01'      |     Interactive
+     *      '10'      |     Urgent
+     *      '11'      |     Emergency
+     *  ----------------------------------
+     *  Any Other values included Negative considered as Invalid Priority Indicator of the message.
+     * @param expectMore is a boolean to indicate the sending messages through same link or not.
+     * @param validityPeriod Validity Period of the message in mins.
+     *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+     *  Validity Period(Minimum) -> 5 mins
+     *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+     *  Any Other values included Negative considered as Invalid Validity Period of the message.
+     *
+     * @throws IllegalArgumentException if destinationAddress or text are empty
+     * {@hide}
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void sendTextMessage(
+            String destinationAddress, String scAddress, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent,
+            int priority, boolean expectMore, int validityPeriod) {
+        sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+                true /* persistMessage*/, priority, expectMore, validityPeriod);
+    }
+
+    private void sendTextMessageInternal(String destinationAddress, String scAddress,
+            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean persistMessage, String packageName, String attributionTag, long messageId) {
+        if (TextUtils.isEmpty(destinationAddress)) {
+            throw new IllegalArgumentException("Invalid destinationAddress");
+        }
+
+        if (TextUtils.isEmpty(text)) {
+            throw new IllegalArgumentException("Invalid message body");
+        }
+
+        // We will only show the SMS disambiguation dialog in the case that the message is being
+        // persisted. This is for two reasons:
+        // 1) Messages that are not persisted are sent by carrier/OEM apps for a specific
+        //    subscription and require special permissions. These messages are usually not sent by
+        //    the device user and should not have an SMS disambiguation dialog associated with them
+        //    because the device user did not trigger them.
+        // 2) The SMS disambiguation dialog ONLY checks to make sure that the user has the SEND_SMS
+        //    permission. If we call resolveSubscriptionForOperation from a carrier/OEM app that has
+        //    the correct MODIFY_PHONE_STATE or carrier permissions, but no SEND_SMS, it will throw
+        //    an incorrect SecurityException.
+        if (persistMessage) {
+            resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+                @Override
+                public void onSuccess(int subId) {
+                    ISms iSms = getISmsServiceOrThrow();
+                    try {
+                        iSms.sendTextForSubscriber(subId, packageName, attributionTag,
+                                destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+                                persistMessage, messageId);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - "
+                                + e.getMessage() + " " + formatCrossStackMessageId(messageId));
+                        notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
+                    }
+                }
+
+                @Override
+                public void onFailure() {
+                    notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP);
+                }
+            });
+        } else {
+            // Not persisting the message, used by sendTextMessageWithoutPersisting() and is not
+            // visible to the user.
+            ISms iSms = getISmsServiceOrThrow();
+            try {
+                iSms.sendTextForSubscriber(getSubscriptionId(), packageName, attributionTag,
+                        destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+                        persistMessage, messageId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "sendTextMessageInternal (no persist): Couldn't send SMS, exception - "
+                        + e.getMessage() + " " + formatCrossStackMessageId(messageId));
+                notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
+            }
+        }
+    }
+
+    /**
+     * Send a text based SMS without writing it into the SMS Provider.
+     *
+     * <p>
+     * The message will be sent directly over the network and will not be visible in SMS
+     * applications. Intended for internal carrier use only.
+     * </p>
+     *
+     * <p>Requires Permission: Both {@link android.Manifest.permission#SEND_SMS} and
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE}, or that the calling app has carrier
+     * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), or that the calling app is
+     * the default IMS app (see
+     * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}).
+     * </p>
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+     * applications or the Telephony framework and will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the SMS being sent on the subscription associated with logical
+     * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the
+     * correct subscription.
+     * </p>
+     *
+     * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            android.Manifest.permission.SEND_SMS
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void sendTextMessageWithoutPersisting(
+            String destinationAddress, String scAddress, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+                false /* persistMessage */, getOpPackageName(),
+                getAttributionTag(), 0L /* messageId */);
+    }
+
+    private void sendTextMessageInternal(
+            String destinationAddress, String scAddress, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage,
+            int priority, boolean expectMore, int validityPeriod) {
+        if (TextUtils.isEmpty(destinationAddress)) {
+            throw new IllegalArgumentException("Invalid destinationAddress");
+        }
+
+        if (TextUtils.isEmpty(text)) {
+            throw new IllegalArgumentException("Invalid message body");
+        }
+
+        if (priority < 0x00 || priority > 0x03) {
+            Log.e(TAG, "Invalid Priority " + priority);
+            priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
+        }
+
+        if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+            Log.e(TAG, "Invalid Validity Period " + validityPeriod);
+            validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
+        }
+
+        final int finalPriority = priority;
+        final int finalValidity = validityPeriod;
+        // We will only show the SMS disambiguation dialog in the case that the message is being
+        // persisted. This is for two reasons:
+        // 1) Messages that are not persisted are sent by carrier/OEM apps for a specific
+        //    subscription and require special permissions. These messages are usually not sent by
+        //    the device user and should not have an SMS disambiguation dialog associated with them
+        //    because the device user did not trigger them.
+        // 2) The SMS disambiguation dialog ONLY checks to make sure that the user has the SEND_SMS
+        //    permission. If we call resolveSubscriptionForOperation from a carrier/OEM app that has
+        //    the correct MODIFY_PHONE_STATE or carrier permissions, but no SEND_SMS, it will throw
+        //    an incorrect SecurityException.
+        if (persistMessage) {
+            resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+                @Override
+                public void onSuccess(int subId) {
+                    try {
+                        ISms iSms = getISmsServiceOrThrow();
+                        if (iSms != null) {
+                            iSms.sendTextForSubscriberWithOptions(subId,
+                                    null, getAttributionTag(), destinationAddress,
+                                    scAddress,
+                                    text, sentIntent, deliveryIntent, persistMessage, finalPriority,
+                                    expectMore, finalValidity);
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - "
+                                + e.getMessage());
+                        notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
+                    }
+                }
+
+                @Override
+                public void onFailure() {
+                    notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP);
+                }
+            });
+        } else {
+            try {
+                ISms iSms = getISmsServiceOrThrow();
+                if (iSms != null) {
+                    iSms.sendTextForSubscriberWithOptions(getSubscriptionId(),
+                            null, getAttributionTag(), destinationAddress,
+                            scAddress,
+                            text, sentIntent, deliveryIntent, persistMessage, finalPriority,
+                            expectMore, finalValidity);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "sendTextMessageInternal(no persist): Couldn't send SMS, exception - "
+                        + e.getMessage());
+                notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
+            }
+        }
+    }
+
+    /**
+     *
+     * Inject an SMS PDU into the android application framework.
+     *
+     * <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
+     * privileges per {@link android.telephony.TelephonyManager#hasCarrierPrivileges}.
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+     * applications or the Telephony framework and will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the SMS being injected on the subscription associated with
+     * logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is
+     * delivered to the correct subscription.
+     * </p>
+     *
+     * @param pdu is the byte array of pdu to be injected into android application framework
+     * @param format is the format of SMS pdu ({@link SmsMessage#FORMAT_3GPP} or
+     *  {@link SmsMessage#FORMAT_3GPP2})
+     * @param receivedIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully received by the
+     *  android application framework, or failed. This intent is broadcasted at
+     *  the same time an SMS received from radio is acknowledged back.
+     *  The result code will be {@link android.provider.Telephony.Sms.Intents#RESULT_SMS_HANDLED}
+     *  for success, or {@link android.provider.Telephony.Sms.Intents#RESULT_SMS_GENERIC_ERROR} or
+     *  {@link #RESULT_REMOTE_EXCEPTION} for error.
+     *
+     * @throws IllegalArgumentException if the format is invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void injectSmsPdu(
+            byte[] pdu, @SmsMessage.Format String format, PendingIntent receivedIntent) {
+        if (!format.equals(SmsMessage.FORMAT_3GPP) && !format.equals(SmsMessage.FORMAT_3GPP2)) {
+            // Format must be either 3gpp or 3gpp2.
+            throw new IllegalArgumentException(
+                    "Invalid pdu format. format must be either 3gpp or 3gpp2");
+        }
+        try {
+            ISms iSms = TelephonyManager.getSmsService();
+            if (iSms != null) {
+                iSms.injectSmsPduForSubscriber(
+                        getSubscriptionId(), pdu, format, receivedIntent);
+            }
+        } catch (RemoteException ex) {
+            try {
+                if (receivedIntent != null) {
+                    receivedIntent.send(RESULT_REMOTE_EXCEPTION);
+                }
+            } catch (PendingIntent.CanceledException cx) {
+                // Don't worry about it, we do not need to notify the caller if this is the case.
+            }
+        }
+    }
+
+    /**
+     * Divide a message text into several fragments, none bigger than the maximum SMS message size.
+     *
+     * @param text the original message. Must not be null.
+     * @return an <code>ArrayList</code> of strings that, in order, comprise the original message.
+     * @throws IllegalArgumentException if text is null.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public ArrayList<String> divideMessage(String text) {
+        if (null == text) {
+            throw new IllegalArgumentException("text is null");
+        }
+        return SmsMessage.fragmentText(text, getSubscriptionId());
+    }
+
+    /**
+     * Send a multi-part text based SMS.  The callee should have already
+     * divided the message into correctly sized parts by calling
+     * <code>divideMessage</code>.
+     *
+     * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
+     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
+     *
+     * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
+     * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
+     * writes messages sent using this method to the SMS Provider (the default SMS app is always
+     * responsible for writing its sent messages to the SMS Provider). For information about
+     * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
+     *
+     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+     * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+     * suitable default subscription could be found. In this case, if {@code sentIntent} is
+     * non-null, then the {@link PendingIntent} will be sent with an error code
+     * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+     * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+     * where this operation may fail.
+     * </p>
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *  the current default SMSC
+     * @param parts an <code>ArrayList</code> of strings that, in order,
+     *  comprise the original message
+     * @param sentIntents if not null, an <code>ArrayList</code> of
+     *  <code>PendingIntent</code>s (one for each message part) that is
+     *  broadcast when the corresponding message part has been sent.
+     *  The result code will be <code>Activity.RESULT_OK</code> for success,
+     *  or one of these errors:<br>
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>RESULT_ERROR_NULL_PDU</code><br>
+     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
+     *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+     *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+     *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+     *  <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+     *  <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>RESULT_NETWORK_REJECT</code><br>
+     *  <code>RESULT_INVALID_ARGUMENTS</code><br>
+     *  <code>RESULT_INVALID_STATE</code><br>
+     *  <code>RESULT_NO_MEMORY</code><br>
+     *  <code>RESULT_INVALID_SMS_FORMAT</code><br>
+     *  <code>RESULT_SYSTEM_ERROR</code><br>
+     *  <code>RESULT_MODEM_ERROR</code><br>
+     *  <code>RESULT_NETWORK_ERROR</code><br>
+     *  <code>RESULT_ENCODING_ERROR</code><br>
+     *  <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>RESULT_INTERNAL_ERROR</code><br>
+     *  <code>RESULT_NO_RESOURCES</code><br>
+     *  <code>RESULT_CANCELLED</code><br>
+     *  <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
+     *  <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+     *  <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
+     *  <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+     *  <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+     *  <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
+     *  <code>RESULT_REMOTE_EXCEPTION</code><br>
+     *  <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
+     *  <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+     *  <code>RESULT_RIL_NETWORK_REJECT</code><br>
+     *  <code>RESULT_RIL_INVALID_STATE</code><br>
+     *  <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
+     *  <code>RESULT_RIL_NO_MEMORY</code><br>
+     *  <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+     *  <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+     *  <code>RESULT_RIL_SYSTEM_ERR</code><br>
+     *  <code>RESULT_RIL_ENCODING_ERR</code><br>
+     *  <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>RESULT_RIL_MODEM_ERR</code><br>
+     *  <code>RESULT_RIL_NETWORK_ERR</code><br>
+     *  <code>RESULT_RIL_INTERNAL_ERR</code><br>
+     *  <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
+     *  <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
+     *  <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_NO_RESOURCES</code><br>
+     *  <code>RESULT_RIL_CANCELLED</code><br>
+     *  <code>RESULT_RIL_SIM_ABSENT</code><br>
+     *  <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_ACCESS_BARRED</code><br>
+     *  <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+     *  the sentIntent may include the extra "errorCode" containing a radio technology specific
+     *  value, generally only useful for troubleshooting.<br>
+     * @param deliveryIntents if not null, an <code>ArrayList</code> of
+     *  <code>PendingIntent</code>s (one for each message part) that is
+     *  broadcast when the corresponding message part has been delivered
+     *  to the recipient.  The raw pdu of the status report is in the
+     *  extended data ("pdu").
+     *
+     * @throws IllegalArgumentException if destinationAddress or data are empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void sendMultipartTextMessage(
+            String destinationAddress, String scAddress, ArrayList<String> parts,
+            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
+        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+                deliveryIntents, true /* persistMessage*/, getOpPackageName(),
+                getAttributionTag(), 0L /* messageId */);
+    }
+
+    /**
+     * Send a multi-part text based SMS. Same as #sendMultipartTextMessage(String, String,
+     * ArrayList, ArrayList, ArrayList), but adds an optional messageId.
+     * @param messageId An id that uniquely identifies the message requested to be sent.
+     * Used for logging and diagnostics purposes. The id may be 0.
+     *
+     * @throws IllegalArgumentException if destinationAddress or data are empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void sendMultipartTextMessage(
+            @NonNull String destinationAddress, @Nullable String scAddress,
+            @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
+            @Nullable List<PendingIntent> deliveryIntents, long messageId) {
+        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+                deliveryIntents, true /* persistMessage*/, getOpPackageName(),
+                getAttributionTag(), messageId);
+    }
+
+    /**
+     * Similar method as #sendMultipartTextMessage(String, String, ArrayList, ArrayList, ArrayList)
+     * With an additional argument.
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use the Telephony
+     * framework and will never trigger an SMS disambiguation dialog. If this method is called on a
+     * device that has multiple active subscriptions, this {@link SmsManager} instance has been
+     * created with {@link #getDefault()}, and no user-defined default subscription is defined, the
+     * subscription ID associated with this message will be INVALID, which will result in the SMS
+     * being sent on the subscription associated with logical slot 0. Use
+     * {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the correct
+     * subscription.
+     * </p>
+     *
+     * @param packageName serves as the default package name if the package name that is
+     *        associated with the user id is null.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void sendMultipartTextMessage(
+            @NonNull String destinationAddress, @Nullable String scAddress,
+            @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
+            @Nullable List<PendingIntent> deliveryIntents, @NonNull String packageName,
+            @Nullable String attributionTag) {
+        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+                deliveryIntents, true /* persistMessage*/, packageName, attributionTag,
+                0L /* messageId */);
+    }
+
+    private void sendMultipartTextMessageInternal(
+            String destinationAddress, String scAddress, List<String> parts,
+            List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
+            boolean persistMessage, String packageName, @Nullable String attributionTag,
+            long messageId) {
+        if (TextUtils.isEmpty(destinationAddress)) {
+            throw new IllegalArgumentException("Invalid destinationAddress");
+        }
+        if (parts == null || parts.size() < 1) {
+            throw new IllegalArgumentException("Invalid message body");
+        }
+
+        if (parts.size() > 1) {
+            // We will only show the SMS disambiguation dialog in the case that the message is being
+            // persisted. This is for two reasons:
+            // 1) Messages that are not persisted are sent by carrier/OEM apps for a specific
+            //    subscription and require special permissions. These messages are usually not sent
+            //    by the device user and should not have an SMS disambiguation dialog associated
+            //    with them because the device user did not trigger them.
+            // 2) The SMS disambiguation dialog ONLY checks to make sure that the user has the
+            //    SEND_SMS permission. If we call resolveSubscriptionForOperation from a carrier/OEM
+            //    app that has the correct MODIFY_PHONE_STATE or carrier permissions, but no
+            //    SEND_SMS, it will throw an incorrect SecurityException.
+            if (persistMessage) {
+                resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+                    @Override
+                    public void onSuccess(int subId) {
+                        try {
+                            ISms iSms = getISmsServiceOrThrow();
+                            iSms.sendMultipartTextForSubscriber(subId, packageName, attributionTag,
+                                    destinationAddress, scAddress, parts, sentIntents,
+                                    deliveryIntents, persistMessage, messageId);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
+                                    + e.getMessage() + " " + formatCrossStackMessageId(messageId));
+                            notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure() {
+                        notifySmsError(sentIntents, RESULT_NO_DEFAULT_SMS_APP);
+                    }
+                });
+            } else {
+                // Called by apps that are not user facing, don't show disambiguation dialog.
+                try {
+                    ISms iSms = getISmsServiceOrThrow();
+                    if (iSms != null) {
+                        iSms.sendMultipartTextForSubscriber(getSubscriptionId(), packageName,
+                                attributionTag, destinationAddress, scAddress, parts, sentIntents,
+                                deliveryIntents, persistMessage, messageId);
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
+                            + e.getMessage() + " " + formatCrossStackMessageId(messageId));
+                    notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
+                }
+            }
+        } else {
+            PendingIntent sentIntent = null;
+            PendingIntent deliveryIntent = null;
+            if (sentIntents != null && sentIntents.size() > 0) {
+                sentIntent = sentIntents.get(0);
+            }
+            if (deliveryIntents != null && deliveryIntents.size() > 0) {
+                deliveryIntent = deliveryIntents.get(0);
+            }
+            sendTextMessageInternal(destinationAddress, scAddress, parts.get(0),
+                    sentIntent, deliveryIntent, true, packageName, attributionTag, messageId);
+        }
+    }
+
+    /**
+     * Send a multi-part text based SMS without writing it into the SMS Provider.
+     *
+     * <p>
+     * If this method is called on a device with multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the SMS sent on the subscription associated with slot
+     * 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent using the
+     * correct subscription.
+     * </p>
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
+     * privileges.
+     * </p>
+     *
+     * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList, ArrayList)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     * @hide
+     **/
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void sendMultipartTextMessageWithoutPersisting(
+            String destinationAddress, String scAddress, List<String> parts,
+            List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
+        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+                deliveryIntents, false /* persistMessage*/, getOpPackageName(),
+                getAttributionTag(), 0L /* messageId */);
+    }
+
+    /**
+     * Send a multi-part text based SMS with messaging options. The callee should have already
+     * divided the message into correctly sized parts by calling
+     * <code>divideMessage</code>.
+     *
+     * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
+     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
+     *
+     * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
+     * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
+     * writes messages sent using this method to the SMS Provider (the default SMS app is always
+     * responsible for writing its sent messages to the SMS Provider). For information about
+     * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
+     *
+     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+     * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+     * suitable default subscription could be found. In this case, if {@code sentIntent} is
+     * non-null, then the {@link PendingIntent} will be sent with an error code
+     * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+     * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+     * where this operation may fail.
+     * </p>
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *  the current default SMSC
+     * @param parts an <code>ArrayList</code> of strings that, in order,
+     *  comprise the original message
+     * @param sentIntents if not null, an <code>ArrayList</code> of
+     *  <code>PendingIntent</code>s (one for each message part) that is
+     *  broadcast when the corresponding message part has been sent.
+     *  The result code will be <code>Activity.RESULT_OK</code> for success,
+     *  or one of these errors:<br>
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>RESULT_ERROR_NULL_PDU</code><br>
+     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
+     *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+     *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+     *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+     *  <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+     *  <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>RESULT_NETWORK_REJECT</code><br>
+     *  <code>RESULT_INVALID_ARGUMENTS</code><br>
+     *  <code>RESULT_INVALID_STATE</code><br>
+     *  <code>RESULT_NO_MEMORY</code><br>
+     *  <code>RESULT_INVALID_SMS_FORMAT</code><br>
+     *  <code>RESULT_SYSTEM_ERROR</code><br>
+     *  <code>RESULT_MODEM_ERROR</code><br>
+     *  <code>RESULT_NETWORK_ERROR</code><br>
+     *  <code>RESULT_ENCODING_ERROR</code><br>
+     *  <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>RESULT_INTERNAL_ERROR</code><br>
+     *  <code>RESULT_NO_RESOURCES</code><br>
+     *  <code>RESULT_CANCELLED</code><br>
+     *  <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
+     *  <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+     *  <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
+     *  <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+     *  <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+     *  <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
+     *  <code>RESULT_REMOTE_EXCEPTION</code><br>
+     *  <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
+     *  <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+     *  <code>RESULT_RIL_NETWORK_REJECT</code><br>
+     *  <code>RESULT_RIL_INVALID_STATE</code><br>
+     *  <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
+     *  <code>RESULT_RIL_NO_MEMORY</code><br>
+     *  <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+     *  <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+     *  <code>RESULT_RIL_SYSTEM_ERR</code><br>
+     *  <code>RESULT_RIL_ENCODING_ERR</code><br>
+     *  <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>RESULT_RIL_MODEM_ERR</code><br>
+     *  <code>RESULT_RIL_NETWORK_ERR</code><br>
+     *  <code>RESULT_RIL_INTERNAL_ERR</code><br>
+     *  <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
+     *  <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
+     *  <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_NO_RESOURCES</code><br>
+     *  <code>RESULT_RIL_CANCELLED</code><br>
+     *  <code>RESULT_RIL_SIM_ABSENT</code><br>
+     *  <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_ACCESS_BARRED</code><br>
+     *  <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+     *  the sentIntent may include the extra "errorCode" containing a radio technology specific
+     *  value, generally only useful for troubleshooting.<br>
+     * @param deliveryIntents if not null, an <code>ArrayList</code> of
+     *  <code>PendingIntent</code>s (one for each message part) that is
+     *  broadcast when the corresponding message part has been delivered
+     *  to the recipient.  The raw pdu of the status report is in the
+     *  extended data ("pdu").
+     * @param priority Priority level of the message
+     *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+     *  ---------------------------------
+     *  PRIORITY      | Level of Priority
+     *  ---------------------------------
+     *      '00'      |     Normal
+     *      '01'      |     Interactive
+     *      '10'      |     Urgent
+     *      '11'      |     Emergency
+     *  ----------------------------------
+     *  Any Other values included Negative considered as Invalid Priority Indicator of the message.
+     * @param expectMore is a boolean to indicate the sending messages through same link or not.
+     * @param validityPeriod Validity Period of the message in mins.
+     *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+     *  Validity Period(Minimum) -> 5 mins
+     *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+     *  Any Other values included Negative considered as Invalid Validity Period of the message.
+     *
+     * @throws IllegalArgumentException if destinationAddress or data are empty
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public void sendMultipartTextMessage(
+            String destinationAddress, String scAddress, ArrayList<String> parts,
+            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents,
+            int priority, boolean expectMore, int validityPeriod) {
+        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+                deliveryIntents, true /* persistMessage*/, priority, expectMore,
+                validityPeriod);
+    }
+
+    private void sendMultipartTextMessageInternal(
+            String destinationAddress, String scAddress, List<String> parts,
+            List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
+            boolean persistMessage, int priority, boolean expectMore, int validityPeriod) {
+        if (TextUtils.isEmpty(destinationAddress)) {
+            throw new IllegalArgumentException("Invalid destinationAddress");
+        }
+        if (parts == null || parts.size() < 1) {
+            throw new IllegalArgumentException("Invalid message body");
+        }
+
+        if (priority < 0x00 || priority > 0x03) {
+            Log.e(TAG, "Invalid Priority " + priority);
+            priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
+        }
+
+        if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+            Log.e(TAG, "Invalid Validity Period " + validityPeriod);
+            validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
+        }
+
+        if (parts.size() > 1) {
+            final int finalPriority = priority;
+            final int finalValidity = validityPeriod;
+            if (persistMessage) {
+                resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+                    @Override
+                    public void onSuccess(int subId) {
+                        try {
+                            ISms iSms = getISmsServiceOrThrow();
+                            if (iSms != null) {
+                                iSms.sendMultipartTextForSubscriberWithOptions(subId,
+                                        null, null, destinationAddress,
+                                        scAddress, parts, sentIntents, deliveryIntents,
+                                        persistMessage, finalPriority, expectMore, finalValidity);
+                            }
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
+                                    + e.getMessage());
+                            notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure() {
+                        notifySmsError(sentIntents, RESULT_NO_DEFAULT_SMS_APP);
+                    }
+                });
+            } else {
+                // Sent by apps that are not user visible, so don't show SIM disambiguation dialog.
+                try {
+                    ISms iSms = getISmsServiceOrThrow();
+                    if (iSms != null) {
+                        iSms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(),
+                                null, null, destinationAddress,
+                                scAddress, parts, sentIntents, deliveryIntents,
+                                persistMessage, finalPriority, expectMore, finalValidity);
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "sendMultipartTextMessageInternal (no persist): Couldn't send SMS - "
+                            + e.getMessage());
+                    notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
+                }
+            }
+        } else {
+            PendingIntent sentIntent = null;
+            PendingIntent deliveryIntent = null;
+            if (sentIntents != null && sentIntents.size() > 0) {
+                sentIntent = sentIntents.get(0);
+            }
+            if (deliveryIntents != null && deliveryIntents.size() > 0) {
+                deliveryIntent = deliveryIntents.get(0);
+            }
+            sendTextMessageInternal(destinationAddress, scAddress, parts.get(0),
+                    sentIntent, deliveryIntent, persistMessage, priority, expectMore,
+                    validityPeriod);
+        }
+    }
+
+    /**
+     * Send a data based SMS to a specific application port.
+     *
+     * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
+     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
+     *
+     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+     * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+     * suitable default subscription could be found. In this case, if {@code sentIntent} is
+     * non-null, then the {@link PendingIntent} will be sent with an error code
+     * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+     * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+     * where this operation may fail.
+     * </p>
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *  the current default SMSC
+     * @param destinationPort the port to deliver the message to
+     * @param data the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK</code> for success,
+     *  or one of these errors:<br>
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>RESULT_ERROR_NULL_PDU</code><br>
+     *  <code>RESULT_ERROR_NO_SERVICE</code><br>
+     *  <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+     *  <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+     *  <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+     *  <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+     *  <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>RESULT_NETWORK_REJECT</code><br>
+     *  <code>RESULT_INVALID_ARGUMENTS</code><br>
+     *  <code>RESULT_INVALID_STATE</code><br>
+     *  <code>RESULT_NO_MEMORY</code><br>
+     *  <code>RESULT_INVALID_SMS_FORMAT</code><br>
+     *  <code>RESULT_SYSTEM_ERROR</code><br>
+     *  <code>RESULT_MODEM_ERROR</code><br>
+     *  <code>RESULT_NETWORK_ERROR</code><br>
+     *  <code>RESULT_ENCODING_ERROR</code><br>
+     *  <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>RESULT_INTERNAL_ERROR</code><br>
+     *  <code>RESULT_NO_RESOURCES</code><br>
+     *  <code>RESULT_CANCELLED</code><br>
+     *  <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
+     *  <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+     *  <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
+     *  <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+     *  <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+     *  <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
+     *  <code>RESULT_REMOTE_EXCEPTION</code><br>
+     *  <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
+     *  <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+     *  <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+     *  <code>RESULT_RIL_NETWORK_REJECT</code><br>
+     *  <code>RESULT_RIL_INVALID_STATE</code><br>
+     *  <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
+     *  <code>RESULT_RIL_NO_MEMORY</code><br>
+     *  <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+     *  <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+     *  <code>RESULT_RIL_SYSTEM_ERR</code><br>
+     *  <code>RESULT_RIL_ENCODING_ERR</code><br>
+     *  <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+     *  <code>RESULT_RIL_MODEM_ERR</code><br>
+     *  <code>RESULT_RIL_NETWORK_ERR</code><br>
+     *  <code>RESULT_RIL_INTERNAL_ERR</code><br>
+     *  <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+     *  <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
+     *  <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
+     *  <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_NO_RESOURCES</code><br>
+     *  <code>RESULT_RIL_CANCELLED</code><br>
+     *  <code>RESULT_RIL_SIM_ABSENT</code><br>
+     *  <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+     *  <code>RESULT_RIL_ACCESS_BARRED</code><br>
+     *  <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+     *  the sentIntent may include the extra "errorCode" containing a radio technology specific
+     *  value, generally only useful for troubleshooting.<br>
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     *
+     * @throws IllegalArgumentException if destinationAddress or data are empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void sendDataMessage(
+            String destinationAddress, String scAddress, short destinationPort,
+            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        if (TextUtils.isEmpty(destinationAddress)) {
+            throw new IllegalArgumentException("Invalid destinationAddress");
+        }
+
+        if (data == null || data.length == 0) {
+            throw new IllegalArgumentException("Invalid message data");
+        }
+
+        resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+            @Override
+            public void onSuccess(int subId) {
+                try {
+                    ISms iSms = getISmsServiceOrThrow();
+                    iSms.sendDataForSubscriber(subId, null, getAttributionTag(), destinationAddress,
+                            scAddress, destinationPort & 0xFFFF, data, sentIntent, deliveryIntent);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "sendDataMessage: Couldn't send SMS - Exception: " + e.getMessage());
+                    notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
+                }
+            }
+            @Override
+            public void onFailure() {
+                notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP);
+            }
+        });
+    }
+
+    /**
+     * Get the SmsManager associated with the default subscription id. The instance will always be
+     * associated with the default subscription id, even if the default subscription id changes.
+     *
+     * <p class="note"><strong>Note:</strong> For devices that support multiple active subscriptions
+     * at a time, SmsManager will track the subscription set by the user as the default SMS
+     * subscription. If the user has not set a default, {@link SmsManager} may
+     * start an activity to kick off a subscription disambiguation dialog. Most operations will not
+     * complete until the user has chosen the subscription that will be associated with the
+     * operation. If the user cancels the dialog without choosing a subscription, one of the
+     * following will happen, depending on the target SDK version of the application. For
+     * compatibility purposes, if the target SDK level is <= 28, telephony will still send the SMS
+     * over the first available subscription. If the target SDK level is > 28, the operation will
+     * fail to complete.
+     * </p>
+     *
+     * <p class="note"><strong>Note:</strong> If this method is used to perform an operation on a
+     * device that has multiple active subscriptions, the user has not set a default SMS
+     * subscription, and the operation is being performed while the application is not in the
+     * foreground, the SMS disambiguation dialog will not be shown. The result of the operation will
+     * conclude as if the user cancelled the disambiguation dialog and the operation will finish as
+     * outlined above, depending on the target SDK version of the calling application. It is safer
+     * to use {@link #getSmsManagerForSubscriptionId(int)} if the application will perform the
+     * operation while in the background because this can cause unpredictable results, such as the
+     * operation being sent over the wrong subscription or failing completely, depending on the
+     * user's default SMS subscription setting.
+     * </p>
+     *
+     * @return the {@link SmsManager} associated with the default subscription id.
+     *
+     * @see SubscriptionManager#getDefaultSmsSubscriptionId()
+     *
+     * @deprecated Use {@link Context#getSystemService Context.getSystemService(SmsManager.class)}
+     * instead
+     */
+    @Deprecated
+    public static SmsManager getDefault() {
+        return DEFAULT_INSTANCE;
+    }
+
+    /**
+     * Get the instance of the SmsManager associated with a particular context and subscription ID.
+     *
+     * @param context The context the manager belongs to
+     * @param subId an SMS subscription ID, typically accessed using {@link SubscriptionManager}
+     *
+     * @return the instance of the SmsManager associated with subscription
+     *
+     * @hide
+     */
+    public static @NonNull SmsManager getSmsManagerForContextAndSubscriptionId(
+            @Nullable Context context, int subId) {
+        synchronized(sLockObject) {
+            Pair<Context, Integer> key = new Pair<>(context, subId);
+
+            SmsManager smsManager = sSubInstances.get(key);
+            if (smsManager == null) {
+                smsManager = new SmsManager(context, subId);
+                sSubInstances.put(key, smsManager);
+            }
+            return smsManager;
+        }
+    }
+
+    /**
+     * Get the instance of the SmsManager associated with a particular subscription ID.
+     *
+     * <p class="note"><strong>Note:</strong> Constructing an {@link SmsManager} in this manner will
+     * never cause an SMS disambiguation dialog to appear, unlike {@link #getDefault()}.
+     * </p>
+     *
+     * @param subId an SMS subscription ID, typically accessed using {@link SubscriptionManager}
+     * @return the instance of the SmsManager associated with subscription
+     *
+     * @see SubscriptionManager#getActiveSubscriptionInfoList()
+     * @see SubscriptionManager#getDefaultSmsSubscriptionId()
+     * @deprecated Use {@link Context#getSystemService Context.getSystemService(SmsManager.class)}
+     * .{@link #createForSubscriptionId createForSubscriptionId(subId)} instead
+     */
+    @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public static SmsManager getSmsManagerForSubscriptionId(int subId) {
+        return getSmsManagerForContextAndSubscriptionId(null, subId);
+    }
+
+    /**
+     * Get the instance of the SmsManager associated with a particular subscription ID.
+     *
+     * <p class="note"><strong>Note:</strong> Constructing an {@link SmsManager} in this manner will
+     * never cause an SMS disambiguation dialog to appear, unlike {@link #getDefault()}.
+     * </p>
+     *
+     * @param subId an SMS subscription ID, typically accessed using {@link SubscriptionManager}
+     * @return the instance of the SmsManager associated with subscription
+     *
+     * @see SubscriptionManager#getActiveSubscriptionInfoList()
+     * @see SubscriptionManager#getDefaultSmsSubscriptionId()
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public @NonNull SmsManager createForSubscriptionId(int subId) {
+        return getSmsManagerForContextAndSubscriptionId(mContext, subId);
+    }
+
+    private SmsManager(@Nullable Context context, int subId) {
+        mContext = context;
+        mSubId = subId;
+    }
+
+    /**
+     * Get the associated subscription id. If the instance was returned by {@link #getDefault()},
+     * then this method may return different values at different points in time (if the user
+     * changes the default subscription id).
+     *
+     * <p class="note"><strong>Note:</strong> This method used to display a disambiguation dialog to
+     * the user asking them to choose a default subscription to send SMS messages over if they
+     * haven't chosen yet. Starting in API level 29, we allow the user to not have a default set as
+     * a valid option for the default SMS subscription on multi-SIM devices. We no longer show the
+     * disambiguation dialog and return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if the
+     * device has multiple active subscriptions and no default is set.
+     * </p>
+     *
+     * @return associated subscription ID or {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if
+     * the default subscription id cannot be determined or the device has multiple active
+     * subscriptions and and no default is set ("ask every time") by the user.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public int getSubscriptionId() {
+        try {
+            return (mSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
+                    ? getISmsServiceOrThrow().getPreferredSmsSubscription() : mSubId;
+        } catch (RemoteException e) {
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        }
+    }
+
+    /**
+     * Resolves the subscription id to use for the associated operation if
+     * {@link #getSubscriptionId()} returns {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+     *
+     * If app targets API level 28 or below and they are either sending the SMS from the background
+     * or the device has more than one active subscription available and no default is set, we will
+     * use the first logical slot to send the SMS and possibly fail later in the SMS sending
+     * process.
+     *
+     * Regardless of the API level, if the app is the foreground app, then we will show the SMS
+     * disambiguation dialog. If the app is in the background and tries to perform an operation, we
+     * will not show the disambiguation dialog.
+     *
+     * See {@link #getDefault()} for a detailed explanation of how this method operates.
+     *
+     * @param resolverResult The callback that will be called when the subscription is resolved or
+     *                       fails to be resolved.
+     */
+    private void resolveSubscriptionForOperation(SubscriptionResolverResult resolverResult) {
+        int subId = getSubscriptionId();
+        boolean isSmsSimPickActivityNeeded = false;
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                // Determines if the SMS SIM pick activity should be shown. This is only shown if:
+                // 1) The device has multiple active subscriptions and an SMS default subscription
+                //    hasn't been set, and
+                // 2) SmsManager is being called from the foreground app.
+                // Android does not allow background activity starts, so we need to block this.
+                // if Q+, do not perform requested operation if these two operations are not set. If
+                // <P, perform these operations on phone 0 (for compatibility purposes, since we
+                // used to not wait for the result of this activity).
+                isSmsSimPickActivityNeeded = iSms.isSmsSimPickActivityNeeded(subId);
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "resolveSubscriptionForOperation", ex);
+        }
+        if (!isSmsSimPickActivityNeeded) {
+            sendResolverResult(resolverResult, subId, false /*pickActivityShown*/);
+            return;
+        }
+        // We need to ask the user pick an appropriate subid for the operation.
+        Log.d(TAG, "resolveSubscriptionForOperation isSmsSimPickActivityNeeded is true for calling"
+                + " package. ");
+        try {
+            // Create the SMS pick activity and call back once the activity is complete. Can't do
+            // it here because we do not have access to the activity context that is performing this
+            // operation.
+            // Requires that the calling process has the SEND_SMS permission.
+            getITelephony().enqueueSmsPickResult(null, null,
+                    new IIntegerConsumer.Stub() {
+                        @Override
+                        public void accept(int subId) {
+                            // Runs on binder thread attached to this app's process.
+                            sendResolverResult(resolverResult, subId, true /*pickActivityShown*/);
+                        }
+                    });
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Unable to launch activity", ex);
+            // pickActivityShown is true here because we want to call sendResolverResult and always
+            // have this operation fail. This is because we received a RemoteException here, which
+            // means that telephony is not available and the next operation to Telephony will fail
+            // as well anyways, so we might as well shortcut fail here first.
+            sendResolverResult(resolverResult, subId, true /*pickActivityShown*/);
+        }
+    }
+
+    /**
+     * To check the SDK version for SmsManager.sendResolverResult method.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P)
+    private static final long GET_TARGET_SDK_VERSION_CODE_CHANGE = 145147528L;
+
+    private void sendResolverResult(SubscriptionResolverResult resolverResult, int subId,
+            boolean pickActivityShown) {
+        if (SubscriptionManager.isValidSubscriptionId(subId)) {
+            resolverResult.onSuccess(subId);
+            return;
+        }
+
+        if (!Compatibility.isChangeEnabled(GET_TARGET_SDK_VERSION_CODE_CHANGE)
+                && !pickActivityShown) {
+            // Do not fail, return a success with an INVALID subid for apps targeting P or below
+            // that tried to perform an operation and the SMS disambiguation dialog was never shown,
+            // as these applications may not have been written to handle the failure case properly.
+            // This will resolve to performing the operation on phone 0 in telephony.
+            resolverResult.onSuccess(subId);
+        } else {
+            // Fail if the app targets Q or above or it targets P and below and the disambiguation
+            // dialog was shown and the user clicked out of it.
+            resolverResult.onFailure();
+        }
+    }
+
+    private static ITelephony getITelephony() {
+        ITelephony binder = ITelephony.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getTelephonyServiceRegisterer()
+                        .get());
+        if (binder == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+        return binder;
+    }
+
+    private static void notifySmsError(PendingIntent pendingIntent, int error) {
+        if (pendingIntent != null) {
+            try {
+                pendingIntent.send(error);
+            } catch (PendingIntent.CanceledException e) {
+                // Don't worry about it, we do not need to notify the caller if this is the case.
+            }
+        }
+    }
+
+    private static void notifySmsError(List<PendingIntent> pendingIntents, int error) {
+        if (pendingIntents != null) {
+            for (PendingIntent pendingIntent : pendingIntents) {
+                notifySmsError(pendingIntent, error);
+            }
+        }
+    }
+
+    /**
+     * Returns the ISms service, or throws an UnsupportedOperationException if
+     * the service does not exist.
+     */
+    private static ISms getISmsServiceOrThrow() {
+        ISms iSms = TelephonyManager.getSmsService();
+        if (iSms == null) {
+            throw new UnsupportedOperationException("Sms is not supported");
+        }
+        return iSms;
+    }
+
+    private static ISms getISmsService() {
+        return TelephonyManager.getSmsService();
+    }
+
+    /**
+     * Copies a raw SMS PDU to the ICC.
+     * ICC (Integrated Circuit Card) is the card of the device.
+     * For example, this can be the SIM or USIM for GSM.
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+     * applications or the Telephony framework and will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+     * operation is performed on the correct subscription.
+     * </p>
+     *
+     * @param smsc the SMSC for this messag or null for the default SMSC.
+     * @param pdu the raw PDU to store.
+     * @param status message status. One of these status:
+     *               <code>STATUS_ON_ICC_READ</code>
+     *               <code>STATUS_ON_ICC_UNREAD</code>
+     *               <code>STATUS_ON_ICC_SENT</code>
+     *               <code>STATUS_ON_ICC_UNSENT</code>
+     * @return true for success. Otherwise false.
+     *
+     * @throws IllegalArgumentException if pdu is null.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
+    public boolean copyMessageToIcc(
+            @Nullable byte[] smsc, @NonNull byte[] pdu, @StatusOnIcc int status) {
+        boolean success = false;
+
+        if (pdu == null) {
+            throw new IllegalArgumentException("pdu is null");
+        }
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                success = iSms.copyMessageToIccEfForSubscriber(getSubscriptionId(),
+                        null,
+                        status, pdu, smsc);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Deletes the specified message from the ICC.
+     * ICC (Integrated Circuit Card) is the card of the device.
+     * For example, this can be the SIM or USIM for GSM.
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+     * applications or the Telephony framework and will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+     * operation is performed on the correct subscription.
+     * </p>
+     *
+     * @param messageIndex the message index of the message in the ICC (1-based index).
+     * @return true for success, false if the operation fails. Failure can be due to IPC failure,
+     * RIL/modem error which results in SMS failed to be deleted on SIM
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
+    public boolean deleteMessageFromIcc(int messageIndex) {
+        boolean success = false;
+
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                success = iSms.updateMessageOnIccEfForSubscriber(getSubscriptionId(),
+                        null,
+                        messageIndex, STATUS_ON_ICC_FREE, null /* pdu */);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Update the specified message on the ICC.
+     * ICC (Integrated Circuit Card) is the card of the device.
+     * For example, this can be the SIM or USIM for GSM.
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+     * applications or the Telephony framework and will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+     * operation is performed on the correct subscription.
+     * </p>
+     *
+     * @param messageIndex record index of message to update
+     * @param newStatus new message status (STATUS_ON_ICC_READ,
+     *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
+     *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
+     * @param pdu the raw PDU to store
+     * @return true for success
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
+    public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) {
+        boolean success = false;
+
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                success = iSms.updateMessageOnIccEfForSubscriber(getSubscriptionId(),
+                        null,
+                        messageIndex, newStatus, pdu);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Retrieves all messages currently stored on the ICC.
+     * ICC (Integrated Circuit Card) is the card of the device.
+     * For example, this can be the SIM or USIM for GSM.
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+     * applications or the Telephony framework and will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+     * operation is performed on the correct subscription.
+     * </p>
+     *
+     * @return <code>List</code> of <code>SmsMessage</code> objects for valid records only.
+     *
+     * {@hide}
+     */
+    @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
+    public @NonNull List<SmsMessage> getMessagesFromIcc() {
+        return getAllMessagesFromIcc();
+    }
+
+    /**
+     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
+     *
+     * This is similar to {@link #getMessagesFromIcc} except that it will return ArrayList.
+     * Suggested to use {@link #getMessagesFromIcc} instead.
+     *
+     * {@hide}
+     */
+    @UnsupportedAppUsage
+    public ArrayList<SmsMessage> getAllMessagesFromIcc() {
+        List<SmsRawData> records = null;
+
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                records = iSms.getAllMessagesFromIccEfForSubscriber(
+                        getSubscriptionId(),
+                        null);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return createMessageListFromRawRecords(records);
+    }
+
+    /**
+     * Enable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier range and RAN type. The RAN type specifies if this message ID
+     * belongs to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients enable
+     * the same message identifier, they must both disable it for the device to stop
+     * receiving those messages. All received messages will be broadcast in an
+     * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
+     * Note: This call is blocking, callers may want to avoid calling it from
+     * the main thread of an application.
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+     * applications or the Telephony framework and will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}, which will result in the operation
+     * being completed on the subscription associated with logical slot 0. Use
+     * {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation is performed on the
+     * correct subscription.
+     * </p>
+     *
+     * <p>Requires {@link android.Manifest.permission#RECEIVE_EMERGENCY_BROADCAST}</p>
+     *
+     * @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
+     * or C.R1001-G (3GPP2)
+     * @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
+     * or C.R1001-G (3GPP2)
+     * @param ranType the message format as defined in {@link SmsCbMessage}
+     * @return true if successful, false if the modem reports a failure (e.g. the given range or
+     * RAN type is invalid).
+     * @see #disableCellBroadcastRange(int, int, int)
+     *
+     * @throws IllegalArgumentException if endMessageId < startMessageId
+     * @deprecated Use {@link TelephonyManager#setCellBroadcastIdRanges} instead.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     * {@hide}
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public boolean enableCellBroadcastRange(int startMessageId, int endMessageId,
+            @android.telephony.SmsCbMessage.MessageFormat int ranType) {
+        boolean success = false;
+        if (endMessageId < startMessageId) {
+            throw new IllegalArgumentException("endMessageId < startMessageId");
+        }
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
+                // the default phone internally.
+                int subId = getSubscriptionId();
+                success = iSms.enableCellBroadcastRangeForSubscriber(subId,
+                        startMessageId, endMessageId, ranType);
+                Rlog.d(TAG, "enableCellBroadcastRange: " + (success ? "succeeded" : "failed")
+                        + " at calling enableCellBroadcastRangeForSubscriber. subId = " + subId);
+            }
+        } catch (RemoteException ex) {
+            Rlog.d(TAG, "enableCellBroadcastRange: ", ex);
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Disable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier range and RAN type. The RAN type specify this message
+     * ID range belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different
+     * clients enable the same message identifier, they must both disable it for
+     * the device to stop receiving those messages.
+     * Note: This call is blocking, callers may want to avoid calling it from
+     * the main thread of an application.
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+     * applications or the Telephony framework and will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+     * operation is performed on the correct subscription.
+     * </p>
+     *
+     * <p>Requires {@link android.Manifest.permission#RECEIVE_EMERGENCY_BROADCAST}</p>
+     *
+     * @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
+     * or C.R1001-G (3GPP2)
+     * @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
+     * or C.R1001-G (3GPP2)
+     * @param ranType the message format as defined in {@link SmsCbMessage}
+     * @return true if successful, false if the modem reports a failure (e.g. the given range or
+     * RAN type is invalid).
+     *
+     * @see #enableCellBroadcastRange(int, int, int)
+     *
+     * @throws IllegalArgumentException if endMessageId < startMessageId
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     *
+     * @deprecated Use {@link TelephonyManager#setCellBroadcastIdRanges} instead.
+     * {@hide}
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public boolean disableCellBroadcastRange(int startMessageId, int endMessageId,
+            @android.telephony.SmsCbMessage.MessageFormat int ranType) {
+        boolean success = false;
+
+        if (endMessageId < startMessageId) {
+            throw new IllegalArgumentException("endMessageId < startMessageId");
+        }
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
+                // the default phone internally.
+                int subId = getSubscriptionId();
+                success = iSms.disableCellBroadcastRangeForSubscriber(subId,
+                        startMessageId, endMessageId, ranType);
+                Rlog.d(TAG, "disableCellBroadcastRange: " + (success ? "succeeded" : "failed")
+                        + " at calling disableCellBroadcastRangeForSubscriber. subId = " + subId);
+            }
+        } catch (RemoteException ex) {
+            Rlog.d(TAG, "disableCellBroadcastRange: ", ex);
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Creates a list of <code>SmsMessage</code>s from a list of SmsRawData records.
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+     * applications or the Telephony framework and will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+     * operation is performed on the correct subscription.
+     * </p>
+     *
+     * @param records SMS EF records.
+     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
+     */
+    private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
+        ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
+        if (records != null) {
+            int count = records.size();
+            for (int i = 0; i < count; i++) {
+                SmsRawData data = records.get(i);
+                // List contains all records, including "free" records (null)
+                if (data != null) {
+                    SmsMessage sms = SmsMessage.createFromEfRecord(i + 1, data.getBytes(),
+                            getSubscriptionId());
+                    if (sms != null) {
+                        messages.add(sms);
+                    }
+                }
+            }
+        }
+        return messages;
+    }
+
+    /**
+     * SMS over IMS is supported if IMS is registered and SMS is supported
+     * on IMS.
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+     * applications or the Telephony framework and will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+     * operation is performed on the correct subscription.
+     * </p>
+     *
+     * @return true if SMS over IMS is supported, false otherwise
+     *
+     * @see #getImsSmsFormat()
+     *
+     * @hide
+     */
+    public boolean isImsSmsSupported() {
+        boolean boSupported = false;
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                boSupported = iSms.isImsSmsSupportedForSubscriber(getSubscriptionId());
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+        return boSupported;
+    }
+
+    /**
+     * Gets SMS format supported on IMS.  SMS over IMS format is either 3GPP or 3GPP2.
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+     * applications or the Telephony framework and will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+     * operation is performed on the correct subscription.
+     * </p>
+     *
+     * @return SmsMessage.FORMAT_3GPP,
+     *         SmsMessage.FORMAT_3GPP2
+     *      or SmsMessage.FORMAT_UNKNOWN
+     *
+     * @see #isImsSmsSupported()
+     *
+     * @hide
+     */
+    public String getImsSmsFormat() {
+        String format = com.android.internal.telephony.SmsConstants.FORMAT_UNKNOWN;
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                format = iSms.getImsSmsFormatForSubscriber(getSubscriptionId());
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+        return format;
+    }
+
+    /**
+     * Get default sms subscription id.
+     *
+     * <p class="note"><strong>Note:</strong>This returns a value different from
+     * {@link SubscriptionManager#getDefaultSmsSubscriptionId} if the user has not chosen a default.
+     * In this case it returns the active subscription id if there's only one active subscription
+     * available.
+     *
+     * @return the user-defined default SMS subscription id, or the active subscription id if
+     * there's only one active subscription available, otherwise
+     * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public static int getDefaultSmsSubscriptionId() {
+        try {
+            return getISmsService().getPreferredSmsSubscription();
+        } catch (RemoteException e) {
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        } catch (NullPointerException e) {
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        }
+    }
+
+    /**
+     * Get SMS prompt property,  enabled or not
+     *
+     * @return true if enabled, false otherwise
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean isSMSPromptEnabled() {
+        ISms iSms = null;
+        try {
+            iSms = TelephonyManager.getSmsService();
+            return iSms.isSMSPromptEnabled();
+        } catch (RemoteException ex) {
+            return false;
+        } catch (NullPointerException ex) {
+            return false;
+        }
+    }
+
+    /**
+     * Gets the total capacity of SMS storage on the SIM card.
+     *
+     * <p>
+     * This is the number of 176 byte EF-SMS records which can be stored on the SIM card.
+     * See 3GPP TS 31.102 - 4.2.25 - EF-SMS for more information.
+     * </p>
+     *
+     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this method will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation
+     * is performed on the correct subscription.
+     * </p>
+     *
+     * @return the total number of SMS records which can be stored on the SIM card.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+    @IntRange(from = 0)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public int getSmsCapacityOnIcc() {
+        int ret = 0;
+        try {
+            ISms iccISms = getISmsService();
+            if (iccISms != null) {
+                ret = iccISms.getSmsCapacityOnIccForSubscriber(getSubscriptionId());
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getSmsCapacityOnIcc() RemoteException", ex);
+        }
+        return ret;
+    }
+
+    /** @hide */
+    @IntDef(prefix = { "STATUS_ON_ICC_" }, value = {
+            STATUS_ON_ICC_FREE,
+            STATUS_ON_ICC_READ,
+            STATUS_ON_ICC_UNREAD,
+            STATUS_ON_ICC_SENT,
+            STATUS_ON_ICC_UNSENT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StatusOnIcc {}
+
+    // see SmsMessage.getStatusOnIcc
+
+    /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+    public static final int STATUS_ON_ICC_FREE      = 0;
+
+    /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+    public static final int STATUS_ON_ICC_READ      = 1;
+
+    /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+    public static final int STATUS_ON_ICC_UNREAD    = 3;
+
+    /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+    public static final int STATUS_ON_ICC_SENT      = 5;
+
+    /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+    public static final int STATUS_ON_ICC_UNSENT    = 7;
+
+    // SMS send failure result codes
+
+    /** @hide */
+    @IntDef(prefix = { "RESULT" }, value = {
+            RESULT_ERROR_NONE,
+            RESULT_ERROR_GENERIC_FAILURE,
+            RESULT_ERROR_RADIO_OFF,
+            RESULT_ERROR_NULL_PDU,
+            RESULT_ERROR_NO_SERVICE,
+            RESULT_ERROR_LIMIT_EXCEEDED,
+            RESULT_ERROR_FDN_CHECK_FAILURE,
+            RESULT_ERROR_SHORT_CODE_NOT_ALLOWED,
+            RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED,
+            RESULT_RADIO_NOT_AVAILABLE,
+            RESULT_NETWORK_REJECT,
+            RESULT_INVALID_ARGUMENTS,
+            RESULT_INVALID_STATE,
+            RESULT_NO_MEMORY,
+            RESULT_INVALID_SMS_FORMAT,
+            RESULT_SYSTEM_ERROR,
+            RESULT_MODEM_ERROR,
+            RESULT_NETWORK_ERROR,
+            RESULT_INVALID_SMSC_ADDRESS,
+            RESULT_OPERATION_NOT_ALLOWED,
+            RESULT_INTERNAL_ERROR,
+            RESULT_NO_RESOURCES,
+            RESULT_CANCELLED,
+            RESULT_REQUEST_NOT_SUPPORTED,
+            RESULT_NO_BLUETOOTH_SERVICE,
+            RESULT_INVALID_BLUETOOTH_ADDRESS,
+            RESULT_BLUETOOTH_DISCONNECTED,
+            RESULT_UNEXPECTED_EVENT_STOP_SENDING,
+            RESULT_SMS_BLOCKED_DURING_EMERGENCY,
+            RESULT_SMS_SEND_RETRY_FAILED,
+            RESULT_REMOTE_EXCEPTION,
+            RESULT_NO_DEFAULT_SMS_APP,
+            RESULT_USER_NOT_ALLOWED,
+            RESULT_RIL_RADIO_NOT_AVAILABLE,
+            RESULT_RIL_SMS_SEND_FAIL_RETRY,
+            RESULT_RIL_NETWORK_REJECT,
+            RESULT_RIL_INVALID_STATE,
+            RESULT_RIL_INVALID_ARGUMENTS,
+            RESULT_RIL_NO_MEMORY,
+            RESULT_RIL_REQUEST_RATE_LIMITED,
+            RESULT_RIL_INVALID_SMS_FORMAT,
+            RESULT_RIL_SYSTEM_ERR,
+            RESULT_RIL_ENCODING_ERR,
+            RESULT_RIL_INVALID_SMSC_ADDRESS,
+            RESULT_RIL_MODEM_ERR,
+            RESULT_RIL_NETWORK_ERR,
+            RESULT_RIL_INTERNAL_ERR,
+            RESULT_RIL_REQUEST_NOT_SUPPORTED,
+            RESULT_RIL_INVALID_MODEM_STATE,
+            RESULT_RIL_NETWORK_NOT_READY,
+            RESULT_RIL_OPERATION_NOT_ALLOWED,
+            RESULT_RIL_NO_RESOURCES,
+            RESULT_RIL_CANCELLED,
+            RESULT_RIL_SIM_ABSENT,
+            RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED,
+            RESULT_RIL_ACCESS_BARRED,
+            RESULT_RIL_BLOCKED_DUE_TO_CALL,
+            RESULT_RIL_GENERIC_ERROR,
+            RESULT_RIL_INVALID_RESPONSE,
+            RESULT_RIL_SIM_PIN2,
+            RESULT_RIL_SIM_PUK2,
+            RESULT_RIL_SUBSCRIPTION_NOT_AVAILABLE,
+            RESULT_RIL_SIM_ERROR,
+            RESULT_RIL_INVALID_SIM_STATE,
+            RESULT_RIL_NO_SMS_TO_ACK,
+            RESULT_RIL_SIM_BUSY,
+            RESULT_RIL_SIM_FULL,
+            RESULT_RIL_NO_SUBSCRIPTION,
+            RESULT_RIL_NO_NETWORK_FOUND,
+            RESULT_RIL_DEVICE_IN_USE,
+            RESULT_RIL_ABORTED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Result {}
+
+    /**
+     * No error.
+     */
+    public static final int RESULT_ERROR_NONE    = 0;
+
+    /** Generic failure cause */
+    public static final int RESULT_ERROR_GENERIC_FAILURE    = 1;
+
+    /** Failed because radio was explicitly turned off */
+    public static final int RESULT_ERROR_RADIO_OFF          = 2;
+
+    /** Failed because no pdu provided */
+    public static final int RESULT_ERROR_NULL_PDU           = 3;
+
+    /** Failed because service is currently unavailable */
+    public static final int RESULT_ERROR_NO_SERVICE         = 4;
+
+    /** Failed because we reached the sending queue limit. */
+    public static final int RESULT_ERROR_LIMIT_EXCEEDED     = 5;
+
+    /**
+     * Failed because FDN is enabled.
+     */
+    public static final int RESULT_ERROR_FDN_CHECK_FAILURE  = 6;
+
+    /** Failed because user denied the sending of this short code. */
+    public static final int RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 7;
+
+    /** Failed because the user has denied this app ever send premium short codes. */
+    public static final int RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 8;
+
+    /**
+     * Failed because the radio was not available
+     */
+    public static final int RESULT_RADIO_NOT_AVAILABLE = 9;
+
+    /**
+     * Failed because of network rejection
+     */
+    public static final int RESULT_NETWORK_REJECT = 10;
+
+    /**
+     * Failed because of invalid arguments
+     */
+    public static final int RESULT_INVALID_ARGUMENTS = 11;
+
+    /**
+     * Failed because of an invalid state
+     */
+    public static final int RESULT_INVALID_STATE = 12;
+
+    /**
+     * Failed because there is no memory
+     */
+    public static final int RESULT_NO_MEMORY = 13;
+
+    /**
+     * Failed because the sms format is not valid
+     */
+    public static final int RESULT_INVALID_SMS_FORMAT = 14;
+
+    /**
+     * Failed because of a system error
+     */
+    public static final int RESULT_SYSTEM_ERROR = 15;
+
+    /**
+     * Failed because of a modem error
+     */
+    public static final int RESULT_MODEM_ERROR = 16;
+
+    /**
+     * Failed because of a network error
+     */
+    public static final int RESULT_NETWORK_ERROR = 17;
+
+    /**
+     * Failed because of an encoding error
+     */
+    public static final int RESULT_ENCODING_ERROR = 18;
+
+    /**
+     * Failed because of an invalid smsc address
+     */
+    public static final int RESULT_INVALID_SMSC_ADDRESS = 19;
+
+    /**
+     * Failed because the operation is not allowed
+     */
+    public static final int RESULT_OPERATION_NOT_ALLOWED = 20;
+
+    /**
+     * Failed because of an internal error
+     */
+    public static final int RESULT_INTERNAL_ERROR = 21;
+
+    /**
+     * Failed because there are no resources
+     */
+    public static final int RESULT_NO_RESOURCES = 22;
+
+    /**
+     * Failed because the operation was cancelled
+     */
+    public static final int RESULT_CANCELLED = 23;
+
+    /**
+     * Failed because the request is not supported
+     */
+    public static final int RESULT_REQUEST_NOT_SUPPORTED = 24;
+
+    /**
+     * Failed sending via bluetooth because the bluetooth service is not available
+     */
+    public static final int RESULT_NO_BLUETOOTH_SERVICE = 25;
+
+    /**
+     * Failed sending via bluetooth because the bluetooth device address is invalid
+     */
+    public static final int RESULT_INVALID_BLUETOOTH_ADDRESS = 26;
+
+    /**
+     * Failed sending via bluetooth because bluetooth disconnected
+     */
+    public static final int RESULT_BLUETOOTH_DISCONNECTED = 27;
+
+    /**
+     * Failed sending because the user denied or canceled the dialog displayed for a premium
+     * shortcode sms or rate-limited sms.
+     */
+    public static final int RESULT_UNEXPECTED_EVENT_STOP_SENDING = 28;
+
+    /**
+     * Failed sending during an emergency call
+     */
+    public static final int RESULT_SMS_BLOCKED_DURING_EMERGENCY = 29;
+
+    /**
+     * Failed to send an sms retry
+     */
+    public static final int RESULT_SMS_SEND_RETRY_FAILED = 30;
+
+    /**
+     * Set by BroadcastReceiver to indicate a remote exception while handling a message.
+     */
+    public static final int RESULT_REMOTE_EXCEPTION = 31;
+
+    /**
+     * Set by BroadcastReceiver to indicate there's no default sms app.
+     */
+    public static final int RESULT_NO_DEFAULT_SMS_APP = 32;
+
+    /**
+     * User is not associated with the subscription.
+     */
+    public static final int RESULT_USER_NOT_ALLOWED = 33;
+
+    // Radio Error results
+
+    /**
+     * The radio did not start or is resetting.
+     */
+    public static final int RESULT_RIL_RADIO_NOT_AVAILABLE = 100;
+
+    /**
+     * The radio failed to send the sms and needs to retry.
+     */
+    public static final int RESULT_RIL_SMS_SEND_FAIL_RETRY = 101;
+
+    /**
+     * The sms request was rejected by the network.
+     */
+    public static final int RESULT_RIL_NETWORK_REJECT = 102;
+
+    /**
+     * The radio returned an unexpected request for the current state.
+     */
+    public static final int RESULT_RIL_INVALID_STATE = 103;
+
+    /**
+     * The radio received invalid arguments in the request.
+     */
+    public static final int RESULT_RIL_INVALID_ARGUMENTS = 104;
+
+    /**
+     * The radio didn't have sufficient memory to process the request.
+     */
+    public static final int RESULT_RIL_NO_MEMORY = 105;
+
+    /**
+     * The radio denied the operation due to overly-frequent requests.
+     */
+    public static final int RESULT_RIL_REQUEST_RATE_LIMITED = 106;
+
+    /**
+     * The radio returned an error indicating invalid sms format.
+     */
+    public static final int RESULT_RIL_INVALID_SMS_FORMAT = 107;
+
+    /**
+     * The radio encountered a platform or system error.
+     */
+    public static final int RESULT_RIL_SYSTEM_ERR = 108;
+
+    /**
+     * The SMS message was not encoded properly.
+     */
+    public static final int RESULT_RIL_ENCODING_ERR = 109;
+
+    /**
+     * The specified SMSC address was invalid.
+     */
+    public static final int RESULT_RIL_INVALID_SMSC_ADDRESS = 110;
+
+    /**
+     * The vendor RIL received an unexpected or incorrect response.
+     */
+    public static final int RESULT_RIL_MODEM_ERR = 111;
+
+    /**
+     * The radio received an error from the network.
+     */
+    public static final int RESULT_RIL_NETWORK_ERR = 112;
+
+    /**
+     * The modem encountered an unexpected error scenario while handling the request.
+     */
+    public static final int RESULT_RIL_INTERNAL_ERR = 113;
+
+    /**
+     * The request was not supported by the radio.
+     */
+    public static final int RESULT_RIL_REQUEST_NOT_SUPPORTED = 114;
+
+    /**
+     * The radio cannot process the request in the current modem state.
+     */
+    public static final int RESULT_RIL_INVALID_MODEM_STATE = 115;
+
+    /**
+     * The network is not ready to perform the request.
+     */
+    public static final int RESULT_RIL_NETWORK_NOT_READY = 116;
+
+    /**
+     * The radio reports the request is not allowed.
+     */
+    public static final int RESULT_RIL_OPERATION_NOT_ALLOWED = 117;
+
+    /**
+     * There are insufficient resources to process the request.
+     */
+    public static final int RESULT_RIL_NO_RESOURCES = 118;
+
+    /**
+     * The request has been cancelled.
+     */
+    public static final int RESULT_RIL_CANCELLED = 119;
+
+    /**
+     * The radio failed to set the location where the CDMA subscription
+     * can be retrieved because the SIM or RUIM is absent.
+     */
+    public static final int RESULT_RIL_SIM_ABSENT = 120;
+
+    /**
+     * 1X voice and SMS are not allowed simultaneously.
+     */
+    public static final int RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED = 121;
+
+    /**
+     * Access is barred.
+     */
+    public static final int RESULT_RIL_ACCESS_BARRED = 122;
+
+    /**
+     * SMS is blocked due to call control, e.g., resource unavailable in the SMR entity.
+     */
+    public static final int RESULT_RIL_BLOCKED_DUE_TO_CALL = 123;
+
+    /**
+     * A RIL error occurred during the SMS send.
+     */
+    public static final int RESULT_RIL_GENERIC_ERROR = 124;
+
+    /**
+     * A RIL internal error when one of the RIL layers receives an unrecognized response from a
+     * lower layer.
+     */
+    public static final int RESULT_RIL_INVALID_RESPONSE = 125;
+
+    /**
+     * Operation requires SIM PIN2 to be entered
+     */
+    public static final int RESULT_RIL_SIM_PIN2 = 126;
+
+    /**
+     * Operation requires SIM PUK2 to be entered
+     */
+    public static final int RESULT_RIL_SIM_PUK2 = 127;
+
+    /**
+     * Fail to find CDMA subscription from specified location
+     */
+    public static final int RESULT_RIL_SUBSCRIPTION_NOT_AVAILABLE = 128;
+
+    /**
+     * Received error from SIM card
+     */
+    public static final int RESULT_RIL_SIM_ERROR = 129;
+
+    /**
+     * Cannot process the request in current SIM state
+     */
+    public static final int RESULT_RIL_INVALID_SIM_STATE = 130;
+
+    /**
+     * ACK received when there is no SMS to ack
+     */
+    public static final int RESULT_RIL_NO_SMS_TO_ACK = 131;
+
+    /**
+     * SIM is busy
+     */
+    public static final int RESULT_RIL_SIM_BUSY = 132;
+
+    /**
+     * The target EF is full
+     */
+    public static final int RESULT_RIL_SIM_FULL = 133;
+
+    /**
+     * Device does not have subscription
+     */
+    public static final int RESULT_RIL_NO_SUBSCRIPTION = 134;
+
+    /**
+     * Network cannot be found
+     */
+    public static final int RESULT_RIL_NO_NETWORK_FOUND = 135;
+
+    /**
+     * Operation cannot be performed because the device is currently in use
+     */
+    public static final int RESULT_RIL_DEVICE_IN_USE = 136;
+
+    /**
+     * Operation aborted
+     */
+    public static final int RESULT_RIL_ABORTED = 137;
+
+
+    // SMS receiving results sent as a "result" extra in {@link Intents.SMS_REJECTED_ACTION}
+
+    /**
+     * SMS receive dispatch failure.
+     */
+    public static final int RESULT_RECEIVE_DISPATCH_FAILURE = 500;
+
+    /**
+     * SMS receive injected null PDU.
+     */
+    public static final int RESULT_RECEIVE_INJECTED_NULL_PDU = 501;
+
+    /**
+     * SMS receive encountered runtime exception.
+     */
+    public static final int RESULT_RECEIVE_RUNTIME_EXCEPTION = 502;
+
+    /**
+     * SMS received null message from the radio interface layer.
+     */
+    public static final int RESULT_RECEIVE_NULL_MESSAGE_FROM_RIL = 503;
+
+    /**
+     * SMS short code received while the phone is in encrypted state.
+     */
+    public static final int RESULT_RECEIVE_WHILE_ENCRYPTED = 504;
+
+    /**
+     * SMS receive encountered an SQL exception.
+     */
+    public static final int RESULT_RECEIVE_SQL_EXCEPTION = 505;
+
+    /**
+     * SMS receive an exception parsing a uri.
+     */
+    public static final int RESULT_RECEIVE_URI_EXCEPTION = 506;
+
+
+
+    /**
+     * Send an MMS message
+     *
+     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+     * manager on a multi-SIM device, this operation may fail sending the MMS message because no
+     * suitable default subscription could be found. In this case, if {@code sentIntent} is
+     * non-null, then the {@link PendingIntent} will be sent with an error code
+     * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the
+     * conditions where this operation may fail.
+     * </p>
+     *
+     * @param context application context
+     * @param contentUri the content Uri from which the message pdu will be read
+     * @param locationUrl the optional location url where message should be sent to
+     * @param configOverrides the carrier-specific messaging configuration values to override for
+     *  sending the message.
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed
+     * The result code will be <code>Activity.RESULT_OK</code> for success
+     * or one of these errors:<br>
+     * <code>MMS_ERROR_UNSPECIFIED</code><br>
+     * <code>MMS_ERROR_INVALID_APN</code><br>
+     * <code>MMS_ERROR_UNABLE_CONNECT_MMS</code><br>
+     * <code>MMS_ERROR_HTTP_FAILURE</code><br>
+     * <code>MMS_ERROR_IO_ERROR</code><br>
+     * <code>MMS_ERROR_RETRY</code><br>
+     * <code>MMS_ERROR_CONFIGURATION_ERROR</code><br>
+     * <code>MMS_ERROR_NO_DATA_NETWORK</code><br>
+     * <code>MMS_ERROR_INVALID_SUBSCRIPTION_ID</code><br>
+     * <code>MMS_ERROR_INACTIVE_SUBSCRIPTION</code><br>
+     * <code>MMS_ERROR_DATA_DISABLED</code><br>
+     * <code>MMS_ERROR_MMS_DISABLED_BY_CARRIER</code><br>
+     * @throws IllegalArgumentException if contentUri is empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
+            Bundle configOverrides, PendingIntent sentIntent) {
+        sendMultimediaMessage(context, contentUri, locationUrl, configOverrides, sentIntent,
+                0L /* messageId */);
+    }
+
+    /**
+     * Send an MMS message
+     *
+     * Same as {@link #sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
+     *           Bundle configOverrides, PendingIntent sentIntent)}, but adds an optional messageId.
+     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+     * manager on a multi-SIM device, this operation may fail sending the MMS message because no
+     * suitable default subscription could be found. In this case, if {@code sentIntent} is
+     * non-null, then the {@link PendingIntent} will be sent with an error code
+     * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the
+     * conditions where this operation may fail.
+     * </p>
+     *
+     * @param context application context
+     * @param contentUri the content Uri from which the message pdu will be read
+     * @param locationUrl the optional location url where message should be sent to
+     * @param configOverrides the carrier-specific messaging configuration values to override for
+     *  sending the message.
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed
+     * The result code will be <code>Activity.RESULT_OK</code> for success
+     * or one of these errors:<br>
+     * <code>MMS_ERROR_UNSPECIFIED</code><br>
+     * <code>MMS_ERROR_INVALID_APN</code><br>
+     * <code>MMS_ERROR_UNABLE_CONNECT_MMS</code><br>
+     * <code>MMS_ERROR_HTTP_FAILURE</code><br>
+     * <code>MMS_ERROR_IO_ERROR</code><br>
+     * <code>MMS_ERROR_RETRY</code><br>
+     * <code>MMS_ERROR_CONFIGURATION_ERROR</code><br>
+     * <code>MMS_ERROR_NO_DATA_NETWORK</code><br>
+     * <code>MMS_ERROR_INVALID_SUBSCRIPTION_ID</code><br>
+     * <code>MMS_ERROR_INACTIVE_SUBSCRIPTION</code><br>
+     * <code>MMS_ERROR_DATA_DISABLED</code><br>
+     * <code>MMS_ERROR_MMS_DISABLED_BY_CARRIER</code><br>
+     * @param messageId an id that uniquely identifies the message requested to be sent.
+     * Used for logging and diagnostics purposes. The id may be 0.
+     * @throws IllegalArgumentException if contentUri is empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void sendMultimediaMessage(@NonNull Context context, @NonNull Uri contentUri,
+            @Nullable String locationUrl,
+            @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
+            @Nullable PendingIntent sentIntent, long messageId) {
+        if (contentUri == null) {
+            throw new IllegalArgumentException("Uri contentUri null");
+        }
+        MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
+        if (m != null) {
+            resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+                @Override
+                public void onSuccess(int subId) {
+                    m.sendMultimediaMessage(subId, contentUri, locationUrl, configOverrides,
+                            sentIntent, messageId);
+                }
+
+                @Override
+                public void onFailure() {
+                    notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP);
+                }
+            });
+        }
+    }
+
+    /**
+     * Download an MMS message from carrier by a given location URL
+     *
+     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+     * manager on a multi-SIM device, this operation may fail downloading the MMS message because no
+     * suitable default subscription could be found. In this case, if {@code downloadedIntent} is
+     * non-null, then the {@link PendingIntent} will be sent with an error code
+     * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the
+     * conditions where this operation may fail.
+     * </p>
+     *
+     * @param context application context
+     * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
+     *  from the MMS WAP push notification
+     * @param contentUri the content uri to which the downloaded pdu will be written
+     * @param configOverrides the carrier-specific messaging configuration values to override for
+     *  downloading the message.
+     * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is downloaded, or the download is failed
+     * The result code will be <code>Activity.RESULT_OK</code> for success
+     * or one of these errors:<br>
+     * <code>MMS_ERROR_UNSPECIFIED</code><br>
+     * <code>MMS_ERROR_INVALID_APN</code><br>
+     * <code>MMS_ERROR_UNABLE_CONNECT_MMS</code><br>
+     * <code>MMS_ERROR_HTTP_FAILURE</code><br>
+     * <code>MMS_ERROR_IO_ERROR</code><br>
+     * <code>MMS_ERROR_RETRY</code><br>
+     * <code>MMS_ERROR_CONFIGURATION_ERROR</code><br>
+     * <code>MMS_ERROR_NO_DATA_NETWORK</code><br>
+     * <code>MMS_ERROR_INVALID_SUBSCRIPTION_ID</code><br>
+     * <code>MMS_ERROR_INACTIVE_SUBSCRIPTION</code><br>
+     * <code>MMS_ERROR_DATA_DISABLED</code><br>
+     * <code>MMS_ERROR_MMS_DISABLED_BY_CARRIER</code><br>
+     * @throws IllegalArgumentException if locationUrl or contentUri is empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri,
+            Bundle configOverrides, PendingIntent downloadedIntent) {
+        downloadMultimediaMessage(context, locationUrl, contentUri, configOverrides,
+                downloadedIntent, 0L /* messageId */);
+    }
+
+    /**
+     * Download an MMS message from carrier by a given location URL
+     *
+     * Same as {@link #downloadMultimediaMessage(Context context, String locationUrl,
+     *      Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent)},
+     *      but adds an optional messageId.
+     * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+     * manager on a multi-SIM device, this operation may fail downloading the MMS message because no
+     * suitable default subscription could be found. In this case, if {@code downloadedIntent} is
+     * non-null, then the {@link PendingIntent} will be sent with an error code
+     * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the
+     * conditions where this operation may fail.
+     * </p>
+     *
+     * @param context application context
+     * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
+     *  from the MMS WAP push notification
+     * @param contentUri the content uri to which the downloaded pdu will be written
+     * @param configOverrides the carrier-specific messaging configuration values to override for
+     *  downloading the message.
+     * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is downloaded, or the download is failed
+     * The result code will be <code>Activity.RESULT_OK</code> for success
+     * or one of these errors:<br>
+     * <code>MMS_ERROR_UNSPECIFIED</code><br>
+     * <code>MMS_ERROR_INVALID_APN</code><br>
+     * <code>MMS_ERROR_UNABLE_CONNECT_MMS</code><br>
+     * <code>MMS_ERROR_HTTP_FAILURE</code><br>
+     * <code>MMS_ERROR_IO_ERROR</code><br>
+     * <code>MMS_ERROR_RETRY</code><br>
+     * <code>MMS_ERROR_CONFIGURATION_ERROR</code><br>
+     * <code>MMS_ERROR_NO_DATA_NETWORK</code><br>
+     * <code>MMS_ERROR_INVALID_SUBSCRIPTION_ID</code><br>
+     * <code>MMS_ERROR_INACTIVE_SUBSCRIPTION</code><br>
+     * <code>MMS_ERROR_DATA_DISABLED</code><br>
+     * <code>MMS_ERROR_MMS_DISABLED_BY_CARRIER</code><br>
+     * @param messageId an id that uniquely identifies the message requested to be downloaded.
+     * Used for logging and diagnostics purposes. The id may be 0.
+     * @throws IllegalArgumentException if locationUrl or contentUri is empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void downloadMultimediaMessage(@NonNull Context context, @NonNull String locationUrl,
+            @NonNull Uri contentUri,
+            @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
+            @Nullable PendingIntent downloadedIntent, long messageId) {
+        if (TextUtils.isEmpty(locationUrl)) {
+            throw new IllegalArgumentException("Empty MMS location URL");
+        }
+        if (contentUri == null) {
+            throw new IllegalArgumentException("Uri contentUri null");
+        }
+        MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
+        if (m != null) {
+            resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+                @Override
+                public void onSuccess(int subId) {
+                    m.downloadMultimediaMessage(subId, locationUrl, contentUri, configOverrides,
+                            downloadedIntent, messageId);
+                }
+
+                @Override
+                public void onFailure() {
+                    notifySmsError(downloadedIntent, RESULT_NO_DEFAULT_SMS_APP);
+                }
+            });
+        }
+    }
+
+    // MMS send/download failure result codes
+
+    /**
+     * Unspecific MMS error occurred during send/download.
+     */
+    public static final int MMS_ERROR_UNSPECIFIED = 1;
+
+    /**
+     * ApnException occurred during MMS network setup.
+     */
+    public static final int MMS_ERROR_INVALID_APN = 2;
+
+    /**
+     * An error occurred during the MMS connection setup.
+     */
+    public static final int MMS_ERROR_UNABLE_CONNECT_MMS = 3;
+
+    /**
+     * An error occurred during the HTTP client setup.
+     */
+    public static final int MMS_ERROR_HTTP_FAILURE = 4;
+
+    /**
+     * An I/O error occurred reading the PDU.
+     */
+    public static final int MMS_ERROR_IO_ERROR = 5;
+
+    /**
+     * An error occurred while retrying sending/downloading the MMS.
+     */
+    public static final int MMS_ERROR_RETRY = 6;
+
+    /**
+     * The carrier-dependent configuration values could not be loaded.
+     */
+    public static final int MMS_ERROR_CONFIGURATION_ERROR = 7;
+
+    /**
+     * There is neither Wi-Fi nor mobile data network.
+     */
+    public static final int MMS_ERROR_NO_DATA_NETWORK = 8;
+
+    /**
+     * The subscription id for the send/download is invalid.
+     */
+    public static final int MMS_ERROR_INVALID_SUBSCRIPTION_ID = 9;
+
+    /**
+     * The subscription id for the send/download is inactive.
+     */
+    public static final int MMS_ERROR_INACTIVE_SUBSCRIPTION = 10;
+
+    /**
+     * Data is disabled for the MMS APN.
+     */
+    public static final int MMS_ERROR_DATA_DISABLED = 11;
+
+    /**
+     * MMS is disabled by a carrier.
+     */
+    @FlaggedApi(Flags.FLAG_MMS_DISABLED_ERROR)
+    public static final int MMS_ERROR_MMS_DISABLED_BY_CARRIER = 12;
+
+    /**
+     * The MMS pdu was too large to send or too large to download over the current connection.
+     * @hide
+     */
+    public static final int MMS_ERROR_TOO_LARGE_FOR_TRANSPORT = 13;
+
+    /** Intent extra name for MMS sending result data in byte array type */
+    public static final String EXTRA_MMS_DATA = "android.telephony.extra.MMS_DATA";
+    /** Intent extra name for HTTP status code for MMS HTTP failure in integer type */
+    public static final String EXTRA_MMS_HTTP_STATUS = "android.telephony.extra.MMS_HTTP_STATUS";
+
+    /**
+     * Get carrier-dependent MMS configuration values.
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+     * applications or the Telephony framework and will never trigger an SMS disambiguation dialog.
+     * If this method is called on a device that has multiple active subscriptions, this {@link
+     * SmsManager} instance has been created with {@link #getDefault()}, and no user-defined default
+     * subscription is defined, the subscription ID associated with this message will be INVALID,
+     * which will result in the operation being completed on the subscription associated with
+     * logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation is
+     * performed on the correct subscription.
+     * </p>
+     *
+     * @return the bundle key/values pairs that contains MMS configuration values
+     *  or an empty Bundle if they cannot be found.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    @NonNull public Bundle getCarrierConfigValues() {
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                return iSms.getCarrierConfigValuesForSubscriber(getSubscriptionId());
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+        return new Bundle();
+    }
+
+    /**
+     * Create a single use app specific incoming SMS request for the calling package.
+     *
+     * This method returns a token that if included in a subsequent incoming SMS message will cause
+     * {@code intent} to be sent with the SMS data.
+     *
+     * The token is only good for one use, after an SMS has been received containing the token all
+     * subsequent SMS messages with the token will be routed as normal.
+     *
+     * An app can only have one request at a time, if the app already has a request pending it will
+     * be replaced with a new request.
+     *
+     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+     * operation is performed on the correct subscription.
+     * </p>
+     *
+     * @return Token to include in an SMS message. The token will be 11 characters long.
+     * @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public String createAppSpecificSmsToken(PendingIntent intent) {
+        try {
+            ISms iccSms = getISmsServiceOrThrow();
+            return iccSms.createAppSpecificSmsToken(getSubscriptionId(),
+                    null, intent);
+
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
+     * callback for providing asynchronous sms messages for financial app.
+     */
+    public abstract static class FinancialSmsCallback {
+        /**
+         * Callback to send sms messages back to financial app asynchronously.
+         *
+         * @param msgs SMS messages.
+         */
+        public abstract void onFinancialSmsMessages(CursorWindow msgs);
+    };
+
+    /**
+     * Get SMS messages for the calling financial app.
+     * The result will be delivered asynchronously in the passing in callback interface.
+     *
+     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+     * operation is performed on the correct subscription.
+     * </p>
+     *
+     * @param params the parameters to filter SMS messages returned.
+     * @param executor the executor on which callback will be invoked.
+     * @param callback a callback to receive CursorWindow with SMS messages.
+     *
+     */
+    @RequiresPermission(android.Manifest.permission.SMS_FINANCIAL_TRANSACTIONS)
+    public void getSmsMessagesForFinancialApp(
+            Bundle params,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull FinancialSmsCallback callback) {
+        // This API is not functional and thus removed to avoid future confusion.
+    }
+
+    /**
+     * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
+     * The prefixes is a list of prefix {@code String} separated by this delimiter.
+     * @hide
+     */
+    public static final String REGEX_PREFIX_DELIMITER = ",";
+    /**
+     * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
+     * The success status to be added into the intent to be sent to the calling package.
+     * @hide
+     */
+    public static final int RESULT_STATUS_SUCCESS = 0;
+    /**
+     * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
+     * The timeout status to be added into the intent to be sent to the calling package.
+     * @hide
+     */
+    public static final int RESULT_STATUS_TIMEOUT = 1;
+    /**
+     * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
+     * Intent extra key of the retrieved SMS message as a {@code String}.
+     * @hide
+     */
+    public static final String EXTRA_SMS_MESSAGE = "android.telephony.extra.SMS_MESSAGE";
+    /**
+     * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
+     * Intent extra key of SMS retriever status, which indicates whether the request for the
+     * coming SMS message is SUCCESS or TIMEOUT
+     * @hide
+     */
+    public static final String EXTRA_STATUS = "android.telephony.extra.STATUS";
+    /**
+     * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
+     * [Optional] Intent extra key of the retrieved Sim card subscription Id if any. {@code int}
+     * @hide
+     */
+    public static final String EXTRA_SIM_SUBSCRIPTION_ID =
+            "android.telephony.extra.SIM_SUBSCRIPTION_ID";
+
+    /**
+     * Create a single use app specific incoming SMS request for the calling package.
+     *
+     * This method returns a token that if included in a subsequent incoming SMS message, and the
+     * SMS message has a prefix from the given prefixes list, the provided {@code intent} will be
+     * sent with the SMS data to the calling package.
+     *
+     * The token is only good for one use within a reasonable amount of time. After an SMS has been
+     * received containing the token all subsequent SMS messages with the token will be routed as
+     * normal.
+     *
+     * An app can only have one request at a time, if the app already has a request pending it will
+     * be replaced with a new request.
+     *
+     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+     * operation is performed on the correct subscription.
+     * </p>
+     *
+     * @param prefixes this is a list of prefixes string separated by REGEX_PREFIX_DELIMITER. The
+     *  matching SMS message should have at least one of the prefixes in the beginning of the
+     *  message.
+     * @param intent this intent is sent when the matching SMS message is received.
+     * @return Token to include in an SMS message.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    @Nullable
+    public String createAppSpecificSmsTokenWithPackageInfo(
+            @Nullable String prefixes, @NonNull PendingIntent intent) {
+        try {
+            ISms iccSms = getISmsServiceOrThrow();
+            return iccSms.createAppSpecificSmsTokenWithPackageInfo(getSubscriptionId(),
+                    null, prefixes, intent);
+
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
+     * Set Storage Availability in SmsStorageMonitor
+     * @param storageAvailable storage availability to be set true or false
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @TestApi
+    public void setStorageMonitorMemoryStatusOverride(boolean storageAvailable) {
+        try {
+            ISms iccISms = getISmsServiceOrThrow();
+            if (iccISms != null) {
+                iccISms.setStorageMonitorMemoryStatusOverride(getSubscriptionId(),
+                                                                storageAvailable);
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Clear the memory status override set by
+     * {@link #setStorageMonitorMemoryStatusOverride(boolean)}
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @TestApi
+    public void clearStorageMonitorMemoryStatusOverride() {
+        try {
+            ISms iccISms = getISmsServiceOrThrow();
+            if (iccISms != null) {
+                iccISms.clearStorageMonitorMemoryStatusOverride(getSubscriptionId());
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"SMS_CATEGORY_"},
+            value = {
+                    SmsManager.SMS_CATEGORY_NOT_SHORT_CODE,
+                    SmsManager.SMS_CATEGORY_FREE_SHORT_CODE,
+                    SmsManager.SMS_CATEGORY_STANDARD_SHORT_CODE,
+                    SmsManager.SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE,
+                    SmsManager.SMS_CATEGORY_PREMIUM_SHORT_CODE})
+    public @interface SmsShortCodeCategory {}
+
+    /**
+     * Return value from {@link #checkSmsShortCodeDestination(String, String)} ()} for regular
+     * phone numbers.
+     * @hide
+     */
+    @TestApi
+    public static final int SMS_CATEGORY_NOT_SHORT_CODE = 0;
+    /**
+     * Return value from {@link #checkSmsShortCodeDestination(String, String)} ()} for free
+     * (no cost) short codes.
+     * @hide
+     */
+    @TestApi
+    public static final int SMS_CATEGORY_FREE_SHORT_CODE = 1;
+    /**
+     * Return value from {@link #checkSmsShortCodeDestination(String, String)} ()} for
+     * standard rate (non-premium)
+     * short codes.
+     * @hide
+     */
+    @TestApi
+    public static final int SMS_CATEGORY_STANDARD_SHORT_CODE = 2;
+    /**
+     * Return value from {@link #checkSmsShortCodeDestination(String, String)} ()} for possible
+     * premium short codes.
+     * @hide
+     */
+    @TestApi
+    public static final int SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
+    /**
+     * Return value from {@link #checkSmsShortCodeDestination(String, String)} ()} for
+     * premium short codes.
+     * @hide
+     */
+    @TestApi
+    public static final int SMS_CATEGORY_PREMIUM_SHORT_CODE = 4;
+
+    /**
+     * Check if the destination address is a possible premium short code.
+     * NOTE: the caller is expected to strip non-digits from the destination number with
+     * {@link PhoneNumberUtils#extractNetworkPortion} before calling this method.
+     *
+     * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+     * applications or the Telephony framework and will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this message will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+     * operation is performed on the correct subscription.
+     * </p>
+     *
+     * @param destAddress the destination address to test for possible short code
+     * @param countryIso the ISO country code
+     *
+     * @return
+     * {@link SmsManager#SMS_CATEGORY_NOT_SHORT_CODE},
+     * {@link SmsManager#SMS_CATEGORY_FREE_SHORT_CODE},
+     * {@link SmsManager#SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE},
+     * {@link SmsManager#SMS_CATEGORY_PREMIUM_SHORT_CODE}, or
+     * {@link SmsManager#SMS_CATEGORY_STANDARD_SHORT_CODE}
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @TestApi
+    public @SmsShortCodeCategory int checkSmsShortCodeDestination(
+            String destAddress, String countryIso) {
+        try {
+            ISms iccISms = getISmsServiceOrThrow();
+            if (iccISms != null) {
+                return iccISms.checkSmsShortCodeDestination(getSubscriptionId(),
+                        null, null, destAddress, countryIso);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "checkSmsShortCodeDestination() RemoteException", e);
+        }
+        return SmsManager.SMS_CATEGORY_NOT_SHORT_CODE;
+    }
+
+    /**
+     * Gets the SMSC address from (U)SIM.
+     *
+     * <p class="note"><strong>Note:</strong> Using this method requires that your app is the
+     * default SMS application, or READ_PRIVILEGED_PHONE_STATE permission, or has the carrier
+     * privileges.</p>
+     *
+     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this method will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation
+     * is performed on the correct subscription.
+     * </p>
+     *
+     * @return the SMSC address string, null if failed.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @SuppressAutoDoc // for carrier privileges and default SMS application.
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    @Nullable
+    public String getSmscAddress() {
+        String smsc = null;
+
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                smsc = iSms.getSmscAddressFromIccEfForSubscriber(
+                        getSubscriptionId(), null);
+            }
+        } catch (RemoteException ex) {
+            throw new RuntimeException(ex);
+        }
+        return smsc;
+    }
+
+    /**
+     * Sets the SMSC address on (U)SIM.
+     *
+     * <p class="note"><strong>Note:</strong> Using this method requires that your app is the
+     * default SMS application, or has {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     * permission, or has the carrier privileges.</p>
+     *
+     * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
+     * dialog. If this method is called on a device that has multiple active subscriptions, this
+     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+     * default subscription is defined, the subscription ID associated with this method will be
+     * INVALID, which will result in the operation being completed on the subscription associated
+     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation
+     * is performed on the correct subscription.
+     * </p>
+     *
+     * @param smsc the SMSC address string.
+     * @return true for success, false otherwise. Failure can be due modem returning an error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @SuppressAutoDoc // for carrier privileges and default SMS application.
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public boolean setSmscAddress(@NonNull String smsc) {
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                return iSms.setSmscAddressOnIccEfForSubscriber(
+                        smsc, getSubscriptionId(), null);
+            }
+        } catch (RemoteException ex) {
+            throw new RuntimeException(ex);
+        }
+        return false;
+    }
+
+    /**
+     * Gets the premium SMS permission for the specified package. If the package has never
+     * been seen before, the default {@link SmsManager#PREMIUM_SMS_CONSENT_UNKNOWN}
+     * will be returned.
+     * @param packageName the name of the package to query permission
+     * @return one of {@link SmsManager#PREMIUM_SMS_CONSENT_UNKNOWN},
+     *  {@link SmsManager#PREMIUM_SMS_CONSENT_ASK_USER},
+     *  {@link SmsManager#PREMIUM_SMS_CONSENT_NEVER_ALLOW}, or
+     *  {@link SmsManager#PREMIUM_SMS_CONSENT_ALWAYS_ALLOW}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public @PremiumSmsConsent int getPremiumSmsConsent(@NonNull String packageName) {
+        int permission = 0;
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                permission = iSms.getPremiumSmsPermission(packageName);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "getPremiumSmsPermission() RemoteException", e);
+        }
+        return permission;
+    }
+
+    /**
+     * Sets the premium SMS permission for the specified package and save the value asynchronously
+     * to persistent storage.
+     * @param packageName the name of the package to set permission
+     * @param permission one of {@link SmsManager#PREMIUM_SMS_CONSENT_ASK_USER},
+     *  {@link SmsManager#PREMIUM_SMS_CONSENT_NEVER_ALLOW}, or
+     *  {@link SmsManager#PREMIUM_SMS_CONSENT_ALWAYS_ALLOW}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void setPremiumSmsConsent(
+            @NonNull String packageName, @PremiumSmsConsent int permission) {
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                iSms.setPremiumSmsPermission(packageName, permission);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "setPremiumSmsPermission() RemoteException", e);
+        }
+    }
+
+    /**
+     * Reset all cell broadcast ranges. Previously enabled ranges will become invalid after this.
+     * @deprecated Use {@link TelephonyManager#setCellBroadcastIdRanges} with empty list instead
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void resetAllCellBroadcastRanges() {
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
+                // the default phone internally.
+                iSms.resetAllCellBroadcastRanges(getSubscriptionId());
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    private static String formatCrossStackMessageId(long id) {
+        return "{x-message-id:" + id + "}";
+    }
+
+    /**
+     * Fetches the EF_PSISMSC value from the UICC that contains the Public Service Identity of
+     * the SM-SC (either a SIP URI or tel URI). The EF_PSISMSC of ISIM and USIM can be found in
+     * DF_TELECOM.
+     * The EF_PSISMSC value is used by the ME to submit SMS over IP as defined in 24.341 [55].
+     *
+     * @return Uri : Public Service Identity of SM-SC from the ISIM or USIM if the ISIM is not
+     * available.
+     * @throws SecurityException if the caller does not have the required permission/privileges.
+     * @throws IllegalStateException in case of telephony service is not available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public Uri getSmscIdentity() {
+        Uri smscUri = Uri.EMPTY;
+        try {
+            IPhoneSubInfo info = TelephonyManager.getSubscriberInfoService();
+            if (info == null) {
+                Rlog.e(TAG, "getSmscIdentity(): IPhoneSubInfo instance is NULL");
+                throw new IllegalStateException("Telephony service is not available");
+            }
+            /** Fetches the SIM EF_PSISMSC value based on subId and appType */
+            smscUri = info.getSmscIdentity(getSubscriptionId(), TelephonyManager.APPTYPE_ISIM);
+            if (Uri.EMPTY.equals(smscUri)) {
+                /** Fallback in case where ISIM is not available */
+                smscUri = info.getSmscIdentity(getSubscriptionId(), TelephonyManager.APPTYPE_USIM);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getSmscIdentity(): Exception : " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return smscUri;
+    }
+
+    /**
+     * Gets the message size of a WAP from the cache.
+     *
+     * @param locationUrl the location to use as a key for looking up the size in the cache.
+     * The locationUrl may or may not have the transactionId appended to the url.
+     *
+     * @return long representing the message size
+     * @throws java.util.NoSuchElementException if the WAP push doesn't exist in the cache
+     * @throws IllegalArgumentException if the locationUrl is empty
+     *
+     * @hide
+     */
+    public long getWapMessageSize(@NonNull String locationUrl) {
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                return iSms.getWapMessageSize(locationUrl);
+            } else {
+                throw new RuntimeException("Could not acquire ISms service.");
+            }
+        } catch (RemoteException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+}
diff --git a/android-35/android/telephony/SmsMessage.java b/android-35/android/telephony/SmsMessage.java
new file mode 100644
index 0000000..845449e
--- /dev/null
+++ b/android-35/android/telephony/SmsMessage.java
@@ -0,0 +1,1217 @@
+/*
+ * Copyright (C) 2008 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;
+
+import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.Resources;
+import android.os.Binder;
+import android.os.Build;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.Sms7BitEncodingTranslator;
+import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
+import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A Short Message Service message.
+ * @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent
+ */
+public class SmsMessage {
+    private static final String LOG_TAG = "SmsMessage";
+
+    /**
+     * SMS Class enumeration.
+     * See TS 23.038.
+     *
+     */
+    public enum MessageClass{
+        UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
+    }
+
+    /** @hide */
+    @IntDef(prefix = { "ENCODING_" }, value = {
+            ENCODING_UNKNOWN,
+            ENCODING_7BIT,
+            ENCODING_8BIT,
+            ENCODING_16BIT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EncodingSize {}
+
+    /** User data text encoding code unit size */
+    public static final int ENCODING_UNKNOWN = 0;
+    public static final int ENCODING_7BIT = 1;
+    public static final int ENCODING_8BIT = 2;
+    public static final int ENCODING_16BIT = 3;
+    /**
+     * This value is not defined in global standard. Only in Korea, this is used.
+     */
+    public static final int ENCODING_KSC5601 = 4;
+
+    /** The maximum number of payload bytes per message */
+    public static final int MAX_USER_DATA_BYTES = 140;
+
+    /**
+     * The maximum number of payload bytes per message if a user data header
+     * is present.  This assumes the header only contains the
+     * CONCATENATED_8_BIT_REFERENCE element.
+     */
+    public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134;
+
+    /** The maximum number of payload septets per message */
+    public static final int MAX_USER_DATA_SEPTETS = 160;
+
+    /**
+     * The maximum number of payload septets per message if a user data header
+     * is present.  This assumes the header only contains the
+     * CONCATENATED_8_BIT_REFERENCE element.
+     */
+    public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
+
+    /** @hide */
+    @StringDef(prefix = { "FORMAT_" }, value = {
+            FORMAT_3GPP,
+            FORMAT_3GPP2
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Format {}
+
+    /**
+     * Indicates a 3GPP format SMS message.
+     * @see SmsManager#injectSmsPdu(byte[], String, PendingIntent)
+     */
+    public static final String FORMAT_3GPP = "3gpp";
+
+    /**
+     * Indicates a 3GPP2 format SMS message.
+     * @see SmsManager#injectSmsPdu(byte[], String, PendingIntent)
+     */
+    public static final String FORMAT_3GPP2 = "3gpp2";
+
+    /** Contains actual SmsMessage. Only public for debugging and for framework layer.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public SmsMessageBase mWrappedSmsMessage;
+
+    /** Indicates the subId
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private int mSubId = 0;
+
+    /** set Subscription information
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public void setSubId(int subId) {
+        mSubId = subId;
+    }
+
+    /** get Subscription information
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getSubId() {
+        return mSubId;
+    }
+
+    public static class SubmitPdu {
+
+        public byte[] encodedScAddress; // Null if not applicable.
+        public byte[] encodedMessage;
+
+        @Override
+        public String toString() {
+            return "SubmitPdu: encodedScAddress = "
+                    + Arrays.toString(encodedScAddress)
+                    + ", encodedMessage = "
+                    + Arrays.toString(encodedMessage);
+        }
+
+        /**
+         * @hide
+         */
+        protected SubmitPdu(SubmitPduBase spb) {
+            this.encodedMessage = spb.encodedMessage;
+            this.encodedScAddress = spb.encodedScAddress;
+        }
+
+    }
+
+    /**
+     * @hide
+     */
+    public SmsMessage(SmsMessageBase smb) {
+        mWrappedSmsMessage = smb;
+    }
+
+    /**
+     * Create an SmsMessage from a raw PDU. Guess format based on Voice
+     * technology first, if it fails use other format.
+     * All applications which handle
+     * incoming SMS messages by processing the {@code SMS_RECEIVED_ACTION} broadcast
+     * intent <b>must</b> now pass the new {@code format} String extra from the intent
+     * into the new method {@code createFromPdu(byte[], String)} which takes an
+     * extra format parameter. This is required in order to correctly decode the PDU on
+     * devices that require support for both 3GPP and 3GPP2 formats at the same time,
+     * such as dual-mode GSM/CDMA and CDMA/LTE phones.
+     * @deprecated Use {@link #createFromPdu(byte[], String)} instead.
+     */
+    @Deprecated
+    public static SmsMessage createFromPdu(byte[] pdu) {
+         SmsMessage message = null;
+
+        // cdma(3gpp2) vs gsm(3gpp) format info was not given,
+        // guess from active voice phone type
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+        String format = (PHONE_TYPE_CDMA == activePhone) ?
+                SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP;
+        return createFromPdu(pdu, format);
+    }
+
+    /**
+     * Create an SmsMessage from a raw PDU with the specified message format. The
+     * message format is passed in the
+     * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} as the {@code format}
+     * String extra, and will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
+     * or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
+     *
+     * @param pdu the message PDU from the
+     * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent
+     * @param format the format extra from the
+     * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent
+     */
+    public static SmsMessage createFromPdu(byte[] pdu, String format) {
+        return createFromPdu(pdu, format, true);
+    }
+
+    private static SmsMessage createFromPdu(byte[] pdu, String format,
+            boolean fallbackToOtherFormat) {
+        if (pdu == null) {
+            Rlog.i(LOG_TAG, "createFromPdu(): pdu is null");
+            return null;
+        }
+        SmsMessageBase wrappedMessage;
+        String otherFormat = SmsConstants.FORMAT_3GPP2.equals(format) ? SmsConstants.FORMAT_3GPP :
+                SmsConstants.FORMAT_3GPP2;
+        if (SmsConstants.FORMAT_3GPP2.equals(format)) {
+            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
+        } else if (SmsConstants.FORMAT_3GPP.equals(format)) {
+            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
+        } else {
+            Rlog.e(LOG_TAG, "createFromPdu(): unsupported message format " + format);
+            return null;
+        }
+
+        if (wrappedMessage != null) {
+            return new SmsMessage(wrappedMessage);
+        } else {
+            if (fallbackToOtherFormat) {
+                return createFromPdu(pdu, otherFormat, false);
+            } else {
+                Rlog.e(LOG_TAG, "createFromPdu(): wrappedMessage is null");
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Creates an SmsMessage from an SMS EF record.
+     *
+     * @param index Index of SMS EF record.
+     * @param data Record data.
+     * @return An SmsMessage representing the record.
+     *
+     * @hide
+     */
+    public static SmsMessage createFromEfRecord(int index, byte[] data) {
+        return createFromEfRecord(index, data, SmsManager.getDefaultSmsSubscriptionId());
+    }
+
+    /**
+     * Creates an SmsMessage from an SMS EF record.
+     *
+     * @param index Index of SMS EF record.
+     * @param data Record data.
+     * @param subId Subscription Id associated with the record.
+     * @return An SmsMessage representing the record.
+     *
+     * @hide
+     */
+    public static SmsMessage createFromEfRecord(int index, byte[] data, int subId) {
+        SmsMessageBase wrappedMessage;
+
+        if (isCdmaVoice(subId)) {
+            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
+                    index, data);
+        } else {
+            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
+                    index, data);
+        }
+
+        return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null;
+    }
+
+    /**
+     * Create an SmsMessage from a native SMS-Submit PDU, specified by Bluetooth Message Access
+     * Profile Specification v1.4.2 5.8.
+     * This is used by Bluetooth MAP profile to decode message when sending non UTF-8 SMS messages.
+     *
+     * @param data Message data.
+     * @param isCdma Indicates weather the type of the SMS is CDMA.
+     * @return An SmsMessage representing the message.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public static SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[] data, boolean isCdma) {
+        SmsMessageBase wrappedMessage;
+
+        if (isCdma) {
+            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
+                    0, data);
+        } else {
+            // Bluetooth uses its own method to decode GSM PDU so this part is not called.
+            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
+                    0, data);
+        }
+
+        return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null;
+    }
+
+    /**
+     * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
+     * length in bytes (not hex chars) less the SMSC header
+     *
+     * FIXME: This method is only used by a CTS test case that isn't run on CDMA devices.
+     * We should probably deprecate it and remove the obsolete test case.
+     */
+    public static int getTPLayerLengthForPDU(String pdu) {
+        if (isCdmaVoice()) {
+            return com.android.internal.telephony.cdma.SmsMessage.getTPLayerLengthForPDU(pdu);
+        } else {
+            return com.android.internal.telephony.gsm.SmsMessage.getTPLayerLengthForPDU(pdu);
+        }
+    }
+
+    /*
+     * TODO(cleanup): It would make some sense if the result of
+     * preprocessing a message to determine the proper encoding (i.e.
+     * the resulting data structure from calculateLength) could be
+     * passed as an argument to the actual final encoding function.
+     * This would better ensure that the logic behind size calculation
+     * actually matched the encoding.
+     */
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and the number of
+     * characters remaining until the next message.
+     *
+     * @param msgBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the radio-specific 7-bit encoding
+     *     are counted as single space chars. If false, and if the messageBody contains non-7-bit
+     *     encodable characters, length is calculated using a 16-bit encoding.
+     * @return an int[6] with int[0] being the number of SMS's required, int[1] the number of code
+     *     units used, and int[2] is the number of code units remaining until the next message.
+     *     int[3] is an indicator of the encoding code unit size (see the ENCODING_* definitions in
+     *     SmsConstants). int[4] is the GSM national language table to use, or 0 for the default
+     *     7-bit alphabet. int[5] The GSM national language shift table to use, or 0 for the default
+     *     7-bit extension table.
+     */
+    public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) {
+        return calculateLength(msgBody, use7bitOnly, SmsManager.getDefaultSmsSubscriptionId());
+    }
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and the number of
+     * characters remaining until the next message.
+     *
+     * @param msgBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the radio-specific 7-bit encoding
+     *     are counted as single space chars. If false, and if the messageBody contains non-7-bit
+     *     encodable characters, length is calculated using a 16-bit encoding.
+     * @param subId Subscription to take SMS format.
+     * @return an int[6] with int[0] being the number of SMS's required, int[1] the number of code
+     *     units used, and int[2] is the number of code units remaining until the next message.
+     *     int[3] is an indicator of the encoding code unit size (see the ENCODING_* definitions in
+     *     SmsConstants). int[4] is the GSM national language table to use, or 0 for the default
+     *     7-bit alphabet. int[5] The GSM national language shift table to use, or 0 for the default
+     *     7-bit extension table.
+     * @hide
+     */
+    public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly, int subId) {
+        // this function is for MO SMS
+        TextEncodingDetails ted =
+                useCdmaFormatForMoSms(subId)
+                        ? com.android.internal.telephony.cdma.SmsMessage.calculateLength(
+                                msgBody, use7bitOnly, true)
+                        : com.android.internal.telephony.gsm.SmsMessage.calculateLength(
+                                msgBody, use7bitOnly);
+        int[] ret = new int[6];
+        ret[0] = ted.msgCount;
+        ret[1] = ted.codeUnitCount;
+        ret[2] = ted.codeUnitsRemaining;
+        ret[3] = ted.codeUnitSize;
+        ret[4] = ted.languageTable;
+        ret[5] = ted.languageShiftTable;
+        return ret;
+    }
+
+    /**
+     * Divide a message text into several fragments, none bigger than the maximum SMS message text
+     * size.
+     *
+     * @param text text, must not be null.
+     * @return an <code>ArrayList</code> of strings that, in order, comprise the original msg text.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static ArrayList<String> fragmentText(String text) {
+        return fragmentText(text, SmsManager.getDefaultSmsSubscriptionId());
+    }
+
+    /**
+     * Divide a message text into several fragments, none bigger than the maximum SMS message text
+     * size.
+     *
+     * @param text text, must not be null.
+     * @param subId Subscription to take SMS format.
+     * @return an <code>ArrayList</code> of strings that, in order, comprise the original msg text.
+     * @hide
+     */
+    public static ArrayList<String> fragmentText(String text, int subId) {
+        // This function is for MO SMS
+        final boolean isCdma = useCdmaFormatForMoSms(subId);
+
+        TextEncodingDetails ted =
+                isCdma
+                        ? com.android.internal.telephony.cdma.SmsMessage.calculateLength(
+                                text, false, true)
+                        : com.android.internal.telephony.gsm.SmsMessage.calculateLength(
+                                text, false);
+
+        // TODO(cleanup): The code here could be rolled into the logic
+        // below cleanly if these MAX_* constants were defined more
+        // flexibly...
+
+        int limit;
+        if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
+            int udhLength;
+            if (ted.languageTable != 0 && ted.languageShiftTable != 0) {
+                udhLength = GsmAlphabet.UDH_SEPTET_COST_TWO_SHIFT_TABLES;
+            } else if (ted.languageTable != 0 || ted.languageShiftTable != 0) {
+                udhLength = GsmAlphabet.UDH_SEPTET_COST_ONE_SHIFT_TABLE;
+            } else {
+                udhLength = 0;
+            }
+
+            if (ted.msgCount > 1) {
+                udhLength += GsmAlphabet.UDH_SEPTET_COST_CONCATENATED_MESSAGE;
+            }
+
+            if (udhLength != 0) {
+                udhLength += GsmAlphabet.UDH_SEPTET_COST_LENGTH;
+            }
+
+            limit = SmsConstants.MAX_USER_DATA_SEPTETS - udhLength;
+        } else {
+            if (ted.msgCount > 1) {
+                limit = SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
+                // If EMS is not supported, break down EMS into single segment SMS
+                // and add page info " x/y".
+                // In the case of UCS2 encoding, we need 8 bytes for this,
+                // but we only have 6 bytes from UDH, so truncate the limit for
+                // each segment by 2 bytes (1 char).
+                // Make sure total number of segments is less than 10.
+                if (!hasEmsSupport() && ted.msgCount < 10) {
+                    limit -= 2;
+                }
+            } else {
+                limit = SmsConstants.MAX_USER_DATA_BYTES;
+            }
+        }
+
+        String newMsgBody = null;
+        Resources r = Resources.getSystem();
+        if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) {
+            // 7-bit ASCII table based translation is required only for CDMA single-part SMS since
+            // ENCODING_7BIT_ASCII is used for CDMA single-part SMS and ENCODING_GSM_7BIT_ALPHABET
+            // is used for CDMA multi-part SMS.
+            newMsgBody = Sms7BitEncodingTranslator.translate(text, isCdma && ted.msgCount == 1);
+        }
+        if (TextUtils.isEmpty(newMsgBody)) {
+            newMsgBody = text;
+        }
+
+        int pos = 0;  // Index in code units.
+        int textLen = newMsgBody.length();
+        ArrayList<String> result = new ArrayList<String>(ted.msgCount);
+        while (pos < textLen) {
+            int nextPos = 0;  // Counts code units.
+            if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
+                if (isCdma && ted.msgCount == 1) {
+                    // For a singleton CDMA message, the encoding must be ASCII...
+                    nextPos = pos + Math.min(limit, textLen - pos);
+                } else {
+                    // For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
+                    nextPos = GsmAlphabet.findGsmSeptetLimitIndex(newMsgBody, pos, limit,
+                            ted.languageTable, ted.languageShiftTable);
+                }
+            } else {  // Assume unicode.
+                nextPos = SmsMessageBase.findNextUnicodePosition(pos, limit, newMsgBody);
+            }
+            if ((nextPos <= pos) || (nextPos > textLen)) {
+                Rlog.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
+                          nextPos + " >= " + textLen + ")");
+                break;
+            }
+            result.add(newMsgBody.substring(pos, nextPos));
+            pos = nextPos;
+        }
+        return result;
+    }
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and the number of
+     * characters remaining until the next message, given the current encoding.
+     *
+     * @param messageBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the radio specific (GSM / CDMA)
+     *     alphabet encoding are converted to as a single space characters. If false, a messageBody
+     *     containing non-GSM or non-CDMA alphabet characters are encoded using 16-bit encoding.
+     * @return an int[4] with int[0] being the number of SMS's required, int[1] the number of code
+     *     units used, and int[2] is the number of code units remaining until the next message.
+     *     int[3] is the encoding type that should be used for the message.
+     */
+    public static int[] calculateLength(String messageBody, boolean use7bitOnly) {
+        return calculateLength((CharSequence)messageBody, use7bitOnly);
+    }
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and the number of
+     * characters remaining until the next message, given the current encoding.
+     *
+     * @param messageBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the radio specific (GSM / CDMA)
+     *     alphabet encoding are converted to as a single space characters. If false, a messageBody
+     *     containing non-GSM or non-CDMA alphabet characters are encoded using 16-bit encoding.
+     * @param subId Subscription to take SMS format.
+     * @return an int[4] with int[0] being the number of SMS's required, int[1] the number of code
+     *     units used, and int[2] is the number of code units remaining until the next message.
+     *     int[3] is the encoding type that should be used for the message.
+     * @hide
+     */
+    public static int[] calculateLength(String messageBody, boolean use7bitOnly, int subId) {
+        return calculateLength((CharSequence) messageBody, use7bitOnly, subId);
+    }
+
+    /*
+     * TODO(cleanup): It looks like there is now no useful reason why
+     * apps should generate pdus themselves using these routines,
+     * instead of handing the raw data to SMSDispatcher (and thereby
+     * have the phone process do the encoding).  Moreover, CDMA now
+     * has shared state (in the form of the msgId system property)
+     * which can only be modified by the phone process, and hence
+     * makes the output of these routines incorrect.  Since they now
+     * serve no purpose, they should probably just return null
+     * directly, and be deprecated.  Going further in that direction,
+     * the above parsers of serialized pdu data should probably also
+     * be gotten rid of, hiding all but the necessarily visible
+     * structured data from client apps.  A possible concern with
+     * doing this is that apps may be using these routines to generate
+     * pdus that are then sent elsewhere, some network server, for
+     * example, and that always returning null would thereby break
+     * otherwise useful apps.
+     */
+
+    /**
+     * Gets an SMS-SUBMIT PDU for a destination address and a message.
+     * This method will not attempt to use any GSM national language 7 bit encodings.
+     *
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param message string representation of the message payload.
+     * @param statusReportRequested indicates whether a report is requested for this message.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
+     */
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, String message, boolean statusReportRequested) {
+        return getSubmitPdu(
+                scAddress,
+                destinationAddress,
+                message,
+                statusReportRequested,
+                SmsManager.getDefaultSmsSubscriptionId());
+    }
+
+    /**
+     * Gets an SMS-SUBMIT PDU for a destination address and a message.
+     * This method will not attempt to use any GSM national language 7 bit encodings.
+     *
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param message string representation of the message payload.
+     * @param statusReportRequested indicates whether a report is requested for this message.
+     * @param subId subscription of the message.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
+     * @hide
+     */
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, String message, boolean statusReportRequested, int subId) {
+        SubmitPduBase spb;
+        if (useCdmaFormatForMoSms(subId)) {
+            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested, null);
+        } else {
+            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested);
+        }
+
+        return spb != null ? new SubmitPdu(spb) : null;
+    }
+
+    /**
+     * Gets an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
+     * This method will not attempt to use any GSM national language 7 bit encodings.
+     *
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param destinationPort the port to deliver the message to at the destination.
+     * @param data the data for the message.
+     * @param statusReportRequested indicates whether a report is requested for this message.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
+     */
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, short destinationPort, byte[] data,
+            boolean statusReportRequested) {
+        SubmitPduBase spb;
+
+        if (useCdmaFormatForMoSms()) {
+            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, destinationPort, data, statusReportRequested);
+        } else {
+            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, destinationPort, data, statusReportRequested);
+        }
+
+        return spb != null ? new SubmitPdu(spb) : null;
+    }
+
+    // TODO: SubmitPdu class is used for SMS-DELIVER also now. Refactor for SubmitPdu and new
+    // DeliverPdu accordingly.
+
+    /**
+     * Gets an SMS PDU to store in the ICC.
+     *
+     * @param subId subscription of the message.
+     * @param status message status. One of these status:
+     *               <code>SmsManager.STATUS_ON_ICC_READ</code>
+     *               <code>SmsManager.STATUS_ON_ICC_UNREAD</code>
+     *               <code>SmsManager.STATUS_ON_ICC_SENT</code>
+     *               <code>SmsManager.STATUS_ON_ICC_UNSENT</code>
+     * @param scAddress Service Centre address. Null means use default.
+     * @param address destination or originating address.
+     * @param message string representation of the message payload.
+     * @param date the time stamp the message was received.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public static SubmitPdu getSmsPdu(int subId, @SmsManager.StatusOnIcc int status,
+            @Nullable String scAddress, @NonNull String address, @NonNull String message,
+            long date) {
+        SubmitPduBase spb;
+        if (isCdmaVoice(subId)) { // 3GPP2 format
+            if (status == SmsManager.STATUS_ON_ICC_READ
+                    || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU
+                spb = com.android.internal.telephony.cdma.SmsMessage.getDeliverPdu(address,
+                        message, date);
+            } else { // Submit PDU
+                spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                        address, message, false /* statusReportRequested */, null /* smsHeader */);
+            }
+        } else { // 3GPP format
+            if (status == SmsManager.STATUS_ON_ICC_READ
+                    || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU
+                spb = com.android.internal.telephony.gsm.SmsMessage.getDeliverPdu(scAddress,
+                        address, message, date);
+            } else { // Submit PDU
+                spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                        address, message, false /* statusReportRequested */, null /* header */);
+            }
+        }
+
+        return spb != null ? new SubmitPdu(spb) : null;
+    }
+
+    /**
+     * Get an SMS-SUBMIT PDU's encoded message.
+     * This is used by Bluetooth MAP profile to handle long non UTF-8 SMS messages.
+     *
+     * @param isTypeGsm true when message's type is GSM, false when type is CDMA
+     * @param destinationAddress the address of the destination for the message
+     * @param message message content
+     * @param encoding User data text encoding code unit size
+     * @param languageTable GSM national language table to use, specified by 3GPP
+     *                      23.040 9.2.3.24.16
+     * @param languageShiftTable GSM national language shift table to use, specified by 3GPP
+     *                           23.040 9.2.3.24.15
+     * @param refNumber reference number of concatenated SMS, specified by 3GPP 23.040 9.2.3.24.1
+     * @param seqNumber sequence number of concatenated SMS, specified by 3GPP 23.040 9.2.3.24.1
+     * @param msgCount count of messages of concatenated SMS, specified by 3GPP 23.040 9.2.3.24.2
+     * @return a byte[] containing the encoded message
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    @SystemApi
+    @NonNull
+    public static byte[] getSubmitPduEncodedMessage(boolean isTypeGsm,
+                                                    @NonNull String destinationAddress,
+                                                    @NonNull String message,
+                                                    @EncodingSize int encoding,
+                                                    @IntRange(from = 0) int languageTable,
+                                                    @IntRange(from = 0) int languageShiftTable,
+                                                    @IntRange(from = 0, to = 255) int refNumber,
+                                                    @IntRange(from = 1, to = 255) int seqNumber,
+                                                    @IntRange(from = 1, to = 255) int msgCount) {
+        byte[] data;
+        SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
+        concatRef.refNumber = refNumber;
+        concatRef.seqNumber = seqNumber;  // 1-based sequence
+        concatRef.msgCount = msgCount;
+        // We currently set this to true since our messaging app will never
+        // send more than 255 parts (it converts the message to MMS well before that).
+        // However, we should support 3rd party messaging apps that might need 16-bit
+        // references
+        // Note:  It's not sufficient to just flip this bit to true; it will have
+        // ripple effects (several calculations assume 8-bit ref).
+        concatRef.isEightBits = true;
+        SmsHeader smsHeader = new SmsHeader();
+        smsHeader.concatRef = concatRef;
+
+        /* Depending on the type, call either GSM or CDMA getSubmitPdu(). The encoding
+         * will be determined(again) by getSubmitPdu().
+         * All packets need to be encoded using the same encoding, as the bMessage
+         * only have one filed to describe the encoding for all messages in a concatenated
+         * SMS... */
+        if (encoding == ENCODING_7BIT) {
+            smsHeader.languageTable = languageTable;
+            smsHeader.languageShiftTable = languageShiftTable;
+        }
+
+        if (isTypeGsm) {
+            data = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(null,
+                    destinationAddress, message, false,
+                    SmsHeader.toByteArray(smsHeader), encoding, languageTable,
+                    languageShiftTable).encodedMessage;
+        } else { // SMS_TYPE_CDMA
+            UserData uData = new UserData();
+            uData.payloadStr = message;
+            uData.userDataHeader = smsHeader;
+            if (encoding == ENCODING_7BIT) {
+                uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
+            } else { // assume UTF-16
+                uData.msgEncoding = UserData.ENCODING_UNICODE_16;
+            }
+            uData.msgEncodingSet = true;
+            data = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
+                    destinationAddress, uData, false).encodedMessage;
+        }
+        if (data == null) {
+            return new byte[0];
+        }
+        return data;
+    }
+
+    /**
+     * Returns the address of the SMS service center that relayed this message
+     * or null if there is none.
+     */
+    public String getServiceCenterAddress() {
+        return mWrappedSmsMessage.getServiceCenterAddress();
+    }
+
+    /**
+     * Returns the originating address (sender) of this SMS message in String
+     * form or null if unavailable.
+     *
+     * <p>If the address is a GSM-formatted address, it will be in a format specified by 3GPP
+     * 23.040 Sec 9.1.2.5. If it is a CDMA address, it will be a format specified by 3GPP2
+     * C.S005-D Table 2.7.1.3.2.4-2. The choice of format is carrier-specific, so callers of the
+     * should be careful to avoid assumptions about the returned content.
+     *
+     * @return a String representation of the address; null if unavailable.
+     */
+    @Nullable
+    public String getOriginatingAddress() {
+        return mWrappedSmsMessage.getOriginatingAddress();
+    }
+
+    /**
+     * Returns the originating address, or email from address if this message
+     * was from an email gateway. Returns null if originating address
+     * unavailable.
+     */
+    public String getDisplayOriginatingAddress() {
+        return mWrappedSmsMessage.getDisplayOriginatingAddress();
+    }
+
+    /**
+     * Returns the message body as a String, if it exists and is text based.
+     * @return message body if there is one, otherwise null
+     */
+    public String getMessageBody() {
+        return mWrappedSmsMessage.getMessageBody();
+    }
+
+    /**
+     * Returns the class of this message.
+     */
+    public MessageClass getMessageClass() {
+        switch(mWrappedSmsMessage.getMessageClass()) {
+            case CLASS_0: return MessageClass.CLASS_0;
+            case CLASS_1: return MessageClass.CLASS_1;
+            case CLASS_2: return MessageClass.CLASS_2;
+            case CLASS_3: return MessageClass.CLASS_3;
+            default: return MessageClass.UNKNOWN;
+
+        }
+    }
+
+    /**
+     * Returns the message body, or email message body if this message was from
+     * an email gateway. Returns null if message body unavailable.
+     */
+    public String getDisplayMessageBody() {
+        return mWrappedSmsMessage.getDisplayMessageBody();
+    }
+
+    /**
+     * Unofficial convention of a subject line enclosed in parens empty string
+     * if not present
+     */
+    public String getPseudoSubject() {
+        return mWrappedSmsMessage.getPseudoSubject();
+    }
+
+    /**
+     * Returns the service centre timestamp in currentTimeMillis() format
+     */
+    public long getTimestampMillis() {
+        return mWrappedSmsMessage.getTimestampMillis();
+    }
+
+    /**
+     * Returns true if message is an email.
+     *
+     * @return true if this message came through an email gateway and email
+     *         sender / subject / parsed body are available
+     */
+    public boolean isEmail() {
+        return mWrappedSmsMessage.isEmail();
+    }
+
+     /**
+     * @return if isEmail() is true, body of the email sent through the gateway.
+     *         null otherwise
+     */
+    public String getEmailBody() {
+        return mWrappedSmsMessage.getEmailBody();
+    }
+
+    /**
+     * @return if isEmail() is true, email from address of email sent through
+     *         the gateway. null otherwise
+     */
+    public String getEmailFrom() {
+        return mWrappedSmsMessage.getEmailFrom();
+    }
+
+    /**
+     * Get protocol identifier.
+     */
+    public int getProtocolIdentifier() {
+        return mWrappedSmsMessage.getProtocolIdentifier();
+    }
+
+    /**
+     * See TS 23.040 9.2.3.9 returns true if this is a "replace short message"
+     * SMS
+     */
+    public boolean isReplace() {
+        return mWrappedSmsMessage.isReplace();
+    }
+
+    /**
+     * Returns true for CPHS MWI toggle message.
+     *
+     * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section
+     *         B.4.2
+     */
+    public boolean isCphsMwiMessage() {
+        return mWrappedSmsMessage.isCphsMwiMessage();
+    }
+
+    /**
+     * returns true if this message is a CPHS voicemail / message waiting
+     * indicator (MWI) clear message
+     */
+    public boolean isMWIClearMessage() {
+        return mWrappedSmsMessage.isMWIClearMessage();
+    }
+
+    /**
+     * returns true if this message is a CPHS voicemail / message waiting
+     * indicator (MWI) set message
+     */
+    public boolean isMWISetMessage() {
+        return mWrappedSmsMessage.isMWISetMessage();
+    }
+
+    /**
+     * returns true if this message is a "Message Waiting Indication Group:
+     * Discard Message" notification and should not be stored.
+     */
+    public boolean isMwiDontStore() {
+        return mWrappedSmsMessage.isMwiDontStore();
+    }
+
+    /**
+     * returns the user data section minus the user data header if one was
+     * present.
+     */
+    public byte[] getUserData() {
+        return mWrappedSmsMessage.getUserData();
+    }
+
+    /**
+     * Returns the raw PDU for the message.
+     *
+     * @return the raw PDU for the message.
+     */
+    public byte[] getPdu() {
+        return mWrappedSmsMessage.getPdu();
+    }
+
+    /**
+     * Returns the status of the message on the SIM (read, unread, sent, unsent).
+     *
+     * @return the status of the message on the SIM.  These are:
+     *         SmsManager.STATUS_ON_SIM_FREE
+     *         SmsManager.STATUS_ON_SIM_READ
+     *         SmsManager.STATUS_ON_SIM_UNREAD
+     *         SmsManager.STATUS_ON_SIM_SEND
+     *         SmsManager.STATUS_ON_SIM_UNSENT
+     * @deprecated Use getStatusOnIcc instead.
+     */
+    @Deprecated public int getStatusOnSim() {
+        return mWrappedSmsMessage.getStatusOnIcc();
+    }
+
+    /**
+     * Returns the status of the message on the ICC (read, unread, sent, unsent).
+     *
+     * @return the status of the message on the ICC.  These are:
+     *         SmsManager.STATUS_ON_ICC_FREE
+     *         SmsManager.STATUS_ON_ICC_READ
+     *         SmsManager.STATUS_ON_ICC_UNREAD
+     *         SmsManager.STATUS_ON_ICC_SEND
+     *         SmsManager.STATUS_ON_ICC_UNSENT
+     */
+    public int getStatusOnIcc() {
+        return mWrappedSmsMessage.getStatusOnIcc();
+    }
+
+    /**
+     * Returns the record index of the message on the SIM (1-based index).
+     * @return the record index of the message on the SIM, or -1 if this
+     *         SmsMessage was not created from a SIM SMS EF record.
+     * @deprecated Use getIndexOnIcc instead.
+     */
+    @Deprecated public int getIndexOnSim() {
+        return mWrappedSmsMessage.getIndexOnIcc();
+    }
+
+    /**
+     * Returns the record index of the message on the ICC (1-based index).
+     * @return the record index of the message on the ICC, or -1 if this
+     *         SmsMessage was not created from a ICC SMS EF record.
+     */
+    public int getIndexOnIcc() {
+        return mWrappedSmsMessage.getIndexOnIcc();
+    }
+
+    /**
+     * GSM: For an SMS-STATUS-REPORT message, this returns the status field from the status report.
+     * This field indicates the status of a previously submitted SMS, if requested.
+     * See TS 23.040, 9.2.3.15 TP-Status for a description of values.
+     * CDMA: For not interfering with status codes from GSM, the value is shifted to the bits 31-16.
+     * The value is composed of an error class (bits 25-24) and a status code (bits 23-16). Possible
+     * codes are described in C.S0015-B, v2.0, 4.5.21.
+     *
+     * @return 0 for GSM or 2 shifted left by 16 for CDMA indicates the previously sent message was
+     *         received. See TS 23.040, 9.2.3.15 and C.S0015-B, v2.0, 4.5.21 for a description of
+     *         other possible values.
+     */
+    public int getStatus() {
+        return mWrappedSmsMessage.getStatus();
+    }
+
+    /**
+     * Return true iff the message is a SMS-STATUS-REPORT message.
+     */
+    public boolean isStatusReportMessage() {
+        return mWrappedSmsMessage.isStatusReportMessage();
+    }
+
+    /**
+     * Returns true iff the <code>TP-Reply-Path</code> bit is set in
+     * this message.
+     */
+    public boolean isReplyPathPresent() {
+        return mWrappedSmsMessage.isReplyPathPresent();
+    }
+
+    /**
+     * Return the encoding type of a received SMS message, which is specified using ENCODING_*
+     * GSM: defined in android.telephony.SmsConstants
+     * CDMA: defined in android.telephony.cdma.UserData
+     *
+     * @hide
+     */
+    public int getReceivedEncodingType() {
+        return mWrappedSmsMessage.getReceivedEncodingType();
+    }
+
+    /**
+     * Check if format of the message is 3GPP.
+     *
+     * @hide
+     */
+    public boolean is3gpp() {
+        return (mWrappedSmsMessage instanceof com.android.internal.telephony.gsm.SmsMessage);
+    }
+
+    /**
+     * Determines whether or not to use CDMA format for MO SMS.
+     * If SMS over IMS is supported, then format is based on IMS SMS format,
+     * otherwise format is based on current phone type.
+     *
+     * @return true if Cdma format should be used for MO SMS, false otherwise.
+     */
+    @UnsupportedAppUsage
+    private static boolean useCdmaFormatForMoSms() {
+        // IMS is registered with SMS support, check the SMS format supported
+        return useCdmaFormatForMoSms(SmsManager.getDefaultSmsSubscriptionId());
+    }
+
+    /**
+     * Determines whether or not to use CDMA format for MO SMS.
+     * If SMS over IMS is supported, then format is based on IMS SMS format,
+     * otherwise format is based on current phone type.
+     *
+     * @param subId Subscription for which phone type is returned.
+     *
+     * @return true if Cdma format should be used for MO SMS, false otherwise.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private static boolean useCdmaFormatForMoSms(int subId) {
+        SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
+        if (!smsManager.isImsSmsSupported()) {
+            // use Voice technology to determine SMS format.
+            return isCdmaVoice(subId);
+        }
+        // IMS is registered with SMS support, check the SMS format supported
+        return (SmsConstants.FORMAT_3GPP2.equals(smsManager.getImsSmsFormat()));
+    }
+
+    /**
+     * Determines whether or not to current phone type is cdma.
+     *
+     * @return true if current phone type is cdma, false otherwise.
+     */
+    private static boolean isCdmaVoice() {
+        return isCdmaVoice(SmsManager.getDefaultSmsSubscriptionId());
+    }
+
+     /**
+      * Determines whether or not to current phone type is cdma
+      *
+      * @return true if current phone type is cdma, false otherwise.
+      */
+     private static boolean isCdmaVoice(int subId) {
+         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(subId);
+         return (PHONE_TYPE_CDMA == activePhone);
+   }
+
+    /**
+     * Decide if the carrier supports long SMS.
+     * {@hide}
+     */
+    public static boolean hasEmsSupport() {
+        if (!isNoEmsSupportConfigListExisted()) {
+            return true;
+        }
+
+        String simOperator;
+        String gid;
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            simOperator = TelephonyManager.getDefault().getSimOperatorNumeric();
+            gid = TelephonyManager.getDefault().getGroupIdLevel1();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+
+        if (!TextUtils.isEmpty(simOperator)) {
+            for (NoEmsSupportConfig currentConfig : mNoEmsSupportConfigList) {
+                if (currentConfig == null) {
+                    Rlog.w("SmsMessage", "hasEmsSupport currentConfig is null");
+                    continue;
+                }
+
+                if (simOperator.startsWith(currentConfig.mOperatorNumber) &&
+                        (TextUtils.isEmpty(currentConfig.mGid1) ||
+                                (!TextUtils.isEmpty(currentConfig.mGid1) &&
+                                        currentConfig.mGid1.equalsIgnoreCase(gid)))) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Check where to add " x/y" in each SMS segment, begin or end.
+     * {@hide}
+     */
+    public static boolean shouldAppendPageNumberAsPrefix() {
+        if (!isNoEmsSupportConfigListExisted()) {
+            return false;
+        }
+
+        String simOperator;
+        String gid;
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            simOperator = TelephonyManager.getDefault().getSimOperatorNumeric();
+            gid = TelephonyManager.getDefault().getGroupIdLevel1();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+
+        for (NoEmsSupportConfig currentConfig : mNoEmsSupportConfigList) {
+            if (simOperator.startsWith(currentConfig.mOperatorNumber) &&
+                (TextUtils.isEmpty(currentConfig.mGid1) ||
+                (!TextUtils.isEmpty(currentConfig.mGid1)
+                && currentConfig.mGid1.equalsIgnoreCase(gid)))) {
+                return currentConfig.mIsPrefix;
+            }
+        }
+        return false;
+    }
+
+    private static class NoEmsSupportConfig {
+        String mOperatorNumber;
+        String mGid1;
+        boolean mIsPrefix;
+
+        public NoEmsSupportConfig(String[] config) {
+            mOperatorNumber = config[0];
+            mIsPrefix = "prefix".equals(config[1]);
+            mGid1 = config.length > 2 ? config[2] : null;
+        }
+
+        @Override
+        public String toString() {
+            return "NoEmsSupportConfig { mOperatorNumber = " + mOperatorNumber
+                    + ", mIsPrefix = " + mIsPrefix + ", mGid1 = " + mGid1 + " }";
+        }
+    }
+
+    private static NoEmsSupportConfig[] mNoEmsSupportConfigList = null;
+    private static boolean mIsNoEmsSupportConfigListLoaded = false;
+
+    private static boolean isNoEmsSupportConfigListExisted() {
+        synchronized (SmsMessage.class) {
+            if (!mIsNoEmsSupportConfigListLoaded) {
+                Resources r = Resources.getSystem();
+                if (r != null) {
+                    String[] listArray = r.getStringArray(
+                            com.android.internal.R.array.no_ems_support_sim_operators);
+                    if ((listArray != null) && (listArray.length > 0)) {
+                        mNoEmsSupportConfigList = new NoEmsSupportConfig[listArray.length];
+                        for (int i = 0; i < listArray.length; i++) {
+                            mNoEmsSupportConfigList[i] = new NoEmsSupportConfig(
+                                    listArray[i].split(";"));
+                        }
+                    }
+                    mIsNoEmsSupportConfigListLoaded = true;
+                }
+            }
+        }
+
+        if (mNoEmsSupportConfigList != null && mNoEmsSupportConfigList.length != 0) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the recipient address(receiver) of this SMS message in String form or null if
+     * unavailable.
+     * {@hide}
+     */
+    @Nullable
+    public String getRecipientAddress() {
+        return mWrappedSmsMessage.getRecipientAddress();
+    }
+}
diff --git a/android-35/android/telephony/SubscriptionInfo.java b/android-35/android/telephony/SubscriptionInfo.java
new file mode 100644
index 0000000..58488d1
--- /dev/null
+++ b/android-35/android/telephony/SubscriptionInfo.java
@@ -0,0 +1,1839 @@
+/*
+ * Copyright (C) 2014 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;
+
+import static android.text.TextUtils.formatSimple;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.os.Parcel;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+import android.telephony.SubscriptionManager.ProfileClass;
+import android.telephony.SubscriptionManager.SimDisplayNameSource;
+import android.telephony.SubscriptionManager.SubscriptionType;
+import android.telephony.SubscriptionManager.TransferStatus;
+import android.telephony.SubscriptionManager.UsageSetting;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.telephony.Rlog;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A Parcelable class for Subscription Information.
+ */
+public class SubscriptionInfo implements Parcelable {
+    /**
+     * Size of text to render on the icon.
+     */
+    private static final int TEXT_SIZE = 16;
+
+    /**
+     * Subscription Identifier, this is a device unique number
+     * and not an index into an array
+     */
+    private final int mId;
+
+    /**
+     * The ICCID of the SIM that is associated with this subscription, empty if unknown.
+     */
+    @NonNull
+    private final String mIccId;
+
+    /**
+     * The index of the SIM slot that currently contains the subscription and not necessarily unique
+     * and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or the subscription
+     * is inactive.
+     */
+    private final int mSimSlotIndex;
+
+    /**
+     * The name displayed to the user that identifies this subscription. This name is used
+     * in Settings page and can be renamed by the user.
+     */
+    @NonNull
+    private final CharSequence mDisplayName;
+
+    /**
+     * The name displayed to the user that identifies subscription provider name. This name is the
+     * SPN displayed in status bar and many other places. Can't be renamed by the user.
+     */
+    @NonNull
+    private final CharSequence mCarrierName;
+
+    /**
+     * The source of the {@link #mDisplayName}.
+     */
+    @SimDisplayNameSource
+    private final int mDisplayNameSource;
+
+    /**
+     * The color to be used for tinting the icon when displaying to the user.
+     */
+    private final int mIconTint;
+
+    /**
+     * The number presented to the user identify this subscription.
+     */
+    @NonNull
+    private final String mNumber;
+
+    /**
+     * Whether user enables data roaming for this subscription or not. Either
+     * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+     * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
+     */
+    private final int mDataRoaming;
+
+    /**
+     * Mobile Country Code.
+     */
+    @Nullable
+    private final String mMcc;
+
+    /**
+     * Mobile Network Code.
+     */
+    @Nullable
+    private final String mMnc;
+
+    /**
+     * EHPLMNs associated with the subscription.
+     */
+    @NonNull
+    private final String[] mEhplmns;
+
+    /**
+     * HPLMNs associated with the subscription.
+     */
+    @NonNull
+    private final String[] mHplmns;
+
+    /**
+     * Whether the subscription is from eSIM.
+     */
+    private final boolean mIsEmbedded;
+
+    /**
+     * The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the
+     * EID for an eUICC card.
+     */
+    @NonNull
+    private final String mCardString;
+
+    /**
+     * The access rules for this subscription, if it is embedded and defines any. This does not
+     * include access rules for non-embedded subscriptions.
+     */
+    @Nullable
+    private final UiccAccessRule[] mNativeAccessRules;
+
+    /**
+     * The carrier certificates for this subscription that are saved in carrier configs.
+     * This does not include access rules from the Uicc, whether embedded or non-embedded.
+     */
+    @Nullable
+    private final UiccAccessRule[] mCarrierConfigAccessRules;
+
+    /**
+     * Whether the subscription is opportunistic.
+     */
+    private final boolean mIsOpportunistic;
+
+    /**
+     * A UUID assigned to the subscription group. {@code null} if not assigned.
+     *
+     * @see SubscriptionManager#createSubscriptionGroup(List)
+     */
+    @Nullable
+    private final ParcelUuid mGroupUuid;
+
+    /**
+     * ISO Country code for the subscription's provider.
+     */
+    @NonNull
+    private final String mCountryIso;
+
+    /**
+     * The subscription carrier id.
+     *
+     * @see TelephonyManager#getSimCarrierId()
+     */
+    private final int mCarrierId;
+
+    /**
+     * The profile class populated from the profile metadata if present. Otherwise,
+     * the profile class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no
+     * profile metadata or the subscription is not on an eUICC ({@link #isEmbedded} returns
+     * {@code false}).
+     */
+    @ProfileClass
+    private final int mProfileClass;
+
+    /**
+     * Type of the subscription.
+     */
+    @SubscriptionType
+    private final int mType;
+
+    /**
+     * A package name that specifies who created the group. Empty if not available.
+     */
+    @NonNull
+    private final String mGroupOwner;
+
+    /**
+     * Whether uicc applications are configured to enable or disable.
+     * By default it's true.
+     */
+    private final boolean mAreUiccApplicationsEnabled;
+
+    /**
+     * The port index of the Uicc card.
+     */
+    private final int mPortIndex;
+
+    /**
+     * Subscription's preferred usage setting.
+     */
+    @UsageSetting
+    private final int mUsageSetting;
+
+    /**
+     * Subscription's transfer status
+     */
+    private final int mTransferStatus;
+
+    // Below are the fields that do not exist in the database.
+
+    /**
+     * SIM icon bitmap cache.
+     */
+    @Nullable
+    private Bitmap mIconBitmap;
+
+    /**
+     * The card ID of the SIM card. This maps uniquely to {@link #mCardString}.
+     */
+    private final int mCardId;
+
+    /**
+     * Whether group of the subscription is disabled. This is only useful if it's a grouped
+     * opportunistic subscription. In this case, if all primary (non-opportunistic) subscriptions
+     * in the group are deactivated (unplugged pSIM or deactivated eSIM profile), we should disable
+     * this opportunistic subscription.
+     */
+    private final boolean mIsGroupDisabled;
+
+    /**
+     * Whether this subscription is used for communicating with non-terrestrial networks.
+     */
+    private final boolean mIsOnlyNonTerrestrialNetwork;
+
+    /**
+     * The service capabilities (in the form of bitmask combination) the subscription supports.
+     */
+    private final int mServiceCapabilities;
+
+    /**
+     * @hide
+     *
+     * @deprecated Use {@link SubscriptionInfo.Builder}.
+     */
+    // TODO: Clean up after external usages moved to builder model.
+    @Deprecated
+    public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+            CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+            Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
+            @Nullable UiccAccessRule[] nativeAccessRules, String cardString) {
+        this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, -1,
+                false, null, false, TelephonyManager.UNKNOWN_CARRIER_ID,
+                SubscriptionManager.PROFILE_CLASS_UNSET,
+                SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null, true);
+    }
+
+    /**
+     * @hide
+     *
+     * @deprecated Use {@link SubscriptionInfo.Builder}.
+     */
+    // TODO: Clean up after external usages moved to builder model.
+    @Deprecated
+    public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+            CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+            Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
+            @Nullable UiccAccessRule[] nativeAccessRules, String cardString,
+            boolean isOpportunistic, @Nullable String groupUUID, int carrierId, int profileClass) {
+        this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, -1,
+                isOpportunistic, groupUUID, false, carrierId, profileClass,
+                SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null, true);
+    }
+
+    /**
+     * @hide
+     *
+     * @deprecated Use {@link SubscriptionInfo.Builder}.
+     */
+    // TODO: Clean up after external usages moved to builder model.
+    @Deprecated
+    public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+            CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+            Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
+            @Nullable UiccAccessRule[] nativeAccessRules, String cardString, int cardId,
+            boolean isOpportunistic, @Nullable String groupUUID, boolean isGroupDisabled,
+            int carrierId, int profileClass, int subType, @Nullable String groupOwner,
+            @Nullable UiccAccessRule[] carrierConfigAccessRules,
+            boolean areUiccApplicationsEnabled) {
+        this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString,
+                cardId, isOpportunistic, groupUUID, isGroupDisabled, carrierId, profileClass,
+                subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, 0);
+    }
+
+    /**
+     * @hide
+     *
+     * @deprecated Use {@link SubscriptionInfo.Builder}.
+     */
+    // TODO: Clean up after external usages moved to builder model.
+    @Deprecated
+    public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+            CharSequence carrierName, int displayNameSource, int iconTint, String number,
+            int roaming, Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
+            @Nullable UiccAccessRule[] nativeAccessRules, String cardString, int cardId,
+            boolean isOpportunistic, @Nullable String groupUUID, boolean isGroupDisabled,
+            int carrierId, int profileClass, int subType, @Nullable String groupOwner,
+            @Nullable UiccAccessRule[] carrierConfigAccessRules,
+            boolean areUiccApplicationsEnabled, int portIndex) {
+        this(id, iccId, simSlotIndex, displayName, carrierName, displayNameSource, iconTint, number,
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString,
+                cardId, isOpportunistic, groupUUID, isGroupDisabled, carrierId, profileClass,
+                subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled,
+                portIndex, SubscriptionManager.USAGE_SETTING_DEFAULT);
+    }
+
+    /**
+     * @hide
+     *
+     * @deprecated Use {@link SubscriptionInfo.Builder}.
+     */
+    // TODO: Clean up after external usages moved to builder model.
+    @Deprecated
+    public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+            CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+            Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
+            @Nullable UiccAccessRule[] nativeAccessRules, String cardString, int cardId,
+            boolean isOpportunistic, @Nullable String groupUuid, boolean isGroupDisabled,
+            int carrierId, int profileClass, int subType, @Nullable String groupOwner,
+            @Nullable UiccAccessRule[] carrierConfigAccessRules,
+            boolean areUiccApplicationsEnabled, int portIndex, @UsageSetting int usageSetting) {
+        this.mId = id;
+        this.mIccId = iccId;
+        this.mSimSlotIndex = simSlotIndex;
+        this.mDisplayName =  displayName;
+        this.mCarrierName = carrierName;
+        this.mDisplayNameSource = nameSource;
+        this.mIconTint = iconTint;
+        this.mNumber = number;
+        this.mDataRoaming = roaming;
+        this.mIconBitmap = icon;
+        this.mMcc = TextUtils.emptyIfNull(mcc);
+        this.mMnc = TextUtils.emptyIfNull(mnc);
+        this.mHplmns = null;
+        this.mEhplmns = null;
+        this.mCountryIso = TextUtils.emptyIfNull(countryIso);
+        this.mIsEmbedded = isEmbedded;
+        this.mNativeAccessRules = nativeAccessRules;
+        this.mCardString = TextUtils.emptyIfNull(cardString);
+        this.mCardId = cardId;
+        this.mIsOpportunistic = isOpportunistic;
+        this.mGroupUuid = groupUuid == null ? null : ParcelUuid.fromString(groupUuid);
+        this.mIsGroupDisabled = isGroupDisabled;
+        this.mCarrierId = carrierId;
+        this.mProfileClass = profileClass;
+        this.mType = subType;
+        this.mGroupOwner = TextUtils.emptyIfNull(groupOwner);
+        this.mCarrierConfigAccessRules = carrierConfigAccessRules;
+        this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
+        this.mPortIndex = portIndex;
+        this.mUsageSetting = usageSetting;
+        this.mIsOnlyNonTerrestrialNetwork = false;
+        this.mServiceCapabilities = 0;
+        this.mTransferStatus = 0;
+    }
+
+    /**
+     * Constructor from builder.
+     *
+     * @param builder Builder of {@link SubscriptionInfo}.
+     */
+    private SubscriptionInfo(@NonNull Builder builder) {
+        this.mId = builder.mId;
+        this.mIccId = builder.mIccId;
+        this.mSimSlotIndex = builder.mSimSlotIndex;
+        this.mDisplayName = builder.mDisplayName;
+        this.mCarrierName = builder.mCarrierName;
+        this.mDisplayNameSource = builder.mDisplayNameSource;
+        this.mIconTint = builder.mIconTint;
+        this.mNumber = builder.mNumber;
+        this.mDataRoaming = builder.mDataRoaming;
+        this.mIconBitmap = builder.mIconBitmap;
+        this.mMcc = builder.mMcc;
+        this.mMnc = builder.mMnc;
+        this.mEhplmns = builder.mEhplmns;
+        this.mHplmns = builder.mHplmns;
+        this.mCountryIso = builder.mCountryIso;
+        this.mIsEmbedded = builder.mIsEmbedded;
+        this.mNativeAccessRules = builder.mNativeAccessRules;
+        this.mCardString = builder.mCardString;
+        this.mCardId = builder.mCardId;
+        this.mIsOpportunistic = builder.mIsOpportunistic;
+        this.mGroupUuid = builder.mGroupUuid;
+        this.mIsGroupDisabled = builder.mIsGroupDisabled;
+        this.mCarrierId = builder.mCarrierId;
+        this.mProfileClass = builder.mProfileClass;
+        this.mType = builder.mType;
+        this.mGroupOwner = builder.mGroupOwner;
+        this.mCarrierConfigAccessRules = builder.mCarrierConfigAccessRules;
+        this.mAreUiccApplicationsEnabled = builder.mAreUiccApplicationsEnabled;
+        this.mPortIndex = builder.mPortIndex;
+        this.mUsageSetting = builder.mUsageSetting;
+        this.mIsOnlyNonTerrestrialNetwork = builder.mIsOnlyNonTerrestrialNetwork;
+        this.mServiceCapabilities = builder.mServiceCapabilities;
+        this.mTransferStatus = builder.mTransferStatus;
+    }
+
+    /**
+     * @return The subscription ID.
+     */
+    public int getSubscriptionId() {
+        return mId;
+    }
+
+    /**
+     * Returns the ICC ID.
+     *
+     * Starting with API level 29 Security Patch 2021-04-05, returns the ICC ID if the calling app
+     * has been granted the READ_PRIVILEGED_PHONE_STATE permission, has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}), or is a device owner or profile owner that
+     * has been granted the READ_PHONE_STATE permission. The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile
+     * owner access is deprecated and will be removed in a future release.
+     *
+     * @return the ICC ID, or an empty string if one of these requirements is not met
+     */
+    public String getIccId() {
+        return mIccId;
+    }
+
+    /**
+     * @return The index of the SIM slot that currently contains the subscription and not
+     * necessarily unique and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or
+     * the subscription is inactive.
+     */
+    public int getSimSlotIndex() {
+        return mSimSlotIndex;
+    }
+
+    /**
+     * @return The carrier id of this subscription carrier.
+     *
+     * @see TelephonyManager#getSimCarrierId()
+     */
+    public int getCarrierId() {
+        return mCarrierId;
+    }
+
+    /**
+     * @return The name displayed to the user that identifies this subscription. This name is
+     * used in Settings page and can be renamed by the user.
+     *
+     * @see #getCarrierName()
+     */
+    public CharSequence getDisplayName() {
+        return mDisplayName;
+    }
+
+    /**
+     * @return The name displayed to the user that identifies subscription provider name. This name
+     * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+     *
+     * @see #getDisplayName()
+     */
+    public CharSequence getCarrierName() {
+        return mCarrierName;
+    }
+
+    /**
+     * @return The source of the {@link #getDisplayName()}.
+     *
+     * @hide
+     */
+    @SimDisplayNameSource
+    public int getDisplayNameSource() {
+        return mDisplayNameSource;
+    }
+
+    /**
+     * Creates and returns an icon {@code Bitmap} to represent this {@code SubscriptionInfo} in a
+     * user interface.
+     *
+     * @param context A {@code Context} to get the {@code DisplayMetrics}s from.
+     *
+     * @return A bitmap icon for this {@code SubscriptionInfo}.
+     */
+    public Bitmap createIconBitmap(Context context) {
+        if (mIconBitmap == null) {
+            mIconBitmap = BitmapFactory.decodeResource(context.getResources(),
+                    com.android.internal.R.drawable.ic_sim_card_multi_24px_clr);
+        }
+        int width = mIconBitmap.getWidth();
+        int height = mIconBitmap.getHeight();
+        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+
+        // Create a new bitmap of the same size because it will be modified.
+        Bitmap workingBitmap = Bitmap.createBitmap(metrics, width, height, mIconBitmap.getConfig());
+
+        Canvas canvas = new Canvas(workingBitmap);
+        Paint paint = new Paint();
+
+        // Tint the icon with the color.
+        paint.setColorFilter(new PorterDuffColorFilter(mIconTint, PorterDuff.Mode.SRC_ATOP));
+        canvas.drawBitmap(mIconBitmap, 0, 0, paint);
+        paint.setColorFilter(null);
+
+        // Write the sim slot index.
+        paint.setAntiAlias(true);
+        paint.setTypeface(Typeface.create("sans-serif", Typeface.NORMAL));
+        paint.setColor(Color.WHITE);
+        // Set text size scaled by density
+        paint.setTextSize(TEXT_SIZE * metrics.density);
+        // Convert sim slot index to localized string
+        final String index = formatSimple("%d", mSimSlotIndex + 1);
+        final Rect textBound = new Rect();
+        paint.getTextBounds(index, 0, 1, textBound);
+        final float xOffset = (width / 2.f) - textBound.centerX();
+        final float yOffset = (height / 2.f) - textBound.centerY();
+        canvas.drawText(index, xOffset, yOffset, paint);
+
+        return workingBitmap;
+    }
+
+    /**
+     * A highlight color to use in displaying information about this {@code PhoneAccount}.
+     *
+     * @return A hexadecimal color value.
+     */
+    public int getIconTint() {
+        return mIconTint;
+    }
+
+    /**
+     * Returns the number of this subscription.
+     *
+     * Starting with API level 30, returns the number of this subscription if the calling app meets
+     * at least one of the following requirements:
+     * <ul>
+     *     <li>If the calling app's target SDK is API level 29 or lower and the app has been granted
+     *     the READ_PHONE_STATE permission.
+     *     <li>If the calling app has been granted any of READ_PRIVILEGED_PHONE_STATE,
+     *     READ_PHONE_NUMBERS, or READ_SMS.
+     *     <li>If the calling app has carrier privileges (see {@link
+     *     TelephonyManager#hasCarrierPrivileges}).
+     *     <li>If the calling app is the default SMS role holder.
+     * </ul>
+     *
+     * @return the number of this subscription, or an empty string if none of the requirements
+     * are met.
+     * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead, which takes a
+     *             {@link #getSubscriptionId() subscription ID}.
+     */
+    @Deprecated
+    public String getNumber() {
+        return mNumber;
+    }
+
+    /**
+     * Whether user enables data roaming for this subscription or not. Either
+     * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+     * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
+     */
+    public int getDataRoaming() {
+        return mDataRoaming;
+    }
+
+    /**
+     * @return The mobile country code.
+     *
+     * @deprecated Use {@link #getMccString()} instead.
+     */
+    @Deprecated
+    public int getMcc() {
+        try {
+            return mMcc == null ? 0 : Integer.parseInt(mMcc);
+        } catch (NumberFormatException e) {
+            Log.w(SubscriptionInfo.class.getSimpleName(), "MCC string is not a number");
+            return 0;
+        }
+    }
+
+    /**
+     * @return The mobile network code.
+     *
+     * @deprecated Use {@link #getMncString()} instead.
+     */
+    @Deprecated
+    public int getMnc() {
+        try {
+            return mMnc == null ? 0 : Integer.parseInt(mMnc);
+        } catch (NumberFormatException e) {
+            Log.w(SubscriptionInfo.class.getSimpleName(), "MNC string is not a number");
+            return 0;
+        }
+    }
+
+    /**
+     * @return The mobile country code.
+     */
+    @Nullable
+    public String getMccString() {
+        return mMcc;
+    }
+
+    /**
+     * @return The mobile network code.
+     */
+    @Nullable
+    public String getMncString() {
+        return mMnc;
+    }
+
+    /**
+     * @return The ISO country code. Empty if not available.
+     */
+    public String getCountryIso() {
+        return mCountryIso;
+    }
+
+    /**
+     * @return {@code true} if the subscription is from eSIM.
+     */
+    public boolean isEmbedded() {
+        return mIsEmbedded;
+    }
+
+    /**
+     * An opportunistic subscription connects to a network that is
+     * limited in functionality and / or coverage.
+     *
+     * @return Whether subscription is opportunistic.
+     */
+    public boolean isOpportunistic() {
+        return mIsOpportunistic;
+    }
+
+    /**
+     * @return {@code true} if the subscription is from the actively used SIM.
+     *
+     * @hide
+     */
+    public boolean isActive() {
+        return mSimSlotIndex >= 0 || mType == SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM;
+    }
+
+    /**
+     * Used in scenarios where different subscriptions are bundled as a group.
+     * It's typically a primary and an opportunistic subscription. (see {@link #isOpportunistic()})
+     * Such that those subscriptions will have some affiliated behaviors such as opportunistic
+     * subscription may be invisible to the user.
+     *
+     * @return Group UUID a String of group UUID if it belongs to a group. Otherwise
+     * {@code null}.
+     */
+    @Nullable
+    public ParcelUuid getGroupUuid() {
+        return mGroupUuid;
+    }
+
+    /**
+     * @hide
+     */
+    @NonNull
+    public List<String> getEhplmns() {
+        return Collections.unmodifiableList(mEhplmns == null
+                ? Collections.emptyList() : Arrays.asList(mEhplmns));
+    }
+
+    /**
+     * @hide
+     */
+    @NonNull
+    public List<String> getHplmns() {
+        return Collections.unmodifiableList(mHplmns == null
+                ? Collections.emptyList() : Arrays.asList(mHplmns));
+    }
+
+    /**
+     * @return The owner package of group the subscription belongs to.
+     *
+     * @hide
+     */
+    @NonNull
+    public String getGroupOwner() {
+        return mGroupOwner;
+    }
+
+    /**
+     * @return The profile class populated from the profile metadata if present. Otherwise,
+     * the profile class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no
+     * profile metadata or the subscription is not on an eUICC ({@link #isEmbedded} return
+     * {@code false}).
+     *
+     * @hide
+     */
+    @SystemApi
+    @ProfileClass
+    public int getProfileClass() {
+        return mProfileClass;
+    }
+
+    /**
+     * This method returns the type of a subscription. It can be
+     * {@link SubscriptionManager#SUBSCRIPTION_TYPE_LOCAL_SIM} or
+     * {@link SubscriptionManager#SUBSCRIPTION_TYPE_REMOTE_SIM}.
+     *
+     * @return The type of the subscription.
+     */
+    @SubscriptionType
+    public int getSubscriptionType() {
+        return mType;
+    }
+
+    /**
+     * Checks whether the app with the given context is authorized to manage this subscription
+     * according to its metadata. Only supported for embedded subscriptions (if {@link #isEmbedded}
+     * returns true).
+     *
+     * @param context Context of the application to check.
+     * @return Whether the app is authorized to manage this subscription per its metadata.
+     * @hide
+     * @deprecated - Do not use.
+     */
+    @Deprecated
+    public boolean canManageSubscription(Context context) {
+        return canManageSubscription(context, context.getPackageName());
+    }
+
+    /**
+     * Checks whether the given app is authorized to manage this subscription according to its
+     * metadata. Only supported for embedded subscriptions (if {@link #isEmbedded} returns true).
+     *
+     * @param context Any context.
+     * @param packageName Package name of the app to check.
+     * @return whether the app is authorized to manage this subscription per its metadata.
+     * @hide
+     * @deprecated - Do not use.
+     */
+    @Deprecated
+    public boolean canManageSubscription(Context context, String packageName) {
+        List<UiccAccessRule> allAccessRules = getAccessRules();
+        if (allAccessRules == null) {
+            return false;
+        }
+        PackageManager packageManager = context.getPackageManager();
+        PackageInfo packageInfo;
+        try {
+            packageInfo = packageManager.getPackageInfo(packageName,
+                PackageManager.GET_SIGNING_CERTIFICATES);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.d("SubscriptionInfo", "canManageSubscription: Unknown package: " + packageName, e);
+            return false;
+        }
+        for (UiccAccessRule rule : allAccessRules) {
+            if (rule.getCarrierPrivilegeStatus(packageInfo)
+                    == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return The {@link UiccAccessRule}s that are stored in Uicc, dictating who is authorized to
+     * manage this subscription.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public List<UiccAccessRule> getAccessRules() {
+        List<UiccAccessRule> merged = new ArrayList<>();
+        if (mNativeAccessRules != null) {
+            merged.addAll(Arrays.asList(mNativeAccessRules));
+        }
+        if (mCarrierConfigAccessRules != null) {
+            merged.addAll(Arrays.asList(mCarrierConfigAccessRules));
+        }
+        return merged.isEmpty() ? null : Collections.unmodifiableList(merged);
+    }
+
+    /**
+     * Returns the card string of the SIM card which contains the subscription.
+     *
+     * Starting with API level 29 Security Patch 2021-04-05, returns the card string if the calling
+     * app has been granted the READ_PRIVILEGED_PHONE_STATE permission, has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}), or is a device owner or profile owner that
+     * has been granted the READ_PHONE_STATE permission. The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile
+     * owner access is deprecated and will be removed in a future release.
+     *
+     * @return The card string of the SIM card which contains the subscription or an empty string
+     * if these requirements are not met. The card string is the ICCID for UICCs or the EID for
+     * eUICCs.
+     *
+     * @hide
+     */
+    @NonNull
+    public String getCardString() {
+        return mCardString;
+    }
+
+    /**
+     * @return The card ID of the SIM card which contains the subscription.
+     *
+     * @see UiccCardInfo#getCardId().
+     */
+    public int getCardId() {
+        return mCardId;
+    }
+    /**
+     * @return The port index of the SIM card which contains the subscription.
+     */
+    public int getPortIndex() {
+        return mPortIndex;
+    }
+
+    /**
+     * @return {@code true} if the group of the subscription is disabled. This is only useful if
+     * it's a grouped opportunistic subscription. In this case, if all primary (non-opportunistic)
+     * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile), we
+     * should disable this opportunistic subscription.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isGroupDisabled() {
+        return mIsGroupDisabled;
+    }
+
+    /**
+     * @return {@code true} if Uicc applications are set to be enabled or disabled.
+     * @hide
+     */
+    @SystemApi
+    public boolean areUiccApplicationsEnabled() {
+        return mAreUiccApplicationsEnabled;
+    }
+
+    /**
+     * Get the usage setting for this subscription.
+     *
+     * @return The usage setting used for this subscription.
+     */
+    @UsageSetting
+    public int getUsageSetting() {
+        return mUsageSetting;
+    }
+
+    /**
+     * Check if the subscription is exclusively for non-terrestrial networks.
+     *
+     * @return {@code true} if it is a non-terrestrial network subscription, {@code false}
+     * otherwise.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public boolean isOnlyNonTerrestrialNetwork() {
+        return mIsOnlyNonTerrestrialNetwork;
+    }
+
+    // TODO(b/316183370): replace @code with @link in javadoc after feature is released
+    /**
+     * Retrieves the service capabilities for the current subscription.
+     *
+     * <p>These capabilities are hint to system components and applications, allowing them to
+     * enhance user experience. For instance, a Dialer application can inform the user that the
+     * current subscription is incapable of making voice calls if the voice service is not
+     * available.
+     *
+     * <p>Correct usage of these service capabilities must also consider the device's overall
+     * service capabilities. For example, even if the subscription supports voice calls, a voice
+     * call might not be feasible on a device that only supports data services. To determine the
+     * device's capabilities for voice and SMS services, refer to
+     * {@code TelephonyManager#isDeviceVoiceCapable()} and
+     * {@code TelephonyManager#isDeviceSmsCapable()}.
+     *
+     * <p>Emergency service availability may not directly correlate with the subscription or
+     * device's general service capabilities. In some cases, emergency calls might be possible
+     * even if the subscription or device does not typically support voice services.
+     *
+     * @return A set of integer representing the subscription's service capabilities,
+     * defined by {@code SubscriptionManager#SERVICE_CAPABILITY_VOICE},
+     * {@code SubscriptionManager#SERVICE_CAPABILITY_SMS}
+     * and {@code SubscriptionManager#SERVICE_CAPABILITY_DATA}.
+     *
+     * @see TelephonyManager#isDeviceVoiceCapable()
+     * @see TelephonyManager#isDeviceSmsCapable()
+     * @see CarrierConfigManager#KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY
+     * @see SubscriptionManager#SERVICE_CAPABILITY_VOICE
+     * @see SubscriptionManager#SERVICE_CAPABILITY_SMS
+     * @see SubscriptionManager#SERVICE_CAPABILITY_DATA
+     */
+    @NonNull
+    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+    public @SubscriptionManager.ServiceCapability Set<Integer> getServiceCapabilities() {
+        return SubscriptionManager.getServiceCapabilitiesSet(mServiceCapabilities);
+    }
+
+    /**
+     * Get the transfer status for this subscription.
+     *
+     * @return The transfer status for this subscription.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION)
+    @SystemApi
+    public @TransferStatus int getTransferStatus() {
+        return mTransferStatus;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<SubscriptionInfo> CREATOR =
+            new Parcelable.Creator<SubscriptionInfo>() {
+        @Override
+        public SubscriptionInfo createFromParcel(Parcel source) {
+            return new Builder()
+                    .setId(source.readInt())
+                    .setIccId(source.readString())
+                    .setSimSlotIndex(source.readInt())
+                    .setDisplayName(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source))
+                    .setCarrierName(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source))
+                    .setDisplayNameSource(source.readInt())
+                    .setIconTint(source.readInt())
+                    .setNumber(source.readString())
+                    .setDataRoaming(source.readInt())
+                    .setMcc(source.readString())
+                    .setMnc(source.readString())
+                    .setCountryIso(source.readString())
+                    .setEmbedded(source.readBoolean())
+                    .setNativeAccessRules(source.createTypedArray(UiccAccessRule.CREATOR))
+                    .setCardString(source.readString())
+                    .setCardId(source.readInt())
+                    .setPortIndex(source.readInt())
+                    .setOpportunistic(source.readBoolean())
+                    .setGroupUuid(source.readString8())
+                    .setGroupDisabled(source.readBoolean())
+                    .setCarrierId(source.readInt())
+                    .setProfileClass(source.readInt())
+                    .setType(source.readInt())
+                    .setEhplmns(source.createStringArray())
+                    .setHplmns(source.createStringArray())
+                    .setGroupOwner(source.readString())
+                    .setCarrierConfigAccessRules(source.createTypedArray(
+                            UiccAccessRule.CREATOR))
+                    .setUiccApplicationsEnabled(source.readBoolean())
+                    .setUsageSetting(source.readInt())
+                    .setOnlyNonTerrestrialNetwork(source.readBoolean())
+                    .setServiceCapabilities(
+                            SubscriptionManager.getServiceCapabilitiesSet(source.readInt()))
+                    .setTransferStatus(source.readInt())
+                    .build();
+        }
+
+        @Override
+        public SubscriptionInfo[] newArray(int size) {
+            return new SubscriptionInfo[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mId);
+        dest.writeString(mIccId);
+        dest.writeInt(mSimSlotIndex);
+        TextUtils.writeToParcel(mDisplayName, dest, 0);
+        TextUtils.writeToParcel(mCarrierName, dest, 0);
+        dest.writeInt(mDisplayNameSource);
+        dest.writeInt(mIconTint);
+        dest.writeString(mNumber);
+        dest.writeInt(mDataRoaming);
+        dest.writeString(mMcc);
+        dest.writeString(mMnc);
+        dest.writeString(mCountryIso);
+        // Do not write mIconBitmap since it should be lazily loaded on first usage
+        dest.writeBoolean(mIsEmbedded);
+        dest.writeTypedArray(mNativeAccessRules, flags);
+        dest.writeString(mCardString);
+        dest.writeInt(mCardId);
+        dest.writeInt(mPortIndex);
+        dest.writeBoolean(mIsOpportunistic);
+        dest.writeString8(mGroupUuid == null ? null : mGroupUuid.toString());
+        dest.writeBoolean(mIsGroupDisabled);
+        dest.writeInt(mCarrierId);
+        dest.writeInt(mProfileClass);
+        dest.writeInt(mType);
+        dest.writeStringArray(mEhplmns);
+        dest.writeStringArray(mHplmns);
+        dest.writeString(mGroupOwner);
+        dest.writeTypedArray(mCarrierConfigAccessRules, flags);
+        dest.writeBoolean(mAreUiccApplicationsEnabled);
+        dest.writeInt(mUsageSetting);
+        dest.writeBoolean(mIsOnlyNonTerrestrialNetwork);
+        dest.writeInt(mServiceCapabilities);
+        dest.writeInt(mTransferStatus);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Get stripped PII information from the id.
+     *
+     * @param id The raw id (e.g. ICCID, IMSI, etc...).
+     * @return The stripped string.
+     *
+     * @hide
+     */
+    @Nullable
+    public static String getPrintableId(@Nullable String id) {
+        String idToPrint = null;
+        if (id != null) {
+            if (id.length() > 9 && !TelephonyUtils.IS_DEBUGGABLE) {
+                idToPrint = id.substring(0, 9) + Rlog.pii(false, id.substring(9));
+            } else {
+                idToPrint = id;
+            }
+        }
+        return idToPrint;
+    }
+
+    @Override
+    public String toString() {
+        String iccIdToPrint = getPrintableId(mIccId);
+        String cardStringToPrint = getPrintableId(mCardString);
+        return "[SubscriptionInfo: id=" + mId
+                + " iccId=" + iccIdToPrint
+                + " simSlotIndex=" + mSimSlotIndex
+                + " portIndex=" + mPortIndex
+                + " isEmbedded=" + mIsEmbedded
+                + " carrierId=" + mCarrierId
+                + " displayName=" + mDisplayName
+                + " carrierName=" + mCarrierName
+                + " isOpportunistic=" + mIsOpportunistic
+                + " groupUuid=" + mGroupUuid
+                + " groupOwner=" + mGroupOwner
+                + " isGroupDisabled=" + mIsGroupDisabled
+                + " displayNameSource="
+                + SubscriptionManager.displayNameSourceToString(mDisplayNameSource)
+                + " iconTint=" + mIconTint
+                + " number=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumber)
+                + " dataRoaming=" + mDataRoaming
+                + " mcc=" + mMcc
+                + " mnc=" + mMnc
+                + " ehplmns=" + Arrays.toString(mEhplmns)
+                + " hplmns=" + Arrays.toString(mHplmns)
+                + " cardString=" + cardStringToPrint
+                + " cardId=" + mCardId
+                + " nativeAccessRules=" + Arrays.toString(mNativeAccessRules)
+                + " carrierConfigAccessRules=" + Arrays.toString(mCarrierConfigAccessRules)
+                + " countryIso=" + mCountryIso
+                + " profileClass=" + mProfileClass
+                + " mType=" + SubscriptionManager.subscriptionTypeToString(mType)
+                + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
+                + " usageSetting=" + SubscriptionManager.usageSettingToString(mUsageSetting)
+                + " isOnlyNonTerrestrialNetwork=" + mIsOnlyNonTerrestrialNetwork
+                + " serviceCapabilities=" + SubscriptionManager.getServiceCapabilitiesSet(
+                mServiceCapabilities).toString()
+                + " transferStatus=" + mTransferStatus
+                + "]";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SubscriptionInfo that = (SubscriptionInfo) o;
+        return mId == that.mId && mSimSlotIndex == that.mSimSlotIndex
+                && mDisplayNameSource == that.mDisplayNameSource && mIconTint == that.mIconTint
+                && mDataRoaming == that.mDataRoaming && mIsEmbedded == that.mIsEmbedded
+                && mIsOpportunistic == that.mIsOpportunistic && mCarrierId == that.mCarrierId
+                && mProfileClass == that.mProfileClass && mType == that.mType
+                && mAreUiccApplicationsEnabled == that.mAreUiccApplicationsEnabled
+                && mPortIndex == that.mPortIndex && mUsageSetting == that.mUsageSetting
+                && mCardId == that.mCardId && mIsGroupDisabled == that.mIsGroupDisabled
+                && mIccId.equals(that.mIccId) && mDisplayName.equals(that.mDisplayName)
+                && mCarrierName.equals(that.mCarrierName) && mNumber.equals(that.mNumber)
+                && Objects.equals(mMcc, that.mMcc) && Objects.equals(mMnc,
+                that.mMnc) && Arrays.equals(mEhplmns, that.mEhplmns)
+                && Arrays.equals(mHplmns, that.mHplmns) && mCardString.equals(
+                that.mCardString) && Arrays.equals(mNativeAccessRules,
+                that.mNativeAccessRules) && Arrays.equals(mCarrierConfigAccessRules,
+                that.mCarrierConfigAccessRules) && Objects.equals(mGroupUuid, that.mGroupUuid)
+                && mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner)
+                && mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork
+                && mServiceCapabilities == that.mServiceCapabilities
+                && mTransferStatus == that.mTransferStatus;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mId, mIccId, mSimSlotIndex, mDisplayName, mCarrierName,
+                mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mIsEmbedded,
+                mCardString, mIsOpportunistic, mGroupUuid, mCountryIso, mCarrierId, mProfileClass,
+                mType, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting, mCardId,
+                mIsGroupDisabled, mIsOnlyNonTerrestrialNetwork, mServiceCapabilities,
+                mTransferStatus);
+        result = 31 * result + Arrays.hashCode(mEhplmns);
+        result = 31 * result + Arrays.hashCode(mHplmns);
+        result = 31 * result + Arrays.hashCode(mNativeAccessRules);
+        result = 31 * result + Arrays.hashCode(mCarrierConfigAccessRules);
+        return result;
+    }
+
+    /**
+     * The builder class of {@link SubscriptionInfo}.
+     *
+     * @hide
+     */
+    public static class Builder {
+        /**
+         * The subscription id.
+         */
+        private int mId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+        /**
+         * The ICCID of the SIM that is associated with this subscription, empty if unknown.
+         */
+        @NonNull
+        private String mIccId = "";
+
+        /**
+         * The index of the SIM slot that currently contains the subscription and not necessarily
+         * unique and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or the
+         * subscription is inactive.
+         */
+        private int mSimSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+
+        /**
+         * The name displayed to the user that identifies this subscription. This name is used
+         * in Settings page and can be renamed by the user.
+         */
+        @NonNull
+        private CharSequence mDisplayName = "";
+
+        /**
+         * The name displayed to the user that identifies subscription provider name. This name
+         * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+         */
+        @NonNull
+        private CharSequence mCarrierName = "";
+
+        /**
+         * The source of the display name.
+         */
+        @SimDisplayNameSource
+        private int mDisplayNameSource = SubscriptionManager.NAME_SOURCE_UNKNOWN;
+
+        /**
+         * The color to be used for tinting the icon when displaying to the user.
+         */
+        private int mIconTint = 0;
+
+        /**
+         * The number presented to the user identify this subscription.
+         */
+        @NonNull
+        private String mNumber = "";
+
+        /**
+         * Whether user enables data roaming for this subscription or not. Either
+         * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+         * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
+         */
+        private int mDataRoaming = SubscriptionManager.DATA_ROAMING_DISABLE;
+
+        /**
+         * SIM icon bitmap cache.
+         */
+        @Nullable
+        private Bitmap mIconBitmap = null;
+
+        /**
+         * The mobile country code.
+         */
+        @Nullable
+        private String mMcc = null;
+
+        /**
+         * The mobile network code.
+         */
+        @Nullable
+        private String mMnc = null;
+
+        /**
+         * EHPLMNs associated with the subscription.
+         */
+        @NonNull
+        private String[] mEhplmns = new String[0];
+
+        /**
+         * HPLMNs associated with the subscription.
+         */
+        @NonNull
+        private String[] mHplmns = new String[0];
+
+        /**
+         * The ISO Country code for the subscription's provider.
+         */
+        @NonNull
+        private String mCountryIso = "";
+
+        /**
+         * Whether the subscription is from eSIM.
+         */
+        private boolean mIsEmbedded = false;
+
+        /**
+         * The native access rules for this subscription, if it is embedded and defines any. This
+         * does not include access rules for non-embedded subscriptions.
+         */
+        @Nullable
+        private UiccAccessRule[] mNativeAccessRules = null;
+
+        /**
+         * The card string of the SIM card.
+         */
+        @NonNull
+        private String mCardString = "";
+
+        /**
+         * The card ID of the SIM card which contains the subscription.
+         */
+        private int mCardId = TelephonyManager.UNINITIALIZED_CARD_ID;
+
+        /**
+         * Whether the subscription is opportunistic or not.
+         */
+        private boolean mIsOpportunistic = false;
+
+        /**
+         * The group UUID of the subscription group.
+         */
+        @Nullable
+        private ParcelUuid mGroupUuid = null;
+
+        /**
+         * Whether group of the subscription is disabled. This is only useful if it's a grouped
+         * opportunistic subscription. In this case, if all primary (non-opportunistic)
+         * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile),
+         * we should disable this opportunistic subscription.
+         */
+        private boolean mIsGroupDisabled = false;
+
+        /**
+         * The carrier id.
+         *
+         * @see TelephonyManager#getSimCarrierId()
+         */
+        private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+
+        /**
+         * The profile class populated from the profile metadata if present. Otherwise, the profile
+         * class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no profile
+         * metadata or the subscription is not on an eUICC ({@link #isEmbedded} returns
+         * {@code false}).
+         */
+        @ProfileClass
+        private int mProfileClass = SubscriptionManager.PROFILE_CLASS_UNSET;
+
+        /**
+         * The subscription type.
+         */
+        @SubscriptionType
+        private int mType = SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM;
+
+        /**
+         * The owner package of group the subscription belongs to.
+         */
+        @NonNull
+        private String mGroupOwner = "";
+
+        /**
+         * The carrier certificates for this subscription that are saved in carrier configs.
+         * This does not include access rules from the Uicc, whether embedded or non-embedded.
+         */
+        @Nullable
+        private UiccAccessRule[] mCarrierConfigAccessRules = null;
+
+        /**
+         * Whether Uicc applications are configured to enable or not.
+         */
+        private boolean mAreUiccApplicationsEnabled = true;
+
+        /**
+         * the port index of the Uicc card.
+         */
+        private int mPortIndex = TelephonyManager.INVALID_PORT_INDEX;
+
+        /**
+         * Subscription's preferred usage setting.
+         */
+        @UsageSetting
+        private int mUsageSetting = SubscriptionManager.USAGE_SETTING_UNKNOWN;
+
+        /**
+         * {@code true} if it is a non-terrestrial network subscription, {@code false} otherwise.
+         */
+        private boolean mIsOnlyNonTerrestrialNetwork = false;
+
+        private int mTransferStatus = 0;
+
+        /**
+         * Service capabilities bitmasks the subscription supports.
+         */
+        private int mServiceCapabilities = 0;
+
+        /**
+         * Default constructor.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructor from {@link SubscriptionInfo}.
+         *
+         * @param info The subscription info.
+         */
+        public Builder(@NonNull SubscriptionInfo info) {
+            mId = info.mId;
+            mIccId = info.mIccId;
+            mSimSlotIndex = info.mSimSlotIndex;
+            mDisplayName = info.mDisplayName;
+            mCarrierName = info.mCarrierName;
+            mDisplayNameSource = info.mDisplayNameSource;
+            mIconTint = info.mIconTint;
+            mNumber = info.mNumber;
+            mDataRoaming = info.mDataRoaming;
+            mIconBitmap = info.mIconBitmap;
+            mMcc = info.mMcc;
+            mMnc = info.mMnc;
+            mEhplmns = info.mEhplmns;
+            mHplmns = info.mHplmns;
+            mCountryIso = info.mCountryIso;
+            mIsEmbedded = info.mIsEmbedded;
+            mNativeAccessRules = info.mNativeAccessRules;
+            mCardString = info.mCardString;
+            mCardId = info.mCardId;
+            mIsOpportunistic = info.mIsOpportunistic;
+            mGroupUuid = info.mGroupUuid;
+            mIsGroupDisabled = info.mIsGroupDisabled;
+            mCarrierId = info.mCarrierId;
+            mProfileClass = info.mProfileClass;
+            mType = info.mType;
+            mGroupOwner = info.mGroupOwner;
+            mCarrierConfigAccessRules = info.mCarrierConfigAccessRules;
+            mAreUiccApplicationsEnabled = info.mAreUiccApplicationsEnabled;
+            mPortIndex = info.mPortIndex;
+            mUsageSetting = info.mUsageSetting;
+            mIsOnlyNonTerrestrialNetwork = info.mIsOnlyNonTerrestrialNetwork;
+            mServiceCapabilities = info.mServiceCapabilities;
+            mTransferStatus = info.mTransferStatus;
+        }
+
+        /**
+         * Set the subscription id.
+         *
+         * @param id The subscription id.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setId(int id) {
+            mId = id;
+            return this;
+        }
+
+        /**
+         * Set the ICCID of the SIM that is associated with this subscription.
+         *
+         * @param iccId The ICCID of the SIM that is associated with this subscription.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setIccId(@Nullable String iccId) {
+            mIccId = TextUtils.emptyIfNull(iccId);
+            return this;
+        }
+
+        /**
+         * Set the SIM index of the slot that currently contains the subscription. Set to
+         * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if the subscription is inactive.
+         *
+         * @param simSlotIndex The SIM slot index.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setSimSlotIndex(int simSlotIndex) {
+            mSimSlotIndex = simSlotIndex;
+            return this;
+        }
+
+        /**
+         * The name displayed to the user that identifies this subscription. This name is used
+         * in Settings page and can be renamed by the user.
+         *
+         * @param displayName The display name.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setDisplayName(@Nullable CharSequence displayName) {
+            mDisplayName = displayName == null ? "" : displayName;
+            return this;
+        }
+
+        /**
+         * The name displayed to the user that identifies subscription provider name. This name
+         * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+         *
+         * @param carrierName The carrier name.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setCarrierName(@Nullable CharSequence carrierName) {
+            mCarrierName = carrierName == null ? "" : carrierName;
+            return this;
+        }
+
+        /**
+         * Set the source of the display name.
+         *
+         * @param displayNameSource The source of the display name.
+         * @return The builder.
+         *
+         * @see SubscriptionInfo#getDisplayName()
+         */
+        @NonNull
+        public Builder setDisplayNameSource(@SimDisplayNameSource int displayNameSource) {
+            mDisplayNameSource = displayNameSource;
+            return this;
+        }
+
+        /**
+         * Set the color to be used for tinting the icon when displaying to the user.
+         *
+         * @param iconTint The color to be used for tinting the icon when displaying to the user.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setIconTint(int iconTint) {
+            mIconTint = iconTint;
+            return this;
+        }
+
+        /**
+         * Set the number presented to the user identify this subscription.
+         *
+         * @param number the number presented to the user identify this subscription.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setNumber(@Nullable String number) {
+            mNumber = TextUtils.emptyIfNull(number);
+            return this;
+        }
+
+        /**
+         * Set whether user enables data roaming for this subscription or not.
+         *
+         * @param dataRoaming Data roaming mode. Either
+         * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+         * {@link SubscriptionManager#DATA_ROAMING_DISABLE}
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setDataRoaming(int dataRoaming) {
+            mDataRoaming = dataRoaming;
+            return this;
+        }
+
+        /**
+         * Set SIM icon bitmap cache.
+         *
+         * @param iconBitmap SIM icon bitmap cache.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setIcon(@Nullable Bitmap iconBitmap) {
+            mIconBitmap = iconBitmap;
+            return this;
+        }
+
+        /**
+         * Set the mobile country code.
+         *
+         * @param mcc The mobile country code.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setMcc(@Nullable String mcc) {
+            mMcc = mcc;
+            return this;
+        }
+
+        /**
+         * Set the mobile network code.
+         *
+         * @param mnc Mobile network code.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setMnc(@Nullable String mnc) {
+            mMnc = mnc;
+            return this;
+        }
+
+        /**
+         * Set EHPLMNs associated with the subscription.
+         *
+         * @param ehplmns EHPLMNs associated with the subscription.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setEhplmns(@Nullable String[] ehplmns) {
+            mEhplmns = ehplmns == null ? new String[0] : ehplmns;
+            return this;
+        }
+
+        /**
+         * Set HPLMNs associated with the subscription.
+         *
+         * @param hplmns HPLMNs associated with the subscription.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setHplmns(@Nullable String[] hplmns) {
+            mHplmns = hplmns == null ? new String[0] : hplmns;
+            return this;
+        }
+
+        /**
+         * Set the ISO country code for the subscription's provider.
+         *
+         * @param countryIso The ISO country code for the subscription's provider.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setCountryIso(@Nullable String countryIso) {
+            mCountryIso = TextUtils.emptyIfNull(countryIso);
+            return this;
+        }
+
+        /**
+         * Set whether the subscription is from eSIM or not.
+         *
+         * @param isEmbedded {@code true} if the subscription is from eSIM.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setEmbedded(boolean isEmbedded) {
+            mIsEmbedded = isEmbedded;
+            return this;
+        }
+
+        /**
+         * Set the native access rules for this subscription, if it is embedded and defines any.
+         * This does not include access rules for non-embedded subscriptions.
+         *
+         * @param nativeAccessRules The native access rules for this subscription.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setNativeAccessRules(@Nullable UiccAccessRule[] nativeAccessRules) {
+            mNativeAccessRules = nativeAccessRules;
+            return this;
+        }
+
+        /**
+         * Set the card string of the SIM card.
+         *
+         * @param cardString The card string of the SIM card.
+         * @return The builder.
+         *
+         * @see #getCardString()
+         */
+        @NonNull
+        public Builder setCardString(@Nullable String cardString) {
+            mCardString = TextUtils.emptyIfNull(cardString);
+            return this;
+        }
+
+        /**
+         * Set the card ID of the SIM card which contains the subscription.
+         *
+         * @param cardId The card ID of the SIM card which contains the subscription.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setCardId(int cardId) {
+            mCardId = cardId;
+            return this;
+        }
+
+        /**
+         * Set whether the subscription is opportunistic or not.
+         *
+         * @param isOpportunistic {@code true} if the subscription is opportunistic.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setOpportunistic(boolean isOpportunistic) {
+            mIsOpportunistic = isOpportunistic;
+            return this;
+        }
+
+        /**
+         * Set the group UUID of the subscription group.
+         *
+         * @param groupUuid The group UUID.
+         * @return The builder.
+         *
+         * @see #getGroupUuid()
+         */
+        @NonNull
+        public Builder setGroupUuid(@Nullable String groupUuid) {
+            mGroupUuid = TextUtils.isEmpty(groupUuid) ? null : ParcelUuid.fromString(groupUuid);
+            return this;
+        }
+
+        /**
+         * Whether group of the subscription is disabled. This is only useful if it's a grouped
+         * opportunistic subscription. In this case, if all primary (non-opportunistic)
+         * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile),
+         * we should disable this opportunistic subscription.
+         *
+         * @param isGroupDisabled {@code true} if group of the subscription is disabled.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setGroupDisabled(boolean isGroupDisabled) {
+            mIsGroupDisabled = isGroupDisabled;
+            return this;
+        }
+
+        /**
+         * Set the subscription carrier id.
+         *
+         * @param carrierId The carrier id.
+         * @return The builder
+         *
+         * @see TelephonyManager#getSimCarrierId()
+         */
+        @NonNull
+        public Builder setCarrierId(int carrierId) {
+            mCarrierId = carrierId;
+            return this;
+        }
+
+        /**
+         * Set the profile class populated from the profile metadata if present.
+         *
+         * @param profileClass the profile class populated from the profile metadata if present.
+         * @return The builder
+         *
+         * @see #getProfileClass()
+         */
+        @NonNull
+        public Builder setProfileClass(@ProfileClass int profileClass) {
+            mProfileClass = profileClass;
+            return this;
+        }
+
+        /**
+         * Set the subscription type.
+         *
+         * @param type Subscription type.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setType(@SubscriptionType int type) {
+            mType = type;
+            return this;
+        }
+
+        /**
+         * Set the owner package of group the subscription belongs to.
+         *
+         * @param groupOwner Owner package of group the subscription belongs to.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setGroupOwner(@Nullable String groupOwner) {
+            mGroupOwner = TextUtils.emptyIfNull(groupOwner);
+            return this;
+        }
+
+        /**
+         * Set the carrier certificates for this subscription that are saved in carrier configs.
+         * This does not include access rules from the Uicc, whether embedded or non-embedded.
+         *
+         * @param carrierConfigAccessRules The carrier certificates for this subscription.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setCarrierConfigAccessRules(
+                @Nullable UiccAccessRule[] carrierConfigAccessRules) {
+            mCarrierConfigAccessRules = carrierConfigAccessRules;
+            return this;
+        }
+
+        /**
+         * Set whether Uicc applications are configured to enable or not.
+         *
+         * @param uiccApplicationsEnabled {@code true} if Uicc applications are configured to
+         * enable.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setUiccApplicationsEnabled(boolean uiccApplicationsEnabled) {
+            mAreUiccApplicationsEnabled = uiccApplicationsEnabled;
+            return this;
+        }
+
+        /**
+         * Set the port index of the Uicc card.
+         *
+         * @param portIndex The port index of the Uicc card.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setPortIndex(int portIndex) {
+            mPortIndex = portIndex;
+            return this;
+        }
+
+        /**
+         * Set subscription's preferred usage setting.
+         *
+         * @param usageSetting Subscription's preferred usage setting.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setUsageSetting(@UsageSetting int usageSetting) {
+            mUsageSetting = usageSetting;
+            return this;
+        }
+
+        /**
+         * Set whether the subscription is exclusively used for non-terrestrial networks or not.
+         *
+         * @param isOnlyNonTerrestrialNetwork {@code true} if the subscription is for NTN,
+         * {@code false} otherwise.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setOnlyNonTerrestrialNetwork(boolean isOnlyNonTerrestrialNetwork) {
+            mIsOnlyNonTerrestrialNetwork = isOnlyNonTerrestrialNetwork;
+            return this;
+        }
+
+        /**
+         * Set the service capabilities that the subscription supports.
+         *
+         * @param capabilities Bitmask combination of SubscriptionManager
+         *                     .SERVICE_CAPABILITY_XXX.
+         * @return The builder.
+         *
+         * @throws IllegalArgumentException when any capability is not supported.
+         */
+        @NonNull
+        @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+        public Builder setServiceCapabilities(
+                @NonNull @SubscriptionManager.ServiceCapability Set<Integer> capabilities) {
+            int combinedCapabilities = 0;
+            for (int capability : capabilities) {
+                if (capability < SubscriptionManager.SERVICE_CAPABILITY_VOICE
+                        || capability > SubscriptionManager.SERVICE_CAPABILITY_MAX) {
+                    throw new IllegalArgumentException(
+                            "Invalid service capability value: " + capability);
+                }
+                combinedCapabilities |= SubscriptionManager.serviceCapabilityToBitmask(capability);
+            }
+            mServiceCapabilities = combinedCapabilities;
+            return this;
+        }
+         /**
+         * Set subscription's transfer status
+         *
+         * @param status Subscription's transfer status
+         * @return The builder.
+         */
+        @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION)
+        @NonNull
+        public Builder setTransferStatus(@TransferStatus int status) {
+            mTransferStatus = status;
+            return this;
+        }
+
+        /**
+         * Build the {@link SubscriptionInfo}.
+         *
+         * @return The {@link SubscriptionInfo} instance.
+         */
+        public SubscriptionInfo build() {
+            return new SubscriptionInfo(this);
+        }
+    }
+}
diff --git a/android-35/android/telephony/SubscriptionManager.java b/android-35/android/telephony/SubscriptionManager.java
new file mode 100644
index 0000000..76b4e00
--- /dev/null
+++ b/android-35/android/telephony/SubscriptionManager.java
@@ -0,0 +1,4839 @@
+/*
+ * Copyright (C) 2014 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;
+
+import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED;
+import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.ColorInt;
+import android.annotation.DurationMillisLong;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressAutoDoc;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.app.PendingIntent;
+import android.app.PropertyInvalidatedCache;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.NetworkCapabilities;
+import android.net.NetworkPolicyManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelUuid;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Telephony.SimInfo;
+import android.telephony.euicc.EuiccManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+import android.util.LruCache;
+import android.util.Pair;
+
+import com.android.internal.telephony.ISetOpportunisticDataCallback;
+import com.android.internal.telephony.ISub;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.HandlerExecutor;
+import com.android.internal.util.FunctionalUtils;
+import com.android.internal.util.Preconditions;
+import com.android.telephony.Rlog;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+/**
+ * Subscription manager provides the mobile subscription information.
+ */
+@SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+public class SubscriptionManager {
+    private static final String LOG_TAG = "SubscriptionManager";
+    private static final boolean DBG = false;
+    private static final boolean VDBG = false;
+
+    /** An invalid subscription identifier */
+    public static final int INVALID_SUBSCRIPTION_ID = -1;
+
+    /** Base value for placeholder SUBSCRIPTION_ID's. */
+    /** @hide */
+    public static final int PLACEHOLDER_SUBSCRIPTION_ID_BASE = INVALID_SUBSCRIPTION_ID - 1;
+
+    /** An invalid phone identifier */
+    /** @hide */
+    public static final int INVALID_PHONE_INDEX = -1;
+
+    /** Indicates invalid sim slot. This can be returned by {@link #getSlotIndex(int)}. */
+    public static final int INVALID_SIM_SLOT_INDEX = -1;
+
+    /** Indicates the default subscription ID in Telephony. */
+    public static final int DEFAULT_SUBSCRIPTION_ID = Integer.MAX_VALUE;
+
+    /**
+     * Indicates the default phone id.
+     * @hide
+     */
+    public static final int DEFAULT_PHONE_INDEX = Integer.MAX_VALUE;
+
+    /** Indicates the default slot index. */
+    /** @hide */
+    public static final int DEFAULT_SIM_SLOT_INDEX = Integer.MAX_VALUE;
+
+    /** Minimum possible subid that represents a subscription */
+    /** @hide */
+    public static final int MIN_SUBSCRIPTION_ID_VALUE = 0;
+
+    /** Maximum possible subid that represents a subscription */
+    /** @hide */
+    public static final int MAX_SUBSCRIPTION_ID_VALUE = DEFAULT_SUBSCRIPTION_ID - 1;
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final Uri CONTENT_URI = SimInfo.CONTENT_URI;
+
+    /** The IPC cache key shared by all subscription manager service cacheable properties. */
+    private static final String CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY =
+            "cache_key.telephony.subscription_manager_service";
+
+    /** @hide */
+    public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings";
+
+    /** @hide */
+    public static final String RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME =
+            "restoreSimSpecificSettings";
+
+    /**
+     * The key of the boolean flag indicating whether restoring subscriptions actually changes
+     * the subscription database or not.
+     *
+     * @hide
+     */
+    public static final String RESTORE_SIM_SPECIFIC_SETTINGS_DATABASE_UPDATED =
+            "restoreSimSpecificSettingsDatabaseUpdated";
+
+    /**
+     * Key to the backup & restore data byte array in the Bundle that is returned by {@link
+     * #getAllSimSpecificSettingsForBackup()} or to be pass in to {@link
+     * #restoreAllSimSpecificSettingsFromBackup(byte[])}.
+     *
+     * @hide
+     */
+    public static final String KEY_SIM_SPECIFIC_SETTINGS_DATA = "KEY_SIM_SPECIFIC_SETTINGS_DATA";
+
+    private static final int MAX_CACHE_SIZE = 4;
+
+    private static class VoidPropertyInvalidatedCache<T>
+            extends PropertyInvalidatedCache<Void, T> {
+        private final FunctionalUtils.ThrowingFunction<ISub, T> mInterfaceMethod;
+        private final String mCacheKeyProperty;
+        private final T mDefaultValue;
+
+        VoidPropertyInvalidatedCache(
+                FunctionalUtils.ThrowingFunction<ISub, T> subscriptionInterfaceMethod,
+                String cacheKeyProperty,
+                T defaultValue) {
+            super(MAX_CACHE_SIZE, cacheKeyProperty);
+            mInterfaceMethod = subscriptionInterfaceMethod;
+            mCacheKeyProperty = cacheKeyProperty;
+            mDefaultValue = defaultValue;
+        }
+
+        @Override
+        public T recompute(Void query) {
+            // This always throws on any error.  The exceptions must be handled outside
+            // the cache.
+            try {
+                return mInterfaceMethod.applyOrThrow(TelephonyManager.getSubscriptionService());
+            } catch (Exception re) {
+                throw new RuntimeException(re);
+            }
+        }
+
+        @Override
+        public T query(Void query) {
+            T result = mDefaultValue;
+
+            try {
+                ISub iSub = TelephonyManager.getSubscriptionService();
+                if (iSub != null) {
+                    result = super.query(query);
+                }
+            } catch (Exception ex) {
+                Rlog.w(LOG_TAG, "Failed to recompute cache key for " + mCacheKeyProperty);
+            }
+
+            if (VDBG) logd("recomputing " + mCacheKeyProperty + ", result = " + result);
+            return result;
+        }
+    }
+
+    private static class IntegerPropertyInvalidatedCache<T>
+            extends PropertyInvalidatedCache<Integer, T> {
+        private final FunctionalUtils.ThrowingBiFunction<ISub, Integer, T> mInterfaceMethod;
+        private final String mCacheKeyProperty;
+        private final T mDefaultValue;
+
+        IntegerPropertyInvalidatedCache(
+                FunctionalUtils.ThrowingBiFunction<ISub, Integer, T> subscriptionInterfaceMethod,
+                String cacheKeyProperty,
+                T defaultValue) {
+            super(MAX_CACHE_SIZE, cacheKeyProperty);
+            mInterfaceMethod = subscriptionInterfaceMethod;
+            mCacheKeyProperty = cacheKeyProperty;
+            mDefaultValue = defaultValue;
+        }
+
+        @Override
+        public T recompute(Integer query) {
+            // This always throws on any error.  The exceptions must be handled outside
+            // the cache.
+            try {
+                return mInterfaceMethod.applyOrThrow(
+                    TelephonyManager.getSubscriptionService(), query);
+            } catch (Exception re) {
+                throw new RuntimeException(re);
+            }
+        }
+
+        @Override
+        public T query(Integer query) {
+            T result = mDefaultValue;
+
+            try {
+                ISub iSub = TelephonyManager.getSubscriptionService();
+                if (iSub != null) {
+                    result = super.query(query);
+                }
+            } catch (Exception ex) {
+                Rlog.w(LOG_TAG, "Failed to recompute cache key for " + mCacheKeyProperty);
+            }
+
+            if (VDBG) logd("recomputing " + mCacheKeyProperty + ", result = " + result);
+            return result;
+        }
+    }
+
+    private static IntegerPropertyInvalidatedCache<Integer> sGetDefaultSubIdCacheAsUser =
+            new IntegerPropertyInvalidatedCache<>(ISub::getDefaultSubIdAsUser,
+                    CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
+                    INVALID_SUBSCRIPTION_ID);
+
+    private static VoidPropertyInvalidatedCache<Integer> sGetDefaultDataSubIdCache =
+            new VoidPropertyInvalidatedCache<>(ISub::getDefaultDataSubId,
+                    CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
+                    INVALID_SUBSCRIPTION_ID);
+
+    private static IntegerPropertyInvalidatedCache<Integer> sGetDefaultSmsSubIdCacheAsUser =
+            new IntegerPropertyInvalidatedCache<>(ISub::getDefaultSmsSubIdAsUser,
+                    CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
+                    INVALID_SUBSCRIPTION_ID);
+
+    private static VoidPropertyInvalidatedCache<Integer> sGetActiveDataSubscriptionIdCache =
+            new VoidPropertyInvalidatedCache<>(ISub::getActiveDataSubscriptionId,
+                    CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
+                    INVALID_SUBSCRIPTION_ID);
+
+    private static IntegerPropertyInvalidatedCache<Integer> sGetSlotIndexCache =
+            new IntegerPropertyInvalidatedCache<>(ISub::getSlotIndex,
+                    CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
+                    INVALID_SIM_SLOT_INDEX);
+
+    private static IntegerPropertyInvalidatedCache<Integer> sGetSubIdCache =
+            new IntegerPropertyInvalidatedCache<>(ISub::getSubId,
+                    CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
+                    INVALID_SUBSCRIPTION_ID);
+
+    private static IntegerPropertyInvalidatedCache<Integer> sGetPhoneIdCache =
+            new IntegerPropertyInvalidatedCache<>(ISub::getPhoneId,
+                    CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
+                    INVALID_PHONE_INDEX);
+
+    /**
+     * Generates a content {@link Uri} used to receive updates on simInfo change
+     * on the given subscriptionId
+     * @param subscriptionId the subscriptionId to receive updates on
+     * @return the Uri used to observe carrier identity changes
+     * @hide
+     */
+    public static Uri getUriForSubscriptionId(int subscriptionId) {
+        return Uri.withAppendedPath(CONTENT_URI, String.valueOf(subscriptionId));
+    }
+
+    /**
+     * A content {@link Uri} used to receive updates on wfc enabled user setting.
+     * <p>
+     * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+     * subscription wfc enabled {@link ImsMmTelManager#isVoWiFiSettingEnabled()}
+     * while your app is running. You can also use a {@link android.app.job.JobService}
+     * to ensure your app
+     * is notified of changes to the {@link Uri} even when it is not running.
+     * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely
+     * delivery of updates to the {@link Uri}.
+     * To be notified of changes to a specific subId, append subId to the URI
+     * {@link Uri#withAppendedPath(Uri, String)}.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static final Uri WFC_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc");
+
+    /**
+     * A content {@link Uri} used to receive updates on advanced calling user setting
+     *
+     * <p>
+     * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+     * subscription advanced calling enabled
+     * {@link ImsMmTelManager#isAdvancedCallingSettingEnabled()} while your app is running.
+     * You can also use a {@link android.app.job.JobService} to ensure your app is notified of
+     * changes to the {@link Uri} even when it is not running.
+     * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely
+     * delivery of updates to the {@link Uri}.
+     * To be notified of changes to a specific subId, append subId to the URI
+     * {@link Uri#withAppendedPath(Uri, String)}.
+     *
+     * @see ImsMmTelManager#isAdvancedCallingSettingEnabled()
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static final Uri ADVANCED_CALLING_ENABLED_CONTENT_URI = Uri.withAppendedPath(
+            CONTENT_URI, "advanced_calling");
+
+    /**
+     * A content {@link Uri} used to receive updates on wfc mode setting.
+     * <p>
+     * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+     * subscription wfc mode {@link ImsMmTelManager#getVoWiFiModeSetting()}
+     * while your app is running. You can also use a {@link android.app.job.JobService} to ensure
+     * your app is notified of changes to the {@link Uri} even when it is not running.
+     * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely
+     * delivery of updates to the {@link Uri}.
+     * To be notified of changes to a specific subId, append subId to the URI
+     * {@link Uri#withAppendedPath(Uri, String)}.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static final Uri WFC_MODE_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc_mode");
+
+    /**
+     * A content {@link Uri} used to receive updates on wfc roaming mode setting.
+     * <p>
+     * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+     * subscription wfc roaming mode {@link ImsMmTelManager#getVoWiFiRoamingModeSetting()}
+     * while your app is running. You can also use a {@link android.app.job.JobService}
+     * to ensure your app is notified of changes to the {@link Uri} even when it is not running.
+     * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely
+     * delivery of updates to the {@link Uri}.
+     * To be notified of changes to a specific subId, append subId to the URI
+     * {@link Uri#withAppendedPath(Uri, String)}.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static final Uri WFC_ROAMING_MODE_CONTENT_URI = Uri.withAppendedPath(
+            CONTENT_URI, "wfc_roaming_mode");
+
+    /**
+     * A content {@link Uri} used to receive updates on vt(video telephony over IMS) enabled
+     * setting.
+     * <p>
+     * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+     * subscription vt enabled {@link ImsMmTelManager#isVtSettingEnabled()}
+     * while your app is running. You can also use a {@link android.app.job.JobService} to ensure
+     * your app is notified of changes to the {@link Uri} even when it is not running.
+     * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely
+     * delivery of updates to the {@link Uri}.
+     * To be notified of changes to a specific subId, append subId to the URI
+     * {@link Uri#withAppendedPath(Uri, String)}.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static final Uri VT_ENABLED_CONTENT_URI = Uri.withAppendedPath(
+            CONTENT_URI, "vt_enabled");
+
+    /**
+     * A content {@link Uri} used to receive updates on wfc roaming enabled setting.
+     * <p>
+     * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+     * subscription wfc roaming enabled {@link ImsMmTelManager#isVoWiFiRoamingSettingEnabled()}
+     * while your app is running. You can also use a {@link android.app.job.JobService} to ensure
+     * your app is notified of changes to the {@link Uri} even when it is not running.
+     * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely
+     * delivery of updates to the {@link Uri}.
+     * To be notified of changes to a specific subId, append subId to the URI
+     * {@link Uri#withAppendedPath(Uri, String)}.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath(
+            CONTENT_URI, "wfc_roaming_enabled");
+
+
+    /**
+     * A content {@link uri} used to call the appropriate backup or restore method for sim-specific
+     * settings
+     * <p>
+     * See {@link #GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME} and {@link
+     * #RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME} for information on what method to call.
+     * @hide
+     */
+    @NonNull
+    public static final Uri SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+            CONTENT_URI, "backup_and_restore");
+
+    /**
+     * A content {@link uri} used to notify contentobservers listening to siminfo restore during
+     * SuW.
+     * @hide
+     */
+    @NonNull
+    public static final Uri SIM_INFO_SUW_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+            SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, "suw_restore");
+
+    /**
+     * A content {@link Uri} used to receive updates on cross sim enabled user setting.
+     * <p>
+     * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+     * subscription cross sim calling enabled
+     * {@link ImsMmTelManager#isCrossSimCallingEnabled()}
+     * while your app is running. You can also use a {@link android.app.job.JobService}
+     * to ensure your app
+     * is notified of changes to the {@link Uri} even when it is not running.
+     * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely
+     * delivery of updates to the {@link Uri}.
+     * To be notified of changes to a specific subId, append subId to the URI
+     * {@link Uri#withAppendedPath(Uri, String)}.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static final Uri CROSS_SIM_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI,
+            SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED);
+
+    /**
+     * TelephonyProvider unique key column name is the subscription id.
+     * <P>Type: TEXT (String)</P>
+     */
+    /** @hide */
+    public static final String UNIQUE_KEY_SUBSCRIPTION_ID =
+            SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID;
+
+    /**
+     * TelephonyProvider column name for a unique identifier for the subscription within the
+     * specific subscription type. For example, it contains SIM ICC Identifier subscriptions
+     * on Local SIMs. and Mac-address for Remote-SIM Subscriptions for Bluetooth devices.
+     * <P>Type: TEXT (String)</P>
+     */
+    /** @hide */
+    public static final String ICC_ID = SimInfo.COLUMN_ICC_ID;
+
+    /**
+     * TelephonyProvider column name for user SIM_SlOT_INDEX
+     * <P>Type: INTEGER (int)</P>
+     */
+    /** @hide */
+    public static final String SIM_SLOT_INDEX = SimInfo.COLUMN_SIM_SLOT_INDEX;
+
+    /** SIM is not inserted */
+    /** @hide */
+    public static final int SIM_NOT_INSERTED = SimInfo.SIM_NOT_INSERTED;
+
+    /**
+     * The slot-index for Bluetooth Remote-SIM subscriptions
+     * @hide
+     */
+    public static final int SLOT_INDEX_FOR_REMOTE_SIM_SUB = INVALID_SIM_SLOT_INDEX;
+
+    /**
+     * TelephonyProvider column name Subscription-type.
+     * <P>Type: INTEGER (int)</P> {@link #SUBSCRIPTION_TYPE_LOCAL_SIM} for Local-SIM Subscriptions,
+     * {@link #SUBSCRIPTION_TYPE_REMOTE_SIM} for Remote-SIM Subscriptions.
+     * Default value is 0.
+     */
+    /** @hide */
+    public static final String SUBSCRIPTION_TYPE = SimInfo.COLUMN_SUBSCRIPTION_TYPE;
+
+    /**
+     * TelephonyProvider column name for last used TP - message Reference
+     * <P>Type: INTEGER (int)</P> with -1 as default value
+     * TP - Message Reference valid range [0 - 255]
+     * @hide
+     */
+    public static final String TP_MESSAGE_REF = SimInfo.COLUMN_TP_MESSAGE_REF;
+
+    /**
+     * TelephonyProvider column name enabled_mobile_data_policies.
+     * A list of mobile data policies, each of which represented by an integer and joint by ",".
+     *
+     * Default value is empty string.
+     * @hide
+     */
+    public static final String ENABLED_MOBILE_DATA_POLICIES =
+            SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"SUBSCRIPTION_TYPE_"},
+        value = {
+            SUBSCRIPTION_TYPE_LOCAL_SIM,
+            SUBSCRIPTION_TYPE_REMOTE_SIM})
+    public @interface SubscriptionType {}
+
+    /**
+     * This constant is to designate a subscription as a Local-SIM Subscription.
+     * <p> A Local-SIM can be a physical SIM inserted into a sim-slot in the device, or eSIM on the
+     * device.
+     * </p>
+     */
+    public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = SimInfo.SUBSCRIPTION_TYPE_LOCAL_SIM;
+
+    /**
+     * This constant is to designate a subscription as a Remote-SIM Subscription.
+     * <p>
+     * A Remote-SIM subscription is for a SIM on a phone connected to this device via some
+     * connectivity mechanism, for example bluetooth. Similar to Local SIM, this subscription can
+     * be used for SMS, Voice and data by proxying data through the connected device.
+     * Certain data of the SIM, such as IMEI, are not accessible for Remote SIMs.
+     * </p>
+     *
+     * <p>
+     * A Remote-SIM is available only as long the phone stays connected to this device.
+     * When the phone disconnects, Remote-SIM subscription is removed from this device and is
+     * no longer known. All data associated with the subscription, such as stored SMS, call logs,
+     * contacts etc, are removed from this device.
+     * </p>
+     *
+     * <p>
+     * If the phone re-connects to this device, a new Remote-SIM subscription is created for
+     * the phone. The Subscription Id associated with the new subscription is different from
+     * the Subscription Id of the previous Remote-SIM subscription created (and removed) for the
+     * phone; i.e., new Remote-SIM subscription treats the reconnected phone as a Remote-SIM that
+     * was never seen before.
+     * </p>
+     */
+    public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = SimInfo.SUBSCRIPTION_TYPE_REMOTE_SIM;
+
+    /**
+     * TelephonyProvider column name for user displayed name.
+     * <P>Type: TEXT (String)</P>
+     */
+    /** @hide */
+    public static final String DISPLAY_NAME = SimInfo.COLUMN_DISPLAY_NAME;
+
+    /**
+     * TelephonyProvider column name for the service provider name for the SIM.
+     * <P>Type: TEXT (String)</P>
+     */
+    /** @hide */
+    public static final String CARRIER_NAME = SimInfo.COLUMN_CARRIER_NAME;
+
+    /**
+     * Default name resource
+     * @hide
+     */
+    public static final int DEFAULT_NAME_RES = com.android.internal.R.string.unknownName;
+
+    /**
+     * TelephonyProvider column name for source of the user displayed name.
+     * <P>Type: INT (int)</P> with one of the NAME_SOURCE_XXXX values below
+     *
+     * @hide
+     */
+    public static final String NAME_SOURCE = SimInfo.COLUMN_NAME_SOURCE;
+
+    /**
+     * The name_source is unknown. (for initialization)
+     * @hide
+     */
+    public static final int NAME_SOURCE_UNKNOWN = SimInfo.NAME_SOURCE_UNKNOWN;
+
+    /**
+     * The name_source is from the carrier id.
+     * @hide
+     */
+    public static final int NAME_SOURCE_CARRIER_ID = SimInfo.NAME_SOURCE_CARRIER_ID;
+
+    /**
+     * The name_source is from SIM EF_SPN.
+     * @hide
+     */
+    public static final int NAME_SOURCE_SIM_SPN = SimInfo.NAME_SOURCE_SIM_SPN;
+
+    /**
+     * The name_source is from user input
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public static final int NAME_SOURCE_USER_INPUT = SimInfo.NAME_SOURCE_USER_INPUT;
+
+    /**
+     * The name_source is carrier (carrier app, carrier config, etc.)
+     * @hide
+     */
+    public static final int NAME_SOURCE_CARRIER = SimInfo.NAME_SOURCE_CARRIER;
+
+    /**
+     * The name_source is from SIM EF_PNN.
+     * @hide
+     */
+    public static final int NAME_SOURCE_SIM_PNN = SimInfo.NAME_SOURCE_SIM_PNN;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"NAME_SOURCE_"},
+            value = {
+                    NAME_SOURCE_UNKNOWN,
+                    NAME_SOURCE_CARRIER_ID,
+                    NAME_SOURCE_SIM_SPN,
+                    NAME_SOURCE_USER_INPUT,
+                    NAME_SOURCE_CARRIER,
+                    NAME_SOURCE_SIM_PNN
+            })
+    public @interface SimDisplayNameSource {}
+
+    /**
+     * Device status is not shared to a remote party.
+     */
+    public static final int D2D_SHARING_DISABLED = 0;
+
+    /**
+     * Device status is shared with all numbers in the user's contacts.
+     */
+    public static final int D2D_SHARING_ALL_CONTACTS = 1;
+
+    /**
+     * Device status is shared with all selected contacts.
+     */
+    public static final int D2D_SHARING_SELECTED_CONTACTS = 2;
+
+    /**
+     * Device status is shared whenever possible.
+     */
+    public static final int D2D_SHARING_ALL = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"D2D_SHARING_"},
+            value = {
+                    D2D_SHARING_DISABLED,
+                    D2D_SHARING_ALL_CONTACTS,
+                    D2D_SHARING_SELECTED_CONTACTS,
+                    D2D_SHARING_ALL
+            })
+    public @interface DeviceToDeviceStatusSharingPreference {}
+
+    /**
+     * TelephonyProvider column name for device to device sharing status.
+     * <P>Type: INTEGER (int)</P>
+     */
+    public static final String D2D_STATUS_SHARING = SimInfo.COLUMN_D2D_STATUS_SHARING;
+
+    /**
+     * TelephonyProvider column name for contacts information that allow device to device sharing.
+     * <P>Type: TEXT (String)</P>
+     */
+    public static final String D2D_STATUS_SHARING_SELECTED_CONTACTS =
+            SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS;
+
+    /**
+     * TelephonyProvider column name for the color of a SIM.
+     * <P>Type: INTEGER (int)</P>
+     */
+    /** @hide */
+    public static final String HUE = SimInfo.COLUMN_COLOR;
+
+    /**
+     * TelephonyProvider column name for the phone number of a SIM.
+     * <P>Type: TEXT (String)</P>
+     */
+    /** @hide */
+    public static final String NUMBER = SimInfo.COLUMN_NUMBER;
+
+    /**
+     * TelephonyProvider column name for whether data roaming is enabled.
+     * <P>Type: INTEGER (int)</P>
+     */
+    /** @hide */
+    public static final String DATA_ROAMING = SimInfo.COLUMN_DATA_ROAMING;
+
+    /** Indicates that data roaming is enabled for a subscription */
+    public static final int DATA_ROAMING_ENABLE = SimInfo.DATA_ROAMING_ENABLE;
+
+    /** Indicates that data roaming is disabled for a subscription */
+    public static final int DATA_ROAMING_DISABLE = SimInfo.DATA_ROAMING_DISABLE;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"DATA_ROAMING_"},
+            value = {
+                    DATA_ROAMING_ENABLE,
+                    DATA_ROAMING_DISABLE
+            })
+    public @interface DataRoamingMode {}
+
+    /**
+     * TelephonyProvider column name for subscription carrier id.
+     * @see TelephonyManager#getSimCarrierId()
+     * <p>Type: INTEGER (int) </p>
+     * @hide
+     */
+    public static final String CARRIER_ID = SimInfo.COLUMN_CARRIER_ID;
+
+    /**
+     * @hide A comma-separated list of EHPLMNs associated with the subscription
+     * <P>Type: TEXT (String)</P>
+     */
+    public static final String EHPLMNS = SimInfo.COLUMN_EHPLMNS;
+
+    /**
+     * @hide A comma-separated list of HPLMNs associated with the subscription
+     * <P>Type: TEXT (String)</P>
+     */
+    public static final String HPLMNS = SimInfo.COLUMN_HPLMNS;
+
+    /**
+     * TelephonyProvider column name for the MCC associated with a SIM, stored as a string.
+     * <P>Type: TEXT (String)</P>
+     * @hide
+     */
+    public static final String MCC_STRING = SimInfo.COLUMN_MCC_STRING;
+
+    /**
+     * TelephonyProvider column name for the MNC associated with a SIM, stored as a string.
+     * <P>Type: TEXT (String)</P>
+     * @hide
+     */
+    public static final String MNC_STRING = SimInfo.COLUMN_MNC_STRING;
+
+    /**
+     * TelephonyProvider column name for the MCC associated with a SIM.
+     * <P>Type: INTEGER (int)</P>
+     * @hide
+     */
+    public static final String MCC = SimInfo.COLUMN_MCC;
+
+    /**
+     * TelephonyProvider column name for the MNC associated with a SIM.
+     * <P>Type: INTEGER (int)</P>
+     * @hide
+     */
+    public static final String MNC = SimInfo.COLUMN_MNC;
+
+    /**
+     * TelephonyProvider column name for the iso country code associated with a SIM.
+     * <P>Type: TEXT (String)</P>
+     * @hide
+     */
+    public static final String ISO_COUNTRY_CODE = SimInfo.COLUMN_ISO_COUNTRY_CODE;
+
+    /**
+     * TelephonyProvider column name for whether a subscription is embedded (that is, present on an
+     * eSIM).
+     * <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded.
+     * @hide
+     */
+    public static final String IS_EMBEDDED = SimInfo.COLUMN_IS_EMBEDDED;
+
+    /**
+     * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the
+     * current enabled profile on the card, while for eUICC card it is the EID of the card.
+     * <P>Type: TEXT (String)</P>
+     * @hide
+     */
+    public static final String CARD_ID = SimInfo.COLUMN_CARD_ID;
+
+    /**
+     * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
+     * {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1.
+     * <p>TYPE: BLOB
+     * @hide
+     */
+    public static final String ACCESS_RULES = SimInfo.COLUMN_ACCESS_RULES;
+
+    /**
+     * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
+     * {@link UiccAccessRule#encodeRules} but for the rules that come from CarrierConfigs.
+     * Only present if there are access rules in CarrierConfigs
+     * <p>TYPE: BLOB
+     * @hide
+     */
+    public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS =
+            SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS;
+
+    /**
+     * TelephonyProvider column name identifying whether an embedded subscription is on a removable
+     * card. Such subscriptions are marked inaccessible as soon as the current card is removed.
+     * Otherwise, they will remain accessible unless explicitly deleted. Only present if
+     * {@link #IS_EMBEDDED} is 1.
+     * <p>TYPE: INTEGER (int), 1 for removable or 0 for non-removable.
+     * @hide
+     */
+    public static final String IS_REMOVABLE = SimInfo.COLUMN_IS_REMOVABLE;
+
+    /**
+     *  TelephonyProvider column name for extreme threat in CB settings
+     * @hide
+     */
+    public static final String CB_EXTREME_THREAT_ALERT =
+            SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT;
+
+    /**
+     * TelephonyProvider column name for severe threat in CB settings
+     *@hide
+     */
+    public static final String CB_SEVERE_THREAT_ALERT = SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT;
+
+    /**
+     * TelephonyProvider column name for amber alert in CB settings
+     *@hide
+     */
+    public static final String CB_AMBER_ALERT = SimInfo.COLUMN_CB_AMBER_ALERT;
+
+    /**
+     * TelephonyProvider column name for emergency alert in CB settings
+     *@hide
+     */
+    public static final String CB_EMERGENCY_ALERT = SimInfo.COLUMN_CB_EMERGENCY_ALERT;
+
+    /**
+     * TelephonyProvider column name for alert sound duration in CB settings
+     *@hide
+     */
+    public static final String CB_ALERT_SOUND_DURATION =
+            SimInfo.COLUMN_CB_ALERT_SOUND_DURATION;
+
+    /**
+     * TelephonyProvider column name for alert reminder interval in CB settings
+     *@hide
+     */
+    public static final String CB_ALERT_REMINDER_INTERVAL =
+            SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL;
+
+    /**
+     * TelephonyProvider column name for enabling vibrate in CB settings
+     *@hide
+     */
+    public static final String CB_ALERT_VIBRATE = SimInfo.COLUMN_CB_ALERT_VIBRATE;
+
+    /**
+     * TelephonyProvider column name for enabling alert speech in CB settings
+     *@hide
+     */
+    public static final String CB_ALERT_SPEECH = SimInfo.COLUMN_CB_ALERT_SPEECH;
+
+    /**
+     * TelephonyProvider column name for ETWS test alert in CB settings
+     *@hide
+     */
+    public static final String CB_ETWS_TEST_ALERT = SimInfo.COLUMN_CB_ETWS_TEST_ALERT;
+
+    /**
+     * TelephonyProvider column name for enable channel50 alert in CB settings
+     *@hide
+     */
+    public static final String CB_CHANNEL_50_ALERT = SimInfo.COLUMN_CB_CHANNEL_50_ALERT;
+
+    /**
+     * TelephonyProvider column name for CMAS test alert in CB settings
+     *@hide
+     */
+    public static final String CB_CMAS_TEST_ALERT = SimInfo.COLUMN_CB_CMAS_TEST_ALERT;
+
+    /**
+     * TelephonyProvider column name for Opt out dialog in CB settings
+     *@hide
+     */
+    public static final String CB_OPT_OUT_DIALOG = SimInfo.COLUMN_CB_OPT_OUT_DIALOG;
+
+    /**
+     * TelephonyProvider column name for enable Volte.
+     *
+     * If this setting is not initialized (set to -1)  then we use the Carrier Config value
+     * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
+     *@hide
+     */
+    public static final String ENHANCED_4G_MODE_ENABLED =
+            SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED;
+
+    /**
+     * TelephonyProvider column name for enable VT (Video Telephony over IMS)
+     *@hide
+     */
+    public static final String VT_IMS_ENABLED = SimInfo.COLUMN_VT_IMS_ENABLED;
+
+    /**
+     * TelephonyProvider column name for enable Wifi calling
+     *@hide
+     */
+    public static final String WFC_IMS_ENABLED = SimInfo.COLUMN_WFC_IMS_ENABLED;
+
+    /**
+     * TelephonyProvider column name for Wifi calling mode
+     *@hide
+     */
+    public static final String WFC_IMS_MODE = SimInfo.COLUMN_WFC_IMS_MODE;
+
+    /**
+     * TelephonyProvider column name for Wifi calling mode in roaming
+     *@hide
+     */
+    public static final String WFC_IMS_ROAMING_MODE = SimInfo.COLUMN_WFC_IMS_ROAMING_MODE;
+
+    /**
+     * TelephonyProvider column name for enable Wifi calling in roaming
+     *@hide
+     */
+    public static final String WFC_IMS_ROAMING_ENABLED = SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED;
+
+    /**
+     * Determines if the user has enabled IMS RCS User Capability Exchange (UCE) for this
+     * subscription.
+     * @hide
+     */
+    public static final String IMS_RCS_UCE_ENABLED = SimInfo.COLUMN_IMS_RCS_UCE_ENABLED;
+
+    /**
+     * Determines if the user has enabled cross SIM calling for this subscription.
+     *
+     * @hide
+     */
+    public static final String CROSS_SIM_CALLING_ENABLED = SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED;
+
+    /**
+     * TelephonyProvider column name for whether a subscription is opportunistic, that is,
+     * whether the network it connects to is limited in functionality or coverage.
+     * For example, CBRS.
+     * <p>Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic.
+     * @hide
+     */
+    public static final String IS_OPPORTUNISTIC = SimInfo.COLUMN_IS_OPPORTUNISTIC;
+
+    /**
+     * TelephonyProvider column name for group ID. Subscriptions with same group ID
+     * are considered bundled together, and should behave as a single subscription at
+     * certain scenarios.
+     *
+     * @hide
+     */
+    public static final String GROUP_UUID = SimInfo.COLUMN_GROUP_UUID;
+
+    /**
+     * TelephonyProvider column name for group owner. It's the package name who created
+     * the subscription group.
+     *
+     * @hide
+     */
+    public static final String GROUP_OWNER = SimInfo.COLUMN_GROUP_OWNER;
+
+    /**
+     * TelephonyProvider column name for the profile class of a subscription
+     * Only present if {@link #IS_EMBEDDED} is 1.
+     * <P>Type: INTEGER (int)</P>
+     * @hide
+     */
+    public static final String PROFILE_CLASS = SimInfo.COLUMN_PROFILE_CLASS;
+
+    /**
+     * TelephonyProvider column name for the port index of the active UICC port.
+     * <P>Type: INTEGER (int)</P>
+     * @hide
+     */
+    public static final String PORT_INDEX = SimInfo.COLUMN_PORT_INDEX;
+
+    /**
+     * TelephonyProvider column name for VoIMS opt-in status.
+     *
+     * <P>Type: INTEGER (int)</P>
+     * @hide
+     */
+    public static final String VOIMS_OPT_IN_STATUS = SimInfo.COLUMN_VOIMS_OPT_IN_STATUS;
+
+    /**
+     * TelephonyProvider column name for NR Advanced calling
+     * Determines if the user has enabled VoNR settings for this subscription.
+     *
+     * @hide
+     */
+    public static final String NR_ADVANCED_CALLING_ENABLED =
+            SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED;
+
+    /**
+     * Profile class of the subscription
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "PROFILE_CLASS_" }, value = {
+            SimInfo.PROFILE_CLASS_TESTING,
+            SimInfo.PROFILE_CLASS_PROVISIONING,
+            SimInfo.PROFILE_CLASS_OPERATIONAL,
+            SimInfo.PROFILE_CLASS_UNSET,
+    })
+    public @interface ProfileClass {}
+
+    /**
+     * A testing profile can be pre-loaded or downloaded onto
+     * the eUICC and provides connectivity to test equipment
+     * for the purpose of testing the device and the eUICC. It
+     * is not intended to store any operator credentials.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROFILE_CLASS_TESTING = SimInfo.PROFILE_CLASS_TESTING;
+
+    /**
+     * A provisioning profile is pre-loaded onto the eUICC and
+     * provides connectivity to a mobile network solely for the
+     * purpose of provisioning profiles.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROFILE_CLASS_PROVISIONING = SimInfo.PROFILE_CLASS_PROVISIONING;
+
+    /**
+     * An operational profile can be pre-loaded or downloaded
+     * onto the eUICC and provides services provided by the
+     * operator.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROFILE_CLASS_OPERATIONAL = SimInfo.PROFILE_CLASS_OPERATIONAL;
+
+    /**
+     * The profile class is unset. This occurs when profile class
+     * info is not available. The subscription either has no profile
+     * metadata or the profile metadata did not encode profile class.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROFILE_CLASS_UNSET = SimInfo.PROFILE_CLASS_UNSET;
+
+    /**
+     * Default profile class
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    public static final int PROFILE_CLASS_DEFAULT = SimInfo.PROFILE_CLASS_UNSET;
+
+    /**
+     * IMSI (International Mobile Subscriber Identity).
+     * <P>Type: TEXT </P>
+     * @hide
+     */
+    //TODO: add @SystemApi
+    public static final String IMSI = SimInfo.COLUMN_IMSI;
+
+    /**
+     * Whether uicc applications is set to be enabled or disabled. By default it's enabled.
+     * @hide
+     */
+    public static final String UICC_APPLICATIONS_ENABLED = SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED;
+
+    /**
+     * Indicate which network type is allowed.
+     * @hide
+     */
+    public static final String ALLOWED_NETWORK_TYPES =
+            SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS;
+
+    /**
+     * TelephonyProvider column name for user handle associated with a sim.
+     * <P>Type: INTEGER (int)</P>
+     * @hide
+     */
+    public static final String USER_HANDLE = SimInfo.COLUMN_USER_HANDLE;
+
+    /**
+     * TelephonyProvider column name for satellite enabled.
+     * By default, it's disabled.
+     * <P>Type: INTEGER (int)</P>
+     * @hide
+     */
+    public static final String SATELLITE_ENABLED = SimInfo.COLUMN_SATELLITE_ENABLED;
+
+    /**
+     * TelephonyProvider column name for satellite attach enabled for carrier. The value of this
+     * column is set based on user settings.
+     * By default, it's enabled.
+     * <P>Type: INTEGER (int)</P>
+     * @hide
+     */
+    public static final String SATELLITE_ATTACH_ENABLED_FOR_CARRIER =
+            SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER;
+
+    /**
+     * TelephonyProvider column name to identify eSIM profile of a non-terrestrial network.
+     * By default, it's disabled.
+     * <P>Type: INTEGER (int)</P>
+     * @hide
+     */
+    public static final String IS_NTN = SimInfo.COLUMN_IS_NTN;
+
+    /**
+     * TelephonyProvider column name to identify service capabilities.
+     * Disabled by default.
+     * <P>Type: INTEGER (int)</P>
+     * @hide
+     */
+    public static final String SERVICE_CAPABILITIES = SimInfo.COLUMN_SERVICE_CAPABILITIES;
+
+    /**
+     * TelephonyProvider column name to identify eSIM's transfer status.
+     * By default, it's disabled.
+     * <P>Type: INTEGER (int)</P>
+     * @hide
+     */
+    public static final String TRANSFER_STATUS = SimInfo.COLUMN_TRANSFER_STATUS;
+
+    /**
+     * TelephonyProvider column name for satellite entitlement status. The value of this column is
+     * set based on entitlement query result for satellite configuration.
+     * By default, it's disabled.
+     * <P>Type: INTEGER (int)</P>
+     *
+     * @hide
+     */
+    public static final String SATELLITE_ENTITLEMENT_STATUS =
+            SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS;
+
+    /**
+     * TelephonyProvider column name for satellite entitlement plmns. The value of this column is
+     * set based on entitlement query result for satellite configuration.
+     * By default, it's empty.
+     * <P>Type: TEXT </P>
+     *
+     * @hide
+     */
+    public static final String SATELLITE_ENTITLEMENT_PLMNS =
+            SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"USAGE_SETTING_"},
+        value = {
+            USAGE_SETTING_UNKNOWN,
+            USAGE_SETTING_DEFAULT,
+            USAGE_SETTING_VOICE_CENTRIC,
+            USAGE_SETTING_DATA_CENTRIC})
+    public @interface UsageSetting {}
+
+    /**
+     * The usage setting is unknown.
+     *
+     * This will be the usage setting returned on devices that do not support querying the
+     * or setting the usage setting.
+     *
+     * It may also be provided by a carrier that wishes to provide a value to avoid making any
+     * settings changes.
+     */
+    public static final int USAGE_SETTING_UNKNOWN = -1;
+
+    /**
+     * Subscription uses the default setting.
+     *
+     * The value is based upon device capability and the other properties of the subscription.
+     *
+     * Most subscriptions will default to voice-centric when in a phone.
+     *
+     * An opportunistic subscription will default to data-centric.
+     *
+     * @see SubscriptionInfo#isOpportunistic
+     */
+    public static final int USAGE_SETTING_DEFAULT = 0;
+
+    /**
+     * This subscription is forced to voice-centric mode
+     *
+     * <p>Refer to voice-centric mode in 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3.
+     * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221
+     * Annex A.
+     *
+     * <p>Devices that support {@link PackageManager#FEATURE_TELEPHONY_CALLING} and support usage
+     * setting configuration must support setting this value via
+     * {@link CarrierConfigManager#KEY_CELLULAR_USAGE_SETTING_INT}.
+     */
+    public static final int USAGE_SETTING_VOICE_CENTRIC = 1;
+
+    /**
+     * This subscription is forced to data-centric mode
+     *
+     * <p>Refer to data-centric mode in 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3.
+     * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221
+     * Annex A.
+     *
+     * <p>Devices that support {@link PackageManager#FEATURE_TELEPHONY_DATA} and support usage
+     * setting configuration must support setting this value via.
+     * {@link CarrierConfigManager#KEY_CELLULAR_USAGE_SETTING_INT}.
+     */
+    public static final int USAGE_SETTING_DATA_CENTRIC = 2;
+
+    /**
+     * Indicate the preferred usage setting for the subscription.
+     *
+     * 0 - Default - If the value has not been explicitly set, it will be "default"
+     * 1 - Voice-centric
+     * 2 - Data-centric
+     *
+     * @hide
+     */
+    public static final String USAGE_SETTING = SimInfo.COLUMN_USAGE_SETTING;
+
+    /**
+     * Broadcast Action: The user has changed one of the default subs related to
+     * data, phone calls, or sms</p>
+     *
+     * TODO: Change to a listener
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String SUB_DEFAULT_CHANGED_ACTION =
+            "android.intent.action.SUB_DEFAULT_CHANGED";
+
+    /**
+     * Broadcast Action: The default subscription has changed.  This has the following
+     * extra values:</p>
+     * The {@link #EXTRA_SUBSCRIPTION_INDEX} extra indicates the current default subscription index
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED
+            = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
+
+    /**
+     * Broadcast Action: The default sms subscription has changed.  This has the following
+     * extra values:</p>
+     * {@link #EXTRA_SUBSCRIPTION_INDEX} extra indicates the current default sms
+     * subscription index
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED
+            = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
+
+    /**
+     * Activity Action: Display UI for managing the billing relationship plans
+     * between a carrier and a specific subscriber.
+     * <p>
+     * Carrier apps are encouraged to implement this activity, and the OS will
+     * provide an affordance to quickly enter this activity, typically via
+     * Settings. This affordance will only be shown when the carrier app is
+     * actively providing subscription plan information via
+     * {@link #setSubscriptionPlans(int, List)}.
+     * <p>
+     * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription
+     * the user is interested in.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS
+            = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
+
+    /**
+     * Broadcast Action: Request a refresh of the billing relationship plans
+     * between a carrier and a specific subscriber.
+     * <p>
+     * Carrier apps are encouraged to implement this receiver, and the OS will
+     * provide an affordance to request a refresh. This affordance will only be
+     * shown when the carrier app is actively providing subscription plan
+     * information via {@link #setSubscriptionPlans(int, List)}.
+     * <p>
+     * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription
+     * the user is interested in.
+     * <p>
+     * Receivers should protect themselves by checking that the sender holds the
+     * {@code android.permission.MANAGE_SUBSCRIPTION_PLANS} permission.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_REFRESH_SUBSCRIPTION_PLANS
+            = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS";
+
+    /**
+     * Broadcast Action: The billing relationship plans between a carrier and a
+     * specific subscriber has changed.
+     * <p>
+     * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription
+     * changed.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS)
+    public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED
+            = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED";
+
+    /**
+     * Integer extra used with {@link #ACTION_DEFAULT_SUBSCRIPTION_CHANGED} and
+     * {@link #ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED} to indicate the subscription
+     * which has changed.
+     */
+    public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
+
+    /**
+     * Integer extra to specify SIM slot index.
+     */
+    public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
+
+    /**
+     * A source of phone number: the EF-MSISDN (see 3GPP TS 31.102),
+     * or EF-MDN for CDMA (see 3GPP2 C.P0065-B), from UICC application.
+     *
+     * <p>The availability and accuracy of the number depends on the carrier.
+     * The number may be updated by over-the-air update to UICC applications
+     * from the carrier, or by other means with physical access to the SIM.
+     */
+    public static final int PHONE_NUMBER_SOURCE_UICC = 1;
+
+    /**
+     * A source of phone number: provided by an app that has carrier privilege.
+     *
+     * <p>The number is intended to be set by a carrier app knowing the correct number
+     * which is, for example, different from the number in {@link #PHONE_NUMBER_SOURCE_UICC UICC}
+     * for some reason.
+     * The number is not available until a carrier app sets one via
+     * {@link #setCarrierPhoneNumber(int, String)}.
+     * The app can update the number with the same API should the number change.
+     */
+    public static final int PHONE_NUMBER_SOURCE_CARRIER = 2;
+
+    /**
+     * A source of phone number: provided by IMS (IP Multimedia Subsystem) implementation.
+     * When IMS service is registered (as indicated by
+     * {@link android.telephony.ims.RegistrationManager.RegistrationCallback#onRegistered(int)})
+     * the IMS implementation may return P-Associated-Uri SIP headers (RFC 3455). The URIs
+     * are the user’s public user identities known to the network (see 3GPP TS 24.229 5.4.1.2),
+     * and the phone number is typically one of them (see “global number” in 3GPP TS 23.003 13.4).
+     *
+     * <p>This source provides the phone number from the last IMS registration.
+     * IMS registration may happen on every device reboot or other network condition changes.
+     * The number will be updated should the associated URI change after an IMS registration.
+     */
+    public static final int PHONE_NUMBER_SOURCE_IMS = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"PHONE_NUMBER_SOURCE"},
+            value = {
+                    PHONE_NUMBER_SOURCE_UICC,
+                    PHONE_NUMBER_SOURCE_CARRIER,
+                    PHONE_NUMBER_SOURCE_IMS,
+            })
+    public @interface PhoneNumberSource {}
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"SERVICE_CAPABILITY"},
+            value = {
+                    SERVICE_CAPABILITY_VOICE,
+                    SERVICE_CAPABILITY_SMS,
+                    SERVICE_CAPABILITY_DATA,
+            })
+    public @interface ServiceCapability {
+    }
+
+    /**
+     * Represents a value indicating the voice calling capabilities of a subscription.
+     *
+     * <p>This value identifies whether the subscription supports various voice calling services.
+     * These services can include circuit-switched (CS) calling, packet-switched (PS) IMS (IP
+     * Multimedia Subsystem) calling, and over-the-top (OTT) calling options.
+     *
+     * <p>Note: The availability of emergency calling services is not solely dependent on this
+     * voice capability. Emergency services may be accessible even if the subscription lacks
+     * standard voice capabilities. However, the device's ability to support emergency calls
+     * can be influenced by its inherent voice capabilities, as determined by
+     * {@link TelephonyManager#isDeviceVoiceCapable()}.
+     *
+     * @see TelephonyManager#isDeviceVoiceCapable()
+     */
+    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+    public static final int SERVICE_CAPABILITY_VOICE = 1;
+
+    /**
+     * Represents a value indicating the SMS capabilities of a subscription.
+     *
+     * <p>This value identifies whether the subscription supports various sms services.
+     * These services can include circuit-switched (CS) SMS, packet-switched (PS) IMS (IP
+     * Multimedia Subsystem) SMS, and over-the-top (OTT) SMS options.
+     *
+     * <p>Note: The availability of emergency SMS services is not solely dependent on this
+     * sms capability. Emergency services may be accessible even if the subscription lacks
+     * standard sms capabilities. However, the device's ability to support emergency sms
+     * can be influenced by its inherent sms capabilities, as determined by
+     * {@link TelephonyManager#isDeviceSmsCapable()}.
+     *
+     * @see TelephonyManager#isDeviceSmsCapable()
+     */
+    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+    public static final int SERVICE_CAPABILITY_SMS = 2;
+
+    /**
+     * Represents a value indicating the data calling capabilities of a subscription.
+     */
+    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+    public static final int SERVICE_CAPABILITY_DATA = 3;
+
+    /**
+     * Maximum value of service capabilities supported so far.
+     * @hide
+     */
+    public static final int SERVICE_CAPABILITY_MAX = SERVICE_CAPABILITY_DATA;
+
+    /**
+     * Bitmask for {@code SERVICE_CAPABILITY_VOICE}.
+     * @hide
+     */
+    public static final int SERVICE_CAPABILITY_VOICE_BITMASK =
+            serviceCapabilityToBitmask(SERVICE_CAPABILITY_VOICE);
+
+    /**
+     * Bitmask for {@code SERVICE_CAPABILITY_SMS}.
+     * @hide
+     */
+    public static final int SERVICE_CAPABILITY_SMS_BITMASK =
+            serviceCapabilityToBitmask(SERVICE_CAPABILITY_SMS);
+
+    /**
+     * Bitmask for {@code SERVICE_CAPABILITY_DATA}.
+     * @hide
+     */
+    public static final int SERVICE_CAPABILITY_DATA_BITMASK =
+            serviceCapabilityToBitmask(SERVICE_CAPABILITY_DATA);
+
+    private final Context mContext;
+
+    /**
+     * In order to prevent the overflow of the heap size due to an indiscriminate increase in the
+     * cache, the heap size of the resource cache is set sufficiently large.
+     */
+    private static final int MAX_RESOURCE_CACHE_ENTRY_COUNT = 1_000;
+
+    /**
+     * Cache of Resources that has been created in getResourcesForSubId. Key contains package name,
+     * and Configuration of Resources. If more than the maximum number of resources are stored in
+     * this cache, the least recently used Resources will be removed to maintain the maximum size.
+     */
+    private static final LruCache<Pair<String, Configuration>, Resources> sResourcesCache =
+            new LruCache<>(MAX_RESOURCE_CACHE_ENTRY_COUNT);
+
+
+    /**
+     * The profile has not been transferred or converted to an eSIM.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION)
+    @SystemApi
+    public static final int TRANSFER_STATUS_NONE = 0;
+
+    /**
+     * The existing profile of the old device has been transferred to an eSIM of the new device.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION)
+    @SystemApi
+    public static final int TRANSFER_STATUS_TRANSFERRED_OUT = 1;
+
+    /**
+     * The existing profile of the same device has been converted to an eSIM of the same device
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION)
+    @SystemApi
+    public static final int TRANSFER_STATUS_CONVERTED = 2;
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"TRANSFER_STATUS"},
+            value = {
+                    TRANSFER_STATUS_NONE,
+                    TRANSFER_STATUS_TRANSFERRED_OUT,
+                    TRANSFER_STATUS_CONVERTED,
+            })
+    public @interface TransferStatus {}
+
+
+    /**
+     * A listener class for monitoring changes to {@link SubscriptionInfo} records.
+     * <p>
+     * Override the onSubscriptionsChanged method in the object that extends this
+     * class and pass it to {@link #addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)}
+     * to register your listener and to unregister invoke
+     * {@link #removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener)}
+     * <p>
+     * Permissions android.Manifest.permission.READ_PHONE_STATE is required
+     * for #onSubscriptionsChanged to be invoked.
+     */
+    public static class OnSubscriptionsChangedListener {
+
+        /**
+         * After {@link Build.VERSION_CODES#Q}, it is no longer necessary to instantiate a
+         * Handler inside of the OnSubscriptionsChangedListener in all cases, so it will only
+         * be done for callers that do not supply an Executor.
+         */
+        @ChangeId
+        @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+        private static final long LAZY_INITIALIZE_SUBSCRIPTIONS_CHANGED_HANDLER = 278814050L;
+
+        /**
+         * For backwards compatibility reasons, stashes the Looper associated with the thread
+         * context in which this listener was created.
+         */
+        private final Looper mCreatorLooper;
+
+        /**
+         * @hide
+         */
+        public Looper getCreatorLooper() {
+            return mCreatorLooper;
+        }
+
+        /**
+         * Create an OnSubscriptionsChangedListener.
+         *
+         * For callers targeting {@link Build.VERSION_CODES#P} or earlier, this can only be called
+         * on a thread that already has a prepared Looper. Callers targeting Q or later should
+         * subsequently use {@link SubscriptionManager#addOnSubscriptionsChangedListener(
+         * Executor, OnSubscriptionsChangedListener)}.
+         *
+         * On OS versions prior to {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} callers should
+         * assume that this call will fail if invoked on a thread that does not already have a
+         * prepared looper.
+         */
+        public OnSubscriptionsChangedListener() {
+            mCreatorLooper = Looper.myLooper();
+            if (mCreatorLooper == null
+                    && !Compatibility.isChangeEnabled(
+                            LAZY_INITIALIZE_SUBSCRIPTIONS_CHANGED_HANDLER)) {
+                // matches the implementation of Handler
+                throw new RuntimeException(
+                        "Can't create handler inside thread "
+                        + Thread.currentThread()
+                        + " that has not called Looper.prepare()");
+            }
+        }
+
+        /**
+         * Allow a listener to be created with a custom looper
+         * @param looper the non-null Looper that the underlining handler should run on
+         * @hide
+         */
+        public OnSubscriptionsChangedListener(@NonNull Looper looper) {
+            Objects.requireNonNull(looper);
+            mCreatorLooper = looper;
+        }
+
+        /**
+         * Callback invoked when there is any change to any SubscriptionInfo, as well as once on
+         * registering for changes with {@link #addOnSubscriptionsChangedListener}. Typically
+         * this method would invoke {@link #getActiveSubscriptionInfoList}
+         */
+        public void onSubscriptionsChanged() {
+            if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN");
+        }
+
+        /**
+         * Callback invoked when {@link SubscriptionManager#addOnSubscriptionsChangedListener(
+         * Executor, OnSubscriptionsChangedListener)} or
+         * {@link SubscriptionManager#addOnSubscriptionsChangedListener(
+         * OnSubscriptionsChangedListener)} fails to complete due to the
+         * {@link Context#TELEPHONY_REGISTRY_SERVICE} being unavailable.
+         * @hide
+         */
+        public void onAddListenerFailed() {
+            Rlog.w(LOG_TAG, "onAddListenerFailed not overridden");
+        }
+
+        private void log(String s) {
+            Rlog.d(LOG_TAG, s);
+        }
+    }
+
+    /**
+     * {@code true} if the SubscriptionManager instance should see all subscriptions regardless its
+     * association with particular user profile.
+     *
+     * <p> It only applies to Android SDK 35(V) and above. For Android SDK 34(U) and below, the
+     * caller can see all subscription across user profiles as it does today today even if it's
+     * {@code false}.
+     */
+    private final boolean mIsForAllUserProfiles;
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public SubscriptionManager(Context context) {
+        this(context, false /*isForAllUserProfiles*/);
+    }
+
+    /**  Constructor */
+    private SubscriptionManager(Context context, boolean isForAllUserProfiles) {
+        if (DBG) {
+            logd("SubscriptionManager created "
+                    + (isForAllUserProfiles ? "for all user profile" : ""));
+        }
+        mIsForAllUserProfiles = isForAllUserProfiles;
+        mContext = context;
+    }
+
+    private NetworkPolicyManager getNetworkPolicyManager() {
+        return (NetworkPolicyManager) mContext
+                .getSystemService(Context.NETWORK_POLICY_SERVICE);
+    }
+
+    /**
+     * @deprecated developers should always obtain references directly from
+     *             {@link Context#getSystemService(Class)}.
+     */
+    @Deprecated
+    public static SubscriptionManager from(Context context) {
+        return (SubscriptionManager) context
+                .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+    }
+
+    /**
+     * Register for changes to the list of active {@link SubscriptionInfo} records or to the
+     * individual records themselves. When a change occurs the onSubscriptionsChanged method of
+     * the listener will be invoked immediately if there has been a notification. The
+     * onSubscriptionChanged method will also be triggered once initially when calling this
+     * function. The callback will be invoked on the looper specified in the listener's constructor.
+     *
+     * @param listener an instance of {@link OnSubscriptionsChangedListener} with
+     *                 onSubscriptionsChanged overridden.
+     *
+     * @deprecated Will get exception if the parameter listener is not initialized with a Looper.
+     * Use {@link #addOnSubscriptionsChangedListener(Executor, OnSubscriptionsChangedListener)}.
+     */
+    @Deprecated
+    public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
+        if (listener == null) return;
+
+        Looper looper = listener.getCreatorLooper();
+        if (looper == null) {
+            throw new RuntimeException(
+                    "Can't create handler inside thread " + Thread.currentThread()
+                    + " that has not called Looper.prepare()");
+        }
+
+        addOnSubscriptionsChangedListener(new HandlerExecutor(new Handler(looper)), listener);
+    }
+
+    /**
+     * Register for changes to the list of {@link SubscriptionInfo} records or to the
+     * individual records (active or inactive) themselves. When a change occurs, the
+     * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged()} method of
+     * the listener will be invoked immediately. The
+     * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged()} method will also be invoked
+     * once initially when calling this method.
+     *
+     * @param listener an instance of {@link OnSubscriptionsChangedListener} with
+     * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged()} overridden.
+     * @param executor the executor that will execute callbacks.
+     */
+    public void addOnSubscriptionsChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnSubscriptionsChangedListener listener) {
+        String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        if (DBG) {
+            logd("register OnSubscriptionsChangedListener pkgName=" + pkgName
+                    + " listener=" + listener);
+        }
+        // We use the TelephonyRegistry as it runs in the system and thus is always
+        // available.
+        TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
+                mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+        if (telephonyRegistryManager != null) {
+            telephonyRegistryManager.addOnSubscriptionsChangedListener(listener,
+                    executor);
+        } else {
+            // If the telephony registry isn't available, we will inform the caller on their
+            // listener that it failed so they can try to re-register.
+            loge("addOnSubscriptionsChangedListener: pkgname=" + pkgName + " failed to be added "
+                    + " due to TELEPHONY_REGISTRY_SERVICE being unavailable.");
+            executor.execute(() -> listener.onAddListenerFailed());
+        }
+    }
+
+    /**
+     * Unregister the {@link OnSubscriptionsChangedListener}. This is not strictly necessary
+     * as the listener will automatically be unregistered if an attempt to invoke the listener
+     * fails.
+     *
+     * @param listener that is to be unregistered.
+     */
+    public void removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
+        if (listener == null) return;
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        if (DBG) {
+            logd("unregister OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
+                    + " listener=" + listener);
+        }
+        // We use the TelephonyRegistry as it runs in the system and thus is always
+        // available.
+        TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
+                mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+        if (telephonyRegistryManager != null) {
+            telephonyRegistryManager.removeOnSubscriptionsChangedListener(listener);
+        }
+    }
+
+    /**
+     * A listener class for monitoring changes to {@link SubscriptionInfo} records of opportunistic
+     * subscriptions.
+     * <p>
+     * Override the onOpportunisticSubscriptionsChanged method in the object that extends this
+     * or {@link #addOnOpportunisticSubscriptionsChangedListener(
+     * Executor, OnOpportunisticSubscriptionsChangedListener)}
+     * to register your listener and to unregister invoke
+     * {@link #removeOnOpportunisticSubscriptionsChangedListener(
+     * OnOpportunisticSubscriptionsChangedListener)}
+     * <p>
+     * Permissions android.Manifest.permission.READ_PHONE_STATE is required
+     * for #onOpportunisticSubscriptionsChanged to be invoked.
+     */
+    public static class OnOpportunisticSubscriptionsChangedListener {
+        /**
+         * Callback invoked when there is any change to any SubscriptionInfo. Typically
+         * this method would invoke {@link #getActiveSubscriptionInfoList}
+         */
+        public void onOpportunisticSubscriptionsChanged() {
+            if (DBG) log("onOpportunisticSubscriptionsChanged: NOT OVERRIDDEN");
+        }
+
+        private void log(String s) {
+            Rlog.d(LOG_TAG, s);
+        }
+    }
+
+    /**
+     * Register for changes to the list of opportunistic subscription records or to the
+     * individual records themselves. When a change occurs the onOpportunisticSubscriptionsChanged
+     * method of the listener will be invoked immediately if there has been a notification.
+     *
+     * @param listener an instance of {@link OnOpportunisticSubscriptionsChangedListener} with
+     *                 onOpportunisticSubscriptionsChanged overridden.
+     */
+    public void addOnOpportunisticSubscriptionsChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnOpportunisticSubscriptionsChangedListener listener) {
+        if (executor == null || listener == null) {
+            return;
+        }
+
+        String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        if (DBG) {
+            logd("register addOnOpportunisticSubscriptionsChangedListener pkgName=" + pkgName
+                    + " listener=" + listener);
+        }
+
+        // We use the TelephonyRegistry as it runs in the system and thus is always
+        // available.
+        TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
+                mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+        if (telephonyRegistryManager != null) {
+            telephonyRegistryManager.addOnOpportunisticSubscriptionsChangedListener(
+                    listener, executor);
+        }
+    }
+
+    /**
+     * Unregister the {@link OnOpportunisticSubscriptionsChangedListener} that is currently
+     * listening opportunistic subscriptions change. This is not strictly necessary
+     * as the listener will automatically be unregistered if an attempt to invoke the listener
+     * fails.
+     *
+     * @param listener that is to be unregistered.
+     */
+    public void removeOnOpportunisticSubscriptionsChangedListener(
+            @NonNull OnOpportunisticSubscriptionsChangedListener listener) {
+        Preconditions.checkNotNull(listener, "listener cannot be null");
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        if (DBG) {
+            logd("unregister OnOpportunisticSubscriptionsChangedListener pkgForDebug="
+                    + pkgForDebug + " listener=" + listener);
+        }
+        TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
+                mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+        if (telephonyRegistryManager != null) {
+            telephonyRegistryManager.removeOnOpportunisticSubscriptionsChangedListener(listener);
+        }
+    }
+
+    /**
+     * Get the active SubscriptionInfo with the input subId.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param subId The unique SubscriptionInfo key in database.
+     * @return SubscriptionInfo, maybe null if its not active.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
+        if (VDBG) logd("[getActiveSubscriptionInfo]+ subId=" + subId);
+        if (!isValidSubscriptionId(subId)) {
+            if (DBG) {
+                logd("[getActiveSubscriptionInfo]- invalid subId");
+            }
+            return null;
+        }
+
+        SubscriptionInfo subInfo = null;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(),
+                        mContext.getAttributionTag());
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return subInfo;
+    }
+
+    /**
+     * Gets an active SubscriptionInfo {@link SubscriptionInfo} associated with the Sim IccId.
+     *
+     * @param iccId the IccId of SIM card
+     * @return SubscriptionInfo, maybe null if its not active
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @Nullable
+    @SystemApi
+    public SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String iccId) {
+        if (VDBG) logd("[getActiveSubscriptionInfoForIccIndex]+ iccId=" + iccId);
+        if (iccId == null) {
+            logd("[getActiveSubscriptionInfoForIccIndex]- null iccid");
+            return null;
+        }
+
+        SubscriptionInfo result = null;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(),
+                        mContext.getAttributionTag());
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return result;
+    }
+
+    /**
+     * Get the active SubscriptionInfo associated with the slotIndex
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param slotIndex the slot which the subscription is inserted
+     * @return SubscriptionInfo, maybe null if its not active
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex) {
+        if (VDBG) logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex);
+        if (!isValidSlotIndex(slotIndex)) {
+            logd("[getActiveSubscriptionInfoForSimSlotIndex]- invalid slotIndex");
+            return null;
+        }
+
+        SubscriptionInfo result = null;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return result;
+    }
+
+    /**
+     * Get all subscription info records from SIMs that are inserted now or previously inserted.
+     *
+     * <p>
+     * If the caller does not have {@link Manifest.permission#READ_PHONE_NUMBERS} permission,
+     * {@link SubscriptionInfo#getNumber()} will return empty string.
+     * If the caller does not have {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER},
+     * {@link SubscriptionInfo#getIccId()} will return an empty string, and
+     * {@link SubscriptionInfo#getGroupUuid()} will return {@code null}.
+     *
+     * <p>
+     * The carrier app will only get the list of subscriptions that it has carrier privilege on,
+     * but will have non-stripped {@link SubscriptionInfo} in the list.
+     *
+     * @return List of all {@link SubscriptionInfo} records from SIMs that are inserted or
+     * previously inserted. Sorted by {@link SubscriptionInfo#getSimSlotIndex()}, then
+     * {@link SubscriptionInfo#getSubscriptionId()}.
+     *
+     * @throws SecurityException if callers do not hold the required permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @NonNull
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            "carrier privileges",
+    })
+    public List<SubscriptionInfo> getAllSubscriptionInfoList() {
+        List<SubscriptionInfo> result = null;
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                result = iSub.getAllSubInfoList(mContext.getOpPackageName(),
+                        mContext.getAttributionTag());
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        if (result == null) {
+            result = Collections.emptyList();
+        }
+        return result;
+    }
+
+    /**
+     * Get the SubscriptionInfo(s) of the currently active SIM(s).
+     *
+     * <p> Returned records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
+     * {@link SubscriptionInfo#getSubscriptionId}. Beginning with Android SDK 35, this method will
+     * never return null.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @return a list of the active {@link SubscriptionInfo} that is visible to the caller. If
+     *         an empty list or null is returned, then there are no active subscriptions that
+     *         are visible to the caller. If the number of active subscriptions available to
+     *         any caller changes, then this change will be indicated by
+     *         {@link OnSubscriptionsChangedListener#onSubscriptionsChanged}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *         {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public @Nullable List<SubscriptionInfo> getActiveSubscriptionInfoList() {
+        List<SubscriptionInfo> activeList = null;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
+                        mContext.getAttributionTag(), mIsForAllUserProfiles);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        if (activeList != null) {
+            activeList = activeList.stream().filter(subInfo -> isSubscriptionVisible(subInfo))
+                    .collect(Collectors.toList());
+        } else {
+            activeList = Collections.emptyList();
+        }
+        return activeList;
+    }
+
+    /**
+     * Get both hidden and visible SubscriptionInfo(s) of the currently active SIM(s).
+     * The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex}
+     * then by {@link SubscriptionInfo#getSubscriptionId}.
+     *
+     * Hidden subscriptions refer to those are not meant visible to the users.
+     * For example, an opportunistic subscription that is grouped with other
+     * subscriptions should remain invisible to users as they are only functionally
+     * supplementary to primary ones.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, only records accessible
+     * to the calling app are returned.
+     *
+     * @return Sorted list of the currently available {@link SubscriptionInfo}
+     * records on the device.
+     * This is similar to {@link #getActiveSubscriptionInfoList} except that it will return
+     * both active and hidden SubscriptionInfos.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    public @NonNull List<SubscriptionInfo> getCompleteActiveSubscriptionInfoList() {
+        return getActiveSubscriptionInfoList(/* userVisibleonly */ false);
+    }
+
+    /**
+     * Create a new subscription manager instance that can see all subscriptions across
+     * user profiles.
+     *
+     * The permission check for accessing all subscriptions will be enforced upon calling the
+     * individual APIs linked below.
+     *
+     * @return a SubscriptionManager that can see all subscriptions regardless its user profile
+     * association.
+     *
+     * @see #getActiveSubscriptionInfoList
+     * @see #getActiveSubscriptionInfoCount
+     * @see UserHandle
+     */
+    @FlaggedApi(Flags.FLAG_ENFORCE_SUBSCRIPTION_USER_FILTER)
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)
+    @NonNull public SubscriptionManager createForAllUserProfiles() {
+        return new SubscriptionManager(mContext, true/*isForAllUserProfiles*/);
+    }
+
+    /**
+    * This is similar to {@link #getActiveSubscriptionInfoList()}, but if userVisibleOnly
+    * is true, it will filter out the hidden subscriptions.
+    *
+    * @hide
+    */
+    public @NonNull List<SubscriptionInfo> getActiveSubscriptionInfoList(boolean userVisibleOnly) {
+        List<SubscriptionInfo> activeList = null;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
+                        mContext.getAttributionTag(), true /*isForAllUserProfiles*/);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        if (activeList == null || activeList.isEmpty()) {
+            return Collections.emptyList();
+        } else if (userVisibleOnly) {
+            return activeList.stream().filter(subInfo -> isSubscriptionVisible(subInfo))
+                    .collect(Collectors.toList());
+        } else {
+            return activeList;
+        }
+    }
+
+    /**
+     * Gets the SubscriptionInfo(s) of all available subscriptions, if any.
+     *
+     * <p>Available subscriptions include active ones (those with a non-negative
+     * {@link SubscriptionInfo#getSimSlotIndex()}) as well as inactive but installed embedded
+     * subscriptions.
+     *
+     * <p>The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
+     * {@link SubscriptionInfo#getSubscriptionId}.
+     *
+     * @return Sorted list of the current {@link SubscriptionInfo} records available on the
+     * device.
+     * <ul>
+     * <li>
+     * If null is returned the current state is unknown but if a
+     * {@link OnSubscriptionsChangedListener} has been registered
+     * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be invoked in the future.
+     * <li>
+     * If the list is empty then there are no {@link SubscriptionInfo} records currently available.
+     * <li>
+     * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
+     * then by {@link SubscriptionInfo#getSubscriptionId}.
+     * </ul>
+     *
+     * <p>
+     * Permissions android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required
+     * for #getAvailableSubscriptionInfoList to be invoked.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    public @Nullable List<SubscriptionInfo> getAvailableSubscriptionInfoList() {
+        List<SubscriptionInfo> result = null;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(),
+                        mContext.getAttributionTag());
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+        return (result == null) ? Collections.emptyList() : result;
+    }
+
+    /**
+     * Gets the SubscriptionInfo(s) of all embedded subscriptions accessible to the calling app, if
+     * any.
+     *
+     * <p>Only those subscriptions for which the calling app has carrier privileges per the
+     * subscription metadata, if any, will be included in the returned list.
+     *
+     * <p>The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
+     * {@link SubscriptionInfo#getSubscriptionId}.
+     *
+     * @return Sorted list of the current embedded {@link SubscriptionInfo} records available on the
+     * device which are accessible to the caller.
+     * <ul>
+     * <li>
+     * If null is returned the current state is unknown but if a
+     * {@link OnSubscriptionsChangedListener} has been registered
+     * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be invoked in the future.
+     * <li>
+     * If the list is empty then there are no {@link SubscriptionInfo} records currently available.
+     * <li>
+     * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
+     * then by {@link SubscriptionInfo#getSubscriptionId}.
+     * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public @Nullable List<SubscriptionInfo> getAccessibleSubscriptionInfoList() {
+        List<SubscriptionInfo> result = null;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                result = iSub.getAccessibleSubscriptionInfoList(mContext.getOpPackageName());
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+        return (result == null) ? Collections.emptyList() : result;
+    }
+
+    /**
+     * Request a refresh of the platform cache of profile information for the eUICC which
+     * corresponds to the card ID returned by {@link TelephonyManager#getCardIdForDefaultEuicc()}.
+     *
+     * <p>Should be called by the EuiccService implementation whenever this information changes due
+     * to an operation done outside the scope of a request initiated by the platform to the
+     * EuiccService. There is no need to refresh for downloads, deletes, or other operations that
+     * were made through the EuiccService.
+     *
+     * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @see TelephonyManager#getCardIdForDefaultEuicc() for more information on the card ID.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @SystemApi
+    public void requestEmbeddedSubscriptionInfoListRefresh() {
+        int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc();
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
+            }
+        } catch (RemoteException ex) {
+            logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed.");
+        }
+    }
+
+    /**
+     * Request a refresh of the platform cache of profile information for the eUICC with the given
+     * {@code cardId}.
+     *
+     * <p>Should be called by the EuiccService implementation whenever this information changes due
+     * to an operation done outside the scope of a request initiated by the platform to the
+     * EuiccService. There is no need to refresh for downloads, deletes, or other operations that
+     * were made through the EuiccService.
+     *
+     * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @param cardId the card ID of the eUICC.
+     *
+     * @see TelephonyManager#getCardIdForDefaultEuicc() for more information on the card ID.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @SystemApi
+    public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
+            }
+        } catch (RemoteException ex) {
+            logd("requestEmbeddedSubscriptionInfoListFresh for card = " + cardId + " failed.");
+        }
+    }
+
+    /**
+     * Get the active subscription count.
+     *
+     * @return The current number of active subscriptions.
+     *
+     * @see #getActiveSubscriptionInfoList()
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public int getActiveSubscriptionInfoCount() {
+        int result = 0;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(),
+                        mContext.getAttributionTag(), mIsForAllUserProfiles);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return result;
+    }
+
+    /**
+     * @return the maximum number of active subscriptions that will be returned by
+     * {@link #getActiveSubscriptionInfoList} and the value returned by
+     * {@link #getActiveSubscriptionInfoCount}.
+     */
+    public int getActiveSubscriptionInfoCountMax() {
+        int result = 0;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                result = iSub.getActiveSubInfoCountMax();
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return result;
+    }
+
+    /**
+     * Add a new SubscriptionInfo to SubscriptionInfo database if needed
+     * @param iccId the IccId of the SIM card
+     * @param slotIndex the slot which the SIM is inserted
+     * @return the URL of the newly created row or the updated row
+     * @hide
+     */
+    public Uri addSubscriptionInfoRecord(String iccId, int slotIndex) {
+        if (VDBG) logd("[addSubscriptionInfoRecord]+ iccId:" + iccId + " slotIndex:" + slotIndex);
+        if (iccId == null) {
+            logd("[addSubscriptionInfoRecord]- null iccId");
+        }
+        if (!isValidSlotIndex(slotIndex)) {
+            logd("[addSubscriptionInfoRecord]- invalid slotIndex");
+        }
+
+        addSubscriptionInfoRecord(iccId, null, slotIndex, SUBSCRIPTION_TYPE_LOCAL_SIM);
+
+        // FIXME: Always returns null?
+        return null;
+
+    }
+
+    /**
+     * Add a new SubscriptionInfo to SubscriptionInfo database if needed
+     * @param uniqueId This is the unique identifier for the subscription within the
+     *                 specific subscription type.
+     * @param displayName human-readable name of the device the subscription corresponds to.
+     * @param slotIndex the slot assigned to this subscription. It is ignored for subscriptionType
+     *                  of {@link #SUBSCRIPTION_TYPE_REMOTE_SIM}.
+     * @param subscriptionType the {@link #SUBSCRIPTION_TYPE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void addSubscriptionInfoRecord(@NonNull String uniqueId, @Nullable String displayName,
+            int slotIndex, @SubscriptionType int subscriptionType) {
+        if (VDBG) {
+            logd("[addSubscriptionInfoRecord]+ uniqueId:" + uniqueId
+                    + ", displayName:" + displayName + ", slotIndex:" + slotIndex
+                    + ", subscriptionType: " + subscriptionType);
+        }
+        if (uniqueId == null) {
+            Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- uniqueId is null");
+            return;
+        }
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub == null) {
+                Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- ISub service is null");
+                return;
+            }
+            int result = iSub.addSubInfo(uniqueId, displayName, slotIndex, subscriptionType);
+            if (result < 0) {
+                Log.e(LOG_TAG, "Adding of subscription didn't succeed: error = " + result);
+            } else {
+                logd("successfully added new subscription");
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Remove subscription info record from the subscription database.
+     *
+     * @param uniqueId This is the unique identifier for the subscription within the specific
+     * subscription type.
+     * @param subscriptionType the type of subscription to be removed.
+     *
+     * @throws NullPointerException if {@code uniqueId} is {@code null}.
+     * @throws SecurityException if callers do not hold the required permission.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void removeSubscriptionInfoRecord(@NonNull String uniqueId,
+            @SubscriptionType int subscriptionType) {
+        if (VDBG) {
+            logd("[removeSubscriptionInfoRecord]+ uniqueId:" + uniqueId
+                    + ", subscriptionType: " + subscriptionType);
+        }
+        if (uniqueId == null) {
+            Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- uniqueId is null");
+            return;
+        }
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub == null) {
+                Log.e(LOG_TAG, "[removeSubscriptionInfoRecord]- ISub service is null");
+                return;
+            }
+            boolean result = iSub.removeSubInfo(uniqueId, subscriptionType);
+            if (!result) {
+                Log.e(LOG_TAG, "Removal of subscription didn't succeed");
+            } else {
+                logd("successfully removed subscription");
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Set SIM icon tint color for subscription ID
+     * @param tint the RGB value of icon tint color of the SIM
+     * @param subId the unique subscription ID in database
+     * @return the number of records updated
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public int setIconTint(@ColorInt int tint, int subId) {
+        if (VDBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
+        return setSubscriptionPropertyHelper(subId, "setIconTint",
+                (iSub)-> iSub.setIconTint(subId, tint)
+        );
+    }
+
+    /**
+     * Set the display name for a subscription ID
+     * @param displayName the display name of SIM card
+     * @param subId the unique Subscritpion ID in database
+     * @param nameSource SIM display name source
+     * @return the number of records updated or < 0 if invalid subId
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public int setDisplayName(@Nullable String displayName, int subId,
+            @SimDisplayNameSource int nameSource) {
+        if (VDBG) {
+            logd("[setDisplayName]+  displayName:" + displayName + " subId:" + subId
+                    + " nameSource:" + nameSource);
+        }
+        return setSubscriptionPropertyHelper(subId, "setDisplayName",
+                (iSub)-> iSub.setDisplayNameUsingSrc(displayName, subId, nameSource)
+        );
+    }
+
+    /**
+     * Set phone number by subId
+     * @param number the phone number of the SIM
+     * @param subId the unique SubscriptionInfo index in database
+     * @return the number of records updated
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int setDisplayNumber(String number, int subId) {
+        if (number == null) {
+            logd("[setDisplayNumber]- fail");
+            return -1;
+        }
+        return setSubscriptionPropertyHelper(subId, "setDisplayNumber",
+                (iSub)-> iSub.setDisplayNumber(number, subId)
+        );
+    }
+
+    /**
+     * Set data roaming by simInfo index
+     * @param roaming 0:Don't allow data when roaming, 1:Allow data when roaming
+     * @param subId the unique SubscriptionInfo index in database
+     * @return the number of records updated
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int setDataRoaming(int roaming, int subId) {
+        if (VDBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
+        return setSubscriptionPropertyHelper(subId, "setDataRoaming",
+                (iSub)->iSub.setDataRoaming(roaming, subId)
+        );
+    }
+
+    /**
+     * Get slotIndex associated with the subscription.
+     *
+     * @param subscriptionId the unique SubscriptionInfo index in database
+     * @return slotIndex as a positive integer or {@link #INVALID_SIM_SLOT_INDEX} if the supplied
+     * subscriptionId doesn't have an associated slot index.
+     */
+    public static int getSlotIndex(int subscriptionId) {
+        return sGetSlotIndexCache.query(subscriptionId);
+    }
+
+    /**
+     * Get an array of subscription ids for the specified logical SIM slot Index. The maximum size
+     * of the array is 1. This API was mistakenly designed to return multiple subscription ids,
+     * which is not possible in the current Android telephony architecture.
+     *
+     * @param slotIndex The logical SIM slot index.
+     *
+     * @return Subscription id of the active subscription on the specified logical SIM slot index.
+     * If SIM is absent on the slot, a single element array of {@link #INVALID_SUBSCRIPTION_ID} will
+     * be returned. {@code null} if the provided {@code slotIndex} is not valid.
+     *
+     * @deprecated Use {@link #getSubscriptionId(int)} instead.
+     */
+    @Deprecated
+    @Nullable
+    public int[] getSubscriptionIds(int slotIndex) {
+        if (!isValidSlotIndex(slotIndex)) {
+            return null;
+        }
+        return new int[]{getSubscriptionId(slotIndex)};
+    }
+
+    /**
+     * Get an array of subscription ids for the specified logical SIM slot Index. The maximum size
+     * of the array is 1. This API was mistakenly designed to return multiple subscription ids,
+     * which is not possible in the current Android telephony architecture.
+     *
+     * @param slotIndex The logical SIM slot index.
+     *
+     * @return Subscription id of the active subscription on the specified logical SIM slot index.
+     * If SIM is absent on the slot, a single element array of {@link #INVALID_SUBSCRIPTION_ID} will
+     * be returned. {@code null} if the provided {@code slotIndex} is not valid.
+     *
+     * @deprecated Use {@link #getSubscriptionId(int)} instead.
+     * @hide
+     */
+    @Deprecated
+    public static int[] getSubId(int slotIndex) {
+        if (!isValidSlotIndex(slotIndex)) {
+            return null;
+        }
+        return new int[]{getSubscriptionId(slotIndex)};
+    }
+
+    /**
+     * Get the subscription id for specified logical SIM slot index.
+     *
+     * @param slotIndex The logical SIM slot index.
+     * @return The subscription id. {@link #INVALID_SUBSCRIPTION_ID} if SIM is absent.
+     */
+    public static int getSubscriptionId(int slotIndex) {
+        if (!isValidSlotIndex(slotIndex)) {
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        }
+
+        return sGetSubIdCache.query(slotIndex);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static int getPhoneId(int subId) {
+        return sGetPhoneIdCache.query(subId);
+    }
+
+    private static void logd(String msg) {
+        Rlog.d(LOG_TAG, msg);
+    }
+
+    private static void loge(String msg) {
+        Rlog.e(LOG_TAG, msg);
+    }
+
+    /**
+     * Returns the system's default subscription id.
+     *
+     * For a voice capable device, it will return getDefaultVoiceSubscriptionId.
+     * For a data only device, it will return the getDefaultDataSubscriptionId.
+     * May return an INVALID_SUBSCRIPTION_ID on error.
+     *
+     * @return the "system" default subscription id.
+     */
+    public static int getDefaultSubscriptionId() {
+        return sGetDefaultSubIdCacheAsUser.query(Process.myUserHandle().getIdentifier());
+    }
+
+    /**
+     * Returns the system's default voice subscription id.
+     *
+     * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
+     *
+     * @return the default voice subscription Id.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    public static int getDefaultVoiceSubscriptionId() {
+        int subId = INVALID_SUBSCRIPTION_ID;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                subId = iSub.getDefaultVoiceSubIdAsUser(Process.myUserHandle().getIdentifier());
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        if (VDBG) logd("getDefaultVoiceSubscriptionId, sub id = " + subId);
+        return subId;
+    }
+
+    /**
+     * Sets the system's default voice subscription id.
+     *
+     * On a data-only device, this is a no-op.
+     *
+     * May throw a {@link RuntimeException} if the provided subscription id is equal to
+     * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}
+     *
+     * @param subscriptionId A valid subscription ID to set as the system default, or
+     *                       {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setDefaultVoiceSubscriptionId(int subscriptionId) {
+        if (VDBG) logd("setDefaultVoiceSubId sub id = " + subscriptionId);
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.setDefaultVoiceSubId(subscriptionId);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Same as {@link #setDefaultVoiceSubscriptionId(int)}, but preserved for backwards
+     * compatibility.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    public void setDefaultVoiceSubId(int subId) {
+        setDefaultVoiceSubscriptionId(subId);
+    }
+
+    /**
+     * Return the SubscriptionInfo for default voice subscription.
+     *
+     * Will return null on data only devices, or on error.
+     *
+     * @return the SubscriptionInfo for the default voice subscription.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public SubscriptionInfo getDefaultVoiceSubscriptionInfo() {
+        return getActiveSubscriptionInfo(getDefaultVoiceSubscriptionId());
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static int getDefaultVoicePhoneId() {
+        return getPhoneId(getDefaultVoiceSubscriptionId());
+    }
+
+    /**
+     * Returns the system's default SMS subscription id.
+     *
+     * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
+     *
+     * @return the default SMS subscription Id.
+     */
+    public static int getDefaultSmsSubscriptionId() {
+        return sGetDefaultSmsSubIdCacheAsUser.query(Process.myUserHandle().getIdentifier());
+    }
+
+    /**
+     * Set the subscription which will be used by default for SMS, with the subscription which
+     * the supplied subscription ID corresponds to; or throw a RuntimeException if the supplied
+     * subscription ID is not usable (check with {@link #isUsableSubscriptionId(int)}).
+     *
+     * @param subscriptionId the supplied subscription ID
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setDefaultSmsSubId(int subscriptionId) {
+        if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId);
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.setDefaultSmsSubId(subscriptionId);
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the system's default data subscription id.
+     *
+     * On a voice only device or on error, will return INVALID_SUBSCRIPTION_ID.
+     *
+     * @return the default data subscription Id.
+     */
+    public static int getDefaultDataSubscriptionId() {
+        return sGetDefaultDataSubIdCache.query(null);
+    }
+
+    /**
+     * Set the subscription which will be used by default for data, with the subscription which
+     * the supplied subscription ID corresponds to; or throw a RuntimeException if the supplied
+     * subscription ID is not usable (check with {@link #isUsableSubscriptionId(int)}).
+     *
+     * @param subscriptionId the supplied subscription ID
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setDefaultDataSubId(int subscriptionId) {
+        if (VDBG) logd("setDataSubscription sub id = " + subscriptionId);
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.setDefaultDataSubId(subscriptionId);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Return the SubscriptionInfo for default data subscription.
+     *
+     * Will return null on voice only devices, or on error.
+     *
+     * @return the SubscriptionInfo for the default data subscription.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public SubscriptionInfo getDefaultDataSubscriptionInfo() {
+        return getActiveSubscriptionInfo(getDefaultDataSubscriptionId());
+    }
+
+    /**
+     * Check if the supplied subscription ID is valid.
+     *
+     * <p>A valid subscription ID is not necessarily an active subscription ID
+     * (see {@link #isActiveSubscriptionId(int)}) or an usable subscription ID
+     * (see {@link #isUsableSubscriptionId(int)}). Unless specifically noted, subscription
+     * APIs work with a valid subscription ID.
+     *
+     * @param subscriptionId The subscription ID.
+     * @return {@code true} if the supplied subscriptionId is valid; {@code false} otherwise.
+     */
+    public static boolean isValidSubscriptionId(int subscriptionId) {
+        return subscriptionId > INVALID_SUBSCRIPTION_ID;
+    }
+
+    /**
+     * Check if the supplied subscription ID is usable.
+     *
+     * <p>A usable subscription ID is a valid subscription ID, but not necessarily an active
+     * subscription ID (see {@link #isActiveSubscriptionId(int)}). Some subscription APIs
+     * require a usable subscription ID, and this is noted in their documentation; otherwise, a
+     * subscription ID does not need to be usable for subscription functions, only valid.
+     *
+     * @param subscriptionId the subscription ID
+     * @return {@code true} if the subscription ID is usable; {@code false} otherwise.
+     */
+    public static boolean isUsableSubscriptionId(int subscriptionId) {
+        return isUsableSubIdValue(subscriptionId);
+    }
+
+    /**
+     * @return true if subId is an usable subId value else false. A
+     * usable subId means its neither a INVALID_SUBSCRIPTION_ID nor a DEFAULT_SUB_ID.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static boolean isUsableSubIdValue(int subId) {
+        return subId >= MIN_SUBSCRIPTION_ID_VALUE && subId <= MAX_SUBSCRIPTION_ID_VALUE;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public static boolean isValidSlotIndex(int slotIndex) {
+        return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getActiveModemCount();
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static boolean isValidPhoneId(int phoneId) {
+        return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getActiveModemCount();
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) {
+        int subId = SubscriptionManager.getSubscriptionId(phoneId);
+        if (isValidSubscriptionId(subId)) {
+            putPhoneIdAndSubIdExtra(intent, phoneId, subId);
+        } else {
+            logd("putPhoneIdAndSubIdExtra: no valid subs");
+            intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
+            intent.putExtra(EXTRA_SLOT_INDEX, phoneId);
+        }
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, int subId) {
+        if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
+        intent.putExtra(EXTRA_SLOT_INDEX, phoneId);
+        intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
+        putSubscriptionIdExtra(intent, subId);
+    }
+
+    /**
+     * Get visible subscription Id(s) of the currently active SIM(s).
+     *
+     * @return the list of subId's that are active,
+     *         is never null but the length may be 0.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @NonNull int[] getActiveSubscriptionIdList() {
+        return getActiveSubscriptionIdList(/* visibleOnly */ true);
+    }
+
+    /**
+     * Get both hidden and visible subscription Id(s) of the currently active SIM(s).
+     *
+     * Hidden subscriptions refer to those are not meant visible to the users.
+     * For example, an opportunistic subscription that is grouped with other
+     * subscriptions should remain invisible to users as they are only functionally
+     * supplementary to primary ones.
+     *
+     * @return the list of subId's that are active,
+     *         is never null but the length may be 0.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @NonNull int[] getCompleteActiveSubscriptionIdList() {
+        return getActiveSubscriptionIdList(/* visibleOnly */false);
+    }
+
+    /**
+     * @return a non-null list of subId's that are active.
+     *
+     * @hide
+     */
+    public @NonNull int[] getActiveSubscriptionIdList(boolean visibleOnly) {
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                int[] subId = iSub.getActiveSubIdList(visibleOnly);
+                if (subId != null) return subId;
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return new int[0];
+    }
+
+    /**
+     * Returns true if the device is considered roaming on the current
+     * network for a subscription.
+     * <p>
+     * Availability: Only when user registered to a network.
+     *
+     * @param subId The subscription ID
+     * @return true if the network for the subscription is roaming, false otherwise
+     */
+    public boolean isNetworkRoaming(int subId) {
+        final int phoneId = getPhoneId(subId);
+        if (phoneId < 0) {
+            // What else can we do?
+            return false;
+        }
+        return TelephonyManager.getDefault().isNetworkRoaming(subId);
+    }
+
+    /**
+     * Set a field in the subscription database. Note not all fields are supported.
+     *
+     * @param subscriptionId Subscription Id of Subscription.
+     * @param columnName Column name in the database. Note not all fields are supported.
+     * @param value Value to store in the database.
+     *
+     * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
+     * exposed.
+     * @throws SecurityException if callers do not hold the required permission.
+     *
+     * @see android.provider.Telephony.SimInfo for all the columns.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public static void setSubscriptionProperty(int subscriptionId, @NonNull String columnName,
+            @NonNull String value) {
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.setSubscriptionProperty(subscriptionId, columnName, value);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Serialize list of contacts uri to string
+     * @hide
+     */
+    public static String serializeUriLists(List<Uri> uris) {
+        List<String> contacts = new ArrayList<>();
+        for (Uri uri : uris) {
+            contacts.add(uri.toString());
+        }
+        try {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(bos);
+            oos.writeObject(contacts);
+            oos.flush();
+            return Base64.encodeToString(bos.toByteArray(), Base64.DEFAULT);
+        } catch (IOException e) {
+            logd("serializeUriLists IO exception");
+        }
+        return "";
+    }
+
+    /**
+     * Get specific field in string format from the subscription info database.
+     *
+     * @param context The calling context.
+     * @param subscriptionId Subscription id of the subscription.
+     * @param columnName Column name in subscription database.
+     *
+     * @return Value in string format associated with {@code subscriptionId} and {@code columnName}
+     * from the database. Empty string if the {@code subscriptionId} is invalid (for backward
+     * compatible).
+     *
+     * @throws IllegalArgumentException if the field is not exposed.
+     *
+     * @see android.provider.Telephony.SimInfo for all the columns.
+     *
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            "carrier privileges",
+    })
+    private static String getStringSubscriptionProperty(@NonNull Context context,
+            int subscriptionId, @NonNull String columnName) {
+        String resultValue = null;
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                resultValue = iSub.getSubscriptionProperty(subscriptionId, columnName,
+                        context.getOpPackageName(), context.getAttributionTag());
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+        return TextUtils.emptyIfNull(resultValue);
+    }
+
+    /**
+     * Get specific field in {@code boolean} format from the subscription info database.
+     *
+     * @param subscriptionId Subscription id of the subscription.
+     * @param columnName Column name in subscription database.
+     * @param defaultValue Default value in case not found or error.
+     * @param context The calling context.
+     *
+     * @return Value in {@code boolean} format associated with {@code subscriptionId} and
+     * {@code columnName} from the database, or {@code defaultValue} if not found or error.
+     *
+     * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
+     * exposed.
+     *
+     * @see android.provider.Telephony.SimInfo for all the columns.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            "carrier privileges",
+    })
+    public static boolean getBooleanSubscriptionProperty(int subscriptionId,
+            @NonNull String columnName, boolean defaultValue, @NonNull Context context) {
+        String result = getStringSubscriptionProperty(context, subscriptionId, columnName);
+        if (!result.isEmpty()) {
+            try {
+                return Integer.parseInt(result) == 1;
+            } catch (NumberFormatException err) {
+                logd("getBooleanSubscriptionProperty NumberFormat exception");
+            }
+        }
+        return defaultValue;
+    }
+
+    /**
+     * Get specific field in {@code integer} format from the subscription info database.
+     *
+     * @param subscriptionId Subscription id of the subscription.
+     * @param columnName Column name in subscription database.
+     * @param defaultValue Default value in case not found or error.
+     * @param context The calling context.
+     *
+     * @return Value in {@code integer} format associated with {@code subscriptionId} and
+     * {@code columnName} from the database, or {@code defaultValue} if not found or error.
+     *
+     * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
+     * exposed.
+     *
+     * @see android.provider.Telephony.SimInfo for all the columns.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            "carrier privileges",
+    })
+    public static int getIntegerSubscriptionProperty(int subscriptionId, @NonNull String columnName,
+            int defaultValue, @NonNull Context context) {
+        String result = getStringSubscriptionProperty(context, subscriptionId, columnName);
+        if (!result.isEmpty()) {
+            try {
+                return Integer.parseInt(result);
+            } catch (NumberFormatException err) {
+                logd("getIntegerSubscriptionProperty NumberFormat exception");
+            }
+        }
+        return defaultValue;
+    }
+
+    /**
+     * Get specific field in {@code long} format from the subscription info database.
+     *
+     * @param subscriptionId Subscription id of the subscription.
+     * @param columnName Column name in subscription database.
+     * @param defaultValue Default value in case not found or error.
+     * @param context The calling context.
+     *
+     * @return Value in {@code long} format associated with {@code subscriptionId} and
+     * {@code columnName} from the database, or {@code defaultValue} if not found or error.
+     *
+     * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
+     * exposed.
+     *
+     * @see android.provider.Telephony.SimInfo for all the columns.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            "carrier privileges",
+    })
+    public static long getLongSubscriptionProperty(int subscriptionId, @NonNull String columnName,
+            long defaultValue, @NonNull Context context) {
+        String result = getStringSubscriptionProperty(context, subscriptionId, columnName);
+        if (!result.isEmpty()) {
+            try {
+                return Long.parseLong(result);
+            } catch (NumberFormatException err) {
+                logd("getLongSubscriptionProperty NumberFormat exception");
+            }
+        }
+        return defaultValue;
+    }
+
+    /**
+     * Returns the {@link Resources} from the given {@link Context} for the MCC/MNC associated with
+     * the subscription. If the subscription ID is invalid, the base resources are returned instead.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *
+     * @param context Context object
+     * @param subId Subscription Id of Subscription whose resources are required
+     * @return Resources associated with Subscription.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public static Resources getResourcesForSubId(@NonNull Context context, int subId) {
+        return getResourcesForSubId(context, subId, false);
+    }
+
+    /**
+     * Returns the resources associated with Subscription.
+     * @param context Context object
+     * @param subId Subscription Id of Subscription who's resources are required
+     * @param useRootLocale if root locale should be used. Localized locale is used if false.
+     * @return Resources associated with Subscription.
+     * @hide
+     */
+    @NonNull
+    public static Resources getResourcesForSubId(Context context, int subId,
+            boolean useRootLocale) {
+        // Check if the Resources already exists in the cache based on the given context. Find a
+        // Resource that match Configuration.
+        Pair<String, Configuration> cacheKey = null;
+        if (isValidSubscriptionId(subId)) {
+            Configuration configurationKey =
+                    new Configuration(context.getResources().getConfiguration());
+            if (useRootLocale) {
+                configurationKey.setLocale(Locale.ROOT);
+            }
+            cacheKey = Pair.create(context.getPackageName() + ", subid=" + subId, configurationKey);
+            synchronized (sResourcesCache) {
+                Resources cached = sResourcesCache.get(cacheKey);
+                if (cached != null) {
+                    // Cache hit. Use cached Resources.
+                    return cached;
+                }
+            }
+        }
+
+        final SubscriptionInfo subInfo =
+                SubscriptionManager.from(context).getActiveSubscriptionInfo(subId);
+
+        Configuration overrideConfig = new Configuration();
+        if (subInfo != null) {
+            overrideConfig.mcc = subInfo.getMcc();
+            overrideConfig.mnc = subInfo.getMnc();
+            if (overrideConfig.mnc == 0) {
+                overrideConfig.mnc = Configuration.MNC_ZERO;
+                cacheKey = null;
+            }
+        } else {
+            cacheKey = null;
+        }
+
+        if (useRootLocale) {
+            overrideConfig.setLocale(Locale.ROOT);
+        }
+
+        // Create new context with new configuration so that we can avoid modifying the passed in
+        // context.
+        // Note that if the original context configuration changes, the resources here will also
+        // change for all values except those overridden by newConfig (e.g. if the device has an
+        // orientation change).
+        Context newContext = context.createConfigurationContext(overrideConfig);
+        Resources res = newContext.getResources();
+
+        if (cacheKey != null) {
+            synchronized (sResourcesCache) {
+                // Save the newly created Resources in the resource cache.
+                sResourcesCache.put(cacheKey, res);
+            }
+        }
+        return res;
+    }
+
+    /**
+     * Checks if the supplied subscription ID corresponds to a subscription which is actively in
+     * use on the device. An active subscription ID is a valid and usable subscription ID.
+     *
+     * @param subscriptionId the subscription ID.
+     * @return {@code true} if the supplied subscription ID corresponds to an active subscription;
+     * {@code false} if it does not correspond to an active subscription; or throw a
+     * SecurityException if the caller hasn't got the right permission.
+     *i
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public boolean isActiveSubscriptionId(int subscriptionId) {
+        return isActiveSubId(subscriptionId);
+    }
+
+    /**
+     * @return true if the sub ID is active. i.e. The sub ID corresponds to a known subscription
+     * and the SIM providing the subscription is present in a slot and in "LOADED" state.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean isActiveSubId(int subId) {
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                return iSub.isActiveSubId(subId, mContext.getOpPackageName(),
+                        mContext.getAttributionTag());
+            }
+        } catch (RemoteException ex) {
+        }
+        return false;
+    }
+
+    /**
+     * Get the description of the billing relationship plan between a carrier
+     * and a specific subscriber.
+     * <p>
+     * This method is only accessible to the following narrow set of apps:
+     * <ul>
+     * <li>The carrier app for this subscriberId, as determined by
+     * {@link TelephonyManager#hasCarrierPrivileges()}.
+     * <li>The carrier app explicitly delegated access through
+     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+     * </ul>
+     *
+     * @param subId the subscriber this relationship applies to
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
+     */
+    public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
+        SubscriptionPlan[] subscriptionPlans =
+                getNetworkPolicyManager().getSubscriptionPlans(subId, mContext.getOpPackageName());
+        return subscriptionPlans == null
+                ? Collections.emptyList() : Arrays.asList(subscriptionPlans);
+    }
+
+    /**
+     * Set the description of the billing relationship plan between a carrier
+     * and a specific subscriber.
+     * <p>
+     * This method is only accessible to the following narrow set of apps:
+     * <ul>
+     * <li>The carrier app for this subscriberId, as determined by
+     * {@link TelephonyManager#hasCarrierPrivileges()}.
+     * <li>The carrier app explicitly delegated access through
+     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+     * </ul>
+     *
+     * @param subId the subscriber this relationship applies to. An empty list
+     *            may be sent to clear any existing plans.
+     * @param plans the list of plans. The first plan is always the primary and
+     *            most important plan. Any additional plans are secondary and
+     *            may not be displayed or used by decision making logic.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
+     * @throws IllegalArgumentException if plans don't meet the requirements
+     *             defined in {@link SubscriptionPlan}.
+     * @deprecated use {@link #setSubscriptionPlans(int, List, long)} instead.
+     */
+    @Deprecated
+    public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
+        setSubscriptionPlans(subId, plans, 0);
+    }
+
+    /**
+     * Set the description of the billing relationship plan between a carrier
+     * and a specific subscriber.
+     * <p>
+     * This method is only accessible to the following narrow set of apps:
+     * <ul>
+     * <li>The carrier app for this subscriberId, as determined by
+     * {@link TelephonyManager#hasCarrierPrivileges()}.
+     * <li>The carrier app explicitly delegated access through
+     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+     * </ul>
+     *
+     * @param subId the subscriber this relationship applies to. An empty list
+     *            may be sent to clear any existing plans.
+     * @param plans the list of plans. The first plan is always the primary and
+     *            most important plan. Any additional plans are secondary and
+     *            may not be displayed or used by decision making logic.
+     * @param expirationDurationMillis the duration after which the subscription plans
+     *            will be automatically cleared, or {@code 0} to leave the plans until
+     *            explicitly cleared, or the next reboot, whichever happens first.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
+     * @throws IllegalArgumentException if plans don't meet the requirements
+     *             defined in {@link SubscriptionPlan}.
+     */
+    public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans,
+            @DurationMillisLong long expirationDurationMillis) {
+        getNetworkPolicyManager().setSubscriptionPlans(subId,
+                plans.toArray(new SubscriptionPlan[0]), expirationDurationMillis,
+                mContext.getOpPackageName());
+    }
+
+    /**
+     * Temporarily override the billing relationship plan between a carrier and
+     * a specific subscriber to be considered unmetered. This will be reflected
+     * to apps via {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}.
+     * <p>
+     * This method is only accessible to the following narrow set of apps:
+     * <ul>
+     * <li>The carrier app for this subscriberId, as determined by
+     * {@link TelephonyManager#hasCarrierPrivileges()}.
+     * <li>The carrier app explicitly delegated access through
+     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+     * </ul>
+     *
+     * @param subId the subscriber this override applies to.
+     * @param overrideUnmetered set if the billing relationship should be
+     *            considered unmetered.
+     * @param expirationDurationMillis the duration after which the requested override
+     *            will be automatically cleared, or {@code 0} to leave in the
+     *            requested state until explicitly cleared, or the next reboot,
+     *            whichever happens first.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *            outlined above.
+     */
+    public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
+            @DurationMillisLong long expirationDurationMillis) {
+        setSubscriptionOverrideUnmetered(subId, overrideUnmetered,
+                TelephonyManager.getAllNetworkTypes(), expirationDurationMillis);
+    }
+
+    /**
+     * Temporarily override the billing relationship plan between a carrier and
+     * a specific subscriber to be considered unmetered. This will be reflected
+     * to apps via {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}.
+     * <p>
+     * This method is only accessible to the following narrow set of apps:
+     * <ul>
+     * <li>The carrier app for this subscriberId, as determined by
+     * {@link TelephonyManager#hasCarrierPrivileges()}.
+     * <li>The carrier app explicitly delegated access through
+     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+     * </ul>
+     *
+     * @param subId the subscriber this override applies to.
+     * @param overrideUnmetered set if the billing relationship should be
+     *            considered unmetered.
+     * @param networkTypes the network types this override applies to. If no
+     *            network types are specified, override values will be ignored.
+     * @param expirationDurationMillis the duration after which the requested override
+     *            will be automatically cleared, or {@code 0} to leave in the
+     *            requested state until explicitly cleared, or the next reboot,
+     *            whichever happens first.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *            outlined above.
+     */
+    public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
+            @NonNull @Annotation.NetworkType int[] networkTypes,
+            @DurationMillisLong long expirationDurationMillis) {
+        final int overrideValue = overrideUnmetered ? SUBSCRIPTION_OVERRIDE_UNMETERED : 0;
+        getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_UNMETERED,
+                overrideValue, networkTypes, expirationDurationMillis, mContext.getOpPackageName());
+    }
+
+    /**
+     * Temporarily override the billing relationship plan between a carrier and
+     * a specific subscriber to be considered congested. This will cause the
+     * device to delay certain network requests when possible, such as developer
+     * jobs that are willing to run in a flexible time window.
+     * <p>
+     * This method is only accessible to the following narrow set of apps:
+     * <ul>
+     * <li>The carrier app for this subscriberId, as determined by
+     * {@link TelephonyManager#hasCarrierPrivileges()}.
+     * <li>The carrier app explicitly delegated access through
+     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+     * </ul>
+     *
+     * @param subId the subscriber this override applies to.
+     * @param overrideCongested set if the subscription should be considered
+     *            congested.
+     * @param expirationDurationMillis the duration after which the requested override
+     *            will be automatically cleared, or {@code 0} to leave in the
+     *            requested state until explicitly cleared, or the next reboot,
+     *            whichever happens first.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *            outlined above.
+     */
+    public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
+            @DurationMillisLong long expirationDurationMillis) {
+        setSubscriptionOverrideCongested(subId, overrideCongested,
+                TelephonyManager.getAllNetworkTypes(), expirationDurationMillis);
+    }
+
+    /**
+     * Temporarily override the billing relationship plan between a carrier and
+     * a specific subscriber to be considered congested. This will cause the
+     * device to delay certain network requests when possible, such as developer
+     * jobs that are willing to run in a flexible time window.
+     * <p>
+     * This method is only accessible to the following narrow set of apps:
+     * <ul>
+     * <li>The carrier app for this subscriberId, as determined by
+     * {@link TelephonyManager#hasCarrierPrivileges()}.
+     * <li>The carrier app explicitly delegated access through
+     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+     * </ul>
+     *
+     * @param subId the subscriber this override applies to.
+     * @param overrideCongested set if the subscription should be considered congested.
+     * @param networkTypes the network types this override applies to. If no network types are
+     * specified, override values will be ignored.
+     * @param expirationDurationMillis the duration after which the requested override
+     * will be automatically cleared, or {@code 0} to leave in the requested state until explicitly
+     * cleared, or the next reboot, whichever happens first.
+     *
+     * @throws SecurityException if the caller doesn't meet the requirements outlined above.
+     */
+    public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
+            @NonNull @Annotation.NetworkType int[] networkTypes,
+            @DurationMillisLong long expirationDurationMillis) {
+        final int overrideValue = overrideCongested ? SUBSCRIPTION_OVERRIDE_CONGESTED : 0;
+        getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_CONGESTED,
+                overrideValue, networkTypes, expirationDurationMillis, mContext.getOpPackageName());
+    }
+
+    /**
+     * Checks whether the app with the given context is authorized to manage the given subscription
+     * according to its metadata.
+     *
+     * Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns
+     * true). To check for permissions for non-embedded subscription as well,
+     * see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}.
+     *
+     * @param info The subscription to check.
+     * @return whether the app is authorized to manage this subscription per its metadata.
+     * @see android.telephony.TelephonyManager#hasCarrierPrivileges
+     */
+    public boolean canManageSubscription(SubscriptionInfo info) {
+        return canManageSubscription(info, mContext.getPackageName());
+    }
+
+    /**
+     * Checks whether the given app is authorized to manage the given subscription. An app can only
+     * be authorized if it is included in the {@link android.telephony.UiccAccessRule} of the
+     * {@link android.telephony.SubscriptionInfo} with the access status.
+     *
+     * Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns
+     * true). To check for permissions for non-embedded subscription as well,
+     * see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}.
+     *
+     * @param info The subscription to check.
+     * @param packageName Package name of the app to check.
+     *
+     * @return whether the app is authorized to manage this subscription per its access rules.
+     * @see android.telephony.TelephonyManager#hasCarrierPrivileges
+     * @hide
+     */
+    @SystemApi
+    public boolean canManageSubscription(@NonNull SubscriptionInfo info,
+            @NonNull String packageName) {
+        if (info == null || info.getAccessRules() == null || packageName == null) {
+            return false;
+        }
+        PackageManager packageManager = mContext.getPackageManager();
+        PackageInfo packageInfo;
+        try {
+            packageInfo = packageManager.getPackageInfo(packageName,
+                PackageManager.GET_SIGNING_CERTIFICATES);
+        } catch (PackageManager.NameNotFoundException e) {
+            logd("Unknown package: " + packageName);
+            return false;
+        }
+        for (UiccAccessRule rule : info.getAccessRules()) {
+            if (rule.getCarrierPrivilegeStatus(packageInfo)
+                    == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Set which subscription is preferred for cellular data.
+     * It's also usually the subscription we set up internet connection on.
+     *
+     * PreferredData overwrites user setting of default data subscription. And it's used
+     * by AlternativeNetworkService or carrier apps to switch primary and CBRS
+     * subscription dynamically in multi-SIM devices.
+     *
+     * @param subId which subscription is preferred to for cellular data. If it's
+     *              {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, it means
+     *              it's unset and {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *              is used to determine which modem is preferred.
+     * @param needValidation whether Telephony will wait until the network is validated by
+     *              connectivity service before switching data to it. More details see
+     *              {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED}.
+     * @param executor The executor of where the callback will execute.
+     * @param callback Callback will be triggered once it succeeds or failed.
+     *                 Pass null if don't care about the result.
+     *
+     * @throws IllegalStateException when subscription manager service is not available.
+     * @throws SecurityException when clients do not have MODIFY_PHONE_STATE permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setPreferredDataSubscriptionId(int subId, boolean needValidation,
+            @Nullable @CallbackExecutor Executor executor, @Nullable
+            @TelephonyManager.SetOpportunisticSubscriptionResult Consumer<Integer> callback) {
+        if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId);
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub == null) {
+                throw new IllegalStateException("subscription manager service is null.");
+            }
+
+            ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() {
+                @Override
+                public void onComplete(int result) {
+                    if (executor == null || callback == null) {
+                        return;
+                    }
+                    final long identity = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> {
+                            callback.accept(result);
+                        });
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
+                }
+            };
+            iSub.setPreferredDataSubscriptionId(subId, needValidation, callbackStub);
+        } catch (RemoteException ex) {
+            loge("setPreferredDataSubscriptionId RemoteException=" + ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get which subscription is preferred for cellular data.
+     * It's also usually the subscription we set up internet connection on.
+     *
+     * PreferredData overwrites user setting of default data subscription. And it's used
+     * by AlternativeNetworkService or carrier apps to switch primary and CBRS
+     * subscription dynamically in multi-SIM devices.
+     *
+     * @return preferred subscription id for cellular data. {@link DEFAULT_SUBSCRIPTION_ID} if
+     * there's no prefered subscription.
+     *
+     * @hide
+     *
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public int getPreferredDataSubscriptionId() {
+        int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                preferredSubId = iSub.getPreferredDataSubscriptionId();
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return preferredSubId;
+    }
+
+    /**
+     * Return opportunistic subscriptions that can be visible to the caller.
+     * Opportunistic subscriptions are for opportunistic networks, which are cellular
+     * networks with limited capabilities and coverage, for example, CBRS.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @return the list of opportunistic subscription info. If none exists, an empty list.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public @NonNull List<SubscriptionInfo> getOpportunisticSubscriptions() {
+        String contextPkg = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        String contextAttributionTag = mContext != null ? mContext.getAttributionTag() : null;
+        List<SubscriptionInfo> subInfoList = null;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                subInfoList = iSub.getOpportunisticSubscriptions(contextPkg,
+                        contextAttributionTag);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        if (subInfoList == null) {
+            subInfoList = new ArrayList<>();
+        }
+
+        return subInfoList;
+    }
+
+    /**
+     * Switch to a certain subscription
+     *
+     *  @param subId sub id
+     *  @param callbackIntent pending intent that will be sent after operation is done.
+     *
+     *  @deprecated this API is a duplicate of {@link EuiccManager#switchToSubscription(int,
+     *  PendingIntent)} and does not support Multiple Enabled Profile(MEP). Apps should use
+     *  {@link EuiccManager#switchToSubscription(int, PendingIntent)} or
+     *  {@link EuiccManager#switchToSubscription(int, int, PendingIntent)} instead.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
+    @Deprecated
+    public void switchToSubscription(int subId, @NonNull PendingIntent callbackIntent) {
+        Preconditions.checkNotNull(callbackIntent, "callbackIntent cannot be null");
+        EuiccManager euiccManager = new EuiccManager(mContext);
+        euiccManager.switchToSubscription(subId, callbackIntent);
+    }
+
+    /**
+     * Set whether a subscription is opportunistic, that is, whether the network it connects
+     * to has limited coverage. For example, CBRS. Setting a subscription opportunistic has
+     * following impacts:
+     *  1) Even if it's active, it will be dormant most of the time. The modem will not try
+     *     to scan or camp until it knows an available network is nearby to save power.
+     *  2) Telephony relies on system app or carrier input to notify nearby available networks.
+     *     See {@link TelephonyManager#updateAvailableNetworks(List, Executor, Consumer)}
+     *     for more information.
+     *  3) In multi-SIM devices, when the network is nearby and camped, system may automatically
+     *     switch internet data between it and default data subscription, based on carrier
+     *     recommendation and its signal strength and metered-ness, etc.
+     *
+     *
+     * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
+     * privilege permission of the subscription.
+     *
+     * @param opportunistic whether it’s opportunistic subscription.
+     * @param subId the unique SubscriptionInfo index in database
+     * @return {@code true} if the operation is succeed, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public boolean setOpportunistic(boolean opportunistic, int subId) {
+        if (VDBG) logd("[setOpportunistic]+ opportunistic:" + opportunistic + " subId:" + subId);
+        return setSubscriptionPropertyHelper(subId, "setOpportunistic",
+                (iSub)-> iSub.setOpportunistic(
+                        opportunistic, subId, mContext.getOpPackageName())) == 1;
+    }
+
+    /**
+     * Inform SubscriptionManager that subscriptions in the list are bundled
+     * as a group. It can be multiple primary (non-opportunistic) subscriptions,
+     * or one or more primary plus one or more opportunistic subscriptions.
+     *
+     * This API will always create a new immutable group and assign group UUID to all the
+     * subscriptions, regardless whether they are in a group already or not.
+     *
+     * Grouped subscriptions will have below behaviors:
+     * 1) They will share the same user settings.
+     * 2) The opportunistic subscriptions in the group is considered invisible and will not
+     *    return from {@link #getActiveSubscriptionInfoList()}, unless caller has carrier
+     *    privilege permission of the subscriptions.
+     * 3) The opportunistic subscriptions in the group can't be active by itself. If all other
+     *    non-opportunistic ones are deactivated (unplugged or disabled in Settings),
+     *    the opportunistic ones will be deactivated automatically.
+     *
+     * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     * permission or had carrier privilege permission on the subscriptions:
+     * {@link TelephonyManager#hasCarrierPrivileges()} or
+     * {@link #canManageSubscription(SubscriptionInfo)}
+     *
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
+     * @throws IllegalArgumentException if any of the subscriptions in the list doesn't exist.
+     * @throws IllegalStateException if Telephony service is in bad state.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
+     * @param subIdList list of subId that will be in the same group
+     * @return groupUUID a UUID assigned to the subscription group.
+     *
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public @NonNull ParcelUuid createSubscriptionGroup(@NonNull List<Integer> subIdList) {
+        Preconditions.checkNotNull(subIdList, "can't create group for null subId list");
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        if (VDBG) {
+            logd("[createSubscriptionGroup]");
+        }
+
+        ParcelUuid groupUuid = null;
+        int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                groupUuid = iSub.createSubscriptionGroup(subIdArray, pkgForDebug);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("createSubscriptionGroup RemoteException " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+
+        return groupUuid;
+    }
+
+    /**
+     * Add a list of subscriptions into a group.
+     * See {@link #createSubscriptionGroup(List)} for more details.
+     *
+     * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     * permission or had carrier privilege permission on the subscriptions:
+     * {@link TelephonyManager#hasCarrierPrivileges()} or
+     * {@link #canManageSubscription(SubscriptionInfo)}
+     *
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
+     * @throws IllegalArgumentException if the some subscriptions in the list doesn't exist.
+     * @throws IllegalStateException if Telephony service is in bad state.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
+     * @param subIdList list of subId that need adding into the group
+     * @param groupUuid the groupUuid the subscriptions are being added to.
+     *
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void addSubscriptionsIntoGroup(@NonNull List<Integer> subIdList,
+            @NonNull ParcelUuid groupUuid) {
+        Preconditions.checkNotNull(subIdList, "subIdList can't be null.");
+        Preconditions.checkNotNull(groupUuid, "groupUuid can't be null.");
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        if (VDBG) {
+            logd("[addSubscriptionsIntoGroup]");
+        }
+
+        int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.addSubscriptionsIntoGroup(subIdArray, groupUuid, pkgForDebug);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("addSubscriptionsIntoGroup RemoteException " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    private boolean isSystemProcess() {
+        return Process.myUid() == Process.SYSTEM_UID;
+    }
+
+    /**
+     * Remove a list of subscriptions from their subscription group.
+     *
+     * Caller will either have {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     * permission or has carrier privilege permission on all of the subscriptions provided in
+     * {@code subIdList}.
+     *
+     * @param subIdList list of subId that need removing from their groups.
+     * @param groupUuid The UUID of the subscription group.
+     *
+     * @throws SecurityException if the caller doesn't meet the requirements outlined above.
+     * @throws IllegalArgumentException if the some subscriptions in the list doesn't belong the
+     * specified group.
+     * @throws IllegalStateException if Telephony service is in bad state.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
+     * @see #createSubscriptionGroup(List)
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void removeSubscriptionsFromGroup(@NonNull List<Integer> subIdList,
+            @NonNull ParcelUuid groupUuid) {
+        Preconditions.checkNotNull(subIdList, "subIdList can't be null.");
+        Preconditions.checkNotNull(groupUuid, "groupUuid can't be null.");
+        String callingPackage = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        if (VDBG) {
+            logd("[removeSubscriptionsFromGroup]");
+        }
+
+        int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.removeSubscriptionsFromGroup(subIdArray, groupUuid, callingPackage);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("removeSubscriptionsFromGroup RemoteException " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Get subscriptionInfo list of subscriptions that are in the same group of given subId.
+     *
+     * Caller must have {@link android.Manifest.permission#READ_PHONE_STATE}
+     * or carrier privilege permission on the subscription.
+     * {@link TelephonyManager#hasCarrierPrivileges()}
+     *
+     * <p>Starting with API level 33, the caller also needs permission to access device identifiers
+     * to get the list of subscriptions associated with a group UUID.
+     * This method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the app has carrier privilege permission.
+     *     {@link TelephonyManager#hasCarrierPrivileges()}
+     *     <li>If the app has {@link android.Manifest.permission#READ_PHONE_STATE} permission and
+     *     access to device identifiers.
+     * </ul>
+     *
+     * @throws IllegalStateException if Telephony service is in bad state.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
+     * @param groupUuid of which list of subInfo will be returned.
+     * @return list of subscriptionInfo that belong to the same group, including the given
+     * subscription itself. It will return an empty list if no subscription belongs to the group.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public @NonNull List<SubscriptionInfo> getSubscriptionsInGroup(@NonNull ParcelUuid groupUuid) {
+        Preconditions.checkNotNull(groupUuid, "groupUuid can't be null");
+        String contextPkg = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        String contextAttributionTag = mContext != null ? mContext.getAttributionTag() : null;
+        if (VDBG) {
+            logd("[getSubscriptionsInGroup]+ groupUuid:" + groupUuid);
+        }
+
+        List<SubscriptionInfo> result = null;
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg,
+                        contextAttributionTag);
+            } else {
+                if (!isSystemProcess()) {
+                    throw new IllegalStateException("telephony service is null.");
+                }
+            }
+        } catch (RemoteException ex) {
+            loge("removeSubscriptionsFromGroup RemoteException " + ex);
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
+        }
+
+        // TODO(b/296125268) Really this method should throw, but it's common enough that for
+        // system callers it's worth having a little magic for the system process until it's
+        // made safer.
+        if (result == null) result = Collections.emptyList();
+
+        return result;
+    }
+
+    /**
+     * Whether a subscription is visible to API caller. If it's a bundled opportunistic
+     * subscription, it should be hidden anywhere in Settings, dialer, status bar etc.
+     * Exception is if caller owns carrier privilege, in which case they will
+     * want to see their own hidden subscriptions.
+     *
+     * @param info the subscriptionInfo to check against.
+     *
+     * @return {@code true} if this subscription should be visible to the API caller.
+     *
+     * @hide
+     */
+    public boolean isSubscriptionVisible(SubscriptionInfo info) {
+        if (info == null) return false;
+        // If subscription is NOT grouped opportunistic subscription, it's visible.
+        if (info.getGroupUuid() == null || !info.isOpportunistic()) return true;
+
+        // If the caller is the carrier app and owns the subscription, it should be visible
+        // to the caller.
+        boolean hasCarrierPrivilegePermission = TelephonyManager.from(mContext)
+                .hasCarrierPrivileges(info.getSubscriptionId())
+                || canManageSubscription(info);
+        return hasCarrierPrivilegePermission;
+    }
+
+    /**
+     * Return a list of subscriptions that are available and visible to the user.
+     * Used by Settings app to show a list of subscriptions for user to pick.
+     *
+     * <p>
+     * Permissions android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required
+     * for getSelectableSubscriptionInfoList to be invoked.
+     * @return list of user selectable subscriptions.
+     *
+     * @hide
+     */
+    public @Nullable List<SubscriptionInfo> getSelectableSubscriptionInfoList() {
+        List<SubscriptionInfo> availableList = getAvailableSubscriptionInfoList();
+        if (availableList == null) {
+            return null;
+        } else {
+            // Multiple subscriptions in a group should only have one representative.
+            // It should be the current active primary subscription if any, or any
+            // primary subscription.
+            List<SubscriptionInfo> selectableList = new ArrayList<>();
+            Map<ParcelUuid, SubscriptionInfo> groupMap = new HashMap<>();
+
+            for (SubscriptionInfo info : availableList) {
+                // Grouped opportunistic subscriptions are considered invisible
+                // to users so they should never be returned.
+                if (info.getGroupUuid() != null && info.isOpportunistic()) continue;
+
+                ParcelUuid groupUuid = info.getGroupUuid();
+                if (groupUuid == null) {
+                    // Doesn't belong to any group. Add in the list.
+                    selectableList.add(info);
+                } else if (!groupMap.containsKey(groupUuid)
+                        || (groupMap.get(groupUuid).getSimSlotIndex() == INVALID_SIM_SLOT_INDEX
+                        && info.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX)) {
+                    // If it belongs to a group that has never been recorded or it's the current
+                    // active subscription, add it in the list.
+                    selectableList.remove(groupMap.get(groupUuid));
+                    selectableList.add(info);
+                    groupMap.put(groupUuid, info);
+                }
+
+            }
+            return selectableList;
+        }
+    }
+
+    /**
+     * Enable or disable a subscription. This method is same as
+     * {@link #setUiccApplicationsEnabled(int, boolean)}.
+     *
+     * @param subscriptionId Subscription to be enabled or disabled.
+     * @param enable whether user is turning it on or off.
+     *
+     * @return whether the operation is successful.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public boolean setSubscriptionEnabled(int subscriptionId, boolean enable) {
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.setUiccApplicationsEnabled(enable, subscriptionId);
+            }
+        } catch (RemoteException ex) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Set uicc applications being enabled or disabled.
+     * The value will be remembered on the subscription and will be applied whenever it's present.
+     * If the subscription in currently present, it will also apply the setting to modem
+     * immediately (the setting in the modem will not change until the modem receives and responds
+     * to the request, but typically this should only take a few seconds. The user visible setting
+     * available from SubscriptionInfo.areUiccApplicationsEnabled() will be updated
+     * immediately.)
+     *
+     * @param subscriptionId which subscription to operate on.
+     * @param enabled whether uicc applications are enabled or disabled.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setUiccApplicationsEnabled(int subscriptionId, boolean enabled) {
+        if (VDBG) {
+            logd("setUiccApplicationsEnabled subId= " + subscriptionId + " enable " + enabled);
+        }
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.setUiccApplicationsEnabled(enabled, subscriptionId);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Whether it's supported to disable / re-enable a subscription on a physical (non-euicc) SIM.
+     *
+     * Physical SIM refers non-euicc, or aka non-programmable SIM.
+     *
+     * It provides whether a physical SIM card can be disabled without taking it out, which is done
+     * via {@link #setSubscriptionEnabled(int, boolean)} API.
+     *
+     * @return whether can disable subscriptions on physical SIMs.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean canDisablePhysicalSubscription() {
+        if (VDBG) {
+            logd("canDisablePhysicalSubscription");
+        }
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                return iSub.canDisablePhysicalSubscription();
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return false;
+    }
+
+    /**
+     * Check if the subscription is currently active in any slot.
+     *
+     * @param subscriptionId The subscription id.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isSubscriptionEnabled(int subscriptionId) {
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                return iSub.isSubscriptionEnabled(subscriptionId);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return false;
+    }
+
+    /**
+     * Set the device to device status sharing user preference for a subscription id. The setting
+     * app uses this method to indicate with whom they wish to share device to device status
+     * information.
+     *
+     * @param subscriptionId The subscription id.
+     * @param sharing The status sharing preference.
+     *
+     * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setDeviceToDeviceStatusSharingPreference(int subscriptionId,
+            @DeviceToDeviceStatusSharingPreference int sharing) {
+        if (VDBG) {
+            logd("[setDeviceToDeviceStatusSharing] + sharing: " + sharing + " subId: "
+                    + subscriptionId);
+        }
+        setSubscriptionPropertyHelper(subscriptionId, "setDeviceToDeviceSharingStatus",
+                (iSub)->iSub.setDeviceToDeviceStatusSharing(sharing, subscriptionId));
+    }
+
+    /**
+     * Returns the user-chosen device to device status sharing preference
+     * @param subscriptionId Subscription id of subscription
+     * @return The device to device status sharing preference
+     *
+     * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    public @DeviceToDeviceStatusSharingPreference int getDeviceToDeviceStatusSharingPreference(
+            int subscriptionId) {
+        if (VDBG) {
+            logd("[getDeviceToDeviceStatusSharing] + subId: " + subscriptionId);
+        }
+        return getIntegerSubscriptionProperty(subscriptionId, D2D_STATUS_SHARING,
+                D2D_SHARING_DISABLED, mContext);
+    }
+
+    /**
+     * Set the list of contacts that allow device to device status sharing for a subscription id.
+     * The setting app uses this method to indicate with whom they wish to share device to device
+     * status information.
+     *
+     * @param subscriptionId The subscription id.
+     * @param contacts The list of contacts that allow device to device status sharing.
+     *
+     * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setDeviceToDeviceStatusSharingContacts(int subscriptionId,
+            @NonNull List<Uri> contacts) {
+        String contactString = serializeUriLists(contacts);
+        if (VDBG) {
+            logd("[setDeviceToDeviceStatusSharingContacts] + contacts: " + contactString
+                    + " subId: " + subscriptionId);
+        }
+        setSubscriptionPropertyHelper(subscriptionId, "setDeviceToDeviceSharingStatus",
+                (iSub)->iSub.setDeviceToDeviceStatusSharingContacts(serializeUriLists(contacts),
+                        subscriptionId));
+    }
+
+    /**
+     * Get the list of contacts that allow device to device status sharing.
+     *
+     * @param subscriptionId Subscription id.
+     *
+     * @return The list of contacts that allow device to device status sharing.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    public @NonNull List<Uri> getDeviceToDeviceStatusSharingContacts(int subscriptionId) {
+        String result = getStringSubscriptionProperty(mContext, subscriptionId,
+                D2D_STATUS_SHARING_SELECTED_CONTACTS);
+        if (result != null) {
+            try {
+                byte[] b = Base64.decode(result, Base64.DEFAULT);
+                ByteArrayInputStream bis = new ByteArrayInputStream(b);
+                ObjectInputStream ois = new ObjectInputStream(bis);
+                List<String> contacts = ArrayList.class.cast(ois.readObject());
+                List<Uri> uris = new ArrayList<>();
+                for (String contact : contacts) {
+                    uris.add(Uri.parse(contact));
+                }
+                return uris;
+            } catch (IOException e) {
+                logd("getDeviceToDeviceStatusSharingContacts IO exception");
+            } catch (ClassNotFoundException e) {
+                logd("getDeviceToDeviceStatusSharingContacts ClassNotFound exception");
+            }
+        }
+        return new ArrayList<>();
+    }
+
+    /**
+     * Get the active subscription id by logical SIM slot index.
+     *
+     * @param slotIndex The logical SIM slot index.
+     * @return The active subscription id.
+     *
+     * @throws IllegalArgumentException if the provided slot index is invalid.
+     * @throws SecurityException if callers do not hold the required permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public int getEnabledSubscriptionId(int slotIndex) {
+        int subId = INVALID_SUBSCRIPTION_ID;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                subId = iSub.getEnabledSubscriptionId(slotIndex);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        if (VDBG) logd("getEnabledSubscriptionId, subId = " + subId);
+        return subId;
+    }
+
+    private interface CallISubMethodHelper {
+        int callMethod(ISub iSub) throws RemoteException;
+    }
+
+    private int setSubscriptionPropertyHelper(int subId, String methodName,
+            CallISubMethodHelper helper) {
+        if (!isValidSubscriptionId(subId)) {
+            logd("[" + methodName + "]" + "- fail");
+            return -1;
+        }
+
+        int result = 0;
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                result = helper.callMethod(iSub);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return result;
+    }
+
+    /**
+     * Get active data subscription id. Active data subscription refers to the subscription
+     * currently chosen to provide cellular internet connection to the user. This may be
+     * different from {@link #getDefaultDataSubscriptionId()}.
+     *
+     * @return Active data subscription id if any is chosen, or {@link #INVALID_SUBSCRIPTION_ID} if
+     * not.
+     *
+     * @see TelephonyCallback.ActiveDataSubscriptionIdListener
+     */
+    public static int getActiveDataSubscriptionId() {
+        return sGetActiveDataSubscriptionIdCache.query(null);
+    }
+
+    /**
+     * Helper method that puts a subscription id on an intent with the constants:
+     * PhoneConstant.SUBSCRIPTION_KEY and SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX.
+     * Both constants are used to support backwards compatibility.  Once we know we got all places,
+     * we can remove PhoneConstants.SUBSCRIPTION_KEY.
+     * @param intent Intent to put sub id on.
+     * @param subId SubscriptionId to put on intent.
+     *
+     * @hide
+     */
+    public static void putSubscriptionIdExtra(Intent intent, int subId) {
+        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
+        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+    }
+
+    /** @hide */
+    public static void invalidateSubscriptionManagerServiceCaches() {
+        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY);
+    }
+
+    /**
+     * Allows a test process to disable client-side caching operations.
+     *
+     * @hide
+     */
+    public static void disableCaching() {
+        sGetDefaultSubIdCacheAsUser.disableLocal();
+        sGetDefaultDataSubIdCache.disableLocal();
+        sGetActiveDataSubscriptionIdCache.disableLocal();
+        sGetDefaultSmsSubIdCacheAsUser.disableLocal();
+        sGetSlotIndexCache.disableLocal();
+        sGetSubIdCache.disableLocal();
+        sGetPhoneIdCache.disableLocal();
+    }
+
+    /**
+     * Clears all process-local binder caches.
+     *
+     * @hide */
+    public static void clearCaches() {
+        sGetDefaultSubIdCacheAsUser.clear();
+        sGetDefaultDataSubIdCache.clear();
+        sGetActiveDataSubscriptionIdCache.clear();
+        sGetDefaultSmsSubIdCacheAsUser.clear();
+        sGetSlotIndexCache.clear();
+        sGetSubIdCache.clear();
+        sGetPhoneIdCache.clear();
+    }
+
+    /**
+     * Called to retrieve SIM-specific settings data to be backed up.
+     *
+     * @return data in byte[] to be backed up.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public byte[] getAllSimSpecificSettingsForBackup() {
+        Bundle bundle =  mContext.getContentResolver().call(
+                SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME, null, null);
+        return bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA);
+    }
+
+    /**
+     * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
+     * configs to device for all existing SIMs in the subscription database {@link SimInfo}.
+     * Internally, it will store the backup data in an internal file. This file will persist on
+     * device for device's lifetime and will be used later on when a SIM is inserted to restore that
+     * specific SIM's settings. End result is subscription database is modified to match any backed
+     * up configs for the appropriate inserted SIMs.
+     *
+     * <p>
+     * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any
+     * {@link SimInfo} entry is updated as the result of this method call.
+     *
+     * @param data with the sim specific configs to be backed up.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) {
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.restoreAllSimSpecificSettingsFromBackup(data);
+            } else {
+                throw new IllegalStateException("subscription service unavailable.");
+            }
+        } catch (RemoteException ex) {
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
+        }
+    }
+
+    /**
+     * Returns the phone number for the given {@code subscriptionId} and {@code source},
+     * or an empty string if not available.
+     *
+     * <p>General apps that need to know the phone number should use {@link #getPhoneNumber(int)}
+     * instead. This API may be suitable specific apps that needs to know the phone number from
+     * a specific source. For example, a carrier app needs to know exactly what's on
+     * {@link #PHONE_NUMBER_SOURCE_UICC UICC} and decide if the previously set phone number
+     * of source {@link #PHONE_NUMBER_SOURCE_CARRIER carrier} should be updated.
+     *
+     * <p>The API provides no guarantees of what format the number is in: the format can vary
+     * depending on the {@code source} and the network etc. Programmatic parsing should be done
+     * cautiously, for example, after formatting the number to a consistent format with
+     * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}.
+     *
+     * <p>Note the assumption is that one subscription (which usually means one SIM) has
+     * only one phone number. The multiple sources backup each other so hopefully at least one
+     * is available. For example, for a carrier that doesn't typically set phone numbers
+     * on {@link #PHONE_NUMBER_SOURCE_UICC UICC}, the source {@link #PHONE_NUMBER_SOURCE_IMS IMS}
+     * may provide one. Or, a carrier may decide to provide the phone number via source
+     * {@link #PHONE_NUMBER_SOURCE_CARRIER carrier} if neither source UICC nor IMS is available.
+     *
+     * <p>The availability and correctness of the phone number depends on the underlying source
+     * and the network etc. Additional verification is needed to use this number for
+     * security-related or other sensitive scenarios.
+     *
+     * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
+     * for the default one.
+     * @param source the source of the phone number, one of the PHONE_NUMBER_SOURCE_* constants.
+     *
+     * @return the phone number, or an empty string if not available.
+     *
+     * @throws IllegalArgumentException if {@code source} is invalid.
+     * @throws IllegalStateException if the telephony process is not currently available.
+     * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
+     * @see #PHONE_NUMBER_SOURCE_UICC
+     * @see #PHONE_NUMBER_SOURCE_CARRIER
+     * @see #PHONE_NUMBER_SOURCE_IMS
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PHONE_NUMBERS,
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            "carrier privileges",
+    })
+    @NonNull
+    public String getPhoneNumber(int subscriptionId, @PhoneNumberSource int source) {
+        if (subscriptionId == DEFAULT_SUBSCRIPTION_ID) {
+            subscriptionId = getDefaultSubscriptionId();
+        }
+        if (source != PHONE_NUMBER_SOURCE_UICC
+                && source != PHONE_NUMBER_SOURCE_CARRIER
+                && source != PHONE_NUMBER_SOURCE_IMS) {
+            throw new IllegalArgumentException("invalid source " + source);
+        }
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                return iSub.getPhoneNumber(subscriptionId, source,
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
+            } else {
+                throw new IllegalStateException("subscription service unavailable.");
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Returns the phone number for the given {@code subId}, or an empty string if
+     * not available.
+     *
+     * <p>This API is suitable for general apps that needs to know the phone number.
+     * For specific apps that needs to know the phone number provided by a specific source,
+     * {@link #getPhoneNumber(int, int)} may be suitable.
+     *
+     * <p>This API is built up on {@link #getPhoneNumber(int, int)}, but picks
+     * from available sources in the following order: {@link #PHONE_NUMBER_SOURCE_CARRIER}
+     * > {@link #PHONE_NUMBER_SOURCE_UICC} > {@link #PHONE_NUMBER_SOURCE_IMS}.
+     *
+     * <p>The API provides no guarantees of what format the number is in: the format can vary
+     * depending on the underlying source and the network etc. Programmatic parsing should be done
+     * cautiously, for example, after formatting the number to a consistent format with
+     * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}.
+     *
+     * <p>The availability and correctness of the phone number depends on the underlying source
+     * and the network etc. Additional verification is needed to use this number for
+     * security-related or other sensitive scenarios.
+     *
+     * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
+     *                       for the default one.
+     * @return the phone number, or an empty string if not available.
+     *
+     * @throws IllegalStateException if the telephony process is not currently available.
+     * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
+     * @see #getPhoneNumber(int, int)
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PHONE_NUMBERS,
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            "carrier privileges",
+    })
+    @NonNull
+    public String getPhoneNumber(int subscriptionId) {
+        if (subscriptionId == DEFAULT_SUBSCRIPTION_ID) {
+            subscriptionId = getDefaultSubscriptionId();
+        }
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                return iSub.getPhoneNumberFromFirstAvailableSource(subscriptionId,
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
+            } else {
+                throw new IllegalStateException("subscription service unavailable.");
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Sets the phone number for the given {@code subId} for source
+     * {@link #PHONE_NUMBER_SOURCE_CARRIER carrier}.
+     * Sets an empty string to remove the previously set phone number.
+     *
+     * <p>The API is suitable for carrier apps to provide a phone number, for example when
+     * it's not possible to update {@link #PHONE_NUMBER_SOURCE_UICC UICC} directly.
+     *
+     * <p>It's recommended that the phone number is formatted to well-known formats,
+     * for example, by {@link PhoneNumberUtils} {@code formatNumber*} methods.
+     *
+     * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
+     *                       for the default one.
+     * @param number the phone number, or an empty string to remove the previously set number.
+     * @throws IllegalStateException if the telephony process is not currently available.
+     * @throws NullPointerException if {@code number} is {@code null}.
+     * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresPermission("carrier privileges")
+    public void setCarrierPhoneNumber(int subscriptionId, @NonNull String number) {
+        if (subscriptionId == DEFAULT_SUBSCRIPTION_ID) {
+            subscriptionId = getDefaultSubscriptionId();
+        }
+        if (number == null) {
+            throw new NullPointerException("invalid number null");
+        }
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.setPhoneNumber(subscriptionId, PHONE_NUMBER_SOURCE_CARRIER, number,
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
+            } else {
+                throw new IllegalStateException("subscription service unavailable.");
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the preferred usage setting.
+     *
+     * The cellular usage setting is a switch which controls the mode of operation for the cellular
+     * radio to either require or not require voice service. It is not managed via Android’s
+     * Settings.
+     *
+     * @param subscriptionId the subId of the subscription.
+     * @param usageSetting the requested usage setting.
+     *
+     * @throws IllegalStateException if a specific mode or setting the mode is not supported on a
+     * particular device.
+     *
+     * <p>Requires {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     * or that the calling app has CarrierPrivileges for the given subscription.
+     *
+     * Note: This method will not allow the setting of USAGE_SETTING_UNKNOWN.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    void setUsageSetting(int subscriptionId, @UsageSetting int usageSetting) {
+        if (VDBG) logd("[setUsageSetting]+ setting:" + usageSetting + " subId:" + subscriptionId);
+        setSubscriptionPropertyHelper(subscriptionId, "setUsageSetting",
+                (iSub)-> iSub.setUsageSetting(
+                        usageSetting, subscriptionId, mContext.getOpPackageName()));
+    }
+
+    /**
+     * Convert phone number source to string.
+     *
+     * @param source The phone name source.
+     *
+     * @return The phone name source in string format.
+     *
+     * @hide
+     */
+    @NonNull
+    public static String phoneNumberSourceToString(@PhoneNumberSource int source) {
+        switch (source) {
+            case SubscriptionManager.PHONE_NUMBER_SOURCE_UICC: return "UICC";
+            case SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER: return "CARRIER";
+            case SubscriptionManager.PHONE_NUMBER_SOURCE_IMS: return "IMS";
+            default:
+                return "UNKNOWN(" + source + ")";
+        }
+    }
+
+    /**
+     * Convert display name source to string.
+     *
+     * @param source The display name source.
+     * @return The display name source in string format.
+     *
+     * @hide
+     */
+    @NonNull
+    public static String displayNameSourceToString(
+            @SubscriptionManager.SimDisplayNameSource int source) {
+        switch (source) {
+            case SubscriptionManager.NAME_SOURCE_UNKNOWN: return "UNKNOWN";
+            case SubscriptionManager.NAME_SOURCE_CARRIER_ID: return "CARRIER_ID";
+            case SubscriptionManager.NAME_SOURCE_SIM_SPN: return "SIM_SPN";
+            case SubscriptionManager.NAME_SOURCE_USER_INPUT: return "USER_INPUT";
+            case SubscriptionManager.NAME_SOURCE_CARRIER: return "CARRIER";
+            case SubscriptionManager.NAME_SOURCE_SIM_PNN: return "SIM_PNN";
+            default:
+                return "UNKNOWN(" + source + ")";
+        }
+    }
+
+    /**
+     * Convert subscription type to string.
+     *
+     * @param type The subscription type.
+     * @return The subscription type in string format.
+     *
+     * @hide
+     */
+    @NonNull
+    public static String subscriptionTypeToString(@SubscriptionManager.SubscriptionType int type) {
+        switch (type) {
+            case SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM: return "LOCAL_SIM";
+            case SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM: return "REMOTE_SIM";
+            default:
+                return "UNKNOWN(" + type + ")";
+        }
+    }
+
+    /**
+     * Convert usage setting to string.
+     *
+     * @param usageSetting Usage setting.
+     * @return The usage setting in string format.
+     *
+     * @hide
+     */
+    @NonNull
+    public static String usageSettingToString(@SubscriptionManager.UsageSetting int usageSetting) {
+        switch (usageSetting) {
+            case SubscriptionManager.USAGE_SETTING_UNKNOWN: return "UNKNOWN";
+            case SubscriptionManager.USAGE_SETTING_DEFAULT: return "DEFAULT";
+            case SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC: return "VOICE_CENTRIC";
+            case SubscriptionManager.USAGE_SETTING_DATA_CENTRIC: return "DATA_CENTRIC";
+            default:
+                return "UNKNOWN(" + usageSetting + ")";
+        }
+    }
+
+    /**
+     * Set owner for this subscription.
+     *
+     * @param subscriptionId the subId of the subscription.
+     * @param groupOwner The group owner to assign to the subscription
+     *
+     * @throws SecurityException if caller is not authorized.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setGroupOwner(int subscriptionId, @NonNull String groupOwner) {
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.setGroupOwner(subscriptionId, groupOwner);
+            } else {
+                throw new IllegalStateException("[setGroupOwner]: "
+                        + "subscription service unavailable");
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set userHandle for a subscription.
+     *
+     * Used to set an association between a subscription and a user on the device so that voice
+     * calling and SMS from that subscription can be associated with that user.
+     * Data services are always shared between users on the device.
+     *
+     * @param subscriptionId the subId of the subscription.
+     * @param userHandle the userHandle associated with the subscription.
+     * Pass {@code null} user handle to clear the association.
+     *
+     * @throws IllegalArgumentException if subscription is invalid.
+     * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws IllegalStateException if subscription service is not available.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
+    public void setSubscriptionUserHandle(int subscriptionId, @Nullable UserHandle userHandle) {
+        if (!isValidSubscriptionId(subscriptionId)) {
+            throw new IllegalArgumentException("[setSubscriptionUserHandle]: "
+                    + "Invalid subscriptionId: " + subscriptionId);
+        }
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.setSubscriptionUserHandle(userHandle, subscriptionId);
+            } else {
+                throw new IllegalStateException("[setSubscriptionUserHandle]: "
+                        + "subscription service unavailable");
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Get UserHandle of this subscription.
+     *
+     * Used to get user handle associated with this subscription.
+     *
+     * @param subscriptionId the subId of the subscription.
+     * @return userHandle associated with this subscription
+     * or {@code null} if subscription is not associated with any user.
+     *
+     * @throws IllegalArgumentException if subscription is invalid.
+     * @throws SecurityException if the caller doesn't have permissions required.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
+    public @Nullable UserHandle getSubscriptionUserHandle(int subscriptionId) {
+        if (!isValidSubscriptionId(subscriptionId)) {
+            throw new IllegalArgumentException("[getSubscriptionUserHandle]: "
+                    + "Invalid subscriptionId: " + subscriptionId);
+        }
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                return iSub.getSubscriptionUserHandle(subscriptionId);
+            } else {
+                Log.e(LOG_TAG, "[getSubscriptionUserHandle]: subscription service unavailable");
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+        return null;
+    }
+
+    /**
+     * Check if subscription and user are associated with each other.
+     *
+     * @param subscriptionId the subId of the subscription
+     * @param userHandle user handle of the user
+     * @return {@code true} if subscription is associated with user
+     * else {@code false} if subscription is not associated with user.
+     *
+     * @throws IllegalArgumentException if subscription doesn't exist.
+     * @throws SecurityException if the caller doesn't have permissions required.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
+    public boolean isSubscriptionAssociatedWithUser(int subscriptionId,
+            @NonNull UserHandle userHandle) {
+        if (!isValidSubscriptionId(subscriptionId)) {
+            throw new IllegalArgumentException("[isSubscriptionAssociatedWithUser]: "
+                    + "Invalid subscriptionId: " + subscriptionId);
+        }
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                return iSub.isSubscriptionAssociatedWithUser(subscriptionId, userHandle);
+            } else {
+                Log.e(LOG_TAG, "[isSubscriptionAssociatedWithUser]: subscription service "
+                        + "unavailable");
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether the given subscription is associated with the calling user.
+     *
+     * @param subscriptionId the subscription ID of the subscription
+     * @return {@code true} if the subscription is associated with the user that the current process
+     *         is running in; {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if subscription doesn't exist.
+     * @throws SecurityException if the caller doesn't have permissions required.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @FlaggedApi(Flags.FLAG_SUBSCRIPTION_USER_ASSOCIATION_QUERY)
+    public boolean isSubscriptionAssociatedWithUser(int subscriptionId) {
+        if (!isValidSubscriptionId(subscriptionId)) {
+            throw new IllegalArgumentException("[isSubscriptionAssociatedWithCallingUser]: "
+                    + "Invalid subscriptionId: " + subscriptionId);
+        }
+
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                return iSub.isSubscriptionAssociatedWithCallingUser(subscriptionId);
+            } else {
+                throw new IllegalStateException("subscription service unavailable.");
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+        return false;
+    }
+
+    /**
+     * Get list of subscriptions associated with user.
+     *
+     * @param userHandle user handle of the user
+     * @return list of subscriptionInfo associated with the user.
+     *
+     * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws IllegalStateException if subscription service is not available.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
+    public @NonNull List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser(
+            @NonNull UserHandle userHandle) {
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                return iSub.getSubscriptionInfoListAssociatedWithUser(userHandle);
+            } else {
+                Log.e(LOG_TAG, "[getSubscriptionInfoListAssociatedWithUser]: "
+                        + "subscription service unavailable");
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+        return new ArrayList<>();
+    }
+
+    /**
+     * @return the bitmasks combination of all service capabilities.
+     * @hide
+     */
+    public static int getAllServiceCapabilityBitmasks() {
+        return SERVICE_CAPABILITY_VOICE_BITMASK | SERVICE_CAPABILITY_SMS_BITMASK
+                | SERVICE_CAPABILITY_DATA_BITMASK;
+    }
+
+    /**
+     * @return The set of service capability from a bitmask combined one.
+     * @hide
+     */
+    @NonNull
+    @ServiceCapability
+    public static Set<Integer> getServiceCapabilitiesSet(int combinedServiceCapabilities) {
+        Set<Integer> capabilities = new HashSet<>();
+        for (int i = SERVICE_CAPABILITY_VOICE; i <= SERVICE_CAPABILITY_MAX; i++) {
+            final int capabilityBitmask = serviceCapabilityToBitmask(i);
+            if ((combinedServiceCapabilities & capabilityBitmask) == capabilityBitmask) {
+                capabilities.add(i);
+            }
+        }
+        return Collections.unmodifiableSet(capabilities);
+    }
+
+    /**
+     * @return The service capability bitmask from a {@link ServiceCapability} value.
+     * @hide
+     */
+    public static int serviceCapabilityToBitmask(@ServiceCapability int capability) {
+        return 1 << (capability - 1);
+    }
+
+    /**
+     * Set the transfer status of the subscriptionInfo of the subId.
+     * @param subscriptionId The unique SubscriptionInfo key in database.
+     * @param status The transfer status to change.
+     *
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION)
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public void setTransferStatus(int subscriptionId, @TransferStatus int status) {
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.setTransferStatus(subscriptionId, status);
+            }
+        } catch (RemoteException ex) {
+            logd("setTransferStatus for subId = " + subscriptionId + " failed.");
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/android-35/android/telephony/SubscriptionPlan.java b/android-35/android/telephony/SubscriptionPlan.java
new file mode 100644
index 0000000..7b48a16
--- /dev/null
+++ b/android-35/android/telephony/SubscriptionPlan.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.annotation.BytesLong;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
+import android.util.Range;
+import android.util.RecurrenceRule;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Period;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Objects;
+
+/**
+ * Description of a billing relationship plan between a carrier and a specific
+ * subscriber. This information is used to present more useful UI to users, such
+ * as explaining how much mobile data they have remaining, and what will happen
+ * when they run out.
+ *
+ * If specifying network types, the developer must supply at least one plan
+ * that applies to all network types (default), and all additional plans
+ * may not include a particular network type more than once.
+ * This is enforced by {@link SubscriptionManager} when setting the plans.
+ *
+ * Plan selection will prefer plans that have specific network types defined
+ * over plans that apply to all network types.
+ *
+ * @see SubscriptionManager#setSubscriptionPlans(int, java.util.List)
+ * @see SubscriptionManager#getSubscriptionPlans(int)
+ */
+public final class SubscriptionPlan implements Parcelable {
+    /** {@hide} */
+    @IntDef(prefix = "LIMIT_BEHAVIOR_", value = {
+            LIMIT_BEHAVIOR_UNKNOWN,
+            LIMIT_BEHAVIOR_DISABLED,
+            LIMIT_BEHAVIOR_BILLED,
+            LIMIT_BEHAVIOR_THROTTLED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LimitBehavior {}
+
+    /** When a resource limit is hit, the behavior is unknown. */
+    public static final int LIMIT_BEHAVIOR_UNKNOWN = -1;
+    /** When a resource limit is hit, access is disabled. */
+    public static final int LIMIT_BEHAVIOR_DISABLED = 0;
+    /** When a resource limit is hit, the user is billed automatically. */
+    public static final int LIMIT_BEHAVIOR_BILLED = 1;
+    /** When a resource limit is hit, access is throttled to a slower rate. */
+    public static final int LIMIT_BEHAVIOR_THROTTLED = 2;
+
+    /** Value indicating a number of bytes is unknown. */
+    public static final long BYTES_UNKNOWN = -1;
+    /** Value indicating a number of bytes is unlimited. */
+    public static final long BYTES_UNLIMITED = Long.MAX_VALUE;
+
+    /** Value indicating a timestamp is unknown. */
+    public static final long TIME_UNKNOWN = -1;
+
+    private final RecurrenceRule cycleRule;
+    private CharSequence title;
+    private CharSequence summary;
+    private long dataLimitBytes = BYTES_UNKNOWN;
+    private int dataLimitBehavior = LIMIT_BEHAVIOR_UNKNOWN;
+    private long dataUsageBytes = BYTES_UNKNOWN;
+    private long dataUsageTime = TIME_UNKNOWN;
+    private @NetworkType int[] networkTypes;
+
+    private SubscriptionPlan(RecurrenceRule cycleRule) {
+        this.cycleRule = Preconditions.checkNotNull(cycleRule);
+        this.networkTypes = Arrays.copyOf(TelephonyManager.getAllNetworkTypes(),
+                TelephonyManager.getAllNetworkTypes().length);
+    }
+
+    private SubscriptionPlan(Parcel source) {
+        cycleRule = source.readParcelable(null, android.util.RecurrenceRule.class);
+        title = source.readCharSequence();
+        summary = source.readCharSequence();
+        dataLimitBytes = source.readLong();
+        dataLimitBehavior = source.readInt();
+        dataUsageBytes = source.readLong();
+        dataUsageTime = source.readLong();
+        networkTypes = source.createIntArray();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(cycleRule, flags);
+        dest.writeCharSequence(title);
+        dest.writeCharSequence(summary);
+        dest.writeLong(dataLimitBytes);
+        dest.writeInt(dataLimitBehavior);
+        dest.writeLong(dataUsageBytes);
+        dest.writeLong(dataUsageTime);
+        dest.writeIntArray(networkTypes);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder("SubscriptionPlan{")
+                .append("cycleRule=").append(cycleRule)
+                .append(" title=").append(title)
+                .append(" summary=").append(summary)
+                .append(" dataLimitBytes=").append(dataLimitBytes)
+                .append(" dataLimitBehavior=").append(dataLimitBehavior)
+                .append(" dataUsageBytes=").append(dataUsageBytes)
+                .append(" dataUsageTime=").append(dataUsageTime)
+                .append(" networkTypes=").append(Arrays.toString(networkTypes))
+                .append("}").toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior,
+                dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes));
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (obj instanceof SubscriptionPlan) {
+            final SubscriptionPlan other = (SubscriptionPlan) obj;
+            return Objects.equals(cycleRule, other.cycleRule)
+                    && Objects.equals(title, other.title)
+                    && Objects.equals(summary, other.summary)
+                    && dataLimitBytes == other.dataLimitBytes
+                    && dataLimitBehavior == other.dataLimitBehavior
+                    && dataUsageBytes == other.dataUsageBytes
+                    && dataUsageTime == other.dataUsageTime
+                    && Arrays.equals(networkTypes, other.networkTypes);
+        }
+        return false;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<SubscriptionPlan> CREATOR = new Parcelable.Creator<SubscriptionPlan>() {
+        @Override
+        public SubscriptionPlan createFromParcel(Parcel source) {
+            return new SubscriptionPlan(source);
+        }
+
+        @Override
+        public SubscriptionPlan[] newArray(int size) {
+            return new SubscriptionPlan[size];
+        }
+    };
+
+    /** {@hide} */
+    public @NonNull RecurrenceRule getCycleRule() {
+        return cycleRule;
+    }
+
+    /** Return the short title of this plan. */
+    public @Nullable CharSequence getTitle() {
+        return title;
+    }
+
+    /** Return the short summary of this plan. */
+    public @Nullable CharSequence getSummary() {
+        return summary;
+    }
+
+    /**
+     * Return the usage threshold at which data access changes according to
+     * {@link #getDataLimitBehavior()}.
+     */
+    public @BytesLong long getDataLimitBytes() {
+        return dataLimitBytes;
+    }
+
+    /**
+     * Return the behavior of data access when usage reaches
+     * {@link #getDataLimitBytes()}.
+     */
+    public @LimitBehavior int getDataLimitBehavior() {
+        return dataLimitBehavior;
+    }
+
+    /**
+     * Return a snapshot of currently known mobile data usage at
+     * {@link #getDataUsageTime()}.
+     */
+    public @BytesLong long getDataUsageBytes() {
+        return dataUsageBytes;
+    }
+
+    /**
+     * Return the time at which {@link #getDataUsageBytes()} was valid.
+     */
+    public @CurrentTimeMillisLong long getDataUsageTime() {
+        return dataUsageTime;
+    }
+
+    /**
+     * Return an array containing all network types this SubscriptionPlan applies to.
+     * @see TelephonyManager for network types values
+     */
+    public @NonNull @NetworkType int[] getNetworkTypes() {
+        return Arrays.copyOf(networkTypes, networkTypes.length);
+    }
+
+    /**
+     * Return an iterator that will return all valid data usage cycles based on
+     * any recurrence rules. The iterator starts from the currently active cycle
+     * and walks backwards through time.
+     */
+    public Iterator<Range<ZonedDateTime>> cycleIterator() {
+        return cycleRule.cycleIterator();
+    }
+
+    /**
+     * Builder for a {@link SubscriptionPlan}.
+     */
+    public static class Builder {
+        private final SubscriptionPlan plan;
+
+        /** {@hide} */
+        public Builder(ZonedDateTime start, ZonedDateTime end, Period period) {
+            plan = new SubscriptionPlan(new RecurrenceRule(start, end, period));
+        }
+
+        /**
+         * Start defining a {@link SubscriptionPlan} that covers a very specific
+         * window of time, and never automatically recurs.
+         *
+         * @param start The exact time at which the plan starts.
+         * @param end The exact time at which the plan ends.
+         */
+        public static Builder createNonrecurring(ZonedDateTime start, ZonedDateTime end) {
+            if (!end.isAfter(start)) {
+                throw new IllegalArgumentException(
+                        "End " + end + " isn't after start " + start);
+            }
+            return new Builder(start, end, null);
+        }
+
+        /**
+         * Start defining a {@link SubscriptionPlan} that starts at a specific
+         * time, and automatically recurs after each specific period of time,
+         * repeating indefinitely.
+         * <p>
+         * When the given period is set to exactly one month, the plan will
+         * always recur on the day of the month defined by
+         * {@link ZonedDateTime#getDayOfMonth()}. When a particular month ends
+         * before this day, the plan will recur on the last possible instant of
+         * that month.
+         *
+         * @param start The exact time at which the plan starts.
+         * @param period The period after which the plan automatically recurs.
+         */
+        public static Builder createRecurring(ZonedDateTime start, Period period) {
+            if (period.isZero() || period.isNegative()) {
+                throw new IllegalArgumentException("Period " + period + " must be positive");
+            }
+            return new Builder(start, null, period);
+        }
+
+        /** {@hide} */
+        @SystemApi
+        @Deprecated
+        public static Builder createRecurringMonthly(ZonedDateTime start) {
+            return new Builder(start, null, Period.ofMonths(1));
+        }
+
+        /** {@hide} */
+        @SystemApi
+        @Deprecated
+        public static Builder createRecurringWeekly(ZonedDateTime start) {
+            return new Builder(start, null, Period.ofDays(7));
+        }
+
+        /** {@hide} */
+        @SystemApi
+        @Deprecated
+        public static Builder createRecurringDaily(ZonedDateTime start) {
+            return new Builder(start, null, Period.ofDays(1));
+        }
+
+        public SubscriptionPlan build() {
+            return plan;
+        }
+
+        /** Set the short title of this plan. */
+        public Builder setTitle(@Nullable CharSequence title) {
+            plan.title = title;
+            return this;
+        }
+
+        /** Set the short summary of this plan. */
+        public Builder setSummary(@Nullable CharSequence summary) {
+            plan.summary = summary;
+            return this;
+        }
+
+        /**
+         * Set the usage threshold at which data access changes.
+         *
+         * @param dataLimitBytes the usage threshold at which data access
+         *            changes
+         * @param dataLimitBehavior the behavior of data access when usage
+         *            reaches the threshold
+         */
+        public Builder setDataLimit(@BytesLong long dataLimitBytes,
+                @LimitBehavior int dataLimitBehavior) {
+            if (dataLimitBytes < 0) {
+                throw new IllegalArgumentException("Limit bytes must be positive");
+            }
+            if (dataLimitBehavior < 0) {
+                throw new IllegalArgumentException("Limit behavior must be defined");
+            }
+            plan.dataLimitBytes = dataLimitBytes;
+            plan.dataLimitBehavior = dataLimitBehavior;
+            return this;
+        }
+
+        /**
+         * Set a snapshot of currently known mobile data usage.
+         *
+         * @param dataUsageBytes the currently known mobile data usage
+         * @param dataUsageTime the time at which this snapshot was valid
+         */
+        public Builder setDataUsage(@BytesLong long dataUsageBytes,
+                @CurrentTimeMillisLong long dataUsageTime) {
+            if (dataUsageBytes < 0) {
+                throw new IllegalArgumentException("Usage bytes must be positive");
+            }
+            if (dataUsageTime < 0) {
+                throw new IllegalArgumentException("Usage time must be positive");
+            }
+            plan.dataUsageBytes = dataUsageBytes;
+            plan.dataUsageTime = dataUsageTime;
+            return this;
+        }
+
+        /**
+         * Set the network types this SubscriptionPlan applies to. By default the plan will apply
+         * to all network types. An empty array means this plan applies to no network types.
+         *
+         * @param networkTypes an array of all network types that apply to this plan.
+         * @see TelephonyManager for network type values
+         */
+        public @NonNull Builder setNetworkTypes(@NonNull @NetworkType int[] networkTypes) {
+            plan.networkTypes = Arrays.copyOf(networkTypes, networkTypes.length);
+            return this;
+        }
+
+        /**
+         * Reset any network types that were set with {@link #setNetworkTypes(int[])}.
+         * This will make the SubscriptionPlan apply to all network types.
+         */
+        public @NonNull Builder resetNetworkTypes() {
+            plan.networkTypes = Arrays.copyOf(TelephonyManager.getAllNetworkTypes(),
+                    TelephonyManager.getAllNetworkTypes().length);
+            return this;
+        }
+    }
+}
diff --git a/android-35/android/telephony/TelephonyCallback.java b/android-35/android/telephony/TelephonyCallback.java
new file mode 100644
index 0000000..b8b84d9
--- /dev/null
+++ b/android-35/android/telephony/TelephonyCallback.java
@@ -0,0 +1,2129 @@
+/*
+ * Copyright (C) 2021 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;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.compat.annotation.ChangeId;
+import android.os.Binder;
+import android.os.Build;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.MediaQualityStatus;
+import android.telephony.ims.MediaThreshold;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IPhoneStateListener;
+import com.android.internal.telephony.flags.Flags;
+
+import dalvik.system.VMRuntime;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
+
+/**
+ * A callback class for monitoring changes in specific telephony states
+ * on the device, including service state, signal strength, message
+ * waiting indicator (voicemail), and others.
+ * <p>
+ * To register a callback, use a {@link TelephonyCallback} which implements interfaces regarding
+ * EVENT_*. For example,
+ * FakeServiceStateCallback extends {@link TelephonyCallback} implements
+ * {@link TelephonyCallback.ServiceStateListener}.
+ * <p>
+ * Then override the methods for the state that you wish to receive updates for, and
+ * pass the executor and your TelephonyCallback object to
+ * {@link TelephonyManager#registerTelephonyCallback}.
+ * Methods are called when the state changes, as well as once on initial registration.
+ * <p>
+ * Note that access to some telephony information is
+ * permission-protected. Your application won't receive updates for protected
+ * information unless it has the appropriate permissions declared in
+ * its manifest file. Where permissions apply, they are noted in the
+ * appropriate sub-interfaces.
+ */
+public class TelephonyCallback {
+    private static final String LOG_TAG = "TelephonyCallback";
+    /**
+     * Experiment flag to set the per-pid registration limit for TelephonyCallback
+     *
+     * Limit on registrations of {@link TelephonyCallback}s on a per-pid basis. When this limit is
+     * exceeded, any calls to {@link TelephonyManager#registerTelephonyCallback} will fail with an
+     * {@link IllegalStateException}.
+     *
+     * {@link android.os.Process#PHONE_UID}, {@link android.os.Process#SYSTEM_UID}, and the uid that
+     * TelephonyRegistry runs under are exempt from this limit.
+     *
+     * If the value of the flag is less than 1, enforcement of the limit will be disabled.
+     * @hide
+     */
+    public static final String FLAG_PER_PID_REGISTRATION_LIMIT =
+            "phone_state_listener_per_pid_registration_limit";
+
+    /**
+     * Default value for the per-pid registration limit.
+     * See {@link #FLAG_PER_PID_REGISTRATION_LIMIT}.
+     * @hide
+     */
+    public static final int DEFAULT_PER_PID_REGISTRATION_LIMIT = 50;
+
+    /**
+     * This change enables a limit on the number of {@link TelephonyCallback} objects any process
+     * may register via {@link TelephonyManager#registerTelephonyCallback}. The default limit is 50,
+     * which may change via remote device config updates.
+     *
+     * This limit is enforced via an {@link IllegalStateException} thrown from
+     * {@link TelephonyManager#registerTelephonyCallback} when the offending process attempts to
+     * register one too many callbacks.
+     *
+     * @hide
+     */
+    @ChangeId
+    public static final long PHONE_STATE_LISTENER_LIMIT_CHANGE_ID = 150880553L;
+
+    /**
+     * Event for changes to the network service state (cellular).
+     *
+     * <p>Requires {@link Manifest.permission#ACCESS_FINE_LOCATION} or {@link
+     * Manifest.permission#ACCESS_COARSE_LOCATION} depending on the accuracy of the location info
+     * listeners want to get.
+     *
+     * @hide
+     * @see ServiceStateListener#onServiceStateChanged
+     * @see ServiceState
+     */
+    @SystemApi
+    public static final int EVENT_SERVICE_STATE_CHANGED = 1;
+
+    /**
+     * Event for changes to the network signal strength (cellular).
+     *
+     * @hide
+     * @see SignalStrengthsListener#onSignalStrengthsChanged
+     */
+    @SystemApi
+    public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2;
+
+    /**
+     * Event for changes to the message-waiting indicator.
+     * <p>
+     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
+     * the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     * <p>
+     * Example: The status bar uses this to determine when to display the
+     * voicemail icon.
+     *
+     * @hide
+     * @see MessageWaitingIndicatorListener#onMessageWaitingIndicatorChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3;
+
+    /**
+     * Event for changes to the call-forwarding indicator.
+     * <p>
+     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
+     * the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see CallForwardingIndicatorListener#onCallForwardingIndicatorChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4;
+
+    /**
+     * Event for changes to the device's cell location. Note that
+     * this will result in frequent listeners to the listener.
+     * <p>
+     * If you need regular location updates but want more control over
+     * the update interval or location precision, you can set up a callback
+     * through the {@link android.location.LocationManager location manager}
+     * instead.
+     *
+     * @hide
+     * @see CellLocationListener#onCellLocationChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+    public static final int EVENT_CELL_LOCATION_CHANGED = 5;
+
+    /**
+     * Event for changes to the device call state.
+     * <p>
+     * Handles callbacks to {@link CallStateListener#onCallStateChanged(int)}.
+     * <p>
+     * Note: This is different from the legacy {@link #EVENT_LEGACY_CALL_STATE_CHANGED} listener
+     * which can include the phone number of the caller.  We purposely do not include the phone
+     * number as that information is not required for call state listeners going forward.
+     * @hide
+     */
+    @SystemApi
+    public static final int EVENT_CALL_STATE_CHANGED = 6;
+
+    /**
+     * Event for changes to the data connection state (cellular).
+     *
+     * @hide
+     * @see DataConnectionStateListener#onDataConnectionStateChanged
+     */
+    @SystemApi
+    public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7;
+
+    /**
+     * Event for changes to the direction of data traffic on the data
+     * connection (cellular).
+     * <p>
+     * Example: The status bar uses this to display the appropriate
+     * data-traffic icon.
+     *
+     * @hide
+     * @see DataActivityListener#onDataActivity
+     */
+    @SystemApi
+    public static final int EVENT_DATA_ACTIVITY_CHANGED = 8;
+
+    /**
+     * Event for changes to the network signal strengths (cellular).
+     * <p>
+     * Example: The status bar uses this to control the signal-strength
+     * icon.
+     *
+     * @hide
+     * @see SignalStrengthsListener#onSignalStrengthsChanged
+     */
+    @SystemApi
+    public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9;
+
+    /**
+     * Event for changes of the network signal strengths (cellular) always reported from modem,
+     * even in some situations such as the screen of the device is off.
+     *
+     * @hide
+     * @see TelephonyManager#setSignalStrengthUpdateRequest
+     */
+    @SystemApi
+    public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10;
+
+    /**
+     * Event for changes to observed cell info.
+     *
+     * @hide
+     * @see CellInfoListener#onCellInfoChanged
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })    public static final int EVENT_CELL_INFO_CHANGED = 11;
+
+    /**
+     * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing,
+     * background and foreground calls.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see PreciseCallStateListener#onPreciseCallStateChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12;
+
+    /**
+     * Event for {@link PreciseDataConnectionState} on the data connection (cellular).
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see PreciseDataConnectionStateListener#onPreciseDataConnectionStateChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13;
+
+    /**
+     * Event for real time info for all data connections (cellular)).
+     *
+     * @hide
+     * @see PhoneStateListener#onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
+     * @deprecated Use {@link TelephonyManager#requestModemActivityInfo}
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14;
+
+    /**
+     * Event for OEM hook raw event
+     *
+     * @hide
+     * @see PhoneStateListener#onOemHookRawEvent
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int EVENT_OEM_HOOK_RAW = 15;
+
+    /**
+     * Event for changes to the SRVCC state of the active call.
+     *
+     * @hide
+     * @see SrvccStateListener#onSrvccStateChanged
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int EVENT_SRVCC_STATE_CHANGED = 16;
+
+    /**
+     * Event for carrier network changes indicated by a carrier app.
+     *
+     * @hide
+     * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)
+     * @see CarrierNetworkListener#onCarrierNetworkChange
+     */
+    @SystemApi
+    public static final int EVENT_CARRIER_NETWORK_CHANGED = 17;
+
+    /**
+     * Event for changes to the sim voice activation state
+     *
+     * @hide
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+     * <p>
+     * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
+     * fully activated
+     * @see VoiceActivationStateListener#onVoiceActivationStateChanged
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18;
+
+    /**
+     * Event for changes to the sim data activation state
+     *
+     * @hide
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+     * <p>
+     * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
+     * fully activated
+     * @see DataActivationStateListener#onDataActivationStateChanged
+     */
+    @SystemApi
+    public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19;
+
+    /**
+     * Event for changes to the user mobile data state
+     *
+     * @hide
+     * @see UserMobileDataStateListener#onUserMobileDataStateChanged
+     */
+    @SystemApi
+    public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20;
+
+    /**
+     * Event for display info changed event.
+     *
+     * @hide
+     * @see DisplayInfoListener#onDisplayInfoChanged
+     */
+    @SystemApi
+    public static final int EVENT_DISPLAY_INFO_CHANGED = 21;
+
+    /**
+     * Event for changes to the phone capability.
+     *
+     * @hide
+     * @see PhoneCapabilityListener#onPhoneCapabilityChanged
+     */
+    @SystemApi
+    public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22;
+
+    /**
+     * Event for changes to active data subscription ID. Active data subscription is
+     * the current subscription used to setup Cellular Internet data. The data is only active on the
+     * subscription at a time, even it is multi-SIM mode. For example, it could be the current
+     * active opportunistic subscription in use, or the subscription user selected as default data
+     * subscription in DSDS mode.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see ActiveDataSubscriptionIdListener#onActiveDataSubscriptionIdChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23;
+
+    /**
+     * Event for changes to the radio power state.
+     *
+     * @hide
+     * @see RadioPowerStateListener#onRadioPowerStateChanged
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24;
+
+    /**
+     * Event for changes to emergency number list based on all active subscriptions.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see EmergencyNumberListListener#onEmergencyNumberListChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25;
+
+    /**
+     * Event for call disconnect causes which contains {@link DisconnectCause} and
+     * {@link PreciseDisconnectCause}.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see CallDisconnectCauseListener#onCallDisconnectCauseChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26;
+
+    /**
+     * Event for changes to the call attributes of a currently active call.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see CallAttributesListener#onCallAttributesChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27;
+
+    /**
+     * Event for IMS call disconnect causes which contains
+     * {@link android.telephony.ims.ImsReasonInfo}
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see ImsCallDisconnectCauseListener#onImsCallDisconnectCauseChanged(ImsReasonInfo)
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28;
+
+    /**
+     * Event for the emergency number placed from an outgoing call.
+     *
+     * @hide
+     * @see OutgoingEmergencyCallListener#onOutgoingEmergencyCall
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+    public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29;
+
+    /**
+     * Event for the emergency number placed from an outgoing SMS.
+     *
+     * @hide
+     * @see OutgoingEmergencySmsListener#onOutgoingEmergencySms
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+    public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30;
+
+    /**
+     * Event for registration failures.
+     * <p>
+     * Event for indications that a registration procedure has failed in either the CS or PS
+     * domain. This indication does not necessarily indicate a change of service state, which should
+     * be tracked via {@link #EVENT_SERVICE_STATE_CHANGED}.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * <p>Requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission in case that
+     * listener want to get location info in {@link CellIdentity} regardless of whether the calling
+     * app has carrier privileges.
+     *
+     * @hide
+     * @see RegistrationFailedListener#onRegistrationFailed
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
+    public static final int EVENT_REGISTRATION_FAILURE = 31;
+
+    /**
+     * Event for Barring Information for the current registered / camped cell.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * <p>Requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission in case that
+     * listener want to get {@link BarringInfo} which includes location info in {@link CellIdentity}
+     * regardless of whether the calling app has carrier privileges.
+     *
+     * @hide
+     * @see BarringInfoListener#onBarringInfoChanged
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
+    public static final int EVENT_BARRING_INFO_CHANGED = 32;
+
+    /**
+     * Event for changes to the physical channel configuration.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see PhysicalChannelConfigListener#onPhysicalChannelConfigChanged
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33;
+
+
+    /**
+     * Event for changes to the data enabled.
+     * <p>
+     * Event for indications that the enabled status of current data has changed.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @hide
+     * @see DataEnabledListener#onDataEnabledChanged
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_DATA_ENABLED_CHANGED = 34;
+
+    /**
+     * Event for changes to allowed network list based on all active subscriptions.
+     *
+     * @hide
+     * @see AllowedNetworkTypesListener#onAllowedNetworkTypesChanged
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35;
+
+    /**
+     * Event for changes to the legacy call state changed listener implemented by
+     * {@link PhoneStateListener#onCallStateChanged(int, String)}.  This listener variant is similar
+     * to the new {@link CallStateListener#onCallStateChanged(int)} with the important distinction
+     * that it CAN provide the phone number associated with a call.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
+    public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36;
+
+
+    /**
+     * Event for changes to the link capacity estimate (LCE)
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     *
+     * @see LinkCapacityEstimateChangedListener#onLinkCapacityEstimateChanged
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_LINK_CAPACITY_ESTIMATE_CHANGED = 37;
+
+    /**
+     * Event to norify the Anbr information from Radio to Ims.
+     *
+     * @see ImsCallSessionImplBase#callSessionNotifyAnbr.
+     *
+     * @hide
+     */
+    public static final int EVENT_TRIGGER_NOTIFY_ANBR = 38;
+
+    /**
+     * Event for changes to the media quality status
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+     *
+     * @see MediaQualityStatusChangedListener#onMediaQualityStatusChanged
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final int EVENT_MEDIA_QUALITY_STATUS_CHANGED = 39;
+
+
+    /**
+     * Event for changes to the Emergency callback mode
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @see EmergencyCallbackModeListener#onCallbackModeStarted(int)
+     * @see EmergencyCallbackModeListener#onCallbackModeStopped(int, int)
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final int EVENT_EMERGENCY_CALLBACK_MODE_CHANGED = 40;
+
+    /**
+     * Event for listening to changes in simultaneous cellular calling subscriptions.
+     *
+     * @see SimultaneousCellularCallingSupportListener
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS)
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    public static final int EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED = 41;
+
+    /**
+     * Event for listening to changes in carrier roaming non-terrestrial network mode.
+     *
+     * @see CarrierRoamingNtnModeListener
+     *
+     * @hide
+     */
+    public static final int EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED = 42;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = {"EVENT_"}, value = {
+            EVENT_SERVICE_STATE_CHANGED,
+            EVENT_SIGNAL_STRENGTH_CHANGED,
+            EVENT_MESSAGE_WAITING_INDICATOR_CHANGED,
+            EVENT_CALL_FORWARDING_INDICATOR_CHANGED,
+            EVENT_CELL_LOCATION_CHANGED,
+            EVENT_CALL_STATE_CHANGED,
+            EVENT_DATA_CONNECTION_STATE_CHANGED,
+            EVENT_DATA_ACTIVITY_CHANGED,
+            EVENT_SIGNAL_STRENGTHS_CHANGED,
+            EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED,
+            EVENT_CELL_INFO_CHANGED,
+            EVENT_PRECISE_CALL_STATE_CHANGED,
+            EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED,
+            EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED,
+            EVENT_OEM_HOOK_RAW,
+            EVENT_SRVCC_STATE_CHANGED,
+            EVENT_CARRIER_NETWORK_CHANGED,
+            EVENT_VOICE_ACTIVATION_STATE_CHANGED,
+            EVENT_DATA_ACTIVATION_STATE_CHANGED,
+            EVENT_USER_MOBILE_DATA_STATE_CHANGED,
+            EVENT_DISPLAY_INFO_CHANGED,
+            EVENT_PHONE_CAPABILITY_CHANGED,
+            EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED,
+            EVENT_RADIO_POWER_STATE_CHANGED,
+            EVENT_EMERGENCY_NUMBER_LIST_CHANGED,
+            EVENT_CALL_DISCONNECT_CAUSE_CHANGED,
+            EVENT_CALL_ATTRIBUTES_CHANGED,
+            EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED,
+            EVENT_OUTGOING_EMERGENCY_CALL,
+            EVENT_OUTGOING_EMERGENCY_SMS,
+            EVENT_REGISTRATION_FAILURE,
+            EVENT_BARRING_INFO_CHANGED,
+            EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED,
+            EVENT_DATA_ENABLED_CHANGED,
+            EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED,
+            EVENT_LEGACY_CALL_STATE_CHANGED,
+            EVENT_LINK_CAPACITY_ESTIMATE_CHANGED,
+            EVENT_TRIGGER_NOTIFY_ANBR,
+            EVENT_MEDIA_QUALITY_STATUS_CHANGED,
+            EVENT_EMERGENCY_CALLBACK_MODE_CHANGED,
+            EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED,
+            EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TelephonyEvent {
+    }
+
+    /**
+     * @hide
+     */
+    //TODO: The maxTargetSdk should be S if the build time tool updates it.
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public IPhoneStateListener callback;
+
+    /**
+     * @hide
+     */
+    public void init(@NonNull @CallbackExecutor Executor executor) {
+        if (executor == null) {
+            throw new IllegalArgumentException("TelephonyCallback Executor must be non-null");
+        }
+        callback = new IPhoneStateListenerStub(this, executor);
+    }
+
+    /**
+     * Interface for service state listener.
+     */
+    public interface ServiceStateListener {
+        /**
+         * Callback invoked when device service state changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         * <p>
+         * The instance of {@link ServiceState} passed as an argument here will have various
+         * levels of location information stripped from it depending on the location permissions
+         * that your app holds.
+         * Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will
+         * receive all the information in {@link ServiceState}, otherwise the cellIdentity
+         * will be null if apps only holding the {@link Manifest.permission#ACCESS_COARSE_LOCATION}
+         * permission. Network operator name in long/short alphanumeric format and numeric id will
+         * be null if apps holding neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+         *
+         * @see ServiceState#STATE_EMERGENCY_ONLY
+         * @see ServiceState#STATE_IN_SERVICE
+         * @see ServiceState#STATE_OUT_OF_SERVICE
+         * @see ServiceState#STATE_POWER_OFF
+         */
+        void onServiceStateChanged(@NonNull ServiceState serviceState);
+    }
+
+    /**
+     * Interface for message waiting indicator listener.
+     */
+    public interface MessageWaitingIndicatorListener {
+        /**
+         * Callback invoked when the message-waiting indicator changes on the registered
+         * subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PHONE_STATE}.
+         *
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+        void onMessageWaitingIndicatorChanged(boolean mwi);
+    }
+
+    /**
+     * Interface for call-forwarding indicator listener.
+     */
+    public interface CallForwardingIndicatorListener {
+        /**
+         * Callback invoked when the call-forwarding indicator changes on the registered
+         * subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PHONE_STATE}.
+         *
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+        void onCallForwardingIndicatorChanged(boolean cfi);
+    }
+
+    /**
+     * Interface for device cell location listener.
+     */
+    public interface CellLocationListener {
+        /**
+         * Callback invoked when device cell location changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         */
+        @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+        void onCellLocationChanged(@NonNull CellLocation location);
+    }
+
+    /**
+     * Interface for call state listener.
+     */
+    public interface CallStateListener {
+        /**
+         * Callback invoked when device call state changes.
+         * <p>
+         * Reports the state of Telephony (mobile) calls on the device for the registered
+         * subscription.
+         * <p>
+         * Note: the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         * <p>
+         * Note: The state returned here may differ from that returned by
+         * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that
+         * calling {@link TelephonyManager#getCallState()} from within this callback may return a
+         * different state than the callback reports.
+         *
+         * @param state the current call state
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+        void onCallStateChanged(@Annotation.CallState int state);
+    }
+
+    /**
+     * Interface for data connection state listener.
+     */
+    public interface DataConnectionStateListener {
+        /**
+         * Callback invoked when connection state changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param state       is the current state of data connection.
+         * @param networkType is the current network type of data connection.
+         * @see TelephonyManager#DATA_DISCONNECTED
+         * @see TelephonyManager#DATA_CONNECTING
+         * @see TelephonyManager#DATA_CONNECTED
+         * @see TelephonyManager#DATA_SUSPENDED
+         * @see TelephonyManager#DATA_HANDOVER_IN_PROGRESS
+         */
+        void onDataConnectionStateChanged(@TelephonyManager.DataState int state,
+                @Annotation.NetworkType int networkType);
+    }
+
+    /**
+     * Interface for data activity state listener.
+     */
+    public interface DataActivityListener {
+        /**
+         * Callback invoked when data activity state changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @see TelephonyManager#DATA_ACTIVITY_NONE
+         * @see TelephonyManager#DATA_ACTIVITY_IN
+         * @see TelephonyManager#DATA_ACTIVITY_OUT
+         * @see TelephonyManager#DATA_ACTIVITY_INOUT
+         * @see TelephonyManager#DATA_ACTIVITY_DORMANT
+         */
+        void onDataActivity(@Annotation.DataActivityType int direction);
+    }
+
+    /**
+     * Interface for network signal strengths listener.
+     */
+    public interface SignalStrengthsListener {
+        /**
+         * Callback invoked when network signal strengths changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         */
+        void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
+    }
+
+    /**
+     * Interface for cell info listener.
+     */
+    public interface CellInfoListener {
+        /**
+         * Callback invoked when a observed cell info has changed or new cells have been added
+         * or removed on the registered subscription.
+         * Note, the registration subscription ID s from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param cellInfo is the list of currently visible cells.
+         */
+        @RequiresPermission(allOf = {
+                Manifest.permission.READ_PHONE_STATE,
+                Manifest.permission.ACCESS_FINE_LOCATION
+        })
+        void onCellInfoChanged(@NonNull List<CellInfo> cellInfo);
+    }
+
+    /**
+     * Interface for precise device call state listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface PreciseCallStateListener {
+        /**
+         * Callback invoked when precise device call state changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}.
+         *
+         * @param callState {@link PreciseCallState}
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+        void onPreciseCallStateChanged(@NonNull PreciseCallState callState);
+    }
+
+    /**
+     * Interface for call disconnect cause listener.
+     */
+    public interface CallDisconnectCauseListener {
+        /**
+         * Callback invoked when call disconnect cause changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param disconnectCause        the disconnect cause
+         * @param preciseDisconnectCause the precise disconnect cause
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+        void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause,
+                @Annotation.PreciseDisconnectCauses int preciseDisconnectCause);
+    }
+
+    /**
+     * Interface for IMS call disconnect cause listener.
+     */
+    public interface ImsCallDisconnectCauseListener {
+        /**
+         * Callback invoked when IMS call disconnect cause changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}.
+         *
+         * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+        void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo);
+    }
+
+    /**
+     * Interface for precise data connection state listener.
+     */
+    public interface PreciseDataConnectionStateListener {
+        /**
+         * Callback providing update about the default/internet data connection on the registered
+         * subscription.
+         * <p>
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}.
+         *
+         * @param dataConnectionState {@link PreciseDataConnectionState}
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+        void onPreciseDataConnectionStateChanged(
+            @NonNull PreciseDataConnectionState dataConnectionState);
+    }
+
+    /**
+     * Interface for Single Radio Voice Call Continuity listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface SrvccStateListener {
+        /**
+         * Callback invoked when there has been a change in the Single Radio Voice Call Continuity
+         * (SRVCC) state for the currently active call on the registered subscription.
+         * <p>
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         */
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        void onSrvccStateChanged(@Annotation.SrvccState int srvccState);
+    }
+
+    /**
+     * Interface for SIM voice activation state listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface VoiceActivationStateListener {
+        /**
+         * Callback invoked when the SIM voice activation state has changed on the registered
+         * subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param state is the current SIM voice activation state
+         */
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        void onVoiceActivationStateChanged(@Annotation.SimActivationState int state);
+
+    }
+
+    /**
+     * Interface for SIM data activation state listener.
+     */
+    public interface DataActivationStateListener {
+        /**
+         * Callback invoked when the SIM data activation state has changed on the registered
+         * subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param state is the current SIM data activation state
+         */
+        void onDataActivationStateChanged(@Annotation.SimActivationState int state);
+    }
+
+    /**
+     * Interface for user mobile data state listener.
+     */
+    public interface UserMobileDataStateListener {
+        /**
+         * Callback invoked when the user mobile data state has changed on the registered
+         * subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param enabled indicates whether the current user mobile data state is enabled or
+         *                disabled.
+         */
+        void onUserMobileDataStateChanged(boolean enabled);
+    }
+
+    /**
+     * Interface for display info listener.
+     */
+    public interface DisplayInfoListener {
+        /**
+         * Callback invoked when the display info has changed on the registered subscription.
+         * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user
+         * based on carrier policy.
+         *
+         * @param telephonyDisplayInfo The display information.
+         */
+        void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo);
+    }
+
+    /**
+     * Interface for the current emergency number list listener.
+     */
+    public interface EmergencyNumberListListener {
+        /**
+         * Callback invoked when the current emergency number list has changed on the registered
+         * subscription.
+         * <p>
+         * Note, the registered subscription is associated with {@link TelephonyManager} object
+         * on which
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
+         * was called.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * given subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PHONE_STATE}.
+         *
+         * @param emergencyNumberList Map associating all active subscriptions on the device with
+         *                            the list of emergency numbers originating from that
+         *                            subscription.
+         *                            If there are no active subscriptions, the map will contain a
+         *                            single entry with
+         *                            {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as
+         *                            the key and a list of emergency numbers as the value. If no
+         *                            emergency number information is available, the value will be
+         *                            empty.
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+        void onEmergencyNumberListChanged(@NonNull Map<Integer,
+                List<EmergencyNumber>> emergencyNumberList);
+    }
+
+    /**
+     * Interface for outgoing emergency call listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface OutgoingEmergencyCallListener {
+        /**
+         * Callback invoked when an outgoing call is placed to an emergency number.
+         * <p>
+         * This method will be called when an emergency call is placed on any subscription
+         * (including the no-SIM case), regardless of which subscription this callback was
+         * registered on.
+         * <p>
+         *
+         * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was
+         *                              placed to.
+         * @param subscriptionId        The subscription ID used to place the emergency call. If the
+         *                              emergency call was placed without a valid subscription
+         *                              (e.g. when there are no SIM cards in the device), this
+         *                              will be
+         *                              equal to
+         *                              {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+         */
+        @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+        void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
+                int subscriptionId);
+    }
+
+    /**
+     * Interface for outgoing emergency sms listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface OutgoingEmergencySmsListener {
+        /**
+         * Smsback invoked when an outgoing sms is sent to an emergency number.
+         * <p>
+         * This method will be called when an emergency sms is sent on any subscription,
+         * regardless of which subscription this callback was registered on.
+         *
+         * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to.
+         * @param subscriptionId      The subscription ID used to send the emergency sms.
+         */
+        @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+        void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+                int subscriptionId);
+    }
+
+    /**
+     * Interface for phone capability listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface PhoneCapabilityListener {
+        /**
+         * Callback invoked when phone capability changes.
+         * Note, this callback triggers regardless of registered subscription.
+         *
+         * @param capability the new phone capability
+         */
+        void onPhoneCapabilityChanged(@NonNull PhoneCapability capability);
+    }
+
+    /**
+     * Interface for active data subscription ID listener.
+     */
+    public interface ActiveDataSubscriptionIdListener {
+        /**
+         * Callback invoked when active data subscription ID changes.
+         * Note, this callback triggers regardless of registered subscription.
+         *
+         * @param subId current subscription used to setup Cellular Internet data. The data is
+         *              only active on the subscription at a time, even it is multi-SIM mode.
+         *              For example, it could be the current active opportunistic subscription
+         *              in use, or the subscription user selected as default data subscription in
+         *              DSDS mode.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PHONE_STATE}.
+         *
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+        void onActiveDataSubscriptionIdChanged(int subId);
+    }
+
+    /**
+     * Interface for modem radio power state listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface RadioPowerStateListener {
+        /**
+         * Callback invoked when modem radio power state changes on the registered subscription.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param state the modem radio power state
+         */
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        void onRadioPowerStateChanged(@Annotation.RadioPowerState int state);
+    }
+
+    /**
+     * Interface for carrier network listener.
+     */
+    public interface CarrierNetworkListener {
+        /**
+         * Callback invoked when telephony has received notice from a carrier
+         * app that a network action that could result in connectivity loss
+         * has been requested by an app using
+         * {@link android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)}
+         * <p>
+         * This is optional and is only used to allow the system to provide alternative UI while
+         * telephony is performing an action that may result in intentional, temporary network
+         * lack of connectivity.
+         * <p>
+         * Note, this callback is pinned to the registered subscription and will be invoked when
+         * the notifying carrier app has carrier privilege rule on the registered
+         * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges}
+         *
+         * @param active If the carrier network change is or shortly will be active,
+         *               {@code true} indicate that showing alternative UI, {@code false} otherwise.
+         */
+        void onCarrierNetworkChange(boolean active);
+    }
+
+    /**
+     * Interface for registration failures listener.
+     */
+    public interface RegistrationFailedListener {
+        /**
+         * Report that Registration or a Location/Routing/Tracking Area update has failed.
+         *
+         * <p>Indicate whenever a registration procedure, including a location, routing, or tracking
+         * area update fails. This includes procedures that do not necessarily result in a change of
+         * the modem's registration status. If the modem's registration status changes, that is
+         * reflected in the onNetworkStateChanged() and subsequent
+         * get{Voice/Data}RegistrationState().
+         *
+         * <p>Because registration failures are ephemeral, this callback is not sticky.
+         * Registrants will not receive the most recent past value when registering.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} and
+         * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
+         *
+         * If the calling app doesn't have {@link android.Manifest.permission#ACCESS_FINE_LOCATION},
+         * it will receive {@link CellIdentity} without location-sensitive information included.
+         *
+         * @param cellIdentity        the CellIdentity, which must include the globally unique
+         *                            identifier
+         *                            for the cell (for example, all components of the CGI or ECGI).
+         * @param chosenPlmn          a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those
+         *                            broadcast by the
+         *                            cell that was chosen for the failed registration attempt.
+         * @param domain              DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure.
+         * @param causeCode           the primary failure cause code of the procedure.
+         *                            For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
+         *                            For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
+         *                            For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+         *                            For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
+         *                            Integer.MAX_VALUE if this value is unused.
+         * @param additionalCauseCode the cause code of any secondary/combined procedure
+         *                            if appropriate. For UMTS, if a combined attach succeeds for
+         *                            PS only, then the GMM cause code shall be included as an
+         *                            additionalCauseCode. For LTE (ESM), cause codes are in
+         *                            TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
+         */
+        @RequiresPermission(allOf = {
+                Manifest.permission.READ_PRECISE_PHONE_STATE,
+                Manifest.permission.ACCESS_FINE_LOCATION
+        })
+        void onRegistrationFailed(@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
+                @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode);
+    }
+
+    /**
+     * Interface for the current allowed network type list listener. This list involves values of
+     * allowed network type for each of reasons.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface AllowedNetworkTypesListener {
+        /**
+         * Callback invoked when the current allowed network type list has changed on the
+         * registered subscription for a specified reason.
+         * Note, the registered subscription is associated with {@link TelephonyManager} object
+         * on which {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
+         * was called.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * given subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * @param reason an allowed network type reasons.
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G
+         *
+         * @param allowedNetworkType an allowed network type bitmask value. (for example,
+         * the long bitmask value is {{@link TelephonyManager#NETWORK_TYPE_BITMASK_NR}|
+         * {@link TelephonyManager#NETWORK_TYPE_BITMASK_LTE}})
+         *
+         * For example:
+         * If the latest allowed network type is changed by user, then the system
+         * notifies the {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER} and
+         * long type value}.
+         */
+        @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        void onAllowedNetworkTypesChanged(@TelephonyManager.AllowedNetworkTypesReason int reason,
+                @TelephonyManager.NetworkTypeBitMask long allowedNetworkType);
+    }
+
+    /**
+     * Interface for listening to changes in the simultaneous cellular calling state for active
+     * cellular subscriptions.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS)
+    @SystemApi
+    public interface SimultaneousCellularCallingSupportListener {
+        /**
+         * Notify the Listener that the subscriptions available for simultaneous <b>cellular</b>
+         * calling have changed.
+         * <p>
+         * If we have an ongoing <b>cellular</b> call on one subscription in this Set, a
+         * simultaneous incoming or outgoing <b>cellular</b> call is possible on any of the
+         * subscriptions in this Set. On a traditional Dual Sim Dual Standby device, simultaneous
+         * calling is not possible between subscriptions, where on a Dual Sim Dual Active device,
+         * simultaneous calling may be possible between subscriptions in certain network conditions.
+         * <p>
+         * Note: This listener only tracks the capability of the modem to perform simultaneous
+         * cellular calls and does not track the simultaneous calling state of scenarios based on
+         * multiple IMS registration over multiple transports (WiFi/Internet calling).
+         * <p>
+         * Note: This listener fires for all changes to cellular calling subscriptions independent
+         * of which subscription it is registered on.
+         *
+         * @param simultaneousCallingSubscriptionIds The Set of subscription IDs that support
+         * simultaneous calling. If there is an ongoing call on a subscription in this Set, then a
+         * simultaneous incoming or outgoing call is only possible for other subscriptions in this
+         * Set. If there is an ongoing call on a subscription that is not in this Set, then
+         * simultaneous calling is not possible at the current time.
+         *
+         */
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        void onSimultaneousCellularCallingSubscriptionsChanged(
+                @NonNull Set<Integer> simultaneousCallingSubscriptionIds);
+    }
+
+    /**
+     * Interface for call attributes listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface CallAttributesListener {
+        /**
+         * Callback invoked when the call attributes changes on the active call on the registered
+         * subscription. If the user swaps between a foreground and background call the call
+         * attributes will be reported for the active call only.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}.
+         *
+         * @param callAttributes the call attributes
+         * @deprecated Use onCallStatesChanged({@link List<CallState>}) to get each of call
+         *          state for all ongoing calls on the subscription.
+         */
+        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+        @Deprecated
+        default void onCallAttributesChanged(@NonNull CallAttributes callAttributes) {
+            Log.w(LOG_TAG, "onCallAttributesChanged(List<CallState>) should be "
+                    + "overridden.");
+        }
+
+        /**
+         * Callback invoked when the call attributes changes on the ongoing calls on the registered
+         * subscription. If there are 1 foreground and 1 background call, Two {@link CallState}
+         * will be passed.
+         * Note, the registration subscription ID comes from {@link TelephonyManager} object
+         * which registers TelephonyCallback by
+         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+         * If this TelephonyManager object was created with
+         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+         * subscription ID. Otherwise, this callback applies to
+         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         * In the event that there are no active(state is not
+         * {@link PreciseCallState#PRECISE_CALL_STATE_IDLE}) calls, this API will report empty list.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}.
+         *
+         * @param callStateList the list of call states for each ongoing call. If there are
+         *                           a active call and a holding call, 1 call attributes for
+         *                           {@link PreciseCallState#PRECISE_CALL_STATE_ACTIVE}  and another
+         *                           for {@link PreciseCallState#PRECISE_CALL_STATE_HOLDING}
+         *                           will be in this list.
+         */
+        // Added as default for backward compatibility
+        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+        default void onCallStatesChanged(@NonNull List<CallState> callStateList) {
+            if (callStateList.size() > 0) {
+                int foregroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE;
+                int backgroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE;
+                int ringingCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE;
+                for (CallState cs : callStateList) {
+                    switch (cs.getCallClassification()) {
+                        case CallState.CALL_CLASSIFICATION_FOREGROUND:
+                            foregroundCallState = cs.getCallState();
+                            break;
+                        case CallState.CALL_CLASSIFICATION_BACKGROUND:
+                            backgroundCallState = cs.getCallState();
+                            break;
+                        case CallState.CALL_CLASSIFICATION_RINGING:
+                            ringingCallState = cs.getCallState();
+                            break;
+                        default:
+                            break;
+                    }
+                }
+                onCallAttributesChanged(new CallAttributes(
+                        new PreciseCallState(
+                                ringingCallState, foregroundCallState, backgroundCallState,
+                                DisconnectCause.NOT_VALID, PreciseDisconnectCause.NOT_VALID),
+                        callStateList.get(0).getNetworkType(),
+                        callStateList.get(0).getCallQuality()));
+            } else {
+                onCallAttributesChanged(new CallAttributes(
+                        new PreciseCallState(PreciseCallState.PRECISE_CALL_STATE_IDLE,
+                                PreciseCallState.PRECISE_CALL_STATE_IDLE,
+                                PreciseCallState.PRECISE_CALL_STATE_IDLE,
+                                DisconnectCause.NOT_VALID, PreciseDisconnectCause.NOT_VALID),
+                        TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality()));
+            }
+        }
+    }
+
+    /**
+     * Interface for barring information listener.
+     */
+    public interface BarringInfoListener {
+        /**
+         * Report updated barring information for the current camped/registered cell.
+         *
+         * <p>Barring info is provided for all services applicable to the current camped/registered
+         * cell, for the registered PLMN and current access class/access category.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} and
+         * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
+         *
+         * If the calling app doesn't have {@link android.Manifest.permission#ACCESS_FINE_LOCATION},
+         * it will receive {@link BarringInfo} including {@link CellIdentity} without
+         * location-sensitive information included.
+         *
+         * @param barringInfo for all services on the current cell.
+         * @see android.telephony.BarringInfo
+         */
+        @RequiresPermission(allOf = {
+                Manifest.permission.READ_PRECISE_PHONE_STATE,
+                Manifest.permission.ACCESS_FINE_LOCATION
+        })
+        void onBarringInfoChanged(@NonNull BarringInfo barringInfo);
+    }
+
+    /**
+     * Interface for current physical channel configuration listener.
+     */
+    public interface PhysicalChannelConfigListener {
+        /**
+         * Callback invoked when the current physical channel configuration has changed
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}.
+         *
+         * @param configs List of the current {@link PhysicalChannelConfig}s
+         */
+        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+        void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs);
+    }
+
+    /**
+     * Interface for data enabled listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface DataEnabledListener {
+        /**
+         * Callback invoked when the data enabled changes.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}.
+         *
+         * @param enabled {@code true} if data is enabled, otherwise disabled.
+         * @param reason  Reason for data enabled/disabled.
+         *                See {@link TelephonyManager.DataEnabledChangedReason}.
+         */
+        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+        void onDataEnabledChanged(boolean enabled,
+                @TelephonyManager.DataEnabledChangedReason int reason);
+    }
+
+    /**
+     * Interface for link capacity estimate changed listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface LinkCapacityEstimateChangedListener {
+        /**
+         * Callback invoked when the link capacity estimate (LCE) changes
+         *
+         * @param linkCapacityEstimateList a list of {@link LinkCapacityEstimate}
+         * The list size is at least 1.
+         * In case of a dual connected network, the list size could be 2.
+         * Use {@link LinkCapacityEstimate#getType()} to get the type of each element.
+         */
+        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+        void onLinkCapacityEstimateChanged(
+                @NonNull List<LinkCapacityEstimate> linkCapacityEstimateList);
+    }
+
+    /**
+     * Interface for media quality status changed listener.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface MediaQualityStatusChangedListener {
+        /**
+         * Callback invoked when the media quality status of IMS call changes. This call back
+         * means current media quality status crosses at least one of threshold values in {@link
+         * MediaThreshold}. Listener needs to get quality information & check whether it crossed
+         * listener's threshold.
+         *
+         * <p/> Currently thresholds for this indication can be configurable by CARRIER_CONFIG
+         * {@link CarrierConfigManager#KEY_VOICE_RTP_THRESHOLDS_PACKET_LOSS_RATE_INT}
+         * {@link CarrierConfigManager#KEY_VOICE_RTP_THRESHOLDS_INACTIVITY_TIME_IN_MILLIS_INT}
+         * {@link CarrierConfigManager#KEY_VOICE_RTP_THRESHOLDS_JITTER_INT}
+         *
+         * @param mediaQualityStatus The media quality status currently measured.
+         */
+        @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+        void onMediaQualityStatusChanged(@NonNull MediaQualityStatus mediaQualityStatus);
+    }
+
+    /**
+     * Interface for emergency callback mode listener.
+     *
+     * @hide
+     */
+    public interface EmergencyCallbackModeListener {
+        /**
+         * Indicates that Callback Mode has been started.
+         * <p>
+         * This method will be called when an emergency sms/emergency call is sent
+         * and the callback mode is supported by the carrier.
+         * If an emergency SMS is transmitted during callback mode for SMS, this API will be called
+         * once again with TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS.
+         *
+         * @param type for callback mode entry
+         *             See {@link TelephonyManager.EmergencyCallbackModeType}.
+         * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_CALL
+         * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS
+         */
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        void onCallBackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type);
+
+        /**
+         * Indicates that Callback Mode has been stopped.
+         * <p>
+         * This method will be called when the callback mode timer expires or when
+         * a normal call/SMS is sent
+         *
+         * @param type for callback mode entry
+         * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_CALL
+         * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS
+         *
+         * @param reason for changing callback mode
+         *
+         * @see TelephonyManager#STOP_REASON_UNKNOWN
+         * @see TelephonyManager#STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED
+         * @see TelephonyManager#STOP_REASON_NORMAL_SMS_SENT
+         * @see TelephonyManager#STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED
+         * @see TelephonyManager#STOP_REASON_EMERGENCY_SMS_SENT
+         * @see TelephonyManager#STOP_REASON_TIMER_EXPIRED
+         * @see TelephonyManager#STOP_REASON_USER_ACTION
+         */
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        void onCallBackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type,
+                @TelephonyManager.EmergencyCallbackModeStopReason int reason);
+    }
+
+    /**
+     * Interface for carrier roaming non-terrestrial network listener.
+     *
+     * @hide
+     */
+    public interface CarrierRoamingNtnModeListener {
+        /**
+         * Callback invoked when carrier roaming non-terrestrial network mode changes.
+         *
+         * @param active {@code true} If the device is connected to carrier roaming
+         *                           non-terrestrial network or was connected within the
+         *                           {CarrierConfigManager
+         *                           #KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT} duration,
+         *                           {code false} otherwise.
+         */
+        void onCarrierRoamingNtnModeChanged(boolean active);
+    }
+
+    /**
+     * The callback methods need to be called on the handler thread where
+     * this object was created.  If the binder did that for us it'd be nice.
+     * <p>
+     * Using a static class and weak reference here to avoid memory leak caused by the
+     * IPhoneState.Stub callback retaining references to the outside TelephonyCallback:
+     * even caller has been destroyed and "un-registered" the TelephonyCallback, it is still not
+     * eligible for GC given the references coming from:
+     * Native Stack --> TelephonyCallback --> Context (Activity).
+     * memory of caller's context will be collected after GC from service side get triggered
+     */
+    private static class IPhoneStateListenerStub extends IPhoneStateListener.Stub {
+        private WeakReference<TelephonyCallback> mTelephonyCallbackWeakRef;
+        private Executor mExecutor;
+
+        IPhoneStateListenerStub(TelephonyCallback telephonyCallback, Executor executor) {
+            mTelephonyCallbackWeakRef = new WeakReference<TelephonyCallback>(telephonyCallback);
+            mExecutor = executor;
+        }
+
+        public void onServiceStateChanged(ServiceState serviceState) {
+            ServiceStateListener listener = (ServiceStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onServiceStateChanged(serviceState)));
+        }
+
+        public void onSignalStrengthChanged(int asu) {
+            // default implementation empty
+        }
+
+        public void onMessageWaitingIndicatorChanged(boolean mwi) {
+            MessageWaitingIndicatorListener listener =
+                    (MessageWaitingIndicatorListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onMessageWaitingIndicatorChanged(mwi)));
+        }
+
+        public void onCallForwardingIndicatorChanged(boolean cfi) {
+            CallForwardingIndicatorListener listener =
+                    (CallForwardingIndicatorListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCallForwardingIndicatorChanged(cfi)));
+        }
+
+        public void onCellLocationChanged(CellIdentity cellIdentity) {
+            // There is no system/public API to create an CellIdentity in system server,
+            // so the server pass a null to indicate an empty initial location.
+            CellLocation location =
+                    cellIdentity == null ? CellLocation.getEmpty() : cellIdentity.asCellLocation();
+            CellLocationListener listener = (CellLocationListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCellLocationChanged(location)));
+        }
+
+        public void onLegacyCallStateChanged(int state, String incomingNumber) {
+            // Not used for TelephonyCallback; part of the AIDL which is used by both the legacy
+            // PhoneStateListener and TelephonyCallback.
+        }
+
+        public void onCallStateChanged(int state) {
+            CallStateListener listener = (CallStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCallStateChanged(state)));
+        }
+
+        public void onDataConnectionStateChanged(int state, int networkType) {
+            DataConnectionStateListener listener =
+                    (DataConnectionStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            if (state == TelephonyManager.DATA_DISCONNECTING
+                    && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) {
+                Binder.withCleanCallingIdentity(
+                        () -> mExecutor.execute(() ->
+                                listener.onDataConnectionStateChanged(
+                                        TelephonyManager.DATA_CONNECTED, networkType)));
+            } else {
+                Binder.withCleanCallingIdentity(
+                        () -> mExecutor.execute(() ->
+                                listener.onDataConnectionStateChanged(state, networkType)));
+            }
+        }
+
+        public void onDataActivity(int direction) {
+            DataActivityListener listener = (DataActivityListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onDataActivity(direction)));
+        }
+
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            SignalStrengthsListener listener =
+                    (SignalStrengthsListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onSignalStrengthsChanged(
+                            signalStrength)));
+        }
+
+        public void onCellInfoChanged(List<CellInfo> cellInfo) {
+            CellInfoListener listener = (CellInfoListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCellInfoChanged(cellInfo)));
+        }
+
+        public void onPreciseCallStateChanged(PreciseCallState callState) {
+            PreciseCallStateListener listener =
+                    (PreciseCallStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onPreciseCallStateChanged(callState)));
+        }
+
+        public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
+            CallDisconnectCauseListener listener =
+                    (CallDisconnectCauseListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCallDisconnectCauseChanged(
+                            disconnectCause, preciseDisconnectCause)));
+        }
+
+        public void onPreciseDataConnectionStateChanged(
+                PreciseDataConnectionState dataConnectionState) {
+            PreciseDataConnectionStateListener listener =
+                    (PreciseDataConnectionStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onPreciseDataConnectionStateChanged(
+                                    dataConnectionState)));
+        }
+
+        public void onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo dcRtInfo) {
+            // default implementation empty
+        }
+
+        public void onSrvccStateChanged(int state) {
+            SrvccStateListener listener = (SrvccStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onSrvccStateChanged(state)));
+        }
+
+        public void onVoiceActivationStateChanged(int activationState) {
+            VoiceActivationStateListener listener =
+                    (VoiceActivationStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onVoiceActivationStateChanged(activationState)));
+        }
+
+        public void onDataActivationStateChanged(int activationState) {
+            DataActivationStateListener listener =
+                    (DataActivationStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onDataActivationStateChanged(activationState)));
+        }
+
+        public void onUserMobileDataStateChanged(boolean enabled) {
+            UserMobileDataStateListener listener =
+                    (UserMobileDataStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onUserMobileDataStateChanged(enabled)));
+        }
+
+        public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
+            DisplayInfoListener listener = (DisplayInfoListener)mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onDisplayInfoChanged(telephonyDisplayInfo)));
+        }
+
+        public void onOemHookRawEvent(byte[] rawData) {
+            // default implementation empty
+        }
+
+        public void onCarrierNetworkChange(boolean active) {
+            CarrierNetworkListener listener =
+                    (CarrierNetworkListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCarrierNetworkChange(active)));
+        }
+
+        public void onEmergencyNumberListChanged(Map emergencyNumberList) {
+            EmergencyNumberListListener listener =
+                    (EmergencyNumberListListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onEmergencyNumberListChanged(emergencyNumberList)));
+        }
+
+        public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
+            int subscriptionId) {
+            OutgoingEmergencyCallListener listener =
+                    (OutgoingEmergencyCallListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onOutgoingEmergencyCall(placedEmergencyNumber,
+                                    subscriptionId)));
+        }
+
+        public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+            int subscriptionId) {
+            OutgoingEmergencySmsListener listener =
+                    (OutgoingEmergencySmsListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onOutgoingEmergencySms(sentEmergencyNumber,
+                                    subscriptionId)));
+        }
+
+        public void onPhoneCapabilityChanged(PhoneCapability capability) {
+            PhoneCapabilityListener listener =
+                    (PhoneCapabilityListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onPhoneCapabilityChanged(capability)));
+        }
+
+        public void onRadioPowerStateChanged(@Annotation.RadioPowerState int state) {
+            RadioPowerStateListener listener =
+                    (RadioPowerStateListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onRadioPowerStateChanged(state)));
+        }
+
+        public void onCallStatesChanged(List<CallState> callStateList) {
+            CallAttributesListener listener =
+                    (CallAttributesListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCallStatesChanged(callStateList)));
+        }
+
+        public void onActiveDataSubIdChanged(int subId) {
+            ActiveDataSubscriptionIdListener listener =
+                    (ActiveDataSubscriptionIdListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onActiveDataSubscriptionIdChanged(
+                            subId)));
+        }
+
+        public void onImsCallDisconnectCauseChanged(ImsReasonInfo disconnectCause) {
+            ImsCallDisconnectCauseListener listener =
+                    (ImsCallDisconnectCauseListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onImsCallDisconnectCauseChanged(disconnectCause)));
+        }
+
+        public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
+            @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) {
+            RegistrationFailedListener listener =
+                    (RegistrationFailedListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onRegistrationFailed(
+                            cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
+            // default implementation empty
+        }
+
+        public void onBarringInfoChanged(BarringInfo barringInfo) {
+            BarringInfoListener listener = (BarringInfoListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onBarringInfoChanged(barringInfo)));
+        }
+
+        public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) {
+            PhysicalChannelConfigListener listener =
+                    (PhysicalChannelConfigListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onPhysicalChannelConfigChanged(
+                            configs)));
+        }
+
+        public void onDataEnabledChanged(boolean enabled,
+            @TelephonyManager.DataEnabledReason int reason) {
+            DataEnabledListener listener =
+                    (DataEnabledListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onDataEnabledChanged(
+                            enabled, reason)));
+        }
+
+        public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) {
+            AllowedNetworkTypesListener listener =
+                    (AllowedNetworkTypesListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onAllowedNetworkTypesChanged(reason,
+                                    allowedNetworkType)));
+        }
+
+        public void onSimultaneousCallingStateChanged(int[] subIds) {
+            SimultaneousCellularCallingSupportListener listener =
+                    (SimultaneousCellularCallingSupportListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onSimultaneousCellularCallingSubscriptionsChanged(
+                                    Arrays.stream(subIds).boxed().collect(Collectors.toSet()))));
+        }
+
+        public void onLinkCapacityEstimateChanged(
+                List<LinkCapacityEstimate> linkCapacityEstimateList) {
+            LinkCapacityEstimateChangedListener listener =
+                    (LinkCapacityEstimateChangedListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onLinkCapacityEstimateChanged(
+                            linkCapacityEstimateList)));
+        }
+
+        public void onMediaQualityStatusChanged(
+                MediaQualityStatus mediaQualityStatus) {
+            MediaQualityStatusChangedListener listener =
+                    (MediaQualityStatusChangedListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onMediaQualityStatusChanged(
+                            mediaQualityStatus)));
+        }
+
+        public void onCallBackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type) {
+            EmergencyCallbackModeListener listener =
+                    (EmergencyCallbackModeListener) mTelephonyCallbackWeakRef.get();
+            Log.d(LOG_TAG, "onCallBackModeStarted:type=" + type + ", listener=" + listener);
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCallBackModeStarted(type)));
+        }
+
+        public void onCallBackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type,
+                @TelephonyManager.EmergencyCallbackModeStopReason int reason) {
+            EmergencyCallbackModeListener listener =
+                    (EmergencyCallbackModeListener) mTelephonyCallbackWeakRef.get();
+            Log.d(LOG_TAG, "onCallBackModeStopped:type=" + type
+                    + ", reason=" + reason + ", listener=" + listener);
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCallBackModeStopped(type, reason)));
+        }
+
+        public void onCarrierRoamingNtnModeChanged(boolean active) {
+            if (!Flags.carrierEnabledSatelliteFlag()) return;
+
+            CarrierRoamingNtnModeListener listener =
+                    (CarrierRoamingNtnModeListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> listener.onCarrierRoamingNtnModeChanged(active)));
+        }
+    }
+}
diff --git a/android-35/android/telephony/TelephonyDisplayInfo.java b/android-35/android/telephony/TelephonyDisplayInfo.java
new file mode 100644
index 0000000..e01b10e
--- /dev/null
+++ b/android-35/android/telephony/TelephonyDisplayInfo.java
@@ -0,0 +1,238 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.Annotation.OverrideNetworkType;
+
+import java.util.Objects;
+
+/**
+ * TelephonyDisplayInfo contains telephony-related information used for display purposes only. This
+ * information is provided in accordance with carrier policy and branding preferences; it is not
+ * necessarily a precise or accurate representation of the current state and should be treated
+ * accordingly.
+ * To be notified of changes in TelephonyDisplayInfo, use
+ * {@link TelephonyManager#registerTelephonyCallback} with a {@link TelephonyCallback}
+ * that implements {@link TelephonyCallback.DisplayInfoListener}.
+ * Override the onDisplayInfoChanged() method to handle the broadcast.
+ */
+public final class TelephonyDisplayInfo implements Parcelable {
+    /**
+     * No override. {@link #getNetworkType()} should be used for display network
+     * type.
+     */
+    public static final int OVERRIDE_NETWORK_TYPE_NONE = 0;
+
+    /**
+     * Override network type when the device is connected to
+     * {@link TelephonyManager#NETWORK_TYPE_LTE} cellular network and is using carrier aggregation.
+     */
+    public static final int OVERRIDE_NETWORK_TYPE_LTE_CA = 1;
+
+    /**
+     * Override network type when the device is connected to advanced pro
+     * {@link TelephonyManager#NETWORK_TYPE_LTE} cellular network.
+     */
+    public static final int OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = 2;
+
+    /**
+     * Override network type when the device is connected to
+     * {@link TelephonyManager#NETWORK_TYPE_LTE} network and has E-UTRA-NR Dual Connectivity(EN-DC)
+     * capability or is currently connected to the secondary
+     * {@link TelephonyManager#NETWORK_TYPE_NR} cellular network.
+     */
+    public static final int OVERRIDE_NETWORK_TYPE_NR_NSA = 3;
+
+    /**
+     * Override network type when the device is connected to
+     * {@link TelephonyManager#NETWORK_TYPE_LTE} network and has E-UTRA-NR Dual Connectivity(EN-DC)
+     * capability or is currently connected to the secondary
+     * {@link TelephonyManager#NETWORK_TYPE_NR} cellular network on millimeter wave bands.
+     * @deprecated Use{@link #OVERRIDE_NETWORK_TYPE_NR_ADVANCED} instead.
+     */
+    @Deprecated
+    public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4;
+
+    /**
+     * Override network type when the device is connected NR cellular network and the data rate is
+     * higher than the generic 5G date rate.
+     * Including but not limited to
+     * <ul>
+     *   <li>The device is connected to the NR cellular network on millimeter wave bands. </li>
+     *   <li>The device is connected to the specific network which the carrier is using
+     *   proprietary means to provide a faster overall data connection than would be otherwise
+     *   possible.  This may include using other bands unique to the carrier, or carrier
+     *   aggregation, for example.</li>
+     * </ul>
+     * One of the use case is that UX can show a different icon, for example, "5G+"
+     */
+    public static final int OVERRIDE_NETWORK_TYPE_NR_ADVANCED = 5;
+
+    @NetworkType
+    private final int mNetworkType;
+
+    @OverrideNetworkType
+    private final int mOverrideNetworkType;
+
+    private final boolean mIsRoaming;
+
+    /**
+     * Constructor
+     *
+     * @param networkType Current packet-switching cellular network type
+     * @param overrideNetworkType The override network type
+     *
+     * @deprecated will not use this constructor anymore.
+     * @hide
+     */
+    @Deprecated
+    public TelephonyDisplayInfo(@NetworkType int networkType,
+            @OverrideNetworkType int overrideNetworkType) {
+        this(networkType, overrideNetworkType, false);
+    }
+
+    /**
+     * Constructor
+     *
+     * @param networkType Current packet-switching cellular network type
+     * @param overrideNetworkType The override network type
+     * @param isRoaming True if the device is roaming after override.
+     *
+     * @hide
+     */
+    public TelephonyDisplayInfo(@NetworkType int networkType,
+            @OverrideNetworkType int overrideNetworkType,
+            boolean isRoaming) {
+        mNetworkType = networkType;
+        mOverrideNetworkType = overrideNetworkType;
+        mIsRoaming = isRoaming;
+    }
+
+    /** @hide */
+    public TelephonyDisplayInfo(Parcel p) {
+        mNetworkType = p.readInt();
+        mOverrideNetworkType = p.readInt();
+        mIsRoaming = p.readBoolean();
+    }
+
+    /**
+     * Get current packet-switching cellular network type. This is the actual network type the
+     * device is camped on.
+     *
+     * @return The network type.
+     */
+    @NetworkType
+    public int getNetworkType() {
+        return mNetworkType;
+    }
+
+    /**
+     * Get the override network type. Note the override network type is for market branding
+     * or visualization purposes only. It cannot be treated as the actual network type device is
+     * camped on.
+     *
+     * @return The override network type.
+     */
+    @OverrideNetworkType
+    public int getOverrideNetworkType() {
+        return mOverrideNetworkType;
+    }
+
+    /**
+     * Get device is roaming or not. Note the isRoaming is for market branding or visualization
+     * purposes only. It cannot be treated as the actual roaming device is camped on.
+     *
+     * @return True if the device is registered on roaming network overridden by config.
+     * @see CarrierConfigManager#KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+     * @see CarrierConfigManager#KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+     * @see CarrierConfigManager#KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY
+     * @see CarrierConfigManager#KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY
+     */
+    public boolean isRoaming() {
+        return mIsRoaming;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mNetworkType);
+        dest.writeInt(mOverrideNetworkType);
+        dest.writeBoolean(mIsRoaming);
+    }
+
+    public static final @NonNull Parcelable.Creator<TelephonyDisplayInfo> CREATOR =
+            new Parcelable.Creator<TelephonyDisplayInfo>() {
+                @Override
+                public TelephonyDisplayInfo createFromParcel(Parcel source) {
+                    return new TelephonyDisplayInfo(source);
+                }
+
+                @Override
+                public TelephonyDisplayInfo[] newArray(int size) {
+                    return new TelephonyDisplayInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        TelephonyDisplayInfo that = (TelephonyDisplayInfo) o;
+        return mNetworkType == that.mNetworkType
+                && mOverrideNetworkType == that.mOverrideNetworkType
+                && mIsRoaming == that.mIsRoaming;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mNetworkType, mOverrideNetworkType, mIsRoaming);
+    }
+
+    /**
+     * Convert override network type to string.
+     *
+     * @param type Override network type
+     * @return Override network type in string format
+     * @hide
+     */
+    public static String overrideNetworkTypeToString(@OverrideNetworkType int type) {
+        switch (type) {
+            case OVERRIDE_NETWORK_TYPE_NONE: return "NONE";
+            case OVERRIDE_NETWORK_TYPE_LTE_CA: return "LTE_CA";
+            case OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO: return "LTE_ADV_PRO";
+            case OVERRIDE_NETWORK_TYPE_NR_NSA: return "NR_NSA";
+            case OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE: return "NR_NSA_MMWAVE";
+            case OVERRIDE_NETWORK_TYPE_NR_ADVANCED: return "NR_ADVANCED";
+            default: return "UNKNOWN";
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "TelephonyDisplayInfo {network=" + TelephonyManager.getNetworkTypeName(mNetworkType)
+                + ", overrideNetwork=" + overrideNetworkTypeToString(mOverrideNetworkType)
+                + ", isRoaming=" + mIsRoaming + "}";
+    }
+}
diff --git a/android-35/android/telephony/TelephonyFrameworkInitializer.java b/android-35/android/telephony/TelephonyFrameworkInitializer.java
new file mode 100644
index 0000000..f5688bf
--- /dev/null
+++ b/android-35/android/telephony/TelephonyFrameworkInitializer.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2019 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;
+
+import android.annotation.NonNull;
+import android.app.SystemServiceRegistry;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.os.TelephonyServiceManager;
+import android.telephony.euicc.EuiccCardManager;
+import android.telephony.euicc.EuiccManager;
+import android.telephony.ims.ImsManager;
+import android.telephony.satellite.SatelliteManager;
+
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.util.Preconditions;
+
+
+/**
+ * Class for performing registration for all telephony services.
+ *
+ * @hide
+ */
+public class TelephonyFrameworkInitializer {
+
+    private TelephonyFrameworkInitializer() {
+    }
+
+    /**
+     * Starting with {@link VANILLA_ICE_CREAM}, Telephony feature flags
+     * (e.g. {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}) are being checked before
+     * returning managers that depend on them. If the feature is missing,
+     * {@link Context#getSystemService} will return null.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    static final long ENABLE_CHECKING_TELEPHONY_FEATURES = 330583731;
+
+    private static volatile TelephonyServiceManager sTelephonyServiceManager;
+
+    /**
+     * Sets an instance of {@link TelephonyServiceManager} that allows
+     * the telephony mainline module to register/obtain telephony binder services. This is called
+     * by the platform during the system initialization.
+     *
+     * @param telephonyServiceManager instance of {@link TelephonyServiceManager} that allows
+     * the telephony mainline module to register/obtain telephony binder services.
+     */
+    public static void setTelephonyServiceManager(
+            @NonNull TelephonyServiceManager telephonyServiceManager) {
+        Preconditions.checkState(sTelephonyServiceManager == null,
+                "setTelephonyServiceManager called twice!");
+        sTelephonyServiceManager = Preconditions.checkNotNull(telephonyServiceManager);
+    }
+
+    // Suppressing AndroidFrameworkCompatChange because we're querying vendor
+    // partition SDK level, not application's target SDK version (which BTW we
+    // also check through Compatibility framework a few lines below).
+    @SuppressWarnings("AndroidFrameworkCompatChange")
+    private static boolean hasSystemFeature(Context context, String feature) {
+        // Check release status of this change in behavior.
+        if (!Flags.minimalTelephonyManagersConditionalOnFeatures()) return true;
+
+        // Check SDK version of the vendor partition.
+        final int vendorApiLevel = SystemProperties.getInt(
+                "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
+        if (vendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) return true;
+
+        // Check SDK version of the client app.
+        if (!Compatibility.isChangeEnabled(ENABLE_CHECKING_TELEPHONY_FEATURES)) return true;
+
+        // Finally, check if the system feature is actually present.
+        return context.getPackageManager().hasSystemFeature(feature);
+    }
+
+    /**
+     * Called by {@link SystemServiceRegistry}'s static initializer and registers all telephony
+     * services to {@link Context}, so that {@link Context#getSystemService} can return them.
+     *
+     * @throws IllegalStateException if this is called from anywhere besides
+     * {@link SystemServiceRegistry}
+     */
+    public static void registerServiceWrappers() {
+        SystemServiceRegistry.registerContextAwareService(
+                Context.TELEPHONY_SERVICE,
+                TelephonyManager.class,
+                context -> new TelephonyManager(context)
+        );
+        SystemServiceRegistry.registerContextAwareService(
+                Context.TELEPHONY_SUBSCRIPTION_SERVICE,
+                SubscriptionManager.class,
+                context -> new SubscriptionManager(context)
+        );
+        SystemServiceRegistry.registerContextAwareService(
+                Context.CARRIER_CONFIG_SERVICE,
+                CarrierConfigManager.class,
+                context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+                        ? new CarrierConfigManager(context) : null
+        );
+        SystemServiceRegistry.registerContextAwareService(
+                Context.EUICC_SERVICE,
+                EuiccManager.class,
+                context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_EUICC)
+                        ? new EuiccManager(context) : null
+        );
+        SystemServiceRegistry.registerContextAwareService(
+                Context.EUICC_CARD_SERVICE,
+                EuiccCardManager.class,
+                context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_EUICC)
+                        ? new EuiccCardManager(context) : null
+        );
+        SystemServiceRegistry.registerContextAwareService(
+                Context.TELEPHONY_IMS_SERVICE,
+                ImsManager.class,
+                context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_IMS)
+                        ? new ImsManager(context) : null
+        );
+        SystemServiceRegistry.registerContextAwareService(
+                Context.SMS_SERVICE,
+                SmsManager.class,
+                context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_MESSAGING)
+                        ? SmsManager.getSmsManagerForContextAndSubscriptionId(context,
+                                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) : null
+        );
+        SystemServiceRegistry.registerContextAwareService(
+                Context.SATELLITE_SERVICE,
+                SatelliteManager.class,
+                context -> hasSystemFeature(context, PackageManager.FEATURE_TELEPHONY_SATELLITE)
+                        ? new SatelliteManager(context) : null
+        );
+    }
+
+    /** @hide */
+    public static TelephonyServiceManager getTelephonyServiceManager() {
+        return sTelephonyServiceManager;
+    }
+}
diff --git a/android-35/android/telephony/TelephonyHistogram.java b/android-35/android/telephony/TelephonyHistogram.java
new file mode 100644
index 0000000..b94cb60
--- /dev/null
+++ b/android-35/android/telephony/TelephonyHistogram.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2016 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;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Parcelable class to store Telephony histogram.
+ * @hide
+ */
+@SystemApi
+public final class TelephonyHistogram implements Parcelable {
+    // Type of Telephony histogram Eg: RIL histogram will have all timing data associated with
+    // RIL calls. Similarly we can have any other Telephony histogram.
+    private final int mCategory;
+
+    // Unique Id identifying a sample within particular category of histogram
+    private final int mId;
+
+    // Min time taken in ms
+    private int mMinTimeMs;
+
+    // Max time taken in ms
+    private int mMaxTimeMs;
+
+    // Average time taken in ms
+    private int mAverageTimeMs;
+
+    // Total count of samples
+    private int mSampleCount;
+
+    // Array storing time taken for first #RANGE_CALCULATION_COUNT samples of histogram.
+    private int[] mInitialTimings;
+
+    // Total number of time ranges expected (must be greater than 1)
+    private final int mBucketCount;
+
+    // Array storing endpoints of range buckets. Calculated based on values of minTime & maxTime
+    // after totalTimeCount is #RANGE_CALCULATION_COUNT.
+    private final int[] mBucketEndPoints;
+
+    // Array storing counts for each time range starting from smallest value range
+    private final int[] mBucketCounters;
+
+    /**
+     * Constant for Telephony category
+     */
+    public static final int TELEPHONY_CATEGORY_RIL = 1;
+
+    // Count of Histogram samples after which time buckets are created.
+    private static final int RANGE_CALCULATION_COUNT = 10;
+
+
+    // Constant used to indicate #initialTimings is null while parceling
+    private static final int ABSENT = 0;
+
+    // Constant used to indicate #initialTimings is not null while parceling
+    private static final int PRESENT = 1;
+
+    // Throws exception if #totalBuckets is not greater than one.
+    public TelephonyHistogram (int category, int id, int bucketCount) {
+        if (bucketCount <= 1) {
+            throw new IllegalArgumentException("Invalid number of buckets");
+        }
+        mCategory = category;
+        mId = id;
+        mMinTimeMs = Integer.MAX_VALUE;
+        mMaxTimeMs = 0;
+        mAverageTimeMs = 0;
+        mSampleCount = 0;
+        mInitialTimings = new int[RANGE_CALCULATION_COUNT];
+        mBucketCount = bucketCount;
+        mBucketEndPoints = new int[bucketCount - 1];
+        mBucketCounters = new int[bucketCount];
+    }
+
+    public TelephonyHistogram(TelephonyHistogram th) {
+        mCategory = th.getCategory();
+        mId = th.getId();
+        mMinTimeMs = th.getMinTime();
+        mMaxTimeMs = th.getMaxTime();
+        mAverageTimeMs = th.getAverageTime();
+        mSampleCount = th.getSampleCount();
+        mInitialTimings = th.getInitialTimings();
+        mBucketCount = th.getBucketCount();
+        mBucketEndPoints = th.getBucketEndPoints();
+        mBucketCounters = th.getBucketCounters();
+    }
+
+    public int getCategory() {
+        return mCategory;
+    }
+
+    public int getId() {
+        return mId;
+    }
+
+    public int getMinTime() {
+        return mMinTimeMs;
+    }
+
+    public int getMaxTime() {
+        return mMaxTimeMs;
+    }
+
+    public int getAverageTime() {
+        return mAverageTimeMs;
+    }
+
+    public int getSampleCount () {
+        return mSampleCount;
+    }
+
+    private int[] getInitialTimings() {
+        return mInitialTimings;
+    }
+
+    public int getBucketCount() {
+        return mBucketCount;
+    }
+
+    public int[] getBucketEndPoints() {
+        if (mSampleCount > 1 && mSampleCount < 10) {
+            int[] tempEndPoints = new int[mBucketCount - 1];
+            calculateBucketEndPoints(tempEndPoints);
+            return tempEndPoints;
+        } else {
+            return getDeepCopyOfArray(mBucketEndPoints);
+        }
+    }
+
+    public int[] getBucketCounters() {
+        if (mSampleCount > 1 && mSampleCount < 10) {
+            int[] tempEndPoints = new int[mBucketCount - 1];
+            int[] tempBucketCounters = new int[mBucketCount];
+            calculateBucketEndPoints(tempEndPoints);
+            for (int j = 0; j < mSampleCount; j++) {
+                addToBucketCounter(tempEndPoints, tempBucketCounters, mInitialTimings[j]);
+            }
+            return tempBucketCounters;
+        } else {
+            return getDeepCopyOfArray(mBucketCounters);
+        }
+    }
+
+    private int[] getDeepCopyOfArray(int[] array) {
+        int[] clone = new int[array.length];
+        System.arraycopy(array, 0, clone, 0, array.length);
+        return clone;
+    }
+
+    private void addToBucketCounter(int[] bucketEndPoints, int[] bucketCounters, int time) {
+        int i;
+        for (i = 0; i < bucketEndPoints.length; i++) {
+            if (time <= bucketEndPoints[i]) {
+                bucketCounters[i]++;
+                return;
+            }
+        }
+        bucketCounters[i]++;
+    }
+
+    private void calculateBucketEndPoints(int[] bucketEndPoints) {
+        for (int i = 1; i < mBucketCount; i++) {
+            int endPt = mMinTimeMs + (i * (mMaxTimeMs - mMinTimeMs)) / mBucketCount;
+            bucketEndPoints[i - 1] = endPt;
+        }
+    }
+
+    // Add new value of time taken
+    // This function updates minTime, maxTime, averageTime & totalTimeCount every time it is
+    // called. initialTimings[] is updated if totalTimeCount <= #RANGE_CALCULATION_COUNT. When
+    // totalTimeCount = RANGE_CALCULATION_COUNT, based on the min, max time & the number of buckets
+    // expected, bucketEndPoints[] would be calculated. Then bucketCounters[] would be filled up
+    // using values stored in initialTimings[]. Thereafter bucketCounters[] will always be updated.
+    public void addTimeTaken(int time) {
+        // Initialize all fields if its first entry or if integer overflow is going to occur while
+        // trying to calculate averageTime
+        if (mSampleCount == 0 || (mSampleCount == Integer.MAX_VALUE)) {
+            if (mSampleCount == 0) {
+                mMinTimeMs = time;
+                mMaxTimeMs = time;
+                mAverageTimeMs = time;
+            } else {
+                mInitialTimings = new int[RANGE_CALCULATION_COUNT];
+            }
+            mSampleCount = 1;
+            Arrays.fill(mInitialTimings, 0);
+            mInitialTimings[0] = time;
+            Arrays.fill(mBucketEndPoints, 0);
+            Arrays.fill(mBucketCounters, 0);
+        } else {
+            if (time < mMinTimeMs) {
+                mMinTimeMs = time;
+            }
+            if (time > mMaxTimeMs) {
+                mMaxTimeMs = time;
+            }
+            long totalTime = ((long)mAverageTimeMs) * mSampleCount + time;
+            mAverageTimeMs = (int)(totalTime/++mSampleCount);
+
+            if (mSampleCount < RANGE_CALCULATION_COUNT) {
+                mInitialTimings[mSampleCount - 1] = time;
+            } else if (mSampleCount == RANGE_CALCULATION_COUNT) {
+                mInitialTimings[mSampleCount - 1] = time;
+
+                // Calculate bucket endpoints based on bucketCount expected
+                calculateBucketEndPoints(mBucketEndPoints);
+
+                // Use values stored in initialTimings[] to update bucketCounters
+                for (int j = 0; j < RANGE_CALCULATION_COUNT; j++) {
+                    addToBucketCounter(mBucketEndPoints, mBucketCounters, mInitialTimings[j]);
+                }
+                mInitialTimings = null;
+            } else {
+                addToBucketCounter(mBucketEndPoints, mBucketCounters, time);
+            }
+
+        }
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        String basic = " Histogram id = " + mId + " Time(ms): min = " + mMinTimeMs + " max = "
+                + mMaxTimeMs + " avg = " + mAverageTimeMs + " Count = " + mSampleCount;
+        if (mSampleCount < RANGE_CALCULATION_COUNT) {
+            return basic;
+        } else {
+            StringBuffer intervals = new StringBuffer(" Interval Endpoints:");
+            for (int i = 0; i < mBucketEndPoints.length; i++) {
+                intervals.append(" " + mBucketEndPoints[i]);
+            }
+            intervals.append(" Interval counters:");
+            for (int i = 0; i < mBucketCounters.length; i++) {
+                intervals.append(" " + mBucketCounters[i]);
+            }
+            return basic + intervals;
+        }
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<TelephonyHistogram> CREATOR =
+            new Parcelable.Creator<TelephonyHistogram> () {
+
+                @Override
+                public TelephonyHistogram createFromParcel(Parcel in) {
+                    return new TelephonyHistogram(in);
+                }
+
+                @Override
+                public TelephonyHistogram[] newArray(int size) {
+                    return new TelephonyHistogram[size];
+                }
+            };
+
+    public TelephonyHistogram(Parcel in) {
+        mCategory = in.readInt();
+        mId = in.readInt();
+        mMinTimeMs = in.readInt();
+        mMaxTimeMs = in.readInt();
+        mAverageTimeMs = in.readInt();
+        mSampleCount = in.readInt();
+        if (in.readInt() == PRESENT) {
+            mInitialTimings = new int[RANGE_CALCULATION_COUNT];
+            in.readIntArray(mInitialTimings);
+        }
+        mBucketCount = in.readInt();
+        mBucketEndPoints = new int[mBucketCount - 1];
+        in.readIntArray(mBucketEndPoints);
+        mBucketCounters = new int[mBucketCount];
+        in.readIntArray(mBucketCounters);
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mCategory);
+        out.writeInt(mId);
+        out.writeInt(mMinTimeMs);
+        out.writeInt(mMaxTimeMs);
+        out.writeInt(mAverageTimeMs);
+        out.writeInt(mSampleCount);
+        if (mInitialTimings == null) {
+            out.writeInt(ABSENT);
+        } else {
+            out.writeInt(PRESENT);
+            out.writeIntArray(mInitialTimings);
+        }
+        out.writeInt(mBucketCount);
+        out.writeIntArray(mBucketEndPoints);
+        out.writeIntArray(mBucketCounters);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/android-35/android/telephony/TelephonyLocalConnection.java b/android-35/android/telephony/TelephonyLocalConnection.java
new file mode 100644
index 0000000..1cab267
--- /dev/null
+++ b/android-35/android/telephony/TelephonyLocalConnection.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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;
+
+import java.util.UUID;
+
+/**
+ * Shim used for code in frameworks/opt/telephony to be able to call code in
+ * packages/services/Telephony. A singleton instance of this class is set when the phone process
+ * is brought up.
+ * @hide
+ */
+public class TelephonyLocalConnection {
+    public interface ConnectionImpl {
+        String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid);
+    }
+    private static ConnectionImpl sInstance;
+
+    public static String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid) {
+        checkInstance();
+        return sInstance.getCallComposerServerUrlForHandle(subscriptionId, uuid);
+    }
+
+    private static void checkInstance() {
+        if (sInstance == null) {
+            throw new IllegalStateException("Connection impl is null!");
+        }
+    }
+
+    public static void setInstance(ConnectionImpl impl) {
+        sInstance = impl;
+    }
+}
diff --git a/android-35/android/telephony/TelephonyManager.java b/android-35/android/telephony/TelephonyManager.java
new file mode 100644
index 0000000..dbe4f27
--- /dev/null
+++ b/android-35/android/telephony/TelephonyManager.java
@@ -0,0 +1,19401 @@
+/*
+ * Copyright (C) 2008 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;
+
+import static android.content.Context.TELECOM_SERVICE;
+import static android.provider.Telephony.Carriers.DPC_URI;
+import static android.provider.Telephony.Carriers.INVALID_APN_ID;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.Manifest;
+import android.annotation.BytesLong;
+import android.annotation.CallbackExecutor;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.LongDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringDef;
+import android.annotation.SuppressAutoDoc;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.annotation.WorkerThread;
+import android.app.PendingIntent;
+import android.app.PropertyInvalidatedCache;
+import android.app.role.RoleManager;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextParams;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.SystemProperties;
+import android.os.WorkSource;
+import android.provider.Settings.SettingNotFoundException;
+import android.service.carrier.CarrierIdentifier;
+import android.service.carrier.CarrierService;
+import android.sysprop.TelephonyProperties;
+import android.telecom.Call;
+import android.telecom.CallScreeningService;
+import android.telecom.Connection;
+import android.telecom.InCallService;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.CallState;
+import android.telephony.Annotation.CarrierPrivilegeStatus;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.Annotation.RadioPowerState;
+import android.telephony.Annotation.SimActivationState;
+import android.telephony.Annotation.ThermalMitigationResult;
+import android.telephony.Annotation.UiccAppType;
+import android.telephony.Annotation.UiccAppTypeExt;
+import android.telephony.CallForwardingInfo.CallForwardingReason;
+import android.telephony.VisualVoicemailService.VisualVoicemailTask;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.ApnSetting.MvnoType;
+import android.telephony.data.NetworkSlicingConfig;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
+import android.telephony.gba.UaSecurityProtocolIdentifier;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.telephony.CellNetworkScanResult;
+import com.android.internal.telephony.IBooleanConsumer;
+import com.android.internal.telephony.ICallForwardingInfoCallback;
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.INumberVerificationCallback;
+import com.android.internal.telephony.IOns;
+import com.android.internal.telephony.IPhoneSubInfo;
+import com.android.internal.telephony.ISetOpportunisticDataCallback;
+import com.android.internal.telephony.ISms;
+import com.android.internal.telephony.ISub;
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.IUpdateAvailableNetworksCallback;
+import com.android.internal.telephony.IccLogicalChannelRequest;
+import com.android.internal.telephony.OperatorInfo;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.flags.Flags;
+import com.android.telephony.Rlog;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * Provides access to information about the telephony services on
+ * the device. Applications can use the methods in this class to
+ * determine telephony services and states, as well as to access some
+ * types of subscriber information. Applications can also register
+ * a listener to receive notification of telephony state changes.
+ * <p>
+ * The returned TelephonyManager will use the default subscription for all calls.
+ * To call an API for a specific subscription, use {@link #createForSubscriptionId(int)}. e.g.
+ * <code>
+ *   telephonyManager = defaultSubTelephonyManager.createForSubscriptionId(subId);
+ * </code>
+ * <p>
+ * Note that access to some telephony information is
+ * permission-protected. Your application cannot access the protected
+ * information unless it has the appropriate permissions declared in
+ * its manifest file. Where permissions apply, they are noted in the
+ * the methods through which you access the protected information.
+ *
+ * <p>TelephonyManager is intended for use on devices that implement
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices
+ * that do not implement this feature, the behavior is not reliable.
+ */
+@SystemService(Context.TELEPHONY_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY)
+public class TelephonyManager {
+    private static final String TAG = "TelephonyManager";
+
+    private TelephonyRegistryManager mTelephonyRegistryMgr;
+    /**
+     * To expand the error codes for {@link TelephonyManager#updateAvailableNetworks} and
+     * {@link TelephonyManager#setPreferredOpportunisticDataSubscription}.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    private static final long CALLBACK_ON_MORE_ERROR_CODE_CHANGE = 130595455L;
+
+    /**
+     * The key to use when placing the result of {@link #requestModemActivityInfo(ResultReceiver)}
+     * into the ResultReceiver Bundle.
+     * @hide
+     */
+    public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity";
+
+    /** @hide */
+    public static final String EXCEPTION_RESULT_KEY = "exception";
+
+    /**
+     * The process name of the Phone app as well as many other apps that use this process name, such
+     * as settings and vendor components.
+     * @hide
+     */
+    public static final String PHONE_PROCESS_NAME = "com.android.phone";
+
+    /**
+     * The allowed states of Wi-Fi calling.
+     *
+     * @hide
+     */
+    public interface WifiCallingChoices {
+        /** Always use Wi-Fi calling */
+        static final int ALWAYS_USE = 0;
+        /** Ask the user whether to use Wi-Fi on every call */
+        static final int ASK_EVERY_TIME = 1;
+        /** Never use Wi-Fi calling */
+        static final int NEVER_USE = 2;
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"NETWORK_SELECTION_MODE_"},
+            value = {
+                    NETWORK_SELECTION_MODE_UNKNOWN,
+                    NETWORK_SELECTION_MODE_AUTO,
+                    NETWORK_SELECTION_MODE_MANUAL})
+    public @interface NetworkSelectionMode {}
+
+    public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0;
+    public static final int NETWORK_SELECTION_MODE_AUTO = 1;
+    public static final int NETWORK_SELECTION_MODE_MANUAL = 2;
+
+    /**
+     * Reasons for Radio being powered off.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"RADIO_POWER_REASON_"},
+            value = {
+                    RADIO_POWER_REASON_USER,
+                    RADIO_POWER_REASON_THERMAL,
+                    RADIO_POWER_REASON_CARRIER,
+                    RADIO_POWER_REASON_NEARBY_DEVICE})
+    public @interface RadioPowerReason {}
+
+    /**
+     * This reason is used when users want to turn off radio, e.g., users turn on airplane mode.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int RADIO_POWER_REASON_USER = 0;
+    /**
+     * This reason is used when radio needs to be turned off due to thermal.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int RADIO_POWER_REASON_THERMAL = 1;
+    /**
+     * This reason is used when carriers want to turn off radio. A privileged app can request to
+     * turn off radio via the system service
+     * {@link com.android.carrierdefaultapp.CaptivePortalLoginActivity}, which subsequently calls
+     * the system APIs {@link requestRadioPowerOffForReason} and
+     * {@link clearRadioPowerOffForReason}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int RADIO_POWER_REASON_CARRIER = 2;
+    /**
+     * Used to reduce power on a battery-constrained device when Telephony services are available
+     * via a paired device which is nearby.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int RADIO_POWER_REASON_NEARBY_DEVICE = 3;
+
+    /** The otaspMode passed to SercvieState changes */
+    /** @hide */
+    static public final int OTASP_UNINITIALIZED = 0;
+    /** @hide */
+    static public final int OTASP_UNKNOWN = 1;
+    /** @hide */
+    static public final int OTASP_NEEDED = 2;
+    /** @hide */
+    static public final int OTASP_NOT_NEEDED = 3;
+    /* OtaUtil has conflict enum 4: OtaUtils.OTASP_FAILURE_SPC_RETRIES */
+    /** @hide */
+    static public final int OTASP_SIM_UNPROVISIONED = 5;
+
+    /**
+     * Used in carrier Wi-Fi for IMSI + IMPI encryption, this indicates a public key that's
+     * available for use in ePDG links.
+     *
+     * @hide
+     */
+    @SystemApi
+    static public final int KEY_TYPE_EPDG = 1;
+
+    /**
+     * Used in carrier Wi-Fi for IMSI + IMPI encryption, this indicates a public key that's
+     * available for use in WLAN links.
+     *
+     * @hide
+     */
+    @SystemApi
+    static public final int KEY_TYPE_WLAN = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"KEY_TYPE_"}, value = {KEY_TYPE_EPDG, KEY_TYPE_WLAN})
+    public @interface KeyType {}
+
+    /**
+     * No Single Radio Voice Call Continuity (SRVCC) handover is active.
+     * See TS 23.216 for more information.
+     * @hide
+     */
+    @SystemApi
+    public static final int SRVCC_STATE_HANDOVER_NONE  = -1;
+
+    /**
+     * Single Radio Voice Call Continuity (SRVCC) handover has been started on the network.
+     * See TS 23.216 for more information.
+     * @hide
+     */
+    @SystemApi
+    public static final int SRVCC_STATE_HANDOVER_STARTED  = 0;
+
+    /**
+     * Ongoing Single Radio Voice Call Continuity (SRVCC) handover has successfully completed.
+     * See TS 23.216 for more information.
+     * @hide
+     */
+    @SystemApi
+    public static final int SRVCC_STATE_HANDOVER_COMPLETED = 1;
+
+    /**
+     * Ongoing Single Radio Voice Call Continuity (SRVCC) handover has failed.
+     * See TS 23.216 for more information.
+     * @hide
+     */
+    @SystemApi
+    public static final int SRVCC_STATE_HANDOVER_FAILED   = 2;
+
+    /**
+     * Ongoing Single Radio Voice Call Continuity (SRVCC) handover has been canceled.
+     * See TS 23.216 for more information.
+     * @hide
+     */
+    @SystemApi
+    public static final int SRVCC_STATE_HANDOVER_CANCELED  = 3;
+
+    /**
+     * Convert srvcc handover state to string.
+     *
+     * @param state The srvcc handover state.
+     * @return The srvcc handover state in string format.
+     *
+     * @hide
+     */
+    public static @NonNull String srvccStateToString(int state) {
+        switch (state) {
+            case TelephonyManager.SRVCC_STATE_HANDOVER_NONE:
+                return "NONE";
+            case TelephonyManager.SRVCC_STATE_HANDOVER_STARTED:
+                return "STARTED";
+            case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED:
+                return "COMPLETED";
+            case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED:
+                return "FAILED";
+            case TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED:
+                return "CANCELED";
+            default:
+                return "UNKNOWN(" + state + ")";
+        }
+    }
+
+    /**
+     * A UICC card identifier used if the device does not support the operation.
+     * For example, {@link #getCardIdForDefaultEuicc()} returns this value if the device has no
+     * eUICC, or the eUICC cannot be read.
+     */
+    public static final int UNSUPPORTED_CARD_ID = -1;
+
+    /**
+     * A UICC card identifier used before the UICC card is loaded. See
+     * {@link #getCardIdForDefaultEuicc()} and {@link UiccCardInfo#getCardId()}.
+     * <p>
+     * Note that once the UICC card is loaded, the card ID may become {@link #UNSUPPORTED_CARD_ID}.
+     */
+    public static final int UNINITIALIZED_CARD_ID = -2;
+
+    /**
+     * Default port index for a UICC.
+     *
+     * On physical SIM cards the only available port is 0.
+     * See {@link android.telephony.UiccPortInfo} for more information on ports.
+     *
+     * See {@link android.telephony.euicc.EuiccManager#isSimPortAvailable(int)} for information on
+     * how portIndex is used on eUICCs.
+     */
+    public static final int DEFAULT_PORT_INDEX = 0;
+
+    /** @hide */
+    public static final int INVALID_PORT_INDEX = -1;
+
+    /** @hide */
+    public static final String PROPERTY_ENABLE_NULL_CIPHER_TOGGLE = "enable_null_cipher_toggle";
+
+    /**
+     * To apply the enforcement telephony feature and API
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    public static final long ENABLE_FEATURE_MAPPING = 297989574L;
+
+    private final Context mContext;
+    private final int mSubId;
+    @UnsupportedAppUsage
+    private SubscriptionManager mSubscriptionManager;
+    private TelephonyScanManager mTelephonyScanManager;
+
+    /** Cached service handles, cleared by resetServiceHandles() at death */
+    private static final Object sCacheLock = new Object();
+
+    /** @hide */
+    private static boolean sServiceHandleCacheEnabled = true;
+
+    @GuardedBy("sCacheLock")
+    private static ITelephony sITelephony;
+    @GuardedBy("sCacheLock")
+    private static IPhoneSubInfo sIPhoneSubInfo;
+    @GuardedBy("sCacheLock")
+    private static ISub sISub;
+    @GuardedBy("sCacheLock")
+    private static ISms sISms;
+    @GuardedBy("sCacheLock")
+    private static final DeathRecipient sServiceDeath = new DeathRecipient();
+
+    /**
+     * Cache key for a {@link PropertyInvalidatedCache} which maps from {@link PhoneAccountHandle}
+     * to subscription Id.  The cache is initialized in {@code PhoneInterfaceManager}'s constructor
+     * when {@link PropertyInvalidatedCache#invalidateCache(String)} is called.
+     * The cache is cleared from {@code TelecomAccountRegistry#tearDown} when all phone accounts are
+     * removed from Telecom.
+     * @hide
+     */
+    public static final String CACHE_KEY_PHONE_ACCOUNT_TO_SUBID =
+            "cache_key.telephony.phone_account_to_subid";
+    private static final int CACHE_MAX_SIZE = 4;
+
+    /**
+     * A {@link PropertyInvalidatedCache} which lives in an app's {@link TelephonyManager} instance.
+     * Caches any queries for a mapping between {@link PhoneAccountHandle} and {@code subscription
+     * id}.  The cache may be invalidated from Telephony when phone account re-registration takes
+     * place.
+     */
+    private PropertyInvalidatedCache<PhoneAccountHandle, Integer> mPhoneAccountHandleToSubIdCache =
+            new PropertyInvalidatedCache<PhoneAccountHandle, Integer>(CACHE_MAX_SIZE,
+                    CACHE_KEY_PHONE_ACCOUNT_TO_SUBID) {
+                @Override
+                public Integer recompute(PhoneAccountHandle phoneAccountHandle) {
+                    try {
+                        ITelephony telephony = getITelephony();
+                        if (telephony != null) {
+                            return telephony.getSubIdForPhoneAccountHandle(phoneAccountHandle,
+                                    mContext.getOpPackageName(), mContext.getAttributionTag());
+                        }
+                    } catch (RemoteException e) {
+                        throw e.rethrowAsRuntimeException();
+                    }
+                    return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+                }
+            };
+
+    /** Enum indicating multisim variants
+     *  DSDS - Dual SIM Dual Standby
+     *  DSDA - Dual SIM Dual Active
+     *  TSTS - Triple SIM Triple Standby
+     **/
+    /** @hide */
+    @UnsupportedAppUsage(implicitMember =
+            "values()[Landroid/telephony/TelephonyManager$MultiSimVariants;")
+    public enum MultiSimVariants {
+        @UnsupportedAppUsage
+        DSDS,
+        @UnsupportedAppUsage
+        DSDA,
+        @UnsupportedAppUsage
+        TSTS,
+        @UnsupportedAppUsage
+        UNKNOWN
+    };
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public TelephonyManager(Context context) {
+        this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public TelephonyManager(Context context, int subId) {
+        mSubId = subId;
+        mContext = mergeAttributionAndRenouncedPermissions(context.getApplicationContext(),
+            context);
+        mSubscriptionManager = SubscriptionManager.from(mContext);
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    private TelephonyManager() {
+        mContext = null;
+        mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    }
+
+    private static TelephonyManager sInstance = new TelephonyManager();
+
+    /** @hide
+    /* @deprecated - use getSystemService as described above */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static TelephonyManager getDefault() {
+        return sInstance;
+    }
+
+    // This method takes the Application context and adds the attributionTag
+    // and renouncedPermissions from the given context.
+    private Context mergeAttributionAndRenouncedPermissions(Context to, Context from) {
+        Context contextToReturn = from;
+        if (to != null) {
+            if (!Objects.equals(from.getAttributionTag(), to.getAttributionTag())) {
+                contextToReturn = to.createAttributionContext(from.getAttributionTag());
+            } else {
+                contextToReturn = to;
+            }
+
+            Set<String> renouncedPermissions =
+                    from.getAttributionSource().getRenouncedPermissions();
+            if (!renouncedPermissions.isEmpty()) {
+                if (to.getParams() != null) {
+                    contextToReturn = contextToReturn.createContext(
+                            new ContextParams.Builder(to.getParams())
+                                    .setRenouncedPermissions(renouncedPermissions).build());
+                } else {
+                    contextToReturn = contextToReturn.createContext(
+                            new ContextParams.Builder()
+                                    .setRenouncedPermissions(renouncedPermissions).build());
+                }
+            }
+        }
+        return contextToReturn;
+    }
+
+    private String getOpPackageName() {
+        // For legacy reasons the TelephonyManager has API for getting
+        // a static instance with no context set preventing us from
+        // getting the op package name. As a workaround we do a best
+        // effort and get the context from the current activity thread.
+        if (mContext != null) {
+            return mContext.getOpPackageName();
+        } else {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) return null;
+            try {
+                return telephony.getCurrentPackageName();
+            } catch (RemoteException ex) {
+                return null;
+            } catch (NullPointerException ex) {
+                return null;
+            }
+        }
+    }
+
+    private String getAttributionTag() {
+        // For legacy reasons the TelephonyManager has API for getting
+        // a static instance with no context set preventing us from
+        // getting the attribution tag.
+        if (mContext != null) {
+            return mContext.getAttributionTag();
+        }
+        return null;
+    }
+
+    private Set<String> getRenouncedPermissions() {
+        // For legacy reasons the TelephonyManager has API for getting
+        // a static instance with no context set preventing us from
+        // getting the attribution source.
+        if (mContext != null) {
+            return mContext.getAttributionSource().getRenouncedPermissions();
+        }
+        return Collections.emptySet();
+    }
+
+    /**
+     * Post a runnable to the BackgroundThread.
+     *
+     * Used to invoke user callbacks without calling into the caller's executor from the caller's
+     * calling thread context, for example to provide asynchronous error information that is
+     * generated locally (not over a binder thread).
+     *
+     * <p>This is not necessary unless you are invoking caller's code asynchronously from within
+     * the caller's thread context.
+     *
+     * @param r a runnable.
+     */
+    private static void runOnBackgroundThread(@NonNull Runnable r) {
+        try {
+            BackgroundThread.getExecutor().execute(r);
+        } catch (RejectedExecutionException e) {
+            throw new IllegalStateException(
+                    "Failed to post a callback from the caller's thread context.", e);
+        }
+    }
+
+    /**
+     * Returns the multi SIM variant
+     * Returns DSDS for Dual SIM Dual Standby
+     * Returns DSDA for Dual SIM Dual Active
+     * Returns TSTS for Triple SIM Triple Standby
+     * Returns UNKNOWN for others
+     */
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public MultiSimVariants getMultiSimConfiguration() {
+        String mSimConfig =
+                TelephonyProperties.multi_sim_config().orElse("");
+        if (mSimConfig.equals("dsds")) {
+            return MultiSimVariants.DSDS;
+        } else if (mSimConfig.equals("dsda")) {
+            return MultiSimVariants.DSDA;
+        } else if (mSimConfig.equals("tsts")) {
+            return MultiSimVariants.TSTS;
+        } else {
+            return MultiSimVariants.UNKNOWN;
+        }
+    }
+
+    /**
+     * Returns the number of phones available.
+     * Returns 0 if none of voice, sms, data is not supported
+     * Returns 1 for Single standby mode (Single SIM functionality).
+     * Returns 2 for Dual standby mode (Dual SIM functionality).
+     * Returns 3 for Tri standby mode (Tri SIM functionality).
+     * @deprecated Use {@link #getActiveModemCount} instead.
+     */
+    @Deprecated
+    public int getPhoneCount() {
+        return getActiveModemCount();
+    }
+
+    /**
+     * Returns the number of logical modems currently configured to be activated.
+     *
+     * Returns 0 if none of voice, sms, data is not supported
+     * Returns 1 for Single standby mode (Single SIM functionality).
+     * Returns 2 for Dual standby mode (Dual SIM functionality).
+     * Returns 3 for Tri standby mode (Tri SIM functionality).
+     */
+    public int getActiveModemCount() {
+        int modemCount = 1;
+        switch (getMultiSimConfiguration()) {
+            case UNKNOWN:
+                modemCount = 1;
+                // check for voice and data support, 0 if not supported
+                if (!isVoiceCapable() && !isSmsCapable() && !isDataCapable()) {
+                    modemCount = 0;
+                }
+                break;
+            case DSDS:
+            case DSDA:
+                modemCount = 2;
+                break;
+            case TSTS:
+                modemCount = 3;
+                break;
+        }
+        return modemCount;
+    }
+
+    /**
+     * Return how many logical modem can be potentially active simultaneously, in terms of hardware
+     * capability.
+     * It might return different value from {@link #getActiveModemCount}. For example, for a
+     * dual-SIM capable device operating in single SIM mode (only one logical modem is turned on),
+     * {@link #getActiveModemCount} returns 1 while this API returns 2.
+     */
+    public int getSupportedModemCount() {
+        return TelephonyProperties.max_active_modems().orElse(getActiveModemCount());
+    }
+
+    /**
+     * Gets the maximum number of SIMs that can be active, based on the device's multisim
+     * configuration.
+     * @return 1 for single-SIM, DSDS, and TSTS devices. 2 for DSDA devices.
+     * @hide
+     */
+    @SystemApi
+    public int getMaxNumberOfSimultaneouslyActiveSims() {
+        switch (getMultiSimConfiguration()) {
+            case UNKNOWN:
+            case DSDS:
+            case TSTS:
+                return 1;
+            case DSDA:
+                return 2;
+        }
+        return 1;
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static TelephonyManager from(Context context) {
+        return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+    }
+
+    /**
+     * Create a new TelephonyManager object pinned to the given subscription ID.
+     *
+     * @return a TelephonyManager that uses the given subId for all calls.
+     */
+    public TelephonyManager createForSubscriptionId(int subId) {
+      // Don't reuse any TelephonyManager objects.
+      return new TelephonyManager(mContext, subId);
+    }
+
+    /**
+     * Create a new TelephonyManager object pinned to the subscription ID associated with the given
+     * phone account.
+     *
+     * @return a TelephonyManager that uses the given phone account for all calls, or {@code null}
+     * if the phone account does not correspond to a valid subscription ID.
+     */
+    @Nullable
+    public TelephonyManager createForPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+        int subId = getSubscriptionId(phoneAccountHandle);
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            return null;
+        }
+        return new TelephonyManager(mContext, subId);
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public boolean isMultiSimEnabled() {
+        return getPhoneCount() > 1;
+    }
+
+    private static final int MAXIMUM_CALL_COMPOSER_PICTURE_SIZE = 80000;
+
+    /**
+     * Indicates the maximum size of the call composure picture.
+     *
+     * Pictures sent via
+     * {@link #uploadCallComposerPicture(InputStream, String, Executor, OutcomeReceiver)}
+     * or {@link #uploadCallComposerPicture(Path, String, Executor, OutcomeReceiver)} must not
+     * exceed this size, or an error will be returned via the callback in those methods.
+     *
+     * @return Maximum file size in bytes.
+     */
+    public static @BytesLong long getMaximumCallComposerPictureSize() {
+        return MAXIMUM_CALL_COMPOSER_PICTURE_SIZE;
+    }
+
+    //
+    // Broadcast Intent actions
+    //
+
+    /**
+     * Broadcast intent action indicating that the call state
+     * on the device has changed.
+     *
+     * <p>
+     * The {@link #EXTRA_STATE} extra indicates the new call state.
+     * If a receiving app has {@link android.Manifest.permission#READ_CALL_LOG} permission, a second
+     * extra {@link #EXTRA_INCOMING_NUMBER} provides the phone number for incoming and outgoing
+     * calls as a String.
+     * <p>
+     * If the receiving app has
+     * {@link android.Manifest.permission#READ_CALL_LOG} and
+     * {@link android.Manifest.permission#READ_PHONE_STATE} permission, it will receive the
+     * broadcast twice; one with the {@link #EXTRA_INCOMING_NUMBER} populated with the phone number,
+     * and another with it blank.  Due to the nature of broadcasts, you cannot assume the order
+     * in which these broadcasts will arrive, however you are guaranteed to receive two in this
+     * case.  Apps which are interested in the {@link #EXTRA_INCOMING_NUMBER} can ignore the
+     * broadcasts where {@link #EXTRA_INCOMING_NUMBER} is not present in the extras (e.g. where
+     * {@link Intent#hasExtra(String)} returns {@code false}).
+     * <p class="note">
+     * This was a {@link android.content.Context#sendStickyBroadcast sticky}
+     * broadcast in version 1.0, but it is no longer sticky.
+     * Instead, use {@link #getCallState} to synchronously query the current call state.
+     *
+     * @see #EXTRA_STATE
+     * @see #EXTRA_INCOMING_NUMBER
+     * @see #getCallState
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final String ACTION_PHONE_STATE_CHANGED =
+            "android.intent.action.PHONE_STATE";
+
+    /**
+     * The Phone app sends this intent when a user opts to respond-via-message during an incoming
+     * call. By default, the device's default SMS app consumes this message and sends a text message
+     * to the caller. A third party app can also provide this functionality by consuming this Intent
+     * with a {@link android.app.Service} and sending the message using its own messaging system.
+     * <p>The intent contains a URI (available from {@link android.content.Intent#getData})
+     * describing the recipient, using either the {@code sms:}, {@code smsto:}, {@code mms:},
+     * or {@code mmsto:} URI schema. Each of these URI schema carry the recipient information the
+     * same way: the path part of the URI contains the recipient's phone number or a comma-separated
+     * set of phone numbers if there are multiple recipients. For example, {@code
+     * smsto:2065551234}.</p>
+     *
+     * <p>The intent may also contain extras for the message text (in {@link
+     * android.content.Intent#EXTRA_TEXT}) and a message subject
+     * (in {@link android.content.Intent#EXTRA_SUBJECT}).</p>
+     *
+     * <p class="note"><strong>Note:</strong>
+     * The intent-filter that consumes this Intent needs to be in a {@link android.app.Service}
+     * that requires the
+     * permission {@link android.Manifest.permission#SEND_RESPOND_VIA_MESSAGE}.</p>
+     * <p>For example, the service that receives this intent can be declared in the manifest file
+     * with an intent filter like this:</p>
+     * <pre>
+     * &lt;!-- Service that delivers SMS messages received from the phone "quick response" -->
+     * &lt;service android:name=".HeadlessSmsSendService"
+     *          android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
+     *          android:exported="true" >
+     *   &lt;intent-filter>
+     *     &lt;action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
+     *     &lt;category android:name="android.intent.category.DEFAULT" />
+     *     &lt;data android:scheme="sms" />
+     *     &lt;data android:scheme="smsto" />
+     *     &lt;data android:scheme="mms" />
+     *     &lt;data android:scheme="mmsto" />
+     *   &lt;/intent-filter>
+     * &lt;/service></pre>
+     * <p>
+     * Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.SERVICE_ACTION)
+    public static final String ACTION_RESPOND_VIA_MESSAGE =
+            "android.intent.action.RESPOND_VIA_MESSAGE";
+
+    /**
+     * The emergency dialer may choose to present activities with intent filters for this
+     * action as emergency assistance buttons that launch the activity when clicked.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_EMERGENCY_ASSISTANCE =
+            "android.telephony.action.EMERGENCY_ASSISTANCE";
+
+    /**
+     * A boolean meta-data value indicating whether the voicemail settings should be hidden in the
+     * call settings page launched by
+     * {@link android.telecom.TelecomManager#ACTION_SHOW_CALL_SETTINGS}.
+     * Dialer implementations (see {@link android.telecom.TelecomManager#getDefaultDialerPackage()})
+     * which would also like to manage voicemail settings should set this meta-data to {@code true}
+     * in the manifest registration of their application.
+     *
+     * @see android.telecom.TelecomManager#ACTION_SHOW_CALL_SETTINGS
+     * @see #ACTION_CONFIGURE_VOICEMAIL
+     * @see #EXTRA_HIDE_PUBLIC_SETTINGS
+     */
+    public static final String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU =
+            "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
+
+    /**
+     * Open the voicemail settings activity to make changes to voicemail configuration.
+     *
+     * <p>
+     * The {@link #EXTRA_PHONE_ACCOUNT_HANDLE} extra indicates which {@link PhoneAccountHandle} to
+     * configure voicemail.
+     * The {@link #EXTRA_HIDE_PUBLIC_SETTINGS} hides settings the dialer will modify through public
+     * API if set.
+     *
+     * @see #EXTRA_PHONE_ACCOUNT_HANDLE
+     * @see #EXTRA_HIDE_PUBLIC_SETTINGS
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CONFIGURE_VOICEMAIL =
+            "android.telephony.action.CONFIGURE_VOICEMAIL";
+
+    /**
+     * The boolean value indicating whether the voicemail settings activity launched by {@link
+     * #ACTION_CONFIGURE_VOICEMAIL} should hide settings accessible through public API. This is
+     * used by dialer implementations which provides their own voicemail settings UI, but still
+     * needs to expose device specific voicemail settings to the user.
+     *
+     * @see #ACTION_CONFIGURE_VOICEMAIL
+     * @see #METADATA_HIDE_VOICEMAIL_SETTINGS_MENU
+     */
+    public static final String EXTRA_HIDE_PUBLIC_SETTINGS =
+            "android.telephony.extra.HIDE_PUBLIC_SETTINGS";
+
+    /**
+     * @hide
+     */
+    public static final boolean EMERGENCY_ASSISTANCE_ENABLED = true;
+
+    /**
+     * The lookup key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast
+     * for a String containing the new call state.
+     *
+     * <p class="note">
+     * Retrieve with
+     * {@link android.content.Intent#getStringExtra(String)}.
+     *
+     * @see #EXTRA_STATE_IDLE
+     * @see #EXTRA_STATE_RINGING
+     * @see #EXTRA_STATE_OFFHOOK
+     */
+    public static final String EXTRA_STATE = PhoneConstants.STATE_KEY;
+
+    /**
+     * Value used with {@link #EXTRA_STATE} corresponding to
+     * {@link #CALL_STATE_IDLE}.
+     */
+    public static final String EXTRA_STATE_IDLE = PhoneConstants.State.IDLE.toString();
+
+    /**
+     * Value used with {@link #EXTRA_STATE} corresponding to
+     * {@link #CALL_STATE_RINGING}.
+     */
+    public static final String EXTRA_STATE_RINGING = PhoneConstants.State.RINGING.toString();
+
+    /**
+     * Value used with {@link #EXTRA_STATE} corresponding to
+     * {@link #CALL_STATE_OFFHOOK}.
+     */
+    public static final String EXTRA_STATE_OFFHOOK = PhoneConstants.State.OFFHOOK.toString();
+
+    /**
+     * Extra key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast
+     * for a String containing the incoming or outgoing phone number.
+     * <p>
+     * This extra is only populated for receivers of the {@link #ACTION_PHONE_STATE_CHANGED}
+     * broadcast which have been granted the {@link android.Manifest.permission#READ_CALL_LOG} and
+     * {@link android.Manifest.permission#READ_PHONE_STATE} permissions.
+     * <p>
+     * For incoming calls, the phone number is only guaranteed to be populated when the
+     * {@link #EXTRA_STATE} changes from {@link #EXTRA_STATE_IDLE} to {@link #EXTRA_STATE_RINGING}.
+     * If the incoming caller is from an unknown number, the extra will be populated with an empty
+     * string.
+     * For outgoing calls, the phone number is only guaranteed to be populated when the
+     * {@link #EXTRA_STATE} changes from {@link #EXTRA_STATE_IDLE} to {@link #EXTRA_STATE_OFFHOOK}.
+     * <p class="note">
+     * Retrieve with
+     * {@link android.content.Intent#getStringExtra(String)}.
+     * <p>
+     *
+     * @deprecated Companion apps for wearable devices should use the {@link InCallService} API
+     * to retrieve the phone number for calls instead.  Apps performing call screening should use
+     * the {@link CallScreeningService} API instead.
+     */
+    @Deprecated
+    public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
+
+    /**
+     * Broadcast intent action indicating that call disconnect cause has changed.
+     *
+     * <p>
+     * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause.
+     * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause.
+     *
+     * <p class="note">
+     * Requires the READ_PRECISE_PHONE_STATE permission.
+     *
+     * @see #EXTRA_DISCONNECT_CAUSE
+     * @see #EXTRA_PRECISE_DISCONNECT_CAUSE
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CALL_DISCONNECT_CAUSE_CHANGED =
+            "android.intent.action.CALL_DISCONNECT_CAUSE";
+
+    /**
+     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and {@link
+     * TelephonyCallback.PreciseCallStateListener#onPreciseCallStateChanged(PreciseCallState)} for
+     * an integer containing the disconnect cause.
+     *
+     * @see DisconnectCause
+     *
+     * <p class="note">
+     * Retrieve with
+     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+     *
+     * @deprecated Should use the {@link TelecomManager#EXTRA_DISCONNECT_CAUSE} instead.
+     * @hide
+     */
+    @Deprecated
+    public static final String EXTRA_DISCONNECT_CAUSE = "disconnect_cause";
+
+    /**
+     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and {@link
+     * TelephonyCallback.PreciseCallStateListener#onPreciseCallStateChanged(PreciseCallState)} for
+     * an integer containing the disconnect cause provided by the RIL.
+     *
+     * @see PreciseDisconnectCause
+     *
+     * <p class="note">
+     * Retrieve with
+     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_PRECISE_DISCONNECT_CAUSE = "precise_disconnect_cause";
+
+    /**
+     * Broadcast intent action for letting the default dialer to know to show voicemail
+     * notification.
+     *
+     * <p>
+     * The {@link #EXTRA_PHONE_ACCOUNT_HANDLE} extra indicates which {@link PhoneAccountHandle} the
+     * voicemail is received on.
+     * The {@link #EXTRA_NOTIFICATION_COUNT} extra indicates the total numbers of unheard
+     * voicemails.
+     * The {@link #EXTRA_VOICEMAIL_NUMBER} extra indicates the voicemail number if available.
+     * The {@link #EXTRA_CALL_VOICEMAIL_INTENT} extra is a {@link android.app.PendingIntent} that
+     * will call the voicemail number when sent. This extra will be empty if the voicemail number
+     * is not set, and {@link #EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT} will be set instead.
+     * The {@link #EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT} extra is a
+     * {@link android.app.PendingIntent} that will launch the voicemail settings. This extra is only
+     * available when the voicemail number is not set.
+     * The {@link #EXTRA_IS_REFRESH} extra indicates whether the notification is a refresh or a new
+     * notification.
+     *
+     * @see #EXTRA_PHONE_ACCOUNT_HANDLE
+     * @see #EXTRA_NOTIFICATION_COUNT
+     * @see #EXTRA_VOICEMAIL_NUMBER
+     * @see #EXTRA_CALL_VOICEMAIL_INTENT
+     * @see #EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT
+     * @see #EXTRA_IS_REFRESH
+     */
+    public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION =
+            "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
+
+    /**
+     * The extra used with an {@link #ACTION_CONFIGURE_VOICEMAIL} and
+     * {@link #ACTION_SHOW_VOICEMAIL_NOTIFICATION} {@code Intent} to specify the
+     * {@link PhoneAccountHandle} the configuration or notification is for.
+     * <p class="note">
+     * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
+     */
+    public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
+            "android.telephony.extra.PHONE_ACCOUNT_HANDLE";
+
+    /**
+     * The number of voice messages associated with the notification.
+     */
+    public static final String EXTRA_NOTIFICATION_COUNT =
+            "android.telephony.extra.NOTIFICATION_COUNT";
+
+    /**
+     * The voicemail number.
+     */
+    public static final String EXTRA_VOICEMAIL_NUMBER =
+            "android.telephony.extra.VOICEMAIL_NUMBER";
+
+    /**
+     * The intent to call voicemail.
+     */
+    public static final String EXTRA_CALL_VOICEMAIL_INTENT =
+            "android.telephony.extra.CALL_VOICEMAIL_INTENT";
+
+    /**
+     * The intent to launch voicemail settings.
+     */
+    public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT =
+            "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
+
+    /**
+     * Boolean value representing whether the {@link
+     * TelephonyManager#ACTION_SHOW_VOICEMAIL_NOTIFICATION} is new or a refresh of an existing
+     * notification. Notification refresh happens after reboot or connectivity changes. The user has
+     * already been notified for the voicemail so it should not alert the user, and should not be
+     * shown again if the user has dismissed it.
+     */
+    public static final String EXTRA_IS_REFRESH = "android.telephony.extra.IS_REFRESH";
+
+    /**
+     * {@link android.telecom.Connection} event used to indicate that an IMS call has be
+     * successfully handed over from WIFI to LTE.
+     * <p>
+     * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+     * The {@link Bundle} parameter is expected to be null when this connection event is used.
+     * @hide
+     */
+    public static final String EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE =
+            "android.telephony.event.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE";
+
+    /**
+     * {@link android.telecom.Connection} event used to indicate that an IMS call has be
+     * successfully handed over from LTE to WIFI.
+     * <p>
+     * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+     * The {@link Bundle} parameter is expected to be null when this connection event is used.
+     * @hide
+     */
+    public static final String EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI =
+            "android.telephony.event.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI";
+
+    /**
+     * {@link android.telecom.Connection} event used to indicate that an IMS call failed to be
+     * handed over from LTE to WIFI.
+     * <p>
+     * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+     * The {@link Bundle} parameter is expected to be null when this connection event is used.
+     * @hide
+     */
+    public static final String EVENT_HANDOVER_TO_WIFI_FAILED =
+            "android.telephony.event.EVENT_HANDOVER_TO_WIFI_FAILED";
+
+    /**
+     * {@link android.telecom.Connection} event used to indicate that a video call was downgraded to
+     * audio because the data limit was reached.
+     * <p>
+     * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+     * The {@link Bundle} parameter is expected to be null when this connection event is used.
+     * @hide
+     */
+    public static final String EVENT_DOWNGRADE_DATA_LIMIT_REACHED =
+            "android.telephony.event.EVENT_DOWNGRADE_DATA_LIMIT_REACHED";
+
+    /**
+     * {@link android.telecom.Connection} event used to indicate that a video call was downgraded to
+     * audio because the data was disabled.
+     * <p>
+     * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+     * The {@link Bundle} parameter is expected to be null when this connection event is used.
+     * @hide
+     */
+    public static final String EVENT_DOWNGRADE_DATA_DISABLED =
+            "android.telephony.event.EVENT_DOWNGRADE_DATA_DISABLED";
+
+    /**
+     * {@link android.telecom.Connection} event used to indicate that the InCall UI should notify
+     * the user when an international call is placed while on WFC only.
+     * <p>
+     * Used when the carrier config value
+     * {@link CarrierConfigManager#KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL} is true, the device
+     * is on WFC (VoLTE not available) and an international number is dialed.
+     * <p>
+     * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+     * The {@link Bundle} parameter is expected to be null when this connection event is used.
+     * @hide
+     */
+    public static final String EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC =
+            "android.telephony.event.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC";
+
+    /**
+     * {@link android.telecom.Connection} event used to indicate that an outgoing call has been
+     * forwarded to another number.
+     * <p>
+     * Sent in response to an IMS supplementary service notification indicating the call has been
+     * forwarded.
+     * <p>
+     * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+     * The {@link Bundle} parameter is expected to be null when this connection event is used.
+     * @hide
+     */
+    public static final String EVENT_CALL_FORWARDED =
+            "android.telephony.event.EVENT_CALL_FORWARDED";
+
+    /**
+     * {@link android.telecom.Connection} event used to indicate that a supplementary service
+     * notification has been received.
+     * <p>
+     * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+     * The {@link Bundle} parameter is expected to include the following extras:
+     * <ul>
+     *     <li>{@link #EXTRA_NOTIFICATION_TYPE} - the notification type.</li>
+     *     <li>{@link #EXTRA_NOTIFICATION_CODE} - the notification code.</li>
+     *     <li>{@link #EXTRA_NOTIFICATION_MESSAGE} - human-readable message associated with the
+     *     supplementary service notification.</li>
+     * </ul>
+     * @hide
+     */
+    public static final String EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION =
+            "android.telephony.event.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION";
+
+    /**
+     * Event reported from the Telephony stack to indicate that the {@link Connection} is not
+     * able to find any network and likely will not get connected. Upon receiving this event,
+     * the dialer app should start the app included in the extras bundle of this event if satellite
+     * is provisioned.
+     * <p>
+     * The dialer app receives this event via
+     * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
+     * <p>
+     * The {@link Bundle} parameter is guaranteed to include the following extras if the below
+     * conditions are met:
+     * <ul>
+     *     <li>{@link #EXTRA_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE} - the recommending handover
+     *         type.</li>
+     *     <li>{@link #EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT} - the {@link PendingIntent}
+     *         which will be launched by the Dialer app when receiving this connection event.</li>
+     * </ul>
+     * <p>
+     * If the device is connected to satellite via carrier within the hysteresis time defined by
+     * the carrier config
+     * {@link CarrierConfigManager#KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT}, the component of
+     * the {@link #EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT} will be set to the default SMS
+     * app.
+     * <p>
+     * Otherwise, if the overlay config {@code config_oem_enabled_satellite_handover_app} is
+     * present, the app defined by this config will be used as the component of the
+     * {@link #EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT}. If this overlay config is empty,
+     * {@link #EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT} will not be included in the event
+     * {@link #EVENT_DISPLAY_EMERGENCY_MESSAGE}.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final String EVENT_DISPLAY_EMERGENCY_MESSAGE =
+            "android.telephony.event.DISPLAY_EMERGENCY_MESSAGE";
+
+    /**
+     * Integer extra key used with {@link #EVENT_DISPLAY_EMERGENCY_MESSAGE} which indicates
+     * the type of handover from emergency call to satellite messaging.
+     * <p>
+     * Will be either
+     * android.telephony.satellite.SatelliteManager#EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS
+     * or
+     * android.telephony.satellite.SatelliteManager#EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911
+     * <p>
+     * Set in the extras for the {@link #EVENT_DISPLAY_EMERGENCY_MESSAGE} connection event.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final String EXTRA_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE =
+            "android.telephony.extra.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE";
+
+    /**
+     * Extra key used with the {@link #EVENT_DISPLAY_EMERGENCY_MESSAGE} for a {@link PendingIntent}
+     * which will be launched by the Dialer app.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final String EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT =
+            "android.telephony.extra.EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT";
+
+    /**
+     * Integer extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} which indicates
+     * the type of supplementary service notification which occurred.
+     * Will be either
+     * {@link com.android.internal.telephony.gsm.SuppServiceNotification#NOTIFICATION_TYPE_CODE_1}
+     * or
+     * {@link com.android.internal.telephony.gsm.SuppServiceNotification#NOTIFICATION_TYPE_CODE_2}
+     * <p>
+     * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event.
+     * @hide
+     */
+    public static final String EXTRA_NOTIFICATION_TYPE =
+            "android.telephony.extra.NOTIFICATION_TYPE";
+
+    /**
+     * Integer extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} which indicates
+     * the supplementary service notification which occurred.
+     * <p>
+     * Depending on the {@link #EXTRA_NOTIFICATION_TYPE}, the code will be one of the {@code CODE_*}
+     * codes defined in {@link com.android.internal.telephony.gsm.SuppServiceNotification}.
+     * <p>
+     * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event.
+     * @hide
+     */
+    public static final String EXTRA_NOTIFICATION_CODE =
+            "android.telephony.extra.NOTIFICATION_CODE";
+
+    /**
+     * {@link CharSequence} extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION}
+     * which contains a human-readable message which can be displayed to the user for the
+     * supplementary service notification.
+     * <p>
+     * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event.
+     * @hide
+     */
+    public static final String EXTRA_NOTIFICATION_MESSAGE =
+            "android.telephony.extra.NOTIFICATION_MESSAGE";
+
+    /* Visual voicemail protocols */
+
+    /**
+     * The OMTP protocol.
+     */
+    public static final String VVM_TYPE_OMTP = "vvm_type_omtp";
+
+    /**
+     * A flavor of OMTP protocol with a different mobile originated (MO) format
+     */
+    public static final String VVM_TYPE_CVVM = "vvm_type_cvvm";
+
+    /**
+     * Key in bundle returned by {@link #getVisualVoicemailPackageName()}, indicating whether visual
+     * voicemail was enabled or disabled by the user. If the user never explicitly changed this
+     * setting, this key will not exist.
+     *
+     * @see #getVisualVoicemailSettings()
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL =
+            "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
+
+    /**
+     * Key in bundle returned by {@link #getVisualVoicemailPackageName()}, indicating the voicemail
+     * access PIN scrambled during the auto provisioning process. The user is expected to reset
+     * their PIN if this value is not {@code null}.
+     *
+     * @see #getVisualVoicemailSettings()
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING =
+            "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
+
+    /**
+     * Broadcast action to be received by Broadcast receivers.
+     *
+     * Indicates multi-SIM configuration is changed. For example, it changed
+     * from single SIM capable to dual-SIM capable (DSDS or DSDA) or triple-SIM mode.
+     *
+     * It doesn't indicate how many subscriptions are actually active, or which states SIMs are,
+     * or that all steps during multi-SIM change are done. To know those information you still need
+     * to listen to SIM_STATE changes or active subscription changes.
+     *
+     * See extra of {@link #EXTRA_ACTIVE_SIM_SUPPORTED_COUNT} for updated value.
+     */
+    public static final String ACTION_MULTI_SIM_CONFIG_CHANGED =
+            "android.telephony.action.MULTI_SIM_CONFIG_CHANGED";
+
+
+    /**
+     * The number of active SIM supported by current multi-SIM config. It's not related to how many
+     * SIM/subscriptions are currently active.
+     *
+     * Same value will be returned by {@link #getActiveModemCount()}.
+     *
+     * For single SIM mode, it's 1.
+     * For DSDS or DSDA mode, it's 2.
+     * For triple-SIM mode, it's 3.
+     *
+     * Extra of {@link #ACTION_MULTI_SIM_CONFIG_CHANGED}.
+     *
+     * type: integer
+     */
+    public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT =
+            "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
+
+    /**
+     * @hide
+     */
+    public static final String USSD_RESPONSE = "USSD_RESPONSE";
+
+    /**
+     * USSD return code success.
+     * @hide
+     */
+    public static final int USSD_RETURN_SUCCESS = 100;
+
+    /**
+     * Failed code returned when the mobile network has failed to complete a USSD request.
+     * <p>
+     * Returned via {@link TelephonyManager.UssdResponseCallback#onReceiveUssdResponseFailed(
+     * TelephonyManager, String, int)}.
+     */
+    public static final int USSD_RETURN_FAILURE = -1;
+
+    /**
+     * Failure code returned when a USSD request has failed to execute because the Telephony
+     * service is unavailable.
+     * <p>
+     * Returned via {@link TelephonyManager.UssdResponseCallback#onReceiveUssdResponseFailed(
+     * TelephonyManager, String, int)}.
+     */
+    public static final int USSD_ERROR_SERVICE_UNAVAIL = -2;
+
+    /**
+     * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which leaves the roaming
+     * mode set to the radio default or to the user's preference if they've indicated one.
+     */
+    public static final int CDMA_ROAMING_MODE_RADIO_DEFAULT = -1;
+    /**
+     * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which only permits
+     * connections on home networks.
+     */
+    public static final int CDMA_ROAMING_MODE_HOME = 0;
+    /**
+     * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which permits roaming on
+     * affiliated networks.
+     */
+    public static final int CDMA_ROAMING_MODE_AFFILIATED = 1;
+    /**
+     * Value for {@link CarrierConfigManager#KEY_CDMA_ROAMING_MODE_INT} which permits roaming on
+     * any network.
+     */
+    public static final int CDMA_ROAMING_MODE_ANY = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "CDMA_ROAMING_MODE_" }, value = {
+            CDMA_ROAMING_MODE_RADIO_DEFAULT,
+            CDMA_ROAMING_MODE_HOME,
+            CDMA_ROAMING_MODE_AFFILIATED,
+            CDMA_ROAMING_MODE_ANY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CdmaRoamingMode{}
+
+    /**
+     * An unknown carrier id. It could either be subscription unavailable or the subscription
+     * carrier cannot be recognized. Unrecognized carriers here means
+     * {@link #getSimOperator() MCC+MNC} cannot be identified.
+     */
+    public static final int UNKNOWN_CARRIER_ID = -1;
+
+    /**
+     * An unknown carrier id list version.
+     * @hide
+     */
+    @TestApi
+    public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1;
+
+    /**
+     * Broadcast Action: The subscription carrier identity has changed.
+     * This intent could be sent on the following events:
+     * <ul>
+     *   <li>Subscription absent. Carrier identity could change from a valid id to
+     *   {@link TelephonyManager#UNKNOWN_CARRIER_ID}.</li>
+     *   <li>Subscription loaded. Carrier identity could change from
+     *   {@link TelephonyManager#UNKNOWN_CARRIER_ID} to a valid id.</li>
+     *   <li>The subscription carrier is recognized after a remote update.</li>
+     * </ul>
+     * The intent will have the following extra values:
+     * <ul>
+     *   <li>{@link #EXTRA_CARRIER_ID} The up-to-date carrier id of the current subscription id.
+     *   </li>
+     *   <li>{@link #EXTRA_CARRIER_NAME} The up-to-date carrier name of the current subscription.
+     *   </li>
+     *   <li>{@link #EXTRA_SUBSCRIPTION_ID} The subscription id associated with the changed carrier
+     *   identity.
+     *   </li>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED =
+            "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
+
+    /**
+     * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
+     * the updated carrier id returned by {@link TelephonyManager#getSimCarrierId()}.
+     * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
+     * the carrier cannot be identified.
+     */
+    public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
+
+    /**
+     * An string extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which
+     * indicates the updated carrier name of the current subscription.
+     * @see TelephonyManager#getSimCarrierIdName()
+     * <p>Carrier name is a user-facing name of the carrier id {@link #EXTRA_CARRIER_ID},
+     * usually the brand name of the subsidiary (e.g. T-Mobile).
+     */
+    public static final String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
+
+    /**
+     * Broadcast Action: The subscription specific carrier identity has changed.
+     *
+     * A specific carrier ID returns the fine-grained carrier ID of the current subscription.
+     * It can represent the fact that a carrier may be in effect an aggregation of other carriers
+     * (ie in an MVNO type scenario) where each of these specific carriers which are used to make
+     * up the actual carrier service may have different carrier configurations.
+     * A specific carrier ID could also be used, for example, in a scenario where a carrier requires
+     * different carrier configuration for different service offering such as a prepaid plan.
+     *
+     * the specific carrier ID would be used for configuration purposes, but apps wishing to know
+     * about the carrier itself should use the regular carrier ID returned by
+     * {@link #getSimCarrierId()}.
+     *
+     * <p>Similar like {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED}, this intent will be
+     * sent on the event of {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} while its also
+     * possible to be sent without {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} when
+     * specific carrier ID changes while carrier ID remains the same.
+     * e.g, the same subscription switches to different IMSI could potentially change its
+     * specific carrier ID while carrier id remains the same.
+     * @see #getSimSpecificCarrierId()
+     * @see #getSimCarrierId()
+     *
+     * The intent will have the following extra values:
+     * <ul>
+     *   <li>{@link #EXTRA_SPECIFIC_CARRIER_ID} The up-to-date specific carrier id of the
+     *   current subscription.
+     *   </li>
+     *   <li>{@link #EXTRA_SPECIFIC_CARRIER_NAME} The up-to-date name of the specific carrier id.
+     *   </li>
+     *   <li>{@link #EXTRA_SUBSCRIPTION_ID} The subscription id associated with the changed carrier
+     *   identity.
+     *   </li>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED =
+            "android.telephony.action.SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED";
+
+    /**
+     * An int extra used with {@link #ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED} which
+     * indicates the updated specific carrier id returned by
+     * {@link TelephonyManager#getSimSpecificCarrierId()}. Note, its possible specific carrier id
+     * changes while {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} remains the same
+     * e.g, when subscription switch to different IMSIs.
+     * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
+     * the carrier cannot be identified.
+     */
+    public static final String EXTRA_SPECIFIC_CARRIER_ID =
+            "android.telephony.extra.SPECIFIC_CARRIER_ID";
+
+    /**
+     * An string extra used with {@link #ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED}
+     * which indicates the updated specific carrier name returned by
+     * {@link TelephonyManager#getSimSpecificCarrierIdName()}.
+     * <p>it's a user-facing name of the specific carrier id {@link #EXTRA_SPECIFIC_CARRIER_ID}
+     * e.g, Tracfone-AT&T
+     */
+    public static final String EXTRA_SPECIFIC_CARRIER_NAME =
+            "android.telephony.extra.SPECIFIC_CARRIER_NAME";
+
+    /**
+     * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} to indicate the
+     * subscription which has changed; or in general whenever a subscription ID needs specified.
+     */
+    public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
+
+    /**
+     * Broadcast Action: The Service Provider string(s) have been updated. Activities or
+     * services that use these strings should update their display.
+     *
+     * <p>The intent will have the following extra values:
+     * <dl>
+     *   <dt>{@link #EXTRA_SHOW_PLMN}</dt>
+     *   <dd>Boolean that indicates whether the PLMN should be shown.</dd>
+     *   <dt>{@link #EXTRA_PLMN}</dt>
+     *   <dd>The operator name of the registered network, as a string.</dd>
+     *   <dt>{@link #EXTRA_SHOW_SPN}</dt>
+     *   <dd>Boolean that indicates whether the SPN should be shown.</dd>
+     *   <dt>{@link #EXTRA_SPN}</dt>
+     *   <dd>The service provider name, as a string.</dd>
+     *   <dt>{@link #EXTRA_DATA_SPN}</dt>
+     *   <dd>The service provider name for data service, as a string.</dd>
+     * </dl>
+     *
+     * Note that {@link #EXTRA_SHOW_PLMN} may indicate that {@link #EXTRA_PLMN} should be displayed,
+     * even though the value for {@link #EXTRA_PLMN} is null. This can happen, for example, if the
+     * phone has not registered to a network yet. In this case the receiver may substitute an
+     * appropriate placeholder string (eg, "No service").
+     *
+     * It is recommended to display {@link #EXTRA_PLMN} before / above {@link #EXTRA_SPN} if
+     * both are displayed.
+     *
+     * <p>Note: this is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SERVICE_PROVIDERS_UPDATED =
+            "android.telephony.action.SERVICE_PROVIDERS_UPDATED";
+
+    /**
+     * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+     * whether the PLMN should be shown.
+     * @hide
+     */
+    public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
+
+    /**
+     * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+     * the operator name of the registered network.
+     * @hide
+     */
+    public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
+
+    /**
+     * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+     * whether the PLMN should be shown.
+     * @hide
+     */
+    public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
+
+    /**
+     * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+     * the service provider name.
+     * @hide
+     */
+    public static final String EXTRA_SPN = "android.telephony.extra.SPN";
+
+    /**
+     * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+     * the service provider name for data service.
+     * @hide
+     */
+    public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
+
+    /**
+     * Broadcast intent action indicating that when data stall recovery is attempted by Telephony,
+     * intended for report every data stall recovery step attempted.
+     *
+     * <p>
+     * The {@link #EXTRA_RECOVERY_ACTION} extra indicates the action associated with the data
+     * stall recovery.
+     * The phone id where the data stall recovery is attempted.
+     *
+     * <p class="note">
+     * Requires the READ_PHONE_STATE permission.
+     *
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     *
+     * @see #EXTRA_RECOVERY_ACTION
+     *
+     * @hide
+     */
+    // TODO(b/78370030) : Restrict this to system applications only
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public static final String ACTION_DATA_STALL_DETECTED =
+            "android.intent.action.DATA_STALL_DETECTED";
+
+    /**
+     * A service action that identifies
+     * a {@link android.service.carrier.CarrierMessagingClientService} subclass in the
+     * AndroidManifest.xml.
+     *
+     * <p>See {@link android.service.carrier.CarrierMessagingClientService} for the details.
+     */
+    @SdkConstant(SdkConstantType.SERVICE_ACTION)
+    public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE =
+            "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE";
+
+    /**
+     * An int extra used with {@link #ACTION_DATA_STALL_DETECTED} to indicate the
+     * action associated with the data stall recovery.
+     *
+     * @see #ACTION_DATA_STALL_DETECTED
+     *
+     * @hide
+     */
+    public static final String EXTRA_RECOVERY_ACTION = "recoveryAction";
+
+    private static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000;
+
+    /**
+     * Intent sent when an error occurs that debug tools should log and possibly take further
+     * action such as capturing vendor-specific logs.
+     *
+     * A privileged application that reads these events should take appropriate vendor-specific
+     * action to record the event and collect further information to assist in analysis, debugging,
+     * and resolution of any associated issue.
+     *
+     * <p>This event should not be used for generic logging or diagnostic monitoring purposes and
+     * should generally be sent at a low rate. Instead, this mechanism should be used for the
+     * framework to notify a debugging application that an event (such as a bug) has occured
+     * within the framework if that event should trigger the collection and preservation of other
+     * more detailed device state for debugging.
+     *
+     * <p>At most one application can receive these events and should register a receiver in
+     * in the application manifest. For performance reasons, if no application to receive these
+     * events is detected at boot, then these events will not be sent.
+     *
+     * <p>Each event will include an {@link EXTRA_ANOMALY_ID} that will uniquely identify the
+     * event that has occurred. Each event will be sent to the diagnostic monitor only once per
+     * boot cycle (as another optimization).
+     *
+     * @see #EXTRA_ANOMALY_ID
+     * @see #EXTRA_ANOMALY_DESCRIPTION
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public static final String ACTION_ANOMALY_REPORTED =
+            "android.telephony.action.ANOMALY_REPORTED";
+
+    /**
+     * An arbitrary ParcelUuid which should be consistent for each occurrence of a DebugEvent.
+     *
+     * This field must be included in all {@link ACTION_ANOMALY_REPORTED} events.
+     *
+     * @see #ACTION_ANOMALY_REPORTED
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
+
+    /**
+     * A freeform string description of the Anomaly.
+     *
+     * This field is optional for all {@link ACTION_ANOMALY_REPORTED}s, as a guideline should not
+     * exceed 80 characters, and should be as short as possible to convey the essence of the event.
+     *
+     * @see #ACTION_ANOMALY_REPORTED
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_ANOMALY_DESCRIPTION =
+            "android.telephony.extra.ANOMALY_DESCRIPTION";
+
+    /**
+     * Broadcast intent sent to indicate primary (non-opportunistic) subscription list has changed.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED =
+            "android.telephony.action.PRIMARY_SUBSCRIPTION_LIST_CHANGED";
+
+    /**
+     * Integer intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED}
+     * to indicate what type of SIM selection is needed.
+     *
+     * @hide
+     */
+    public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE =
+            "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE";
+
+    /** @hide */
+    @IntDef({
+            EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE,
+            EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA,
+            EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_VOICE,
+            EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_SMS,
+            EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DefaultSubscriptionSelectType{}
+
+    /**
+     * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+     * to indicate there's no need to re-select any default subscription.
+     * @hide
+     */
+    public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE = 0;
+
+    /**
+     * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+     * to indicate there's a need to select default data subscription.
+     * @hide
+     */
+    public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA = 1;
+
+    /**
+     * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+     * to indicate there's a need to select default voice call subscription.
+     * @hide
+     */
+    public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_VOICE = 2;
+
+    /**
+     * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+     * to indicate there's a need to select default sms subscription.
+     * @hide
+     */
+    public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_SMS = 3;
+
+    /**
+     * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+     * to indicate user to decide whether current SIM should be preferred for all
+     * data / voice / sms. {@link #EXTRA_SUBSCRIPTION_ID} will specified to indicate
+     * which subscription should be the default subscription.
+     * @hide
+     */
+    public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4;
+
+    /**
+     * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+     * to indicate that default subscription for data/sms/voice is now determined, that
+     * it should dismiss any dialog or pop-ups that is asking user to select default sub.
+     * This is used when, for example, opportunistic subscription is configured. At that
+     * time the primary becomes default sub there's no need to ask user to select anymore.
+     * @hide
+     */
+    public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS = 5;
+
+    /**
+     * Integer intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED}
+     * to indicate if the SIM combination in DSDS has limitation or compatible issue.
+     * e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios.
+     *
+     * @hide
+     */
+    public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE =
+            "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
+
+    /** @hide */
+    @IntDef({
+            EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE,
+            EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SimCombinationWarningType{}
+
+    /**
+     * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+     * to indicate there's no SIM combination warning.
+     * @hide
+     */
+    public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0;
+
+    /**
+     * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
+     * to indicate two active SIMs are both CDMA hence there might be functional limitation.
+     * @hide
+     */
+    public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1;
+
+    /**
+     * String intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED}
+     * to indicate what's the name of SIM combination it has limitation or compatible issue.
+     * e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios, and the
+     * name will be "operator1 & operator2".
+     *
+     * @hide
+     */
+    public static final String EXTRA_SIM_COMBINATION_NAMES =
+            "android.telephony.extra.SIM_COMBINATION_NAMES";
+
+    /**
+     * <p>Broadcast Action: The emergency callback mode is changed.
+     * <ul>
+     *   <li><em>EXTRA_PHONE_IN_ECM_STATE</em> - A boolean value,true=phone in ECM,
+     *   false=ECM off</li>
+     * </ul>
+     * <p class="note">
+     * You can <em>not</em> receive this through components declared
+     * in manifests, only by explicitly registering for it with
+     * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver,
+     * android.content.IntentFilter) Context.registerReceiver()}.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     *
+     * @see #EXTRA_PHONE_IN_ECM_STATE
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @SuppressLint("ActionValue")
+    public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED =
+            "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
+
+
+    /**
+     * Extra included in {@link #ACTION_EMERGENCY_CALLBACK_MODE_CHANGED}.
+     * Indicates whether the phone is in an emergency phone state.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_PHONE_IN_ECM_STATE =
+            "android.telephony.extra.PHONE_IN_ECM_STATE";
+
+    /**
+     * Broadcast action sent when a data connection is redirected with validation failure.
+     *
+     * This action is intended for sim/account status checks and only sent to the carrier apps
+     * specified in the carrier config for the subscription ID that's attached to this intent.
+     *
+     * The intent will have the following extra values:
+     * <ul>
+     *   <li>{@link #EXTRA_APN_TYPE}</li><dd>An integer indicating the apn type.</dd>
+     *   <li>{@link #EXTRA_REDIRECTION_URL}</li><dd>A string indicating the redirection url</dd>
+     *   <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li>
+     *          <dd>The subscription ID on which the validation failure happened.</dd>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system.</p>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CARRIER_SIGNAL_REDIRECTED =
+            "android.telephony.action.CARRIER_SIGNAL_REDIRECTED";
+
+    /**
+     * Broadcast action sent when a data connection setup fails.
+     *
+     * This action is intended for sim/account status checks and only sent to the carrier apps
+     * specified in the carrier config for the subscription ID that's attached to this intent.
+     *
+     * The intent will have the following extra values:
+     * <ul>
+     *   <li>{@link #EXTRA_APN_TYPE}</li><dd>An integer indicating the apn type.</dd>
+     *   <li>{@link #EXTRA_DATA_FAIL_CAUSE}</li><dd>A integer indicating the data fail cause.</dd>
+     *   <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li>
+     *          <dd>The subscription ID on which the data setup failure happened.</dd>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system. </p>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED =
+            "android.telephony.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED";
+
+    /**
+     * Broadcast action sent when a PCO value becomes available from the modem.
+     *
+     * This action is intended for sim/account status checks and only sent to the carrier apps
+     * specified in the carrier config for the subscription ID that's attached to this intent.
+     *
+     * The intent will have the following extra values:</p>
+     * <ul>
+     *   <li>{@link #EXTRA_APN_TYPE}</li><dd>An integer indicating the apn type.</dd>
+     *   <li>{@link #EXTRA_APN_PROTOCOL}</li><dd>An integer indicating the protocol of the apn
+     *      connection</dd>
+     *   <li>{@link #EXTRA_PCO_ID}</li><dd>An integer indicating the PCO id for the data.</dd>
+     *   <li>{@link #EXTRA_PCO_VALUE}</li><dd>A byte array of PCO data read from modem.</dd>
+     *   <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li>
+     *          <dd>The subscription ID for which the PCO info was received.</dd>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system. </p>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE =
+            "android.telephony.action.CARRIER_SIGNAL_PCO_VALUE";
+
+    /**
+     * Broadcast action sent when the availability of the system default network changes.
+     *
+     * @see ConnectivityManager#registerDefaultNetworkCallback(ConnectivityManager.NetworkCallback)
+     *
+     * This action is intended for carrier apps to set/reset carrier actions. It is only sent to the
+     * carrier apps specified in the carrier config for the subscription ID attached to this intent.
+     *
+     * The intent will have the following extra values:</p>
+     * <ul>
+     *   <li>{@link #EXTRA_DEFAULT_NETWORK_AVAILABLE}</li>
+     *   <dd>{@code true} if the default network is now available, {@code false} otherwise.</dd>
+     *   <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li>
+     *          <dd>The subscription ID on which the default network availability changed.</dd>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system. </p>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE =
+            "android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
+
+    /**
+     * Broadcast action sent when carrier apps should reset their internal state.
+     *
+     * Sent when certain events such as turning on/off mobile data, removing the SIM, etc. require
+     * carrier apps to reset their state.
+     *
+     * This action is intended to signal carrier apps to perform cleanup operations. It is only sent
+     * to the carrier apps specified in the carrier config for the subscription ID attached to
+     * this intent.
+     *
+     * The intent will have the following extra values:</p>
+     * <ul>
+     *   <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li>
+     *          <dd>The subscription ID for which state should be reset.</dd>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system.</p>
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CARRIER_SIGNAL_RESET =
+            "android.telephony.action.CARRIER_SIGNAL_RESET";
+
+    /**
+     * String extra containing the redirection URL sent with
+     * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED}.
+     */
+    public static final String EXTRA_REDIRECTION_URL = "android.telephony.extra.REDIRECTION_URL";
+
+    /**
+     * An integer extra containing the data fail cause.
+     *
+     * Sent with {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}. See {@link DataFailCause}
+     * for a list of possible values.
+     */
+    public static final String EXTRA_DATA_FAIL_CAUSE = "android.telephony.extra.DATA_FAIL_CAUSE";
+
+    /**
+     * An integer extra containing the APN type.
+     *
+     * Sent with the  {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED},
+     * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED}, and {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE}
+     * broadcasts.
+     * See the {@code TYPE_} constants in {@link ApnSetting} for a list of possible values.
+     */
+    public static final String EXTRA_APN_TYPE = "android.telephony.extra.APN_TYPE";
+
+    /**
+     * An integer extra containing the protocol of the apn connection.
+     *
+     * Sent with the {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcast.
+     * See the {@code PROTOCOL_*} constants in {@link ApnSetting} for a list of possible values.
+     */
+    public static final String EXTRA_APN_PROTOCOL = "android.telephony.extra.APN_PROTOCOL";
+
+    /**
+     * An integer extra indicating the ID for the PCO data.
+     * Sent with the {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcast.
+     */
+    public static final String EXTRA_PCO_ID = "android.telephony.extra.PCO_ID";
+
+    /**
+     * A byte array extra containing PCO data read from the modem.
+     * Sent with the {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcast.
+     */
+    public static final String EXTRA_PCO_VALUE = "android.telephony.extra.PCO_VALUE";
+
+    /**
+     * A boolean extra indicating the availability of the default network.
+     * Sent with the {@link #ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE} broadcast.
+     */
+    public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE =
+            "android.telephony.extra.DEFAULT_NETWORK_AVAILABLE";
+
+    /**
+     * <p>Broadcast Action: The emergency call state is changed.
+     * <ul>
+     *   <li><em>EXTRA_PHONE_IN_EMERGENCY_CALL</em> - A boolean value, true if phone in emergency
+     *   call, false otherwise</li>
+     * </ul>
+     * <p class="note">
+     * You can <em>not</em> receive this through components declared
+     * in manifests, only by explicitly registering for it with
+     * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver,
+     * android.content.IntentFilter) Context.registerReceiver()}.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     *
+     * @see #EXTRA_PHONE_IN_EMERGENCY_CALL
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @SuppressLint("ActionValue")
+    public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED =
+            "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
+
+
+    /**
+     * Extra included in {@link #ACTION_EMERGENCY_CALL_STATE_CHANGED}.
+     * It indicates whether the phone is making an emergency call.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_PHONE_IN_EMERGENCY_CALL =
+            "android.telephony.extra.PHONE_IN_EMERGENCY_CALL";
+
+    /**
+     * <p>Broadcast Action: It indicates the Emergency callback mode blocks datacall/sms
+     * <p class="note">.
+     * This is to pop up a notice to show user that the phone is in emergency callback mode
+     * and data calls and outgoing sms are blocked.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS =
+            "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
+
+    /**
+     * Broadcast Action: The default data subscription has changed in a multi-SIM device.
+     * This has the following extra values:</p>
+     * <ul>
+     *   <li><em>subscription</em> - A int, the current data default subscription.</li>
+     * </ul>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED =
+            "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED";
+
+    /**
+     * Broadcast Action: The default voice subscription has changed in a mult-SIm device.
+     * This has the following extra values:</p>
+     * <ul>
+     *   <li><em>subscription</em> - A int, the current voice default subscription.</li>
+     * </ul>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED =
+            "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED";
+
+    /**
+     * Broadcast Action: This triggers a client initiated OMA-DM session to the OMA server.
+     * <p class="note">
+     * Open Mobile Alliance (OMA) Device Management (DM).
+     *
+     * This intent is used by the system components to trigger OMA-DM
+     *
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
+            "com.android.omadm.service.CONFIGURATION_UPDATE";
+
+    /**
+     * Activity action: Show setting to reset mobile networks.
+     *
+     * <p>On devices with a settings activity to reset mobile networks, the activity should be
+     * launched without additional permissions.
+     *
+     * <p>On some devices, this settings activity may not exist. Callers should ensure that this
+     * case is appropriately handled.
+     */
+    @FlaggedApi(Flags.FLAG_RESET_MOBILE_NETWORK_SETTINGS)
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS =
+            "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
+
+    //
+    //
+    // Device Info
+    //
+    //
+
+    /**
+     * Returns the software version number for the device, for example,
+     * the IMEI/SV for GSM phones. Return null if the software version is
+     * not available.
+     * <p>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_BASIC_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
+    @Nullable
+    public String getDeviceSoftwareVersion() {
+        return getDeviceSoftwareVersion(getSlotIndex());
+    }
+
+    /**
+     * Returns the software version number for the device, for example,
+     * the IMEI/SV for GSM phones. Return null if the software version is
+     * not available.
+     * <p>
+     * Requires Permission: READ_PHONE_STATE.
+     *
+     * @param slotIndex of which deviceID is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
+    @Nullable
+    public String getDeviceSoftwareVersion(int slotIndex) {
+        ITelephony telephony = getITelephony();
+        if (telephony == null) return null;
+
+        try {
+            return telephony.getDeviceSoftwareVersionForSlot(slotIndex, getOpPackageName(),
+                    getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the unique device ID, for example, the IMEI for GSM and the MEID
+     * or ESN for CDMA phones. Return null if device ID is not available.
+     *
+     * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+     * restrictions, and apps are recommended to use resettable identifiers (see <a
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+     *     is a privileged permission that can only be granted to apps preloaded on the device.
+     *     <li>If the calling app is the device owner of a fully-managed device, a profile
+     *     owner of an organization-owned device, or their delegates (see {@link
+     *     android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
+     *     <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
+     *     active subscription.
+     *     <li>If the calling app is the default SMS role holder (see {@link
+     *     RoleManager#isRoleHeld(String)}).
+     * </ul>
+     *
+     * <p>If the calling app does not meet one of these requirements then this method will behave
+     * as follows:
+     *
+     * <ul>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app has the
+     *     READ_PHONE_STATE permission then null is returned.</li>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app does not have
+     *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
+     *     higher, then a SecurityException is thrown.</li>
+     * </ul>
+     *
+     * @deprecated Use {@link #getImei} which returns IMEI for GSM or {@link #getMeid} which returns
+     * MEID for CDMA.
+     */
+    @Deprecated
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public String getDeviceId() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return null;
+            return telephony.getDeviceIdWithFeature(mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the unique device ID of a subscription, for example, the IMEI for
+     * GSM and the MEID for CDMA phones. Return null if device ID is not available.
+     *
+     * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+     * restrictions, and apps are recommended to use resettable identifiers (see <a
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+     *     is a privileged permission that can only be granted to apps preloaded on the device.
+     *     <li>If the calling app is the device owner of a fully-managed device, a profile
+     *     owner of an organization-owned device, or their delegates (see {@link
+     *     android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
+     *     <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
+     *     active subscription.
+     *     <li>If the calling app is the default SMS role holder (see {@link
+     *     RoleManager#isRoleHeld(String)}).
+     * </ul>
+     *
+     * <p>If the calling app does not meet one of these requirements then this method will behave
+     * as follows:
+     *
+     * <ul>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app has the
+     *     READ_PHONE_STATE permission then null is returned.</li>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app does not have
+     *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
+     *     higher, then a SecurityException is thrown.</li>
+     * </ul>
+     *
+     * @param slotIndex of which deviceID is returned
+     *
+     * @deprecated Use {@link #getImei} which returns IMEI for GSM or {@link #getMeid} which returns
+     * MEID for CDMA.
+     */
+    @Deprecated
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public String getDeviceId(int slotIndex) {
+        // FIXME this assumes phoneId == slotIndex
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            return info.getDeviceIdForPhone(slotIndex, mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
+     * available.
+     *
+     * See {@link #getImei(int)} for details on the required permissions and behavior
+     * when the caller does not hold sufficient permissions.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
+    public String getImei() {
+        return getImei(getSlotIndex());
+    }
+
+    /**
+     * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
+     * available.
+     *
+     * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+     * restrictions, and apps are recommended to use resettable identifiers (see <a
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+     *     is a privileged permission that can only be granted to apps preloaded on the device.
+     *     <li>If the calling app is the device owner of a fully-managed device, a profile
+     *     owner of an organization-owned device, or their delegates (see {@link
+     *     android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
+     *     <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
+     *     active subscription.
+     *     <li>If the calling app is the default SMS role holder (see {@link
+     *     RoleManager#isRoleHeld(String)}).
+     *     <li>If the calling app has been granted the
+     *      {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} permission.
+     * </ul>
+     *
+     * <p>If the calling app does not meet one of these requirements then this method will behave
+     * as follows:
+     *
+     * <ul>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app has the
+     *     READ_PHONE_STATE permission then null is returned.</li>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app does not have
+     *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
+     *     higher, then a SecurityException is thrown.</li>
+     * </ul>
+     *
+     * @param slotIndex of which IMEI is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
+    public String getImei(int slotIndex) {
+        ITelephony telephony = getITelephony();
+        if (telephony == null) return null;
+
+        try {
+            return telephony.getImeiForSlot(slotIndex, getOpPackageName(), getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not
+     * available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
+    @Nullable
+    public String getTypeAllocationCode() {
+        return getTypeAllocationCode(getSlotIndex());
+    }
+
+    /**
+     * Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not
+     * available.
+     *
+     * @param slotIndex of which Type Allocation Code is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
+    @Nullable
+    public String getTypeAllocationCode(int slotIndex) {
+        ITelephony telephony = getITelephony();
+        if (telephony == null) return null;
+
+        try {
+            return telephony.getTypeAllocationCodeForSlot(slotIndex);
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
+     *
+     * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+     * restrictions, and apps are recommended to use resettable identifiers (see <a
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+     *     is a privileged permission that can only be granted to apps preloaded on the device.
+     *     <li>If the calling app is the device owner of a fully-managed device, a profile
+     *     owner of an organization-owned device, or their delegates (see {@link
+     *     android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
+     *     <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
+     *     active subscription.
+     *     <li>If the calling app is the default SMS role holder (see {@link
+     *     RoleManager#isRoleHeld(String)}).
+     * </ul>
+     *
+     * <p>If the calling app does not meet one of these requirements then this method will behave
+     * as follows:
+     *
+     * <ul>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app has the
+     *     READ_PHONE_STATE permission then null is returned.</li>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app does not have
+     *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
+     *     higher, then a SecurityException is thrown.</li>
+     * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    public String getMeid() {
+        return getMeid(getSlotIndex());
+    }
+
+    /**
+     * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
+     *
+     * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+     * restrictions, and apps are recommended to use resettable identifiers (see <a
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+     *     is a privileged permission that can only be granted to apps preloaded on the device.
+     *     <li>If the calling app is the device owner of a fully-managed device, a profile
+     *     owner of an organization-owned device, or their delegates (see {@link
+     *     android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
+     *     <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
+     *     active subscription.
+     *     <li>If the calling app is the default SMS role holder (see {@link
+     *     RoleManager#isRoleHeld(String)}).
+     * </ul>
+     *
+     * <p>If the calling app does not meet one of these requirements then this method will behave
+     * as follows:
+     *
+     * <ul>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app has the
+     *     READ_PHONE_STATE permission then null is returned.</li>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app does not have
+     *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
+     *     higher, then a SecurityException is thrown.</li>
+     * </ul>
+     *
+     * @param slotIndex of which MEID is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    public String getMeid(int slotIndex) {
+        ITelephony telephony = getITelephony();
+        if (telephony == null) return null;
+
+        try {
+            String meid = telephony.getMeidForSlot(slotIndex, getOpPackageName(),
+                    getAttributionTag());
+            if (TextUtils.isEmpty(meid)) {
+                Log.d(TAG, "getMeid: return null because MEID is not available");
+                return null;
+            }
+            return meid;
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
+     * available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    @Nullable
+    public String getManufacturerCode() {
+        return getManufacturerCode(getSlotIndex());
+    }
+
+    /**
+     * Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
+     * available.
+     *
+     * @param slotIndex of which Type Allocation Code is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    @Nullable
+    public String getManufacturerCode(int slotIndex) {
+        ITelephony telephony = getITelephony();
+        if (telephony == null) return null;
+
+        try {
+            return telephony.getManufacturerCodeForSlot(slotIndex);
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the Network Access Identifier (NAI). Return null if NAI is not available.
+     *
+     * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+     * restrictions, and apps are recommended to use resettable identifiers (see <a
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+     *     is a privileged permission that can only be granted to apps preloaded on the device.
+     *     <li>If the calling app is the device owner of a fully-managed device, a profile
+     *     owner of an organization-owned device, or their delegates (see {@link
+     *     android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
+     *     <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *     <li>If the calling app is the default SMS role holder (see {@link
+     *     RoleManager#isRoleHeld(String)}).
+     * </ul>
+     *
+     * <p>If the calling app does not meet one of these requirements then this method will behave
+     * as follows:
+     *
+     * <ul>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app has the
+     *     READ_PHONE_STATE permission then null is returned.</li>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app does not have
+     *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
+     *     higher, then a SecurityException is thrown.</li>
+     * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getNai() {
+        return getNaiBySubscriberId(getSubId());
+    }
+
+    private String getNaiBySubscriberId(int subId) {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Rlog.v(TAG, "Nai = " + nai);
+            }
+            return nai;
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the current location of the device.
+     *<p>
+     * If there is only one radio in the device and that radio has an LTE connection,
+     * this method will return null. The implementation must not to try add LTE
+     * identifiers into the existing cdma/gsm classes.
+     *<p>
+     * @return Current location of the device or null if not available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     *
+     * @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public CellLocation getCellLocation() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                Rlog.d(TAG, "getCellLocation returning null because telephony is null");
+                return null;
+            }
+
+            CellIdentity cellIdentity = telephony.getCellLocation(mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+            CellLocation cl = cellIdentity.asCellLocation();
+            if (cl == null || cl.isEmpty()) {
+                Rlog.d(TAG, "getCellLocation returning null because CellLocation is empty or"
+                        + " phone type doesn't match CellLocation type");
+                return null;
+            }
+
+            return cl;
+        } catch (RemoteException ex) {
+            Rlog.d(TAG, "getCellLocation returning null due to RemoteException " + ex);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the neighboring cell information of the device.
+     *
+     * @return List of NeighboringCellInfo or null if info unavailable.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @removed
+     * @deprecated Use {@link #getAllCellInfo} which returns a superset of the information
+     *             from NeighboringCellInfo, including LTE cell information.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public List<NeighboringCellInfo> getNeighboringCellInfo() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return null;
+            return telephony.getNeighboringCellInfo(mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /** No phone radio. */
+    public static final int PHONE_TYPE_NONE = PhoneConstants.PHONE_TYPE_NONE;
+    /** Phone radio is GSM. */
+    public static final int PHONE_TYPE_GSM = PhoneConstants.PHONE_TYPE_GSM;
+    /** Phone radio is CDMA. */
+    public static final int PHONE_TYPE_CDMA = PhoneConstants.PHONE_TYPE_CDMA;
+    /** Phone is via SIP. */
+    public static final int PHONE_TYPE_SIP = PhoneConstants.PHONE_TYPE_SIP;
+
+    /**
+     * Phone is via IMS.
+     *
+     * @hide
+     */
+    public static final int PHONE_TYPE_IMS = PhoneConstants.PHONE_TYPE_IMS;
+
+    /**
+     * Phone is via Third Party.
+     *
+     * @hide
+     */
+    public static final int PHONE_TYPE_THIRD_PARTY = PhoneConstants.PHONE_TYPE_THIRD_PARTY;
+
+    /**
+     * Returns the current phone type.
+     * TODO: This is a last minute change and hence hidden.
+     *
+     * @see #PHONE_TYPE_NONE
+     * @see #PHONE_TYPE_GSM
+     * @see #PHONE_TYPE_CDMA
+     * @see #PHONE_TYPE_SIP
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
+     * {@hide}
+     */
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
+    public int getCurrentPhoneType() {
+        return getCurrentPhoneType(getSubId());
+    }
+
+    /**
+     * Returns a constant indicating the device phone type for a subscription.
+     *
+     * @see #PHONE_TYPE_NONE
+     * @see #PHONE_TYPE_GSM
+     * @see #PHONE_TYPE_CDMA
+     *
+     * @param subId for which phone type is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
+    public int getCurrentPhoneType(int subId) {
+        int phoneId;
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            // if we don't have any sims, we don't have subscriptions, but we
+            // still may want to know what type of phone we've got.
+            phoneId = 0;
+        } else {
+            phoneId = SubscriptionManager.getPhoneId(subId);
+        }
+
+        return getCurrentPhoneTypeForSlot(phoneId);
+    }
+
+    /**
+     * See getCurrentPhoneType.
+     *
+     * @hide
+     */
+    public int getCurrentPhoneTypeForSlot(int slotIndex) {
+        try{
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getActivePhoneTypeForSlot(slotIndex);
+            } else {
+                // This can happen when the ITelephony interface is not up yet.
+                return getPhoneTypeFromProperty(slotIndex);
+            }
+        } catch (RemoteException ex) {
+            // This shouldn't happen in the normal case, as a backup we
+            // read from the system property.
+            return getPhoneTypeFromProperty(slotIndex);
+        } catch (NullPointerException ex) {
+            // This shouldn't happen in the normal case, as a backup we
+            // read from the system property.
+            return getPhoneTypeFromProperty(slotIndex);
+        }
+    }
+
+    /**
+     * Returns a constant indicating the device phone type.  This
+     * indicates the type of radio used to transmit voice calls.
+     *
+     * @see #PHONE_TYPE_NONE
+     * @see #PHONE_TYPE_GSM
+     * @see #PHONE_TYPE_CDMA
+     * @see #PHONE_TYPE_SIP
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
+    public int getPhoneType() {
+        if (!isVoiceCapable()) {
+            return PHONE_TYPE_NONE;
+        }
+        return getCurrentPhoneType();
+    }
+
+    /** {@hide} */
+    @UnsupportedAppUsage
+    private int getPhoneTypeFromProperty(int phoneId) {
+        Integer type = getTelephonyProperty(
+                phoneId, TelephonyProperties.current_active_phone(), null);
+        if (type != null) return type;
+        return getPhoneTypeFromNetworkType(phoneId);
+    }
+
+    /** {@hide} */
+    private int getPhoneTypeFromNetworkType(int phoneId) {
+        // When the system property CURRENT_ACTIVE_PHONE, has not been set,
+        // use the system property for default network type.
+        // This is a fail safe, and can only happen at first boot.
+        Integer mode = getTelephonyProperty(phoneId, TelephonyProperties.default_network(), null);
+        if (mode != null) {
+            return TelephonyManager.getPhoneType(mode);
+        }
+        return TelephonyManager.PHONE_TYPE_NONE;
+    }
+
+    /**
+     * This function returns the type of the phone, depending
+     * on the network mode.
+     *
+     * @param networkMode
+     * @return Phone Type
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static int getPhoneType(int networkMode) {
+        switch(networkMode) {
+        case RILConstants.NETWORK_MODE_CDMA:
+        case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
+        case RILConstants.NETWORK_MODE_EVDO_NO_CDMA:
+            return PhoneConstants.PHONE_TYPE_CDMA;
+
+        case RILConstants.NETWORK_MODE_WCDMA_PREF:
+        case RILConstants.NETWORK_MODE_GSM_ONLY:
+        case RILConstants.NETWORK_MODE_WCDMA_ONLY:
+        case RILConstants.NETWORK_MODE_GSM_UMTS:
+        case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA:
+        case RILConstants.NETWORK_MODE_LTE_WCDMA:
+        case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
+        case RILConstants.NETWORK_MODE_TDSCDMA_ONLY:
+        case RILConstants.NETWORK_MODE_TDSCDMA_WCDMA:
+        case RILConstants.NETWORK_MODE_LTE_TDSCDMA:
+        case RILConstants.NETWORK_MODE_TDSCDMA_GSM:
+        case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM:
+        case RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA:
+        case RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA:
+        case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA:
+        case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+            return PhoneConstants.PHONE_TYPE_GSM;
+
+        // Use CDMA Phone for the global mode including CDMA
+        case RILConstants.NETWORK_MODE_GLOBAL:
+        case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
+        case RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+            return PhoneConstants.PHONE_TYPE_CDMA;
+
+        case RILConstants.NETWORK_MODE_LTE_ONLY:
+            if (TelephonyProperties.lte_on_cdma_device().orElse(
+                    PhoneConstants.LTE_ON_CDMA_FALSE) == PhoneConstants.LTE_ON_CDMA_TRUE) {
+                return PhoneConstants.PHONE_TYPE_CDMA;
+            } else {
+                return PhoneConstants.PHONE_TYPE_GSM;
+            }
+        default:
+            return PhoneConstants.PHONE_TYPE_GSM;
+        }
+    }
+
+    /**
+     * @return The max value for the timeout passed in {@link #requestNumberVerification}.
+     * @hide
+     */
+    @SystemApi
+    public static long getMaxNumberVerificationTimeoutMillis() {
+        return MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS;
+    }
+
+    //
+    //
+    // Current Network
+    //
+    //
+
+    /**
+     * Returns the alphabetic name of current registered operator.
+     * <p>
+     * Availability: Only when user is registered to a network. Result may be
+     * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+     * on a CDMA network).
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public String getNetworkOperatorName() {
+        return getNetworkOperatorName(getSubId());
+    }
+
+    /**
+     * Returns the alphabetic name of current registered operator
+     * for a particular subscription.
+     * <p>
+     * Availability: Only when user is registered to a network. Result may be
+     * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+     * on a CDMA network).
+     * @param subId
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public String getNetworkOperatorName(int subId) {
+        int phoneId = SubscriptionManager.getPhoneId(subId);
+        return getTelephonyProperty(phoneId, TelephonyProperties.operator_alpha(), "");
+    }
+
+    /**
+     * Returns the numeric name (MCC+MNC) of current registered operator.
+     * <p>
+     * Availability: Only when user is registered to a network. Result may be
+     * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+     * on a CDMA network).
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public String getNetworkOperator() {
+        return getNetworkOperatorForPhone(getPhoneId());
+    }
+
+    /**
+     * Returns the numeric name (MCC+MNC) of current registered operator
+     * for a particular subscription.
+     * <p>
+     * Availability: Only when user is registered to a network. Result may be
+     * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+     * on a CDMA network).
+     *
+     * @param subId
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public String getNetworkOperator(int subId) {
+        int phoneId = SubscriptionManager.getPhoneId(subId);
+        return getNetworkOperatorForPhone(phoneId);
+     }
+
+    /**
+     * Returns the numeric name (MCC+MNC) of current registered operator
+     * for a particular subscription.
+     * <p>
+     * Availability: Only when user is registered to a network. Result may be
+     * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+     * on a CDMA network).
+     *
+     * @param phoneId
+     * @hide
+     **/
+    @UnsupportedAppUsage
+    public String getNetworkOperatorForPhone(int phoneId) {
+        return getTelephonyProperty(phoneId, TelephonyProperties.operator_numeric(), "");
+    }
+
+
+    /**
+     * Returns the network specifier of the subscription ID pinned to the TelephonyManager. The
+     * network specifier is used by {@link
+     * android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to create a {@link
+     * android.net.NetworkRequest} that connects through the subscription.
+     *
+     * @see android.net.NetworkRequest.Builder#setNetworkSpecifier(String)
+     * @see #createForSubscriptionId(int)
+     * @see #createForPhoneAccountHandle(PhoneAccountHandle)
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public String getNetworkSpecifier() {
+        return String.valueOf(getSubId());
+    }
+
+    /**
+     * Returns the carrier config of the subscription ID pinned to the TelephonyManager. If an
+     * invalid subscription ID is pinned to the TelephonyManager, the returned config will contain
+     * default values.
+     *
+     * <p>This method may take several seconds to complete, so it should only be called from a
+     * worker thread.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @see CarrierConfigManager#getConfigForSubId(int)
+     * @see #createForSubscriptionId(int)
+     * @see #createForPhoneAccountHandle(PhoneAccountHandle)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @WorkerThread
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public PersistableBundle getCarrierConfig() {
+        CarrierConfigManager carrierConfigManager = mContext
+                .getSystemService(CarrierConfigManager.class);
+        return carrierConfigManager.getConfigForSubId(getSubId());
+    }
+
+    /**
+     * Returns true if the device is considered roaming on the current
+     * network, for GSM purposes.
+     * <p>
+     * Availability: Only when user registered to a network.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean isNetworkRoaming() {
+        return isNetworkRoaming(getSubId());
+    }
+
+    /**
+     * Returns true if the device is considered roaming on the current
+     * network for a subscription.
+     * <p>
+     * Availability: Only when user registered to a network.
+     *
+     * @param subId
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean isNetworkRoaming(int subId) {
+        int phoneId = SubscriptionManager.getPhoneId(subId);
+        return getTelephonyProperty(phoneId, TelephonyProperties.operator_is_roaming(), false);
+    }
+
+    /**
+     * Returns the ISO-3166-1 alpha-2 country code equivalent of the MCC (Mobile Country Code) of
+     * the current registered operator or the cell nearby, if available.
+     *
+     * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
+     * if on a CDMA network).
+     * <p>
+     * @return the lowercase 2 character ISO-3166-1 alpha-2 country code, or empty string if not
+     * available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public String getNetworkCountryIso() {
+        return getNetworkCountryIso(getSlotIndex());
+    }
+
+    /**
+     * Returns the ISO-3166-1 alpha-2 country code equivalent of the MCC (Mobile Country Code) of
+     * the current registered operator or the cell nearby, if available. This is same as
+     * {@link #getNetworkCountryIso()} but allowing specifying the SIM slot index. This is used for
+     * accessing network country info from the SIM slot that does not have SIM inserted.
+     *
+     * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
+     * if on a CDMA network).
+     * <p>
+     *
+     * @param slotIndex the SIM slot index to get network country ISO.
+     *
+     * @return the lowercase 2 character ISO-3166-1 alpha-2 country code, or empty string if not
+     * available.
+     *
+     * @throws IllegalArgumentException when the slotIndex is invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     *
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    @NonNull
+    public String getNetworkCountryIso(int slotIndex) {
+        try {
+            if (slotIndex != SubscriptionManager.DEFAULT_SIM_SLOT_INDEX
+                    && !SubscriptionManager.isValidSlotIndex(slotIndex)) {
+                throw new IllegalArgumentException("invalid slot index " + slotIndex);
+            }
+
+            ITelephony telephony = getITelephony();
+            if (telephony == null) return "";
+            return telephony.getNetworkCountryIsoForPhone(slotIndex);
+        } catch (RemoteException ex) {
+            return "";
+        }
+    }
+
+    /**
+     * @hide
+     * @deprecated Use {@link #getNetworkCountryIso(int)} instead.
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #getNetworkCountryIso(int)} instead.")
+    public String getNetworkCountryIsoForPhone(int phoneId) {
+        return getNetworkCountryIso(phoneId);
+    }
+
+    /*
+     * When adding a network type to the list below, make sure to add the correct icon to
+     * MobileSignalController.mapIconSets() as well as NETWORK_TYPES
+     * Do not add negative types.
+     */
+    /** Network type is unknown */
+    public static final int NETWORK_TYPE_UNKNOWN = TelephonyProtoEnums.NETWORK_TYPE_UNKNOWN; // = 0.
+    /** Current network is GPRS */
+    public static final int NETWORK_TYPE_GPRS = TelephonyProtoEnums.NETWORK_TYPE_GPRS; // = 1.
+    /** Current network is EDGE */
+    public static final int NETWORK_TYPE_EDGE = TelephonyProtoEnums.NETWORK_TYPE_EDGE; // = 2.
+    /** Current network is UMTS */
+    public static final int NETWORK_TYPE_UMTS = TelephonyProtoEnums.NETWORK_TYPE_UMTS; // = 3.
+    /** Current network is CDMA: Either IS95A or IS95B*/
+    public static final int NETWORK_TYPE_CDMA = TelephonyProtoEnums.NETWORK_TYPE_CDMA; // = 4.
+    /** Current network is EVDO revision 0*/
+    public static final int NETWORK_TYPE_EVDO_0 = TelephonyProtoEnums.NETWORK_TYPE_EVDO_0; // = 5.
+    /** Current network is EVDO revision A*/
+    public static final int NETWORK_TYPE_EVDO_A = TelephonyProtoEnums.NETWORK_TYPE_EVDO_A; // = 6.
+    /** Current network is 1xRTT*/
+    public static final int NETWORK_TYPE_1xRTT = TelephonyProtoEnums.NETWORK_TYPE_1XRTT; // = 7.
+    /** Current network is HSDPA */
+    public static final int NETWORK_TYPE_HSDPA = TelephonyProtoEnums.NETWORK_TYPE_HSDPA; // = 8.
+    /** Current network is HSUPA */
+    public static final int NETWORK_TYPE_HSUPA = TelephonyProtoEnums.NETWORK_TYPE_HSUPA; // = 9.
+    /** Current network is HSPA */
+    public static final int NETWORK_TYPE_HSPA = TelephonyProtoEnums.NETWORK_TYPE_HSPA; // = 10.
+    /**
+     * Current network is iDen
+     * @deprecated Legacy network type no longer being used starting in Android U.
+     */
+    @Deprecated
+    public static final int NETWORK_TYPE_IDEN = TelephonyProtoEnums.NETWORK_TYPE_IDEN; // = 11.
+    /** Current network is EVDO revision B*/
+    public static final int NETWORK_TYPE_EVDO_B = TelephonyProtoEnums.NETWORK_TYPE_EVDO_B; // = 12.
+    /** Current network is LTE */
+    public static final int NETWORK_TYPE_LTE = TelephonyProtoEnums.NETWORK_TYPE_LTE; // = 13.
+    /** Current network is eHRPD */
+    public static final int NETWORK_TYPE_EHRPD = TelephonyProtoEnums.NETWORK_TYPE_EHRPD; // = 14.
+    /** Current network is HSPA+ */
+    public static final int NETWORK_TYPE_HSPAP = TelephonyProtoEnums.NETWORK_TYPE_HSPAP; // = 15.
+    /** Current network is GSM */
+    public static final int NETWORK_TYPE_GSM = TelephonyProtoEnums.NETWORK_TYPE_GSM; // = 16.
+    /** Current network is TD_SCDMA */
+    public static final int NETWORK_TYPE_TD_SCDMA =
+            TelephonyProtoEnums.NETWORK_TYPE_TD_SCDMA; // = 17.
+    /** Current network is IWLAN */
+    public static final int NETWORK_TYPE_IWLAN = TelephonyProtoEnums.NETWORK_TYPE_IWLAN; // = 18.
+    /** Current network is LTE_CA {@hide} */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static final int NETWORK_TYPE_LTE_CA = TelephonyProtoEnums.NETWORK_TYPE_LTE_CA; // = 19.
+    /**
+     * Current network is NR (New Radio) 5G.
+     * This will only be returned for 5G SA.
+     * For 5G NSA, the network type will be {@link #NETWORK_TYPE_LTE}.
+     */
+    public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20.
+
+    private static final @NetworkType int[] NETWORK_TYPES = {
+            NETWORK_TYPE_GPRS,
+            NETWORK_TYPE_EDGE,
+            NETWORK_TYPE_UMTS,
+            NETWORK_TYPE_CDMA,
+            NETWORK_TYPE_EVDO_0,
+            NETWORK_TYPE_EVDO_A,
+            NETWORK_TYPE_1xRTT,
+            NETWORK_TYPE_HSDPA,
+            NETWORK_TYPE_HSUPA,
+            NETWORK_TYPE_HSPA,
+            NETWORK_TYPE_IDEN,
+            NETWORK_TYPE_EVDO_B,
+            NETWORK_TYPE_LTE,
+            NETWORK_TYPE_EHRPD,
+            NETWORK_TYPE_HSPAP,
+            NETWORK_TYPE_GSM,
+            NETWORK_TYPE_TD_SCDMA,
+            NETWORK_TYPE_IWLAN,
+            NETWORK_TYPE_LTE_CA,
+            NETWORK_TYPE_NR
+    };
+
+    /**
+     * Returns an array of all valid network types.
+     *
+     * @return An integer array containing all valid network types in no particular order.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static @NonNull @NetworkType int[] getAllNetworkTypes() {
+        return NETWORK_TYPES.clone();
+    }
+
+    /**
+     * Return the current data network type.
+     *
+     * @deprecated use {@link #getDataNetworkType()}
+     * @return the NETWORK_TYPE_xxxx for current data connection.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @NetworkType int getNetworkType() {
+        return getNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
+    }
+
+    /**
+     * Returns a constant indicating the radio technology (network type)
+     * currently in use on the device for a subscription.
+     * @return the network type
+     *
+     * @param subId for which network type is returned
+     *
+     * @see #NETWORK_TYPE_UNKNOWN
+     * @see #NETWORK_TYPE_GPRS
+     * @see #NETWORK_TYPE_EDGE
+     * @see #NETWORK_TYPE_UMTS
+     * @see #NETWORK_TYPE_HSDPA
+     * @see #NETWORK_TYPE_HSUPA
+     * @see #NETWORK_TYPE_HSPA
+     * @see #NETWORK_TYPE_CDMA
+     * @see #NETWORK_TYPE_EVDO_0
+     * @see #NETWORK_TYPE_EVDO_A
+     * @see #NETWORK_TYPE_EVDO_B
+     * @see #NETWORK_TYPE_1xRTT
+     * @see #NETWORK_TYPE_IDEN
+     * @see #NETWORK_TYPE_LTE
+     * @see #NETWORK_TYPE_EHRPD
+     * @see #NETWORK_TYPE_HSPAP
+     * @see #NETWORK_TYPE_NR
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getNetworkType(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName(),
+                        getAttributionTag());
+            } else {
+                // This can happen when the ITelephony interface is not up yet.
+                return NETWORK_TYPE_UNKNOWN;
+            }
+        } catch (RemoteException ex) {
+            // This shouldn't happen in the normal case
+            return NETWORK_TYPE_UNKNOWN;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return NETWORK_TYPE_UNKNOWN;
+        }
+    }
+
+    /**
+     * Returns a constant indicating the radio technology (network type)
+     * currently in use on the device for data transmission.
+     *
+     * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+     * subId. Otherwise, applies to {@link SubscriptionManager#getActiveDataSubscriptionId()}.
+     *
+     * Note: Before {@link SubscriptionManager#getActiveDataSubscriptionId()} was introduced in API
+     * level 30, it was applied to {@link SubscriptionManager#getDefaultDataSubscriptionId()} which
+     * may be different now from {@link SubscriptionManager#getActiveDataSubscriptionId()}, e.g.
+     * when opportunistic network is providing cellular internet connection to the user.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or {@link android.Manifest.permission#READ_BASIC_PHONE_STATE
+     * READ_BASIC_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link #hasCarrierPrivileges}).
+     *
+     * @return the network type
+     *
+     * @see #NETWORK_TYPE_UNKNOWN
+     * @see #NETWORK_TYPE_GPRS
+     * @see #NETWORK_TYPE_EDGE
+     * @see #NETWORK_TYPE_UMTS
+     * @see #NETWORK_TYPE_HSDPA
+     * @see #NETWORK_TYPE_HSUPA
+     * @see #NETWORK_TYPE_HSPA
+     * @see #NETWORK_TYPE_CDMA
+     * @see #NETWORK_TYPE_EVDO_0
+     * @see #NETWORK_TYPE_EVDO_A
+     * @see #NETWORK_TYPE_EVDO_B
+     * @see #NETWORK_TYPE_1xRTT
+     * @see #NETWORK_TYPE_IDEN
+     * @see #NETWORK_TYPE_LTE
+     * @see #NETWORK_TYPE_EHRPD
+     * @see #NETWORK_TYPE_HSPAP
+     * @see #NETWORK_TYPE_NR
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_BASIC_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @NetworkType int getDataNetworkType() {
+        return getDataNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
+    }
+
+    /**
+     * Returns a constant indicating the radio technology (network type)
+     * currently in use on the device for data transmission for a subscription
+     * @return the network type
+     *
+     * @param subId for which network type is returned
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getDataNetworkType(int subId) {
+        try{
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getDataNetworkTypeForSubscriber(subId, getOpPackageName(),
+                        getAttributionTag());
+            } else {
+                // This can happen when the ITelephony interface is not up yet.
+                return NETWORK_TYPE_UNKNOWN;
+            }
+        } catch(RemoteException ex) {
+            // This shouldn't happen in the normal case
+            return NETWORK_TYPE_UNKNOWN;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return NETWORK_TYPE_UNKNOWN;
+        }
+    }
+
+    /**
+     * Returns the NETWORK_TYPE_xxxx for voice
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or {@link android.Manifest.permission#READ_BASIC_PHONE_STATE
+     * READ_BASIC_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_BASIC_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public @NetworkType int getVoiceNetworkType() {
+        return getVoiceNetworkType(getSubId());
+    }
+
+    /**
+     * Returns the NETWORK_TYPE_xxxx for voice for a subId
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public int getVoiceNetworkType(int subId) {
+        try{
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getVoiceNetworkTypeForSubscriber(subId, getOpPackageName(),
+                        getAttributionTag());
+            } else {
+                // This can happen when the ITelephony interface is not up yet.
+                return NETWORK_TYPE_UNKNOWN;
+            }
+        } catch(RemoteException ex) {
+            // This shouldn't happen in the normal case
+            return NETWORK_TYPE_UNKNOWN;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return NETWORK_TYPE_UNKNOWN;
+        }
+    }
+
+    /**
+     * Returns a string representation of the radio technology (network type)
+     * currently in use on the device.
+     * @return the name of the radio technology
+     *
+     * @hide pending API council review
+     */
+    @UnsupportedAppUsage
+    public String getNetworkTypeName() {
+        return getNetworkTypeName(getNetworkType());
+    }
+
+    /**
+     * Returns a string representation of the radio technology (network type)
+     * currently in use on the device.
+     * @param subId for which network type is returned
+     * @return the name of the radio technology
+     *
+     */
+    /** {@hide} */
+    @UnsupportedAppUsage
+    public static String getNetworkTypeName(@NetworkType int type) {
+        switch (type) {
+            case NETWORK_TYPE_GPRS:
+                return "GPRS";
+            case NETWORK_TYPE_EDGE:
+                return "EDGE";
+            case NETWORK_TYPE_UMTS:
+                return "UMTS";
+            case NETWORK_TYPE_HSDPA:
+                return "HSDPA";
+            case NETWORK_TYPE_HSUPA:
+                return "HSUPA";
+            case NETWORK_TYPE_HSPA:
+                return "HSPA";
+            case NETWORK_TYPE_CDMA:
+                return "CDMA";
+            case NETWORK_TYPE_EVDO_0:
+                return "CDMA - EvDo rev. 0";
+            case NETWORK_TYPE_EVDO_A:
+                return "CDMA - EvDo rev. A";
+            case NETWORK_TYPE_EVDO_B:
+                return "CDMA - EvDo rev. B";
+            case NETWORK_TYPE_1xRTT:
+                return "CDMA - 1xRTT";
+            case NETWORK_TYPE_LTE:
+                return "LTE";
+            case NETWORK_TYPE_EHRPD:
+                return "CDMA - eHRPD";
+            case NETWORK_TYPE_IDEN:
+                return "iDEN";
+            case NETWORK_TYPE_HSPAP:
+                return "HSPA+";
+            case NETWORK_TYPE_GSM:
+                return "GSM";
+            case NETWORK_TYPE_TD_SCDMA:
+                return "TD_SCDMA";
+            case NETWORK_TYPE_IWLAN:
+                return "IWLAN";
+            case NETWORK_TYPE_LTE_CA:
+                return "LTE_CA";
+            case NETWORK_TYPE_NR:
+                return "NR";
+            case NETWORK_TYPE_UNKNOWN:
+                return "UNKNOWN";
+            default:
+                return "UNKNOWN(" + type + ")";
+        }
+    }
+
+    /**
+     * Returns the bitmask for a given technology (network type)
+     * @param networkType for which bitmask is returned
+     * @return the network type bitmask
+     * {@hide}
+     */
+    public static @NetworkTypeBitMask long getBitMaskForNetworkType(@NetworkType int networkType) {
+        switch(networkType) {
+            case NETWORK_TYPE_GSM:
+                return NETWORK_TYPE_BITMASK_GSM;
+            case NETWORK_TYPE_GPRS:
+                return NETWORK_TYPE_BITMASK_GPRS;
+            case NETWORK_TYPE_EDGE:
+                return NETWORK_TYPE_BITMASK_EDGE;
+            case NETWORK_TYPE_CDMA:
+                return NETWORK_TYPE_BITMASK_CDMA;
+            case NETWORK_TYPE_1xRTT:
+                return NETWORK_TYPE_BITMASK_1xRTT;
+            case NETWORK_TYPE_EVDO_0:
+                return NETWORK_TYPE_BITMASK_EVDO_0;
+            case NETWORK_TYPE_EVDO_A:
+                return NETWORK_TYPE_BITMASK_EVDO_A;
+            case NETWORK_TYPE_EVDO_B:
+                return NETWORK_TYPE_BITMASK_EVDO_B;
+            case NETWORK_TYPE_EHRPD:
+                return NETWORK_TYPE_BITMASK_EHRPD;
+            case NETWORK_TYPE_HSUPA:
+                return NETWORK_TYPE_BITMASK_HSUPA;
+            case NETWORK_TYPE_HSDPA:
+                return NETWORK_TYPE_BITMASK_HSDPA;
+            case NETWORK_TYPE_HSPA:
+                return NETWORK_TYPE_BITMASK_HSPA;
+            case NETWORK_TYPE_HSPAP:
+                return NETWORK_TYPE_BITMASK_HSPAP;
+            case NETWORK_TYPE_UMTS:
+                return NETWORK_TYPE_BITMASK_UMTS;
+            case NETWORK_TYPE_TD_SCDMA:
+                return NETWORK_TYPE_BITMASK_TD_SCDMA;
+            case NETWORK_TYPE_LTE:
+            case NETWORK_TYPE_LTE_CA:
+                return NETWORK_TYPE_BITMASK_LTE;
+            case NETWORK_TYPE_NR:
+                return NETWORK_TYPE_BITMASK_NR;
+            case NETWORK_TYPE_IWLAN:
+                return NETWORK_TYPE_BITMASK_IWLAN;
+            case NETWORK_TYPE_IDEN:
+                return NETWORK_TYPE_BITMASK_IDEN;
+            default:
+                return NETWORK_TYPE_BITMASK_UNKNOWN;
+        }
+    }
+
+    //
+    //
+    // SIM Card
+    //
+    //
+
+    /** @hide */
+    @IntDef(prefix = {"SIM_STATE_"},
+            value = {
+                    SIM_STATE_UNKNOWN,
+                    SIM_STATE_ABSENT,
+                    SIM_STATE_PIN_REQUIRED,
+                    SIM_STATE_PUK_REQUIRED,
+                    SIM_STATE_NETWORK_LOCKED,
+                    SIM_STATE_READY,
+                    SIM_STATE_NOT_READY,
+                    SIM_STATE_PERM_DISABLED,
+                    SIM_STATE_CARD_IO_ERROR,
+                    SIM_STATE_CARD_RESTRICTED,
+                    SIM_STATE_LOADED,
+                    SIM_STATE_PRESENT,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SimState {}
+
+    /**
+     * SIM card state: Unknown. Signifies that the SIM is in transition
+     * between states. For example, when the user inputs the SIM pin
+     * under PIN_REQUIRED state, a query for sim status returns
+     * this state before turning to SIM_STATE_READY.
+     *
+     * These are the ordinal value of IccCardConstants.State.
+     */
+
+    public static final int SIM_STATE_UNKNOWN = TelephonyProtoEnums.SIM_STATE_UNKNOWN;  // 0
+    /** SIM card state: no SIM card is available in the device */
+    public static final int SIM_STATE_ABSENT = TelephonyProtoEnums.SIM_STATE_ABSENT;  // 1
+    /** SIM card state: Locked: requires the user's SIM PIN to unlock */
+    public static final int SIM_STATE_PIN_REQUIRED =
+            TelephonyProtoEnums.SIM_STATE_PIN_REQUIRED;  // 2
+    /** SIM card state: Locked: requires the user's SIM PUK to unlock */
+    public static final int SIM_STATE_PUK_REQUIRED =
+            TelephonyProtoEnums.SIM_STATE_PUK_REQUIRED;  // 3
+    /** SIM card state: Locked: requires a network PIN to unlock */
+    public static final int SIM_STATE_NETWORK_LOCKED =
+            TelephonyProtoEnums.SIM_STATE_NETWORK_LOCKED;  // 4
+    /** SIM card state: Ready */
+    public static final int SIM_STATE_READY = TelephonyProtoEnums.SIM_STATE_READY;  // 5
+    /** SIM card state: SIM Card is NOT READY */
+    public static final int SIM_STATE_NOT_READY = TelephonyProtoEnums.SIM_STATE_NOT_READY;  // 6
+    /** SIM card state: SIM Card Error, permanently disabled */
+    public static final int SIM_STATE_PERM_DISABLED =
+            TelephonyProtoEnums.SIM_STATE_PERM_DISABLED;  // 7
+    /** SIM card state: SIM Card Error, present but faulty */
+    public static final int SIM_STATE_CARD_IO_ERROR =
+            TelephonyProtoEnums.SIM_STATE_CARD_IO_ERROR;  // 8
+    /** SIM card state: SIM Card restricted, present but not usable due to
+     * carrier restrictions.
+     */
+    public static final int SIM_STATE_CARD_RESTRICTED =
+            TelephonyProtoEnums.SIM_STATE_CARD_RESTRICTED;  // 9
+    /**
+     * SIM card state: Loaded: SIM card applications have been loaded
+     * @hide
+     */
+    @SystemApi
+    public static final int SIM_STATE_LOADED = TelephonyProtoEnums.SIM_STATE_LOADED;  // 10
+    /**
+     * SIM card state: SIM Card is present
+     * @hide
+     */
+    @SystemApi
+    public static final int SIM_STATE_PRESENT = TelephonyProtoEnums.SIM_STATE_PRESENT;  // 11
+
+    /**
+     * Extra included in {@link #ACTION_SIM_CARD_STATE_CHANGED} and
+     * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED} to indicate the card/application state.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
+
+    /**
+     * Broadcast Action: The sim card state has changed.
+     * The intent will have the following extra values:</p>
+     * <dl>
+     *   <dt>{@link #EXTRA_SIM_STATE}</dt>
+     *   <dd>The sim card state. One of:
+     *     <dl>
+     *       <dt>{@link #SIM_STATE_ABSENT}</dt>
+     *       <dd>SIM card not found</dd>
+     *       <dt>{@link #SIM_STATE_CARD_IO_ERROR}</dt>
+     *       <dd>SIM card IO error</dd>
+     *       <dt>{@link #SIM_STATE_CARD_RESTRICTED}</dt>
+     *       <dd>SIM card is restricted</dd>
+     *       <dt>{@link #SIM_STATE_PRESENT}</dt>
+     *       <dd>SIM card is present</dd>
+     *     </dl>
+     *   </dd>
+     * </dl>
+     *
+     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * <p class="note">The current state can also be queried using {@link #getSimCardState()}.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SIM_CARD_STATE_CHANGED =
+            "android.telephony.action.SIM_CARD_STATE_CHANGED";
+
+    /**
+     * Broadcast Action: The sim application state has changed.
+     * The intent will have the following extra values:</p>
+     * <dl>
+     *   <dt>{@link #EXTRA_SIM_STATE}</dt>
+     *   <dd>The sim application state. One of:
+     *     <dl>
+     *       <dt>{@link #SIM_STATE_NOT_READY}</dt>
+     *       <dd>SIM card applications not ready</dd>
+     *       <dt>{@link #SIM_STATE_PIN_REQUIRED}</dt>
+     *       <dd>SIM card PIN locked</dd>
+     *       <dt>{@link #SIM_STATE_PUK_REQUIRED}</dt>
+     *       <dd>SIM card PUK locked</dd>
+     *       <dt>{@link #SIM_STATE_NETWORK_LOCKED}</dt>
+     *       <dd>SIM card network locked</dd>
+     *       <dt>{@link #SIM_STATE_PERM_DISABLED}</dt>
+     *       <dd>SIM card permanently disabled due to PUK failures</dd>
+     *       <dt>{@link #SIM_STATE_LOADED}</dt>
+     *       <dd>SIM card data loaded</dd>
+     *     </dl>
+     *   </dd>
+     * </dl>
+     *
+     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * <p class="note">The current state can also be queried using
+     * {@link #getSimApplicationState()}.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SIM_APPLICATION_STATE_CHANGED =
+            "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
+
+    /**
+     * Broadcast Action: Status of the SIM slots on the device has changed.
+     *
+     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * <p class="note">The status can be queried using
+     * {@link #getUiccSlotsInfo()}
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SIM_SLOT_STATUS_CHANGED =
+            "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
+
+    /**
+     * Broadcast Action: A debug code has been entered in the dialer.
+     * <p>
+     * This intent is broadcast by the system and OEM telephony apps may need to receive these
+     * broadcasts. And it requires the sender to be default dialer or has carrier privileges
+     * (see {@link #hasCarrierPrivileges}).
+     * <p>
+     * These "secret codes" are used to activate developer menus by dialing certain codes.
+     * And they are of the form {@code *#*#<code>#*#*}. The intent will have the data
+     * URI: {@code android_secret_code://<code>}. It is possible that a manifest
+     * receiver would be woken up even if it is not currently running.
+     * <p>
+     * It is supposed to replace {@link android.provider.Telephony.Sms.Intents#SECRET_CODE_ACTION}
+     * in the next Android version.
+     * Before that both of these two actions will be broadcast.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE";
+
+    /**
+     * This API is used to check if there is an ICC card present in the device.
+     *
+     * An ICC card is a smart card that contains a subscriber identity module (SIM) and is used
+     * to identify and authenticate users to a mobile network.
+     *
+     * Note: In case of embedded SIM there is an ICC card always present irrespective
+     * of whether an active SIM profile is present or not so this API would always return true.
+     *
+     * @return true if a ICC card is present.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public boolean hasIccCard() {
+        return hasIccCard(getSlotIndex());
+    }
+
+    /**
+     * @return true if a ICC card is present for a subscription
+     *
+     * @param slotIndex for which icc card presence is checked
+     */
+    /** {@hide} */
+    // FIXME Input argument slotIndex should be of type int
+    @UnsupportedAppUsage
+    public boolean hasIccCard(int slotIndex) {
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return false;
+            return telephony.hasIccCardUsingSlotIndex(slotIndex);
+        } catch (RemoteException ex) {
+            // Assume no ICC card if remote exception which shouldn't happen
+            return false;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return false;
+        }
+    }
+
+    /**
+     * Returns a constant indicating the state of the default SIM card.
+     *
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_ABSENT
+     * @see #SIM_STATE_PIN_REQUIRED
+     * @see #SIM_STATE_PUK_REQUIRED
+     * @see #SIM_STATE_NETWORK_LOCKED
+     * @see #SIM_STATE_READY
+     * @see #SIM_STATE_NOT_READY
+     * @see #SIM_STATE_PERM_DISABLED
+     * @see #SIM_STATE_CARD_IO_ERROR
+     * @see #SIM_STATE_CARD_RESTRICTED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @SimState int getSimState() {
+        int simState = getSimStateIncludingLoaded();
+        if (simState == SIM_STATE_LOADED) {
+            simState = SIM_STATE_READY;
+        }
+        return simState;
+    }
+
+    private @SimState int getSimStateIncludingLoaded() {
+        int slotIndex = getSlotIndex();
+        // slotIndex may be invalid due to sim being absent. In that case query all slots to get
+        // sim state
+        if (slotIndex < 0) {
+            // query for all slots and return absent if all sim states are absent, otherwise
+            // return unknown
+            for (int i = 0; i < getPhoneCount(); i++) {
+                int simState = getSimState(i);
+                if (simState != SIM_STATE_ABSENT) {
+                    Rlog.d(TAG, "getSimState: default sim:" + slotIndex + ", sim state for " +
+                            "slotIndex=" + i + " is " + simState + ", return state as unknown");
+                    return SIM_STATE_UNKNOWN;
+                }
+            }
+            Rlog.d(TAG, "getSimState: default sim:" + slotIndex + ", all SIMs absent, return " +
+                    "state as absent");
+            return SIM_STATE_ABSENT;
+        }
+        return getSimStateForSlotIndex(slotIndex);
+    }
+
+    /**
+     * Returns a constant indicating the state of the default SIM card.
+     *
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_ABSENT
+     * @see #SIM_STATE_CARD_IO_ERROR
+     * @see #SIM_STATE_CARD_RESTRICTED
+     * @see #SIM_STATE_PRESENT
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @SimState int getSimCardState() {
+        int simState = getSimState();
+        return getSimCardStateFromSimState(simState);
+    }
+
+    /**
+     * Returns a constant indicating the state of the device SIM card in a physical slot.
+     *
+     * @param physicalSlotIndex physical slot index
+     *
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_ABSENT
+     * @see #SIM_STATE_CARD_IO_ERROR
+     * @see #SIM_STATE_CARD_RESTRICTED
+     * @see #SIM_STATE_PRESENT
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     * @deprecated instead use {@link #getSimCardState(int, int)}
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @Deprecated
+    public @SimState int getSimCardState(int physicalSlotIndex) {
+        int activePort = getFirstActivePortIndex(physicalSlotIndex);
+        int simState = getSimState(getLogicalSlotIndex(physicalSlotIndex, activePort));
+        return getSimCardStateFromSimState(simState);
+    }
+
+    /**
+     * Returns a constant indicating the state of the device SIM card in a physical slot and
+     * port index.
+     *
+     * @param physicalSlotIndex physical slot index
+     * @param portIndex The port index is an enumeration of the ports available on the UICC.
+     *                  Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+     *
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_ABSENT
+     * @see #SIM_STATE_CARD_IO_ERROR
+     * @see #SIM_STATE_CARD_RESTRICTED
+     * @see #SIM_STATE_PRESENT
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @SimState int getSimCardState(int physicalSlotIndex, int portIndex) {
+        int simState = getSimState(getLogicalSlotIndex(physicalSlotIndex, portIndex));
+        return getSimCardStateFromSimState(simState);
+    }
+    /**
+     * Converts SIM state to SIM card state.
+     * @param simState
+     * @return SIM card state
+     */
+    private @SimState int getSimCardStateFromSimState(int simState) {
+        switch (simState) {
+            case SIM_STATE_UNKNOWN:
+            case SIM_STATE_ABSENT:
+            case SIM_STATE_CARD_IO_ERROR:
+            case SIM_STATE_CARD_RESTRICTED:
+                return simState;
+            default:
+                return SIM_STATE_PRESENT;
+        }
+    }
+
+    /**
+     * Converts a physical slot index to logical slot index.
+     * @param physicalSlotIndex physical slot index
+     * @param portIndex The port index is an enumeration of the ports available on the UICC.
+     *                  Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+     * @return logical slot index
+     */
+    private int getLogicalSlotIndex(int physicalSlotIndex, int portIndex) {
+        UiccSlotInfo[] slotInfos = getUiccSlotsInfo();
+        if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.length
+                && slotInfos[physicalSlotIndex] != null) {
+            for (UiccPortInfo portInfo : slotInfos[physicalSlotIndex].getPorts()) {
+                if (portInfo.getPortIndex() == portIndex) {
+                    return portInfo.getLogicalSlotIndex();
+                }
+            }
+        }
+
+        return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+    }
+
+    /**
+     * Returns a constant indicating the state of the card applications on the default SIM card.
+     *
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_PIN_REQUIRED
+     * @see #SIM_STATE_PUK_REQUIRED
+     * @see #SIM_STATE_NETWORK_LOCKED
+     * @see #SIM_STATE_NOT_READY
+     * @see #SIM_STATE_PERM_DISABLED
+     * @see #SIM_STATE_LOADED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @SimState int getSimApplicationState() {
+        int simState = getSimStateIncludingLoaded();
+        return getSimApplicationStateFromSimState(simState);
+    }
+
+    /**
+     * Returns a constant indicating the state of the card applications on the device SIM card in
+     * a physical slot.
+     *
+     * @param physicalSlotIndex physical slot index
+     *
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_PIN_REQUIRED
+     * @see #SIM_STATE_PUK_REQUIRED
+     * @see #SIM_STATE_NETWORK_LOCKED
+     * @see #SIM_STATE_NOT_READY
+     * @see #SIM_STATE_PERM_DISABLED
+     * @see #SIM_STATE_LOADED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     * @deprecated instead use {@link #getSimApplicationState(int, int)}
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @Deprecated
+    public @SimState int getSimApplicationState(int physicalSlotIndex) {
+        int activePort = getFirstActivePortIndex(physicalSlotIndex);
+        int simState = getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex, activePort));
+        return getSimApplicationStateFromSimState(simState);
+    }
+
+    /**
+     * Returns a constant indicating the state of the card applications on the device SIM card in
+     * a physical slot.
+     *
+     * @param physicalSlotIndex physical slot index
+     * @param portIndex The port index is an enumeration of the ports available on the UICC.
+     *                  Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+     *
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_PIN_REQUIRED
+     * @see #SIM_STATE_PUK_REQUIRED
+     * @see #SIM_STATE_NETWORK_LOCKED
+     * @see #SIM_STATE_NOT_READY
+     * @see #SIM_STATE_PERM_DISABLED
+     * @see #SIM_STATE_LOADED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @SimState int getSimApplicationState(int physicalSlotIndex, int portIndex) {
+        int simState = getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex, portIndex));
+        return getSimApplicationStateFromSimState(simState);
+    }
+
+    /**
+     * Converts SIM state to SIM application state.
+     * @param simState
+     * @return SIM application state
+     */
+    private @SimState int getSimApplicationStateFromSimState(int simState) {
+        switch (simState) {
+            case SIM_STATE_UNKNOWN:
+            case SIM_STATE_ABSENT:
+            case SIM_STATE_CARD_IO_ERROR:
+            case SIM_STATE_CARD_RESTRICTED:
+                return SIM_STATE_UNKNOWN;
+            case SIM_STATE_READY:
+                // Ready is not a valid state anymore. The state that is broadcast goes from
+                // NOT_READY to either LOCKED or LOADED.
+                return SIM_STATE_NOT_READY;
+            default:
+                return simState;
+        }
+    }
+
+
+    /**
+     * Returns true if the specified type of application (e.g. {@link #APPTYPE_CSIM} is present
+     * on the UICC card.
+     *
+     * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+     *
+     * @param appType the uicc app type like {@link APPTYPE_CSIM}
+     * @return true if the specified type of application in UICC CARD or false if no uicc or error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public boolean isApplicationOnUicc(@UiccAppType int appType) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.isApplicationOnUicc(getSubId(), appType);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isApplicationOnUicc", e);
+        }
+        return false;
+    }
+
+    /**
+     * Returns a constant indicating the state of the device SIM card in a logical slot.
+     *
+     * @param slotIndex logical slot index
+     *
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_ABSENT
+     * @see #SIM_STATE_PIN_REQUIRED
+     * @see #SIM_STATE_PUK_REQUIRED
+     * @see #SIM_STATE_NETWORK_LOCKED
+     * @see #SIM_STATE_READY
+     * @see #SIM_STATE_NOT_READY
+     * @see #SIM_STATE_PERM_DISABLED
+     * @see #SIM_STATE_CARD_IO_ERROR
+     * @see #SIM_STATE_CARD_RESTRICTED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @SimState int getSimState(int slotIndex) {
+        int simState = getSimStateForSlotIndex(slotIndex);
+        if (simState == SIM_STATE_LOADED) {
+            simState = SIM_STATE_READY;
+        }
+        return simState;
+    }
+
+    /**
+     * Returns the MCC+MNC (mobile country code + mobile network code) of the
+     * provider of the SIM. 5 or 6 decimal digits.
+     * <p>
+     * Availability: SIM state must be {@link #SIM_STATE_READY}
+     *
+     * @see #getSimState
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getSimOperator() {
+        return getSimOperatorNumeric();
+    }
+
+    /**
+     * Returns the MCC+MNC (mobile country code + mobile network code) of the
+     * provider of the SIM. 5 or 6 decimal digits.
+     * <p>
+     * Availability: SIM state must be {@link #SIM_STATE_READY}
+     *
+     * @see #getSimState
+     *
+     * @param subId for which SimOperator is returned
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public String getSimOperator(int subId) {
+        return getSimOperatorNumeric(subId);
+    }
+
+    /**
+     * Returns the MCC+MNC (mobile country code + mobile network code) of the
+     * provider of the SIM. 5 or 6 decimal digits.
+     * <p>
+     * Availability: SIM state must be {@link #SIM_STATE_READY}
+     *
+     * @see #getSimState
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public String getSimOperatorNumeric() {
+        int subId = mSubId;
+        if (!SubscriptionManager.isUsableSubIdValue(subId)) {
+            subId = SubscriptionManager.getDefaultDataSubscriptionId();
+            if (!SubscriptionManager.isUsableSubIdValue(subId)) {
+                subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+                if (!SubscriptionManager.isUsableSubIdValue(subId)) {
+                    subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+                    if (!SubscriptionManager.isUsableSubIdValue(subId)) {
+                        subId = SubscriptionManager.getDefaultSubscriptionId();
+                    }
+                }
+            }
+        }
+        return getSimOperatorNumeric(subId);
+    }
+
+    /**
+     * Returns the MCC+MNC (mobile country code + mobile network code) of the
+     * provider of the SIM for a particular subscription. 5 or 6 decimal digits.
+     * <p>
+     * Availability: SIM state must be {@link #SIM_STATE_READY}
+     *
+     * @see #getSimState
+     *
+     * @param subId for which SimOperator is returned
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public String getSimOperatorNumeric(int subId) {
+        int phoneId = SubscriptionManager.getPhoneId(subId);
+        return getSimOperatorNumericForPhone(phoneId);
+    }
+
+    /**
+     * Returns the MCC+MNC (mobile country code + mobile network code) of the
+     * provider of the SIM for a particular subscription. 5 or 6 decimal digits.
+     * <p>
+     *
+     * @param phoneId for which SimOperator is returned
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public String getSimOperatorNumericForPhone(int phoneId) {
+        return getTelephonyProperty(phoneId, TelephonyProperties.icc_operator_numeric(), "");
+    }
+
+    /**
+     * Returns the Service Provider Name (SPN).
+     * <p>
+     * Availability: SIM state must be {@link #SIM_STATE_READY}
+     *
+     * @see #getSimState
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getSimOperatorName() {
+        return getSimOperatorNameForPhone(getPhoneId());
+    }
+
+    /**
+     * Returns the Service Provider Name (SPN).
+     * <p>
+     * Availability: SIM state must be {@link #SIM_STATE_READY}
+     *
+     * @see #getSimState
+     *
+     * @param subId for which SimOperatorName is returned
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public String getSimOperatorName(int subId) {
+        int phoneId = SubscriptionManager.getPhoneId(subId);
+        return getSimOperatorNameForPhone(phoneId);
+    }
+
+    /**
+     * Returns the Service Provider Name (SPN).
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public String getSimOperatorNameForPhone(int phoneId) {
+        return getTelephonyProperty(phoneId, TelephonyProperties.icc_operator_alpha(), "");
+    }
+
+    /**
+     * Returns the ISO-3166-1 alpha-2 country code equivalent for the SIM provider's country code.
+     * <p>
+     * The ISO-3166-1 alpha-2 country code is provided in lowercase 2 character format.
+     * @return the lowercase 2 character ISO-3166-1 alpha-2 country code, or empty string is not
+     * available.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getSimCountryIso() {
+        return getSimCountryIsoForPhone(getPhoneId());
+    }
+
+    /**
+     * Returns the ISO country code equivalent for the SIM provider's country code.
+     *
+     * @param subId for which SimCountryIso is returned
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static String getSimCountryIso(int subId) {
+        int phoneId = SubscriptionManager.getPhoneId(subId);
+        return getSimCountryIsoForPhone(phoneId);
+    }
+
+    /**
+     * Returns the ISO country code equivalent for the SIM provider's country code.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static String getSimCountryIsoForPhone(int phoneId) {
+        return getTelephonyProperty(phoneId, TelephonyProperties.icc_operator_iso_country(), "");
+    }
+
+    /**
+     * Returns the serial number of the SIM, if applicable. Return null if it is
+     * unavailable.
+     *
+     * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+     * restrictions, and apps are recommended to use resettable identifiers (see <a
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+     *     is a privileged permission that can only be granted to apps preloaded on the device.
+     *     <li>If the calling app is the device owner of a fully-managed device, a profile
+     *     owner of an organization-owned device, or their delegates (see {@link
+     *     android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
+     *     <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *     <li>If the calling app is the default SMS role holder (see {@link
+     *     RoleManager#isRoleHeld(String)}).
+     * </ul>
+     *
+     * <p>If the calling app does not meet one of these requirements then this method will behave
+     * as follows:
+     *
+     * <ul>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app has the
+     *     READ_PHONE_STATE permission then null is returned.</li>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app does not have
+     *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
+     *     higher, then a SecurityException is thrown.</li>
+     * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getSimSerialNumber() {
+         return getSimSerialNumber(getSubId());
+    }
+
+    /**
+     * Returns the serial number for the given subscription, if applicable. Return null if it is
+     * unavailable.
+     *
+     * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+     * restrictions, and apps are recommended to use resettable identifiers (see <a
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+     *     is a privileged permission that can only be granted to apps preloaded on the device.
+     *     <li>If the calling app is the device owner of a fully-managed device, a profile
+     *     owner of an organization-owned device, or their delegates (see {@link
+     *     android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
+     *     <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *     <li>If the calling app is the default SMS role holder (see {@link
+     *     RoleManager#isRoleHeld(String)}).
+     * </ul>
+     *
+     * <p>If the calling app does not meet one of these requirements then this method will behave
+     * as follows:
+     *
+     * <ul>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app has the
+     *     READ_PHONE_STATE permission then null is returned.</li>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app does not have
+     *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
+     *     higher, then a SecurityException is thrown.</li>
+     * </ul>
+     *
+     * @param subId for which Sim Serial number is returned
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @UnsupportedAppUsage
+    public String getSimSerialNumber(int subId) {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
+     * Return if the current radio can support both 3GPP and 3GPP2 radio technologies at the same
+     * time. This is also known as global mode, which includes LTE, CDMA, EvDo and GSM/WCDMA.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @return {@code true} if 3GPP and 3GPP2 radio technologies can be supported at the same time
+     *         {@code false} if not supported or unknown
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean isLteCdmaEvdoGsmWcdmaEnabled() {
+        return getLteOnCdmaMode(getSubId()) == PhoneConstants.LTE_ON_CDMA_TRUE;
+    }
+
+    /**
+     * Return if the current radio is LTE on CDMA for Subscription. This
+     * is a tri-state return value as for a period of time
+     * the mode may be unknown.
+     *
+     * @param subId for which radio is LTE on CDMA is returned
+     * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
+     * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @UnsupportedAppUsage
+    public int getLteOnCdmaMode(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
+            return telephony.getLteOnCdmaModeForSubscriber(subId, getOpPackageName(),
+                    getAttributionTag());
+        } catch (RemoteException ex) {
+            // Assume no ICC card if remote exception which shouldn't happen
+            return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
+        }
+    }
+
+    /**
+     * Get the card ID of the default eUICC card. If the eUICCs have not yet been loaded, returns
+     * {@link #UNINITIALIZED_CARD_ID}. If there is no eUICC or the device does not support card IDs
+     * for eUICCs, returns {@link #UNSUPPORTED_CARD_ID}.
+     *
+     * <p>The card ID is a unique identifier associated with a UICC or eUICC card. Card IDs are
+     * unique to a device, and always refer to the same UICC or eUICC card unless the device goes
+     * through a factory reset.
+     *
+     * @return card ID of the default eUICC card, if loaded.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
+    public int getCardIdForDefaultEuicc() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                return UNINITIALIZED_CARD_ID;
+            }
+            return telephony.getCardIdForDefaultEuicc(mSubId, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            return UNINITIALIZED_CARD_ID;
+        }
+    }
+
+    /**
+     * Gets information about currently inserted UICCs and eUICCs.
+     * <p>
+     * Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * <p>
+     * If the caller has carrier priviliges on any active subscription, then they have permission to
+     * get simple information like the card ID ({@link UiccCardInfo#getCardId()}), whether the card
+     * is an eUICC ({@link UiccCardInfo#isEuicc()}), and the physical slot index where the card is
+     * inserted ({@link UiccCardInfo#getPhysicalSlotIndex()}.
+     * <p>
+     * To get private information such as the EID ({@link UiccCardInfo#getEid()}) or ICCID
+     * ({@link UiccCardInfo#getIccId()}), the caller must have carrier priviliges on that specific
+     * UICC or eUICC card.
+     * <p>
+     * See {@link UiccCardInfo} for more details on the kind of information available.
+     *
+     * @return a list of UiccCardInfo objects, representing information on the currently inserted
+     * UICCs and eUICCs. Each UiccCardInfo in the list will have private information filtered out if
+     * the caller does not have adequate permissions for that card.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @NonNull
+    public List<UiccCardInfo> getUiccCardsInfo() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                Log.e(TAG, "Error in getUiccCardsInfo: unable to connect to Telephony service.");
+                return new ArrayList<UiccCardInfo>();
+            }
+            return telephony.getUiccCardsInfo(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in getUiccCardsInfo: " + e);
+            return new ArrayList<UiccCardInfo>();
+        }
+    }
+
+    /**
+     * Gets all the UICC slots. The objects in the array can be null if the slot info is not
+     * available, which is possible between phone process starting and getting slot info from modem.
+     *
+     * @return UiccSlotInfo array.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public UiccSlotInfo[] getUiccSlotsInfo() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                return null;
+            }
+            return telephony.getUiccSlotsInfo(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Test method to reload the UICC profile.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void refreshUiccProfile() {
+        try {
+            ITelephony telephony = getITelephony();
+            telephony.refreshUiccProfile(mSubId);
+        } catch (RemoteException ex) {
+            Rlog.w(TAG, "RemoteException", ex);
+        }
+    }
+
+    /**
+     * Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive. For
+     * example, passing the physicalSlots array [1, 0] means mapping the first item 1, which is
+     * physical slot index 1, to the logical slot 0; and mapping the second item 0, which is
+     * physical slot index 0, to the logical slot 1. The index of the array means the index of the
+     * logical slots.
+     *
+     * @param physicalSlots The content of the array represents the physical slot index. The array
+     *        size should be same as {@link #getUiccSlotsInfo()}.
+     * @return boolean Return true if the switch succeeds, false if the switch fails.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     * @deprecated {@link #setSimSlotMapping(Collection, Executor, Consumer)}
+     */
+     // TODO: once integrating the HAL changes we can  convert int[] to List<UiccSlotMapping> and
+     // converge API's in ITelephony.aidl and PhoneInterfaceManager
+
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public boolean switchSlots(int[] physicalSlots) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                return false;
+            }
+            return telephony.switchSlots(physicalSlots);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * @param slotMapping Logical to physical slot and port mapping.
+     * @return {@code true} if slotMapping is valid.
+     * @return {@code false} if slotMapping is invalid.
+     *
+     * slotMapping is invalid if there are different entries (physical slot + port) mapping to the
+     * same logical slot or if there are same {physical slot + port} mapping to the different
+     * logical slot
+     * @hide
+     */
+    private static boolean isSlotMappingValid(@NonNull Collection<UiccSlotMapping> slotMapping) {
+        // Grouping the collection by logicalSlotIndex, finding different entries mapping to the
+        // same logical slot
+        Map<Integer, List<UiccSlotMapping>> slotMappingInfo = slotMapping.stream().collect(
+                Collectors.groupingBy(UiccSlotMapping::getLogicalSlotIndex));
+        for (Map.Entry<Integer, List<UiccSlotMapping>> entry : slotMappingInfo.entrySet()) {
+            List<UiccSlotMapping> logicalSlotMap = entry.getValue();
+            if (logicalSlotMap.size() > 1) {
+                // duplicate logicalSlotIndex found
+                return false;
+            }
+        }
+
+        // Grouping the collection by physical slot and port, finding same entries mapping to the
+        // different logical slot
+        Map<List<Integer>, List<UiccSlotMapping>> slotMapInfos = slotMapping.stream().collect(
+                Collectors.groupingBy(
+                        slot -> Arrays.asList(slot.getPhysicalSlotIndex(), slot.getPortIndex())));
+        for (Map.Entry<List<Integer>, List<UiccSlotMapping>> entry : slotMapInfos.entrySet()) {
+            List<UiccSlotMapping> portAndPhysicalSlotList = entry.getValue();
+            if (portAndPhysicalSlotList.size() > 1) {
+                // duplicate pair of portIndex and physicalSlotIndex found
+                return false;
+            }
+        }
+        return true;
+    }
+    /**
+     * Maps the logical slots to physical slots and ports. Mapping is specified from
+     * {@link UiccSlotMapping} which consist of both physical slot index and port index.
+     * Logical slot is the slot that is seen by modem. Physical slot is the actual physical slot.
+     * Port index is the index (enumerated value) for the associated port available on the SIM.
+     * Each physical slot can have multiple ports if
+     * {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP} is supported.
+     *
+     * Example: no. of logical slots 1 and physical slots 2 do not support MEP, each physical slot
+     * has one port:
+     * The only logical slot (index 0) can be mapped to first physical slot (value 0), port(index
+     * 0) or
+     * second physical slot(value 1), port (index 0), while the other physical slot remains unmapped
+     * and inactive.
+     * slotMapping[0] = UiccSlotMapping{0 //port index, 0 //physical slot, 0 //logical slot} or
+     * slotMapping[0] = UiccSlotMapping{0 //port index, 1 //physical slot, 0 //logical slot}
+     *
+     * Example no. of logical slots 2 and physical slots 2 supports MEP with 2 ports available:
+     * Each logical slot must be mapped to a port (physical slot and port combination).
+     * First logical slot (index 0) can be mapped to physical slot 1 and the second logical slot
+     * can be mapped to either port from physical slot 2.
+     *
+     * slotMapping[0] = UiccSlotMapping{0, 0, 0} and slotMapping[1] = UiccSlotMapping{0, 1, 1} or
+     * slotMapping[0] = UiccSlotMapping{0, 0, 0} and slotMapping[1] = UiccSlotMapping{1, 1, 1}
+     *
+     * or the other way around, the second logical slot(index 1) can be mapped to physical slot 1
+     * and the first logical slot can be mapped to either port from physical slot 2.
+     *
+     * slotMapping[1] = UiccSlotMapping{0, 0, 0} and slotMapping[0] = UiccSlotMapping{0, 1, 1} or
+     * slotMapping[1] = UiccSlotMapping{0, 0, 0} and slotMapping[0] = UiccSlotMapping{1, 1, 1}
+     *
+     * another possible mapping is each logical slot maps to each port of physical slot 2 and there
+     * is no active logical modem mapped to physical slot 1.
+     *
+     * slotMapping[0] = UiccSlotMapping{0, 1, 0} and slotMapping[1] = UiccSlotMapping{1, 1, 1} or
+     * slotMapping[0] = UiccSlotMapping{1, 1, 0} and slotMapping[1] = UiccSlotMapping{0, 1, 1}
+     *
+     * @param slotMapping Logical to physical slot and port mapping.
+     * @throws IllegalStateException if telephony service is null or slot mapping was sent when the
+     *         radio in middle of a silent restart or other invalid states to handle the command
+     * @throws IllegalArgumentException if the caller passes in an invalid collection of
+     *         UiccSlotMapping like duplicate data, etc
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public void setSimSlotMapping(@NonNull Collection<UiccSlotMapping> slotMapping) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (isSlotMappingValid(slotMapping)) {
+                    boolean result = telephony.setSimSlotMapping(new ArrayList(slotMapping));
+                    if (!result) {
+                        throw new IllegalStateException("setSimSlotMapping has failed");
+                    }
+                } else {
+                    throw new IllegalArgumentException("Duplicate UiccSlotMapping data found");
+                }
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Get the mapping from logical slots to physical slots. The key of the map is the logical slot
+     * id and the value is the physical slots id mapped to this logical slot id.
+     *
+     * @return a map indicates the mapping from logical slots to physical slots. The size of the map
+     * should be {@link #getPhoneCount()} if success, otherwise return an empty map.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     * @deprecated use {@link #getSimSlotMapping()} instead.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @NonNull
+    @Deprecated
+    public Map<Integer, Integer> getLogicalToPhysicalSlotMapping() {
+        Map<Integer, Integer> slotMapping = new HashMap<>();
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                List<UiccSlotMapping> simSlotsMapping = telephony.getSlotsMapping(
+                        mContext.getOpPackageName());
+                for (UiccSlotMapping slotMap : simSlotsMapping) {
+                    slotMapping.put(slotMap.getLogicalSlotIndex(), slotMap.getPhysicalSlotIndex());
+                }
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "getSlotsMapping RemoteException", e);
+        }
+        return slotMapping;
+    }
+
+    /**
+     * Get the mapping from logical slots to physical sim slots and port indexes. Initially the
+     * logical slot index was mapped to physical slot index, but with support for multi-enabled
+     * profile(MEP){@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP},logical slot is now mapped to
+     * port index.
+     *
+     * @return a collection of {@link UiccSlotMapping} which indicates the mapping from logical
+     *         slots to ports and physical slots.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @NonNull
+    public Collection<UiccSlotMapping> getSimSlotMapping() {
+        List<UiccSlotMapping> slotMap;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                slotMap = telephony.getSlotsMapping(mContext.getOpPackageName());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+        return slotMap;
+    }
+    //
+    //
+    // Subscriber Info
+    //
+    //
+
+    /**
+     * Returns the unique subscriber ID, for example, the IMSI for a GSM phone.
+     * Return null if it is unavailable.
+     *
+     * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+     * restrictions, and apps are recommended to use resettable identifiers (see <a
+     * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+     * method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+     *     is a privileged permission that can only be granted to apps preloaded on the device.
+     *     <li>If the calling app is the device owner of a fully-managed device, a profile
+     *     owner of an organization-owned device, or their delegates (see {@link
+     *     android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
+     *     <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *     <li>If the calling app is the default SMS role holder (see {@link
+     *     RoleManager#isRoleHeld(String)}).
+     *     <li>If the calling app has been granted the
+     *     {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} permission.
+     * </ul>
+     *
+     * <p>If the calling app does not meet one of these requirements then this method will behave
+     * as follows:
+     *
+     * <ul>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app has the
+     *     READ_PHONE_STATE permission then null is returned.</li>
+     *     <li>If the calling app's target SDK is API level 28 or lower and the app does not have
+     *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
+     *     higher, then a SecurityException is thrown.</li>
+     * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getSubscriberId() {
+        return getSubscriberId(getSubId());
+    }
+
+    /**
+     * Returns the unique subscriber ID, for example, the IMSI for a GSM phone
+     * for a subscription.
+     * Return null if it is unavailable.
+     *
+     * See {@link #getSubscriberId()} for details on the required permissions and behavior
+     * when the caller does not hold sufficient permissions.
+     *
+     * @param subId whose subscriber id is returned
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public String getSubscriberId(int subId) {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
+     * Returns carrier specific information that will be used to encrypt the IMSI and IMPI,
+     * including the public key and the key identifier; or {@code null} if not available.
+     * <p>
+     * For a multi-sim device, the dafault data sim is used if not specified.
+     * <p>
+     * Requires Permission: READ_PRIVILEGED_PHONE_STATE.
+     *
+     * @param keyType whether the key is being used for EPDG or WLAN. Valid values are
+     *        {@link #KEY_TYPE_EPDG} or {@link #KEY_TYPE_WLAN}.
+     * @return ImsiEncryptionInfo Carrier specific information that will be used to encrypt the
+     *         IMSI and IMPI. This includes the public key and the key identifier. This information
+     *         will be stored in the device keystore. {@code null} will be returned when no key is
+     *         found, and the carrier does not require a key.
+     * @throws IllegalArgumentException when an invalid key is found or when key is required but
+     *         not found.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @SystemApi
+    @Nullable
+    public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(@KeyType int keyType) {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null) {
+                Rlog.e(TAG,"IMSI error: Subscriber Info is null");
+                return null;
+            }
+            int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
+            if (keyType != KEY_TYPE_EPDG && keyType != KEY_TYPE_WLAN) {
+                throw new IllegalArgumentException("IMSI error: Invalid key type");
+            }
+            ImsiEncryptionInfo imsiEncryptionInfo = info.getCarrierInfoForImsiEncryption(
+                    subId, keyType, mContext.getOpPackageName());
+            if (imsiEncryptionInfo == null && isImsiEncryptionRequired(subId, keyType)) {
+                Rlog.e(TAG, "IMSI error: key is required but not found");
+                throw new IllegalArgumentException("IMSI error: key is required but not found");
+            }
+            return imsiEncryptionInfo;
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex);
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            Rlog.e(TAG, "getCarrierInfoForImsiEncryption NullPointerException" + ex);
+        }
+        return null;
+    }
+
+    /**
+     * Resets the carrier keys used to encrypt the IMSI and IMPI.
+     * <p>
+     * This involves 2 steps:
+     *  1. Delete the keys from the database.
+     *  2. Send an intent to download new Certificates.
+     * <p>
+     * For a multi-sim device, the dafault data sim is used if not specified.
+     * <p>
+     * Requires Permission: MODIFY_PHONE_STATE.
+     *
+     * @see #getCarrierInfoForImsiEncryption
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @SystemApi
+    public void resetCarrierKeysForImsiEncryption() {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null) {
+                throw new RuntimeException("IMSI error: Subscriber Info is null");
+            }
+            int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
+            info.resetCarrierKeysForImsiEncryption(subId, mContext.getOpPackageName());
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Telephony#getCarrierInfoForImsiEncryption RemoteException" + ex);
+        }
+    }
+
+    /**
+     * @param keyAvailability bitmask that defines the availabilty of keys for a type.
+     * @param keyType the key type which is being checked. (WLAN, EPDG)
+     * @return true if the digit at position keyType is 1, else false.
+     * @hide
+     */
+    private static boolean isKeyEnabled(int keyAvailability, @KeyType int keyType) {
+        int returnValue = (keyAvailability >> (keyType - 1)) & 1;
+        return (returnValue == 1) ? true : false;
+    }
+
+    /**
+     * If Carrier requires Imsi to be encrypted.
+     * @hide
+     */
+    private boolean isImsiEncryptionRequired(int subId, @KeyType int keyType) {
+        CarrierConfigManager configManager =
+                (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager == null) {
+            return false;
+        }
+        PersistableBundle pb = configManager.getConfigForSubId(subId);
+        if (pb == null) {
+            return false;
+        }
+        int keyAvailability = pb.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT);
+        return isKeyEnabled(keyAvailability, keyType);
+    }
+
+    /**
+     * Sets the Carrier specific information that will be used to encrypt the IMSI and IMPI.
+     * This includes the public key and the key identifier. This information will be stored in the
+     * device keystore.
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * @param imsiEncryptionInfo which includes the Key Type, the Public Key
+     *        (java.security.PublicKey) and the Key Identifier.and the Key Identifier.
+     *        The keyIdentifier Attribute value pair that helps a server locate
+     *        the private key to decrypt the permanent identity. This field is
+     *        optional and if it is present then it’s always separated from encrypted
+     *        permanent identity with “,”. Key identifier AVP is presented in ASCII string
+     *        with “name=value” format.
+     * @hide
+     */
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null) return;
+            info.setCarrierInfoForImsiEncryption(mSubId, mContext.getOpPackageName(),
+                    imsiEncryptionInfo);
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return;
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setCarrierInfoForImsiEncryption RemoteException", ex);
+            return;
+        }
+    }
+
+    /**
+     * Exception that may be supplied to the callback in {@link #uploadCallComposerPicture} if
+     * something goes awry.
+     */
+    public static class CallComposerException extends Exception {
+        /**
+         * Used internally only, signals success of the upload to the carrier.
+         * @hide
+         */
+        public static final int SUCCESS = -1;
+        /**
+         * Indicates that an unknown error was encountered when uploading the call composer picture.
+         *
+         * Clients that encounter this error should retry the upload.
+         */
+        public static final int ERROR_UNKNOWN = 0;
+
+        /**
+         * Indicates that the phone process died or otherwise became unavailable while uploading the
+         * call composer picture.
+         *
+         * Clients that encounter this error should retry the upload.
+         */
+        public static final int ERROR_REMOTE_END_CLOSED = 1;
+
+        /**
+         * Indicates that the file or stream supplied exceeds the size limit defined in
+         * {@link #getMaximumCallComposerPictureSize()}.
+         *
+         * Clients that encounter this error should retry the upload after reducing the size of the
+         * picture.
+         */
+        public static final int ERROR_FILE_TOO_LARGE = 2;
+
+        /**
+         * Indicates that the device failed to authenticate with the carrier when uploading the
+         * picture.
+         *
+         * Clients that encounter this error should not retry the upload unless a reboot or radio
+         * reset has been performed in the interim.
+         */
+        public static final int ERROR_AUTHENTICATION_FAILED = 3;
+
+        /**
+         * Indicates that the {@link InputStream} passed to {@link #uploadCallComposerPicture}
+         * was closed.
+         *
+         * The caller should retry if this error is encountered, and be sure to not close the stream
+         * before the callback is called this time.
+         */
+        public static final int ERROR_INPUT_CLOSED = 4;
+
+        /**
+         * Indicates that an {@link IOException} was encountered while reading the picture.
+         *
+         * The offending {@link IOException} will be available via {@link #getIOException()}.
+         * Clients should use the contents of the exception to determine whether a retry is
+         * warranted.
+         */
+        public static final int ERROR_IO_EXCEPTION = 5;
+
+        /**
+         * Indicates that the device is currently not connected to a network that's capable of
+         * reaching a carrier's RCS servers.
+         *
+         * Clients should prompt the user to remedy the issue by moving to an area with better
+         * signal, by connecting to a different network, or to retry at another time.
+         */
+        public static final int ERROR_NETWORK_UNAVAILABLE = 6;
+
+        /** @hide */
+        @IntDef(prefix = {"ERROR_"}, value = {
+                ERROR_UNKNOWN,
+                ERROR_REMOTE_END_CLOSED,
+                ERROR_FILE_TOO_LARGE,
+                ERROR_AUTHENTICATION_FAILED,
+                ERROR_INPUT_CLOSED,
+                ERROR_IO_EXCEPTION,
+                ERROR_NETWORK_UNAVAILABLE,
+        })
+
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface CallComposerError {}
+
+        private final int mErrorCode;
+        private final IOException mIOException;
+
+        public CallComposerException(@CallComposerError int errorCode,
+                @Nullable IOException ioException) {
+            mErrorCode = errorCode;
+            mIOException = ioException;
+        }
+
+        /**
+         * Fetches the error code associated with this exception.
+         * @return An error code.
+         */
+        public @CallComposerError int getErrorCode() {
+            return mErrorCode;
+        }
+
+        /**
+         * Fetches the {@link IOException} that caused the error.
+         */
+        // Follows the naming of IOException
+        @SuppressLint("AcronymName")
+        public @Nullable IOException getIOException() {
+            return mIOException;
+        }
+    }
+
+    /** @hide */
+    public static final String KEY_CALL_COMPOSER_PICTURE_HANDLE = "call_composer_picture_handle";
+
+    /**
+     * Uploads a picture to the carrier network for use with call composer.
+     *
+     * @see #uploadCallComposerPicture(InputStream, String, Executor, OutcomeReceiver)
+     * @param pictureToUpload Path to a local file containing the picture to upload.
+     * @param contentType The MIME type of the picture you're uploading (e.g. image/jpeg)
+     * @param executor The {@link Executor} on which the {@code pictureToUpload} file will be read
+     *                 from disk, as well as on which {@code callback} will be called.
+     * @param callback A callback called when the upload operation terminates, either in success
+     *                 or in error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void uploadCallComposerPicture(@NonNull Path pictureToUpload,
+            @NonNull String contentType,
+            @CallbackExecutor @NonNull Executor executor,
+            @NonNull OutcomeReceiver<ParcelUuid, CallComposerException> callback) {
+        Objects.requireNonNull(pictureToUpload);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        // Do the role check now so that we can quit early if needed -- there's an additional
+        // permission check on the other side of the binder call as well.
+        RoleManager rm = mContext.getSystemService(RoleManager.class);
+        if (!rm.isRoleHeld(RoleManager.ROLE_DIALER)) {
+            throw new SecurityException("You must hold RoleManager.ROLE_DIALER to do this");
+        }
+
+        executor.execute(() -> {
+            try {
+                if (Looper.getMainLooper().isCurrentThread()) {
+                    Log.w(TAG, "Uploading call composer picture on main thread!"
+                            + " hic sunt dracones!");
+                }
+                long size = Files.size(pictureToUpload);
+                if (size > getMaximumCallComposerPictureSize()) {
+                    callback.onError(new CallComposerException(
+                            CallComposerException.ERROR_FILE_TOO_LARGE, null));
+                    return;
+                }
+                InputStream fileStream = Files.newInputStream(pictureToUpload);
+                try {
+                    uploadCallComposerPicture(fileStream, contentType, executor,
+                            new OutcomeReceiver<ParcelUuid, CallComposerException>() {
+                                @Override
+                                public void onResult(ParcelUuid result) {
+                                    try {
+                                        fileStream.close();
+                                    } catch (IOException e) {
+                                        // ignore
+                                        Log.e(TAG, "Error closing file input stream when"
+                                                + " uploading call composer pic");
+                                    }
+                                    callback.onResult(result);
+                                }
+
+                                @Override
+                                public void onError(CallComposerException error) {
+                                    try {
+                                        fileStream.close();
+                                    } catch (IOException e) {
+                                        // ignore
+                                        Log.e(TAG, "Error closing file input stream when"
+                                                + " uploading call composer pic");
+                                    }
+                                    callback.onError(error);
+                                }
+                            });
+                } catch (Exception e) {
+                    Log.e(TAG, "Got exception calling into stream-version of"
+                            + " uploadCallComposerPicture: " + e);
+                    try {
+                        fileStream.close();
+                    } catch (IOException e1) {
+                        // ignore
+                        Log.e(TAG, "Error closing file input stream when uploading"
+                                + " call composer pic");
+                    }
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "IOException when uploading call composer pic:" + e);
+                callback.onError(
+                        new CallComposerException(CallComposerException.ERROR_IO_EXCEPTION, e));
+            }
+        });
+
+    }
+
+    /**
+     * Uploads a picture to the carrier network for use with call composer.
+     *
+     * This method allows a dialer app to upload a picture to the carrier network that can then
+     * later be attached to an outgoing call. In order to attach the picture to a call, use the
+     * {@link ParcelUuid} returned from {@code callback} upon successful upload as the value to
+     * {@link TelecomManager#EXTRA_OUTGOING_PICTURE}.
+     *
+     * This functionality is only available to the app filling the {@link RoleManager#ROLE_DIALER}
+     * role on the device.
+     *
+     * This functionality is only available when
+     * {@link CarrierConfigManager#KEY_SUPPORTS_CALL_COMPOSER_BOOL} is set to {@code true} in the
+     * bundle returned from {@link #getCarrierConfig()}.
+     *
+     * @param pictureToUpload An {@link InputStream} that supplies the bytes representing the
+     *                        picture to upload. The client bears responsibility for closing this
+     *                        stream after {@code callback} is called with success or failure.
+     *
+     *                        Additionally, if the stream supplies more bytes than the return value
+     *                        of {@link #getMaximumCallComposerPictureSize()}, the upload will be
+     *                        aborted and the callback will be called with an exception containing
+     *                        {@link CallComposerException#ERROR_FILE_TOO_LARGE}.
+     * @param contentType The MIME type of the picture you're uploading (e.g. image/jpeg). The list
+     *                    of acceptable content types can be found at 3GPP TS 26.141 sections
+     *                    4.2 and 4.3.
+     * @param executor The {@link Executor} on which the {@code pictureToUpload} stream will be
+     *                 read, as well as on which the callback will be called.
+     * @param callback A callback called when the upload operation terminates, either in success
+     *                 or in error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void uploadCallComposerPicture(@NonNull InputStream pictureToUpload,
+            @NonNull String contentType,
+            @CallbackExecutor @NonNull Executor executor,
+            @NonNull OutcomeReceiver<ParcelUuid, CallComposerException> callback) {
+        Objects.requireNonNull(pictureToUpload);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        ITelephony telephony = getITelephony();
+        if (telephony == null) {
+            throw new IllegalStateException("Telephony service not available.");
+        }
+
+        ParcelFileDescriptor writeFd;
+        ParcelFileDescriptor readFd;
+        try {
+            ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
+            writeFd = pipe[1];
+            readFd = pipe[0];
+        } catch (IOException e) {
+            executor.execute(() -> callback.onError(
+                    new CallComposerException(CallComposerException.ERROR_IO_EXCEPTION, e)));
+            return;
+        }
+
+        OutputStream output = new ParcelFileDescriptor.AutoCloseOutputStream(writeFd);
+
+        try {
+            telephony.uploadCallComposerPicture(getSubId(), mContext.getOpPackageName(),
+                    contentType, readFd, new ResultReceiver(null) {
+                        @Override
+                        protected void onReceiveResult(int resultCode, Bundle result) {
+                            if (resultCode != CallComposerException.SUCCESS) {
+                                executor.execute(() -> callback.onError(
+                                        new CallComposerException(resultCode, null)));
+                                return;
+                            }
+                            ParcelUuid resultUuid =
+                                    result.getParcelable(KEY_CALL_COMPOSER_PICTURE_HANDLE, android.os.ParcelUuid.class);
+                            if (resultUuid == null) {
+                                Log.e(TAG, "Got null uuid without an error"
+                                        + " while uploading call composer pic");
+                                executor.execute(() -> callback.onError(
+                                        new CallComposerException(
+                                                CallComposerException.ERROR_UNKNOWN, null)));
+                                return;
+                            }
+                            executor.execute(() -> callback.onResult(resultUuid));
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Remote exception uploading call composer pic:" + e);
+            e.rethrowAsRuntimeException();
+        }
+
+        executor.execute(() -> {
+            if (Looper.getMainLooper().isCurrentThread()) {
+                Log.w(TAG, "Uploading call composer picture on main thread!"
+                        + " hic sunt dracones!");
+            }
+
+            int totalBytesRead = 0;
+            byte[] buffer = new byte[16 * 1024];
+            try {
+                while (true) {
+                    int numRead;
+                    try {
+                        numRead = pictureToUpload.read(buffer);
+                    } catch (IOException e) {
+                        Log.e(TAG, "IOException reading from input while uploading pic: " + e);
+                        // Most likely, this was because the stream was closed. We have no way to
+                        // tell though.
+                        callback.onError(new CallComposerException(
+                                CallComposerException.ERROR_INPUT_CLOSED, e));
+                        try {
+                            writeFd.closeWithError("input closed");
+                        } catch (IOException e1) {
+                            // log and ignore
+                            Log.e(TAG, "Error closing fd pipe: " + e1);
+                        }
+                        break;
+                    }
+
+                    if (numRead < 0) {
+                        break;
+                    }
+
+                    totalBytesRead += numRead;
+                    if (totalBytesRead > getMaximumCallComposerPictureSize()) {
+                        Log.e(TAG, "Read too many bytes from call composer pic stream: "
+                                + totalBytesRead);
+                        try {
+                            callback.onError(new CallComposerException(
+                                    CallComposerException.ERROR_FILE_TOO_LARGE, null));
+                            writeFd.closeWithError("too large");
+                        } catch (IOException e1) {
+                            // log and ignore
+                            Log.e(TAG, "Error closing fd pipe: " + e1);
+                        }
+                        break;
+                    }
+
+                    try {
+                        output.write(buffer, 0, numRead);
+                    } catch (IOException e) {
+                        callback.onError(new CallComposerException(
+                                CallComposerException.ERROR_REMOTE_END_CLOSED, e));
+                        try {
+                            writeFd.closeWithError("remote end closed");
+                        } catch (IOException e1) {
+                            // log and ignore
+                            Log.e(TAG, "Error closing fd pipe: " + e1);
+                        }
+                        break;
+                    }
+                }
+            } finally {
+                try {
+                    output.close();
+                } catch (IOException e) {
+                    // Ignore -- we might've already closed it.
+                }
+            }
+        });
+    }
+
+    /**
+     * Returns the Group Identifier Level1 for a GSM phone.
+     * Return null if it is unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getGroupIdLevel1() {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
+     * Returns the Group Identifier Level1 for a GSM phone for a particular subscription.
+     * Return null if it is unavailable.
+     *
+     * @param subId whose subscriber id is returned
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @UnsupportedAppUsage
+    public String getGroupIdLevel1(int subId) {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
+     * Returns the phone number string for line 1, for example, the MSISDN
+     * for a GSM phone for a particular subscription. Return null if it is unavailable.
+     * <p>
+     * The default SMS app can also use this.
+     *
+     * <p>Requires Permission:
+     *     {@link android.Manifest.permission#READ_SMS READ_SMS},
+     *     {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+     *     that the caller is the default SMS app,
+     *     or that the caller has carrier privileges (see {@link #hasCarrierPrivileges})
+     *     for any API level.
+     *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *     for apps targeting SDK API level 29 and below.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead.
+     */
+    @Deprecated
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges or default SMS app
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_SMS,
+            android.Manifest.permission.READ_PHONE_NUMBERS
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getLine1Number() {
+        return getLine1Number(getSubId());
+    }
+
+    /**
+     * Returns the phone number string for line 1, for example, the MSISDN
+     * for a GSM phone for a particular subscription. Return null if it is unavailable.
+     * <p>
+     * The default SMS app can also use this.
+     *
+     * <p>Requires Permission:
+     *     {@link android.Manifest.permission#READ_SMS READ_SMS},
+     *     {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+     *     that the caller is the default SMS app,
+     *     or that the caller has carrier privileges (see {@link #hasCarrierPrivileges})
+     *     for any API level.
+     *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *     for apps targeting SDK API level 29 and below.
+     *
+     * @param subId whose phone number for line 1 is returned
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_SMS,
+            android.Manifest.permission.READ_PHONE_NUMBERS
+    })
+    @UnsupportedAppUsage
+    public String getLine1Number(int subId) {
+        String number = null;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                number = telephony.getLine1NumberForDisplay(subId, mContext.getOpPackageName(),
+                         mContext.getAttributionTag());
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        if (number != null) {
+            return number;
+        }
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
+     * Set the line 1 phone number string and its alphatag for the current ICCID
+     * for display purpose only, for example, displayed in Phone Status. It won't
+     * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
+     * value.
+     *
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param alphaTag alpha-tagging of the dailing nubmer
+     * @param number The dialing number
+     * @return true if the operation was executed correctly.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @deprecated use {@link SubscriptionManager#setCarrierPhoneNumber(int, String)} instead.
+     */
+    @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public boolean setLine1NumberForDisplay(String alphaTag, String number) {
+        return setLine1NumberForDisplay(getSubId(), alphaTag, number);
+    }
+
+    /**
+     * Set the line 1 phone number string and its alphatag for the current ICCID
+     * for display purpose only, for example, displayed in Phone Status. It won't
+     * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
+     * value.
+     *
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param subId the subscriber that the alphatag and dialing number belongs to.
+     * @param alphaTag alpha-tagging of the dailing nubmer
+     * @param number The dialing number
+     * @return true if the operation was executed correctly.
+     * @hide
+     */
+    public boolean setLine1NumberForDisplay(int subId, String alphaTag, String number) {
+        try {
+            // This API is deprecated; call the new API to allow smooth migartion.
+            // The new API doesn't accept null so convert null to empty string.
+            mSubscriptionManager.setCarrierPhoneNumber(subId, (number == null ? "" : number));
+
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.setLine1NumberForDisplayForSubscriber(subId, alphaTag, number);
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return false;
+    }
+
+    /**
+     * Returns the alphabetic identifier associated with the line 1 number.
+     * Return null if it is unavailable.
+     * @hide
+     * nobody seems to call this.
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public String getLine1AlphaTag() {
+        return getLine1AlphaTag(getSubId());
+    }
+
+    /**
+     * Returns the alphabetic identifier associated with the line 1 number
+     * for a subscription.
+     * Return null if it is unavailable.
+     * @param subId whose alphabetic identifier associated with line 1 is returned
+     * nobody seems to call this.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @UnsupportedAppUsage
+    public String getLine1AlphaTag(int subId) {
+        String alphaTag = null;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                alphaTag = telephony.getLine1AlphaTagForDisplay(subId,
+                        getOpPackageName(), getAttributionTag());
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        if (alphaTag != null) {
+            return alphaTag;
+        }
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName(),
+                    getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
+     * Return the set of subscriber IDs that should be considered "merged together" for data usage
+     * purposes. This is commonly {@code null} to indicate no merging is required. Any returned
+     * subscribers are sorted in a deterministic order.
+     * <p>
+     * The returned set of subscriber IDs will include the subscriber ID corresponding to this
+     * TelephonyManager's subId.
+     *
+     * This is deprecated and {@link #getMergedImsisFromGroup()} should be used for data
+     * usage merging purpose.
+     * TODO: remove this API.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public @Nullable String[] getMergedSubscriberIds() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.getMergedSubscriberIds(getSubId(), getOpPackageName(),
+                        getAttributionTag());
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return null;
+    }
+
+    /**
+     * Return the set of IMSIs that should be considered "merged together" for data usage
+     * purposes. This API merges IMSIs based on subscription grouping: IMSI of those in the same
+     * group will all be returned.
+     * Return the current IMSI if there is no subscription group, see
+     * {@link SubscriptionManager#createSubscriptionGroup(List)} for the definition of a group,
+     * otherwise return an empty array if there is a failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @NonNull String[] getMergedImsisFromGroup() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                String[] mergedImsisFromGroup =
+                        telephony.getMergedImsisFromGroup(getSubId(), getOpPackageName());
+                if (mergedImsisFromGroup != null) {
+                    return mergedImsisFromGroup;
+                }
+            }
+        } catch (RemoteException ex) {
+        }
+        return new String[0];
+    }
+
+    /**
+     * Returns the MSISDN string for a GSM phone. Return null if it is unavailable.
+     *
+     * <p>Requires Permission:
+     *     {@link android.Manifest.permission#READ_SMS READ_SMS},
+     *     {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+     *     that the caller is the default SMS app,
+     *     or that the caller has carrier privileges (see {@link #hasCarrierPrivileges})
+     *     for any API level.
+     *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *     for apps targeting SDK API level 29 and below.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_SMS,
+            android.Manifest.permission.READ_PHONE_NUMBERS
+    })
+    @UnsupportedAppUsage
+    public String getMsisdn() {
+        return getMsisdn(getSubId());
+    }
+
+    /**
+     * Returns the MSISDN string for a GSM phone. Return null if it is unavailable.
+     *
+     * @param subId for which msisdn is returned
+     *
+     * <p>Requires Permission:
+     *     {@link android.Manifest.permission#READ_SMS READ_SMS},
+     *     {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+     *     that the caller is the default SMS app,
+     *     or that the caller has carrier privileges (see {@link #hasCarrierPrivileges})
+     *     for any API level.
+     *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *     for apps targeting SDK API level 29 and below.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_SMS,
+            android.Manifest.permission.READ_PHONE_NUMBERS
+    })
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public String getMsisdn(int subId) {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            return info.getMsisdnForSubscriber(subId, getOpPackageName(), getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
+     * Returns the voice mail number. Return null if it is unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public String getVoiceMailNumber() {
+        return getVoiceMailNumber(getSubId());
+    }
+
+    /**
+     * Returns the voice mail number for a subscription.
+     * Return null if it is unavailable.
+     * @param subId whose voice mail number is returned
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @UnsupportedAppUsage
+    public String getVoiceMailNumber(int subId) {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName(),
+                    getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
+     * Sets the voice mail number.
+     *
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param alphaTag The alpha tag to display.
+     * @param number The voicemail number.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public boolean setVoiceMailNumber(String alphaTag, String number) {
+        return setVoiceMailNumber(getSubId(), alphaTag, number);
+    }
+
+    /**
+     * Sets the voicemail number for the given subscriber.
+     *
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param subId The subscription id.
+     * @param alphaTag The alpha tag to display.
+     * @param number The voicemail number.
+     * @hide
+     */
+    public boolean setVoiceMailNumber(int subId, String alphaTag, String number) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.setVoiceMailNumber(subId, alphaTag, number);
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return false;
+    }
+
+    /**
+     * Enables or disables the visual voicemail client for a phone account.
+     *
+     * <p>Requires that the calling app is the default dialer, or has carrier privileges (see
+     * {@link #hasCarrierPrivileges}), or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param phoneAccountHandle the phone account to change the client state
+     * @param enabled the new state of the client
+     * @hide
+     * @deprecated Visual voicemail no longer in telephony. {@link VisualVoicemailService} should
+     * be implemented instead.
+     */
+    @SystemApi
+    @Deprecated
+    @SuppressLint("RequiresPermission")
+    public void setVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle, boolean enabled){
+    }
+
+    /**
+     * Returns whether the visual voicemail client is enabled.
+     *
+     * @param phoneAccountHandle the phone account to check for.
+     * @return {@code true} when the visual voicemail client is enabled for this client
+     * @hide
+     * @deprecated Visual voicemail no longer in telephony. {@link VisualVoicemailService} should
+     * be implemented instead.
+     */
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @SuppressLint("RequiresPermission")
+    public boolean isVisualVoicemailEnabled(PhoneAccountHandle phoneAccountHandle){
+        return false;
+    }
+
+    /**
+     * Returns an opaque bundle of settings formerly used by the visual voicemail client for the
+     * subscription ID pinned to the TelephonyManager, or {@code null} if the subscription ID is
+     * invalid. This method allows the system dialer to migrate settings out of the pre-O visual
+     * voicemail client in telephony.
+     *
+     * <p>Requires the caller to be the system dialer.
+     *
+     * @see #KEY_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL
+     * @see #KEY_VOICEMAIL_SCRAMBLED_PIN_STRING
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("RequiresPermission")
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @Nullable
+    public Bundle getVisualVoicemailSettings(){
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony
+                        .getVisualVoicemailSettings(mContext.getOpPackageName(), mSubId);
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return null;
+    }
+
+    /**
+     * Returns the package responsible of processing visual voicemail for the subscription ID pinned
+     * to the TelephonyManager. Returns {@code null} when there is no package responsible for
+     * processing visual voicemail for the subscription.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @see #createForSubscriptionId(int)
+     * @see #createForPhoneAccountHandle(PhoneAccountHandle)
+     * @see VisualVoicemailService
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @Nullable
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public String getVisualVoicemailPackageName() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getVisualVoicemailPackageName(mContext.getOpPackageName(),
+                        getAttributionTag(), getSubId());
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return null;
+    }
+
+    /**
+     * Set the visual voicemail SMS filter settings for the subscription ID pinned
+     * to the TelephonyManager.
+     * When the filter is enabled, {@link
+     * VisualVoicemailService#onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)} will be
+     * called when a SMS matching the settings is received. Caller must be the default dialer,
+     * system dialer, or carrier visual voicemail app.
+     *
+     * @param settings The settings for the filter, or {@code null} to disable the filter.
+     *
+     * @see TelecomManager#getDefaultDialerPackage()
+     * @see CarrierConfigManager#KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings settings) {
+        if (settings == null) {
+            disableVisualVoicemailSmsFilter(mSubId);
+        } else {
+            enableVisualVoicemailSmsFilter(mSubId, settings);
+        }
+    }
+
+    /**
+     * Send a visual voicemail SMS. The caller must be the current default dialer.
+     * A {@link VisualVoicemailService} uses this method to send a command via SMS to the carrier's
+     * visual voicemail server.  Some examples for carriers using the OMTP standard include
+     * activating and deactivating visual voicemail, or requesting the current visual voicemail
+     * provisioning status.  See the OMTP Visual Voicemail specification for more information on the
+     * format of these SMS messages.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#SEND_SMS SEND_SMS}
+     *
+     * @param number The destination number.
+     * @param port The destination port for data SMS, or 0 for text SMS.
+     * @param text The message content. For data sms, it will be encoded as a UTF-8 byte stream.
+     * @param sentIntent The sent intent passed to the {@link SmsManager}
+     *
+     * @throws SecurityException if the caller is not the current default dialer
+     *
+     * @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent)
+     * @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void sendVisualVoicemailSms(String number, int port, String text,
+            PendingIntent sentIntent) {
+        sendVisualVoicemailSmsForSubscriber(mSubId, number, port, text, sentIntent);
+    }
+
+    /**
+     * Enables the visual voicemail SMS filter for a phone account. When the filter is
+     * enabled, Incoming SMS messages matching the OMTP VVM SMS interface will be redirected to the
+     * visual voicemail client with
+     * {@link android.provider.VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED}.
+     *
+     * <p>This takes effect only when the caller is the default dialer. The enabled status and
+     * settings persist through default dialer changes, but the filter will only honor the setting
+     * set by the current default dialer.
+     *
+     *
+     * @param subId The subscription id of the phone account.
+     * @param settings The settings for the filter.
+     */
+    /** @hide */
+    public void enableVisualVoicemailSmsFilter(int subId,
+            VisualVoicemailSmsFilterSettings settings) {
+        if(settings == null){
+            throw new IllegalArgumentException("Settings cannot be null");
+        }
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.enableVisualVoicemailSmsFilter(mContext.getOpPackageName(), subId,
+                        settings);
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+    }
+
+    /**
+     * Disables the visual voicemail SMS filter for a phone account.
+     *
+     * <p>This takes effect only when the caller is the default dialer. The enabled status and
+     * settings persist through default dialer changes, but the filter will only honor the setting
+     * set by the current default dialer.
+     */
+    /** @hide */
+    public void disableVisualVoicemailSmsFilter(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.disableVisualVoicemailSmsFilter(mContext.getOpPackageName(), subId);
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+    }
+
+    /**
+     * @returns the settings of the visual voicemail SMS filter for a phone account, or {@code null}
+     * if the filter is disabled.
+     *
+     * <p>This takes effect only when the caller is the default dialer. The enabled status and
+     * settings persist through default dialer changes, but the filter will only honor the setting
+     * set by the current default dialer.
+     */
+    /** @hide */
+    @Nullable
+    public VisualVoicemailSmsFilterSettings getVisualVoicemailSmsFilterSettings(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony
+                        .getVisualVoicemailSmsFilterSettings(mContext.getOpPackageName(), subId);
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+
+        return null;
+    }
+
+    /**
+     * @returns the settings of the visual voicemail SMS filter for a phone account set by the
+     * current active visual voicemail client, or {@code null} if the filter is disabled.
+     *
+     * <p>Requires the calling app to have READ_PRIVILEGED_PHONE_STATE permission.
+     */
+    /** @hide */
+    @Nullable
+    public VisualVoicemailSmsFilterSettings getActiveVisualVoicemailSmsFilterSettings(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getActiveVisualVoicemailSmsFilterSettings(subId);
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+
+        return null;
+    }
+
+    /**
+     * Send a visual voicemail SMS. The IPC caller must be the current default dialer.
+     *
+     * @param phoneAccountHandle The account to send the SMS with.
+     * @param number The destination number.
+     * @param port The destination port for data SMS, or 0 for text SMS.
+     * @param text The message content. For data sms, it will be encoded as a UTF-8 byte stream.
+     * @param sentIntent The sent intent passed to the {@link SmsManager}
+     *
+     * @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent)
+     * @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.SEND_SMS)
+    public void sendVisualVoicemailSmsForSubscriber(int subId, String number, int port,
+            String text, PendingIntent sentIntent) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.sendVisualVoicemailSmsForSubscriber(
+                        mContext.getOpPackageName(), mContext.getAttributionTag(), subId, number,
+                        port, text, sentIntent);
+            }
+        } catch (RemoteException ex) {
+        }
+    }
+
+    /**
+     * Initial SIM activation state, unknown. Not set by any carrier apps.
+     * @hide
+     */
+    @SystemApi
+    public static final int SIM_ACTIVATION_STATE_UNKNOWN = 0;
+
+    /**
+     * indicate SIM is under activation procedure now.
+     * intermediate state followed by another state update with activation procedure result:
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     * @hide
+     */
+    @SystemApi
+    public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1;
+
+    /**
+     * Indicate SIM has been successfully activated with full service
+     * @hide
+     */
+    @SystemApi
+    public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2;
+
+    /**
+     * Indicate SIM has been deactivated by the carrier so that service is not available
+     * and requires activation service to enable services.
+     * Carrier apps could be signalled to set activation state to deactivated if detected
+     * deactivated sim state and set it back to activated after successfully run activation service.
+     * @hide
+     */
+    @SystemApi
+    public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3;
+
+    /**
+     * Restricted state indicate SIM has been activated but service are restricted.
+     * note this is currently available for data activation state. For example out of byte sim.
+     * @hide
+     */
+    @SystemApi
+    public static final int SIM_ACTIVATION_STATE_RESTRICTED = 4;
+
+     /**
+      * Sets the voice activation state
+      *
+      * <p>Requires Permission:
+      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+      * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+      *
+      * @param activationState The voice activation state
+      * @see #SIM_ACTIVATION_STATE_UNKNOWN
+      * @see #SIM_ACTIVATION_STATE_ACTIVATING
+      * @see #SIM_ACTIVATION_STATE_ACTIVATED
+      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+      *
+      * @throws UnsupportedOperationException If the device does not have
+      *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}
+      * @hide
+      */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void setVoiceActivationState(@SimActivationState int activationState) {
+        setVoiceActivationState(getSubId(), activationState);
+    }
+
+    /**
+     * Sets the voice activation state for the given subscriber.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param subId The subscription id.
+     * @param activationState The voice activation state of the given subscriber.
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoiceActivationState(int subId, @SimActivationState int activationState) {
+        try {
+           ITelephony telephony = getITelephony();
+           if (telephony != null)
+               telephony.setVoiceActivationState(subId, activationState);
+       } catch (RemoteException ex) {
+       } catch (NullPointerException ex) {
+       }
+    }
+
+    /**
+     * Sets the data activation state
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param activationState The data activation state
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public void setDataActivationState(@SimActivationState int activationState) {
+        setDataActivationState(getSubId(), activationState);
+    }
+
+    /**
+     * Sets the data activation state for the given subscriber.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param subId The subscription id.
+     * @param activationState The data activation state of the given subscriber.
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setDataActivationState(int subId, @SimActivationState int activationState) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.setDataActivationState(subId, activationState);
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+    }
+
+    /**
+     * Returns the voice activation state
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return voiceActivationState
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public @SimActivationState int getVoiceActivationState() {
+        return getVoiceActivationState(getSubId());
+    }
+
+    /**
+     * Returns the voice activation state for the given subscriber.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param subId The subscription id.
+     *
+     * @return voiceActivationState for the given subscriber
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @SimActivationState int getVoiceActivationState(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.getVoiceActivationState(subId, getOpPackageName());
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return SIM_ACTIVATION_STATE_UNKNOWN;
+    }
+
+    /**
+     * Returns the data activation state
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return dataActivationState for the given subscriber
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public @SimActivationState int getDataActivationState() {
+        return getDataActivationState(getSubId());
+    }
+
+    /**
+     * Returns the data activation state for the given subscriber.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param subId The subscription id.
+     *
+     * @return dataActivationState for the given subscriber
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @SimActivationState int getDataActivationState(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.getDataActivationState(subId, getOpPackageName());
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return SIM_ACTIVATION_STATE_UNKNOWN;
+    }
+
+    /**
+     * Returns the voice mail count. Return 0 if unavailable, -1 if there are unread voice messages
+     * but the count is unknown.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @UnsupportedAppUsage
+    public int getVoiceMessageCount() {
+        return getVoiceMessageCount(getSubId());
+    }
+
+    /**
+     * Returns the voice mail count for a subscription. Return 0 if unavailable or the caller does
+     * not have the READ_PHONE_STATE permission.
+     * @param subId whose voice message count is returned
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @UnsupportedAppUsage
+    public int getVoiceMessageCount(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return 0;
+            return telephony.getVoiceMessageCountForSubscriber(subId, getOpPackageName(),
+                    getAttributionTag());
+        } catch (RemoteException ex) {
+            return 0;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return 0;
+        }
+    }
+
+    /**
+     * Retrieves the alphabetic identifier associated with the voice
+     * mail number.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public String getVoiceMailAlphaTag() {
+        return getVoiceMailAlphaTag(getSubId());
+    }
+
+    /**
+     * Retrieves the alphabetic identifier associated with the voice
+     * mail number for a subscription.
+     * @param subId whose alphabetic identifier associated with the
+     * voice mail number is returned
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @UnsupportedAppUsage
+    public String getVoiceMailAlphaTag(int subId) {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName(),
+                    getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
+     * Send the special dialer code. The IPC caller must be the current default dialer or have
+     * carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param inputCode The special dialer code to send
+     *
+     * @throws SecurityException if the caller does not have carrier privileges or is not the
+     *         current default dialer
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void sendDialerSpecialCode(String inputCode) {
+        try {
+            final ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                return;
+            }
+            telephony.sendDialerSpecialCode(mContext.getOpPackageName(), inputCode);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Telephony#sendDialerSpecialCode RemoteException" + ex);
+        }
+    }
+
+    /**
+     * Returns the IMS private user identity (IMPI) that was loaded from the ISIM.
+     * @return the IMPI, or null if not present or not loaded
+     * @hide
+     * @deprecated use {@link #getImsPrivateUserIdentity()}
+     */
+    @UnsupportedAppUsage
+    public String getIsimImpi() {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            //get the Isim Impi based on subId
+            return info.getIsimImpi(getSubId());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
+     * Returns the IMS private user identity (IMPI) of the subscription that was loaded from the
+     * ISIM records {@link #APPTYPE_ISIM}. This value is fetched from the Elementary file EF_IMPI.
+     * The contents of the file is a <b>Ip Multimedia Service Private User Identity</b> of the user
+     * as defined in the section 4.2.2 of 3GPP TS 131 103.
+     *
+     * @return IMPI (IMS private user identity) of type string.
+     * @throws IllegalStateException in case the ISIM has’t been loaded
+     * @throws SecurityException if the caller does not have the required permission/privileges
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getImsPrivateUserIdentity() {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null) {
+                Rlog.e(TAG, "getImsPrivateUserIdentity(): IPhoneSubInfo instance is NULL");
+                throw new RuntimeException("IMPI error: Subscriber Info is null");
+            }
+            return info.getImsPrivateUserIdentity(getSubId(), getOpPackageName(),
+                    getAttributionTag());
+        } catch (RemoteException | NullPointerException | IllegalArgumentException ex) {
+            Rlog.e(TAG, "getImsPrivateUserIdentity() Exception = " + ex);
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    /**
+     * Returns the IMS home network domain name that was loaded from the ISIM {@see #APPTYPE_ISIM}.
+     * @return the IMS domain name. Returns {@code null} if ISIM hasn't been loaded or IMS domain
+     * hasn't been loaded or isn't present on the ISIM.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getIsimDomain() {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            //get the Isim Domain based on subId
+            return info.getIsimDomain(getSubId());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
+     * Returns the IMS public user identities (IMPU) that were loaded from the ISIM.
+     * @return an array of IMPU strings, with one IMPU per string, or null if
+     *      not present or not loaded
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     * @deprecated use {@link #getImsPublicUserIdentities()}
+     */
+    @UnsupportedAppUsage
+    @Nullable
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public String[] getIsimImpu() {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            //get the Isim Impu based on subId
+            return info.getIsimImpu(getSubId());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
+     * Returns the IMS public user identities (IMPU) of the subscription that was loaded from the
+     * ISIM records {@link #APPTYPE_ISIM}. This value is fetched from the Elementary file EF_IMPU.
+     * The contents of the file are <b>Ip Multimedia Service Public User Identities</b> of the user
+     * as defined in the section 4.2.4 of 3GPP TS 131 103. It contains one or more records.
+     *
+     * @return List of public user identities of type android.net.Uri or empty list  if
+     *         EF_IMPU is not available.
+     * @throws IllegalStateException in case the ISIM hasn’t been loaded
+     * @throws SecurityException if the caller does not have the required permission/privilege
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_NUMBERS,
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public List<Uri> getImsPublicUserIdentities() {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null) {
+                throw new RuntimeException("IMPU error: Subscriber Info is null");
+            }
+            return info.getImsPublicUserIdentities(getSubId(), getOpPackageName(),
+                    getAttributionTag());
+        } catch (IllegalArgumentException | NullPointerException ex) {
+            Rlog.e(TAG, "getImsPublicUserIdentities Exception = " + ex);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getImsPublicUserIdentities Exception = " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return Collections.EMPTY_LIST;
+    }
+
+    /**
+     * Device call state: No activity.
+     */
+    public static final int CALL_STATE_IDLE = 0;
+    /**
+     * Device call state: Ringing. A new call arrived and is
+     *  ringing or waiting. In the latter case, another call is
+     *  already active.
+     */
+    public static final int CALL_STATE_RINGING = 1;
+    /**
+     * Device call state: Off-hook. At least one call exists
+     * that is dialing, active, or on hold, and no calls are ringing
+     * or waiting.
+     */
+    public static final int CALL_STATE_OFFHOOK = 2;
+
+    /**
+     * Returns the state of all calls on the device.
+     * <p>
+     * This method considers not only calls in the Telephony stack, but also calls via other
+     * {@link android.telecom.ConnectionService} implementations.
+     * <p>
+     * Note: The call state returned via this method may differ from what is reported by {@link
+     * TelephonyCallback.CallStateListener#onCallStateChanged(int)}, as that callback only considers
+     * Telephony (mobile) calls.
+     * <p>
+     * Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
+     * targeting API level 31+.
+     *
+     * @return the current call state.
+     * @deprecated Use {@link #getCallStateForSubscription} to retrieve the call state for a
+     * specific telephony subscription (which allows carrier privileged apps),
+     * {@link TelephonyCallback.CallStateListener} for real-time call state updates, or
+     * {@link TelecomManager#isInCall()}, which supplies an aggregate "in call" state for the entire
+     * device.
+     */
+    @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
+    @Deprecated
+    public @CallState int getCallState() {
+        if (mContext != null) {
+            TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
+            if (telecomManager != null) {
+                return telecomManager.getCallState();
+            }
+        }
+        return CALL_STATE_IDLE;
+    }
+
+    /**
+     * Retrieve the call state for a specific subscription that was specified when this
+     * TelephonyManager instance was created.
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or that the calling
+     * application has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * @see TelephonyManager#createForSubscriptionId(int)
+     * @see TelephonyManager#createForPhoneAccountHandle(PhoneAccountHandle)
+     * @return The call state of the subscription associated with this TelephonyManager instance.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public @CallState int getCallStateForSubscription() {
+        return getCallState(getSubId());
+    }
+
+    /**
+     * Returns the Telephony call state for calls on a specific subscription.
+     * <p>
+     * Note: This method considers ONLY telephony/mobile calls, where {@link #getCallState()}
+     * considers the state of calls from other {@link android.telecom.ConnectionService}
+     * implementations.
+     * <p>
+     * Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
+     * targeting API level 31+ or that the calling application has carrier privileges
+     * (see {@link #hasCarrierPrivileges()}).
+     *
+     * @param subId the subscription to check call state for.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
+    public @CallState int getCallState(int subId) {
+        ITelephony telephony = getITelephony();
+        if (telephony == null) {
+            return CALL_STATE_IDLE;
+        }
+        try {
+            return telephony.getCallStateForSubscription(subId, mContext.getPackageName(),
+                    mContext.getAttributionTag());
+        } catch (RemoteException e) {
+            return CALL_STATE_IDLE;
+        }
+    }
+
+    /** Data connection activity: No traffic. */
+    public static final int DATA_ACTIVITY_NONE = 0x00000000;
+    /** Data connection activity: Currently receiving IP PPP traffic. */
+    public static final int DATA_ACTIVITY_IN = 0x00000001;
+    /** Data connection activity: Currently sending IP PPP traffic. */
+    public static final int DATA_ACTIVITY_OUT = 0x00000002;
+    /** Data connection activity: Currently both sending and receiving
+     *  IP PPP traffic. */
+    public static final int DATA_ACTIVITY_INOUT = DATA_ACTIVITY_IN | DATA_ACTIVITY_OUT;
+    /**
+     * Data connection is active, but physical link is down
+     */
+    public static final int DATA_ACTIVITY_DORMANT = 0x00000004;
+
+    /**
+     * Returns a constant indicating the type of activity on a data connection
+     * (cellular).
+     *
+     * @see #DATA_ACTIVITY_NONE
+     * @see #DATA_ACTIVITY_IN
+     * @see #DATA_ACTIVITY_OUT
+     * @see #DATA_ACTIVITY_INOUT
+     * @see #DATA_ACTIVITY_DORMANT
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public int getDataActivity() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return DATA_ACTIVITY_NONE;
+            return telephony.getDataActivityForSubId(
+                    getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
+        } catch (RemoteException ex) {
+            // the phone process is restarting.
+            return DATA_ACTIVITY_NONE;
+        } catch (NullPointerException ex) {
+          // the phone process is restarting.
+          return DATA_ACTIVITY_NONE;
+      }
+    }
+
+    /** @hide */
+    @IntDef(prefix = {"DATA_"}, value = {
+            DATA_UNKNOWN,
+            DATA_DISCONNECTED,
+            DATA_CONNECTING,
+            DATA_CONNECTED,
+            DATA_SUSPENDED,
+            DATA_DISCONNECTING,
+            DATA_HANDOVER_IN_PROGRESS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DataState{}
+
+    /** Data connection state: Unknown.  Used before we know the state. */
+    public static final int DATA_UNKNOWN        = -1;
+    /** Data connection state: Disconnected. IP traffic not available. */
+    public static final int DATA_DISCONNECTED   = 0;
+    /** Data connection state: Currently setting up a data connection. */
+    public static final int DATA_CONNECTING     = 1;
+    /** Data connection state: Connected. IP traffic should be available. */
+    public static final int DATA_CONNECTED      = 2;
+    /** Data connection state: Suspended. The connection is up, but IP
+     * traffic is temporarily unavailable. For example, in a 2G network,
+     * data activity may be suspended when a voice call arrives. */
+    public static final int DATA_SUSPENDED      = 3;
+    /**
+     * Data connection state: Disconnecting.
+     *
+     * IP traffic may be available but will cease working imminently.
+     */
+    public static final int DATA_DISCONNECTING = 4;
+
+    /**
+     * Data connection state: Handover in progress. The connection is being transited from cellular
+     * network to IWLAN, or from IWLAN to cellular network.
+     */
+    public static final int DATA_HANDOVER_IN_PROGRESS = 5;
+
+    /**
+     * Used for checking if the SDK version for {@link TelephonyManager#getDataState} is above Q.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    private static final long GET_DATA_STATE_R_VERSION = 148534348L;
+
+    /**
+     * Returns a constant indicating the current data connection state
+     * (cellular).
+     *
+     * @see #DATA_DISCONNECTED
+     * @see #DATA_CONNECTING
+     * @see #DATA_CONNECTED
+     * @see #DATA_SUSPENDED
+     * @see #DATA_DISCONNECTING
+     * @see #DATA_HANDOVER_IN_PROGRESS
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public int getDataState() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return DATA_DISCONNECTED;
+            int state = telephony.getDataStateForSubId(
+                    getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
+            if (state == TelephonyManager.DATA_DISCONNECTING
+                    && !Compatibility.isChangeEnabled(GET_DATA_STATE_R_VERSION)) {
+                return TelephonyManager.DATA_CONNECTED;
+            }
+
+            return state;
+        } catch (RemoteException ex) {
+            // the phone process is restarting.
+            return DATA_DISCONNECTED;
+        } catch (NullPointerException ex) {
+            return DATA_DISCONNECTED;
+        }
+    }
+
+    private static ITelephony getITelephony() {
+        // Keeps cache disabled until test fixes are checked into AOSP.
+        if (!sServiceHandleCacheEnabled) {
+            return ITelephony.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getTelephonyServiceRegisterer()
+                            .get());
+        }
+
+        if (sITelephony == null) {
+            ITelephony temp = ITelephony.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getTelephonyServiceRegisterer()
+                            .get());
+            synchronized (sCacheLock) {
+                if (sITelephony == null && temp != null) {
+                    try {
+                        sITelephony = temp;
+                        sITelephony.asBinder().linkToDeath(sServiceDeath, 0);
+                    } catch (Exception e) {
+                        // something has gone horribly wrong
+                        sITelephony = null;
+                    }
+                }
+            }
+        }
+        return sITelephony;
+    }
+
+    private IOns getIOns() {
+        return IOns.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getOpportunisticNetworkServiceRegisterer()
+                        .get());
+    }
+
+    //
+    //
+    // PhoneStateListener
+    //
+    //
+
+    /**
+     * Registers a listener object to receive notification of changes
+     * in specified telephony states.
+     * <p>
+     * To register a listener, pass a {@link PhoneStateListener} and specify at least one telephony
+     * state of interest in the events argument.
+     *
+     * At registration, and when a specified telephony state changes, the telephony manager invokes
+     * the appropriate callback method on the listener object and passes the current (updated)
+     * values.
+     * <p>
+     * To un-register a listener, pass the listener object and set the events argument to
+     * {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0).
+     *
+     * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
+     * applies to the given subId. Otherwise, applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds,
+     * pass a separate listener object to each TelephonyManager object created with
+     * {@link #createForSubscriptionId}.
+     *
+     * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
+     * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
+     * {@link SecurityException} will be thrown otherwise.
+     *
+     * This API should be used sparingly -- large numbers of listeners will cause system
+     * instability. If a process has registered too many listeners without unregistering them, it
+     * may encounter an {@link IllegalStateException} when trying to register more listeners.
+     *
+     * @param listener The {@link PhoneStateListener} object to register
+     *                 (or unregister)
+     * @param events The telephony state(s) of interest to the listener,
+     *               as a bitwise-OR combination of {@link PhoneStateListener}
+     *               LISTEN_ flags.
+     * @deprecated Use {@link #registerTelephonyCallback(Executor, TelephonyCallback)}.
+     */
+    @Deprecated
+    public void listen(PhoneStateListener listener, int events) {
+        if (mContext == null) return;
+        boolean notifyNow = (getITelephony() != null);
+        TelephonyRegistryManager telephonyRegistry =
+                (TelephonyRegistryManager)
+                        mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+        if (telephonyRegistry != null) {
+            Set<String> renouncedPermissions = getRenouncedPermissions();
+            boolean renounceFineLocationAccess = renouncedPermissions
+                    .contains(Manifest.permission.ACCESS_FINE_LOCATION);
+            boolean renounceCoarseLocationAccess = renouncedPermissions
+                    .contains(Manifest.permission.ACCESS_COARSE_LOCATION);
+            telephonyRegistry.listenFromListener(mSubId, renounceFineLocationAccess,
+                    renounceCoarseLocationAccess, getOpPackageName(), getAttributionTag(),
+                    listener, events, notifyNow);
+        } else {
+            Rlog.w(TAG, "telephony registry not ready.");
+        }
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"ERI_"}, value = {
+            ERI_ON,
+            ERI_OFF,
+            ERI_FLASH
+    })
+    public @interface EriIconIndex {}
+
+    /**
+     * ERI (Enhanced Roaming Indicator) is ON i.e value 0 defined by
+     * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+     */
+    public static final int ERI_ON = 0;
+
+    /**
+     * ERI (Enhanced Roaming Indicator) is OFF i.e value 1 defined by
+     * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+     */
+    public static final int ERI_OFF = 1;
+
+    /**
+     * ERI (Enhanced Roaming Indicator) is FLASH i.e value 2 defined by
+     * 3GPP2 C.R1001-H v1.0 Table 8.1-1.
+     */
+    public static final int ERI_FLASH = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"ERI_ICON_MODE_"}, value = {
+            ERI_ICON_MODE_NORMAL,
+            ERI_ICON_MODE_FLASH
+    })
+    public @interface EriIconMode {}
+
+    /**
+     * ERI (Enhanced Roaming Indicator) icon mode is normal. This constant represents that
+     * the ERI icon should be displayed normally.
+     *
+     * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
+     * @hide
+     */
+    public static final int ERI_ICON_MODE_NORMAL = 0;
+
+    /**
+     * ERI (Enhanced Roaming Indicator) icon mode flash. This constant represents that
+     * the ERI icon should be flashing.
+     *
+     * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1
+     * @hide
+     */
+    public static final int ERI_ICON_MODE_FLASH = 1;
+
+    /**
+     * Returns the CDMA ERI icon display number. The number is assigned by
+     * 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own ERI display numbers.
+     * Defined values are {@link #ERI_ON}, {@link #ERI_OFF}, and {@link #ERI_FLASH}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    public @EriIconIndex int getCdmaEnhancedRoamingIndicatorDisplayNumber() {
+        return getCdmaEriIconIndex(getSubId());
+    }
+
+    /**
+     * Returns the CDMA ERI icon index to display for a subscription.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @UnsupportedAppUsage
+    public @EriIconIndex int getCdmaEriIconIndex(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return -1;
+            return telephony.getCdmaEriIconIndexForSubscriber(subId, getOpPackageName(),
+                    getAttributionTag());
+        } catch (RemoteException ex) {
+            // the phone process is restarting.
+            return -1;
+        } catch (NullPointerException ex) {
+            return -1;
+        }
+    }
+
+    /**
+     * Returns the CDMA ERI icon mode for a subscription.
+     * 0 - ON
+     * 1 - FLASHING
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @UnsupportedAppUsage
+    public @EriIconMode int getCdmaEriIconMode(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return -1;
+            return telephony.getCdmaEriIconModeForSubscriber(subId, getOpPackageName(),
+                    getAttributionTag());
+        } catch (RemoteException ex) {
+            // the phone process is restarting.
+            return -1;
+        } catch (NullPointerException ex) {
+            return -1;
+        }
+    }
+
+    /**
+     * Returns the CDMA ERI text,
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public String getCdmaEriText() {
+        return getCdmaEriText(getSubId());
+    }
+
+    /**
+     * Returns the CDMA ERI text, of a subscription
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @UnsupportedAppUsage
+    public String getCdmaEriText(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return null;
+            return telephony.getCdmaEriTextForSubscriber(subId, getOpPackageName(),
+                    getAttributionTag());
+        } catch (RemoteException ex) {
+            // the phone process is restarting.
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    // TODO(b/316183370): replace all @code with @link in javadoc after feature is released
+    /**
+     * @return true if the current device is "voice capable".
+     * <p>
+     * "Voice capable" means that this device supports circuit-switched
+     * (i.e. voice) phone calls over the telephony network, and is allowed
+     * to display the in-call UI while a cellular voice call is active.
+     * This will be false on "data only" devices which can't make voice
+     * calls and don't support any in-call UI.
+     * <p>
+     * Note: the meaning of this flag is subtly different from the
+     * PackageManager.FEATURE_TELEPHONY system feature, which is available
+     * on any device with a telephony radio, even if the device is
+     * data-only.
+     * @deprecated Replaced by {@code #isDeviceVoiceCapable()}. Starting from Android 15, voice
+     * capability may also be overridden by carriers for a given subscription. For voice capable
+     * device (when {@code #isDeviceVoiceCapable} return {@code true}), caller should check for
+     * subscription-level voice capability as well. See {@code #isDeviceVoiceCapable} for details.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @Deprecated
+    public boolean isVoiceCapable() {
+        if (mContext == null) return true;
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_voice_capable);
+    }
+
+    /**
+     * @return true if the current device is "voice capable".
+     * <p>
+     * "Voice capable" means that this device supports circuit-switched or IMS packet switched
+     * (i.e. voice) phone calls over the telephony network, and is allowed to display the in-call
+     * UI while a cellular voice call is active. This will be false on "data only" devices which
+     * can't make voice calls and don't support any in-call UI.
+     * <p>
+     * Note: the meaning of this flag is subtly different from the PackageManager
+     * .FEATURE_TELEPHONY system feature, which is available on any device with a telephony
+     * radio, even if the device is data-only.
+     * <p>
+     * Starting from Android 15, voice capability may also be overridden by carrier for a given
+     * subscription on a voice capable device. To check if a subscription is "voice capable",
+     * call method {@code SubscriptionInfo#getServiceCapabilities()} and check if
+     * {@code SubscriptionManager#SERVICE_CAPABILITY_VOICE} is included.
+     *
+     * @see SubscriptionInfo#getServiceCapabilities()
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+    public boolean isDeviceVoiceCapable() {
+        return isVoiceCapable();
+    }
+
+    /**
+     * @return true if the current device supports sms service.
+     * <p>
+     * If true, this means that the device supports both sending and
+     * receiving sms via the telephony network.
+     * <p>
+     * Note: Voicemail waiting sms, cell broadcasting sms, and MMS are
+     *       disabled when device doesn't support sms.
+     * @deprecated Replaced by {@code #isDeviceSmsCapable()}. Starting from Android 15, SMS
+     * capability may also be overridden by carriers for a given subscription. For SMS capable
+     * device (when {@code #isDeviceSmsCapable} return {@code true}), caller should check for
+     * subscription-level SMS capability as well. See {@code #isDeviceSmsCapable} for details.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public boolean isSmsCapable() {
+        if (mContext == null) return true;
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_sms_capable);
+    }
+
+    /**
+     * @return true if the current device supports SMS service.
+     * <p>
+     * If true, this means that the device supports both sending and
+     * receiving SMS via the telephony network.
+     * <p>
+     * Note: Voicemail waiting SMS, cell broadcasting SMS, and MMS are
+     *       disabled when device doesn't support SMS.
+     * <p>
+     * Starting from Android 15, SMS capability may also be overridden by carriers for a given
+     * subscription on an SMS capable device. To check if a subscription is "SMS capable",
+     * call method {@code SubscriptionInfo#getServiceCapabilities()} and check if
+     * {@code SubscriptionManager#SERVICE_CAPABILITY_SMS} is included.
+     *
+     * @see SubscriptionInfo#getServiceCapabilities()
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
+    public boolean isDeviceSmsCapable() {
+        return isSmsCapable();
+    }
+
+    /**
+     * Requests all available cell information from all radios on the device including the
+     * camped/registered, serving, and neighboring cells.
+     *
+     * <p>The response can include one or more {@link android.telephony.CellInfoGsm CellInfoGsm},
+     * {@link android.telephony.CellInfoCdma CellInfoCdma},
+     * {@link android.telephony.CellInfoTdscdma CellInfoTdscdma},
+     * {@link android.telephony.CellInfoLte CellInfoLte}, and
+     * {@link android.telephony.CellInfoWcdma CellInfoWcdma} objects, in any combination.
+     * It is typical to see instances of one or more of any these in the list. In addition, zero
+     * or more of the returned objects may be considered registered; that is, their
+     * {@link android.telephony.CellInfo#isRegistered CellInfo.isRegistered()}
+     * methods may return true, indicating that the cell is being used or would be used for
+     * signaling communication if necessary.
+     *
+     * <p>Beginning with {@link android.os.Build.VERSION_CODES#Q Android Q},
+     * if this API results in a change of the cached CellInfo, that change will be reported via
+     * {@link TelephonyCallback.CellInfoListener#onCellInfoChanged(List) onCellInfoChanged()}.
+     *
+     * <p>Apps targeting {@link android.os.Build.VERSION_CODES#Q Android Q} or higher will no
+     * longer trigger a refresh of the cached CellInfo by invoking this API. Instead, those apps
+     * will receive the latest cached results, which may not be current. Apps targeting
+     * {@link android.os.Build.VERSION_CODES#Q Android Q} or higher that wish to request updated
+     * CellInfo should call
+     * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()};
+     * however, in all cases, updates will be rate-limited and are not guaranteed. To determine the
+     * recency of CellInfo data, callers should check
+     * {@link android.telephony.CellInfo#getTimeStamp CellInfo#getTimeStamp()}.
+     *
+     * <p>This method returns valid data for devices with
+     * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. In cases
+     * where only partial information is available for a particular CellInfo entry, unavailable
+     * fields will be reported as {@link android.telephony.CellInfo#UNAVAILABLE}. All reported
+     * cells will include at least a valid set of technology-specific identification info and a
+     * power level measurement.
+     *
+     * <p>This method is preferred over using {@link
+     * android.telephony.TelephonyManager#getCellLocation getCellLocation()}.
+     *
+     * @return List of {@link android.telephony.CellInfo}; null if cell
+     * information is unavailable.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public List<CellInfo> getAllCellInfo() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return null;
+            return telephony.getAllCellInfo(getOpPackageName(), getAttributionTag());
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return null;
+    }
+
+    /** Callback for providing asynchronous {@link CellInfo} on request */
+    public abstract static class CellInfoCallback {
+        /**
+         * Success response to
+         * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}.
+         *
+         * Invoked when there is a response to
+         * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}
+         * to provide a list of {@link CellInfo}. If no {@link CellInfo} is available then an empty
+         * list will be provided. If an error occurs, null will be provided unless the onError
+         * callback is overridden.
+         *
+         * @param cellInfo a list of {@link CellInfo} or an empty list.
+         *
+         * {@see android.telephony.TelephonyManager#getAllCellInfo getAllCellInfo()}
+         */
+        public abstract void onCellInfo(@NonNull List<CellInfo> cellInfo);
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"ERROR_"}, value = {ERROR_TIMEOUT, ERROR_MODEM_ERROR})
+        public @interface CellInfoCallbackError {}
+
+        /**
+         * The system timed out waiting for a response from the Radio.
+         */
+        public static final int ERROR_TIMEOUT = 1;
+
+        /**
+         * The modem returned a failure.
+         */
+        public static final int ERROR_MODEM_ERROR = 2;
+
+        /**
+         * Error response to
+         * {@link TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}.
+         *
+         * Invoked when an error condition prevents updated {@link CellInfo} from being fetched
+         * and returned from the modem. Callers of requestCellInfoUpdate() should override this
+         * function to receive detailed status information in the event of an error. By default,
+         * this function will invoke onCellInfo() with null.
+         *
+         * @param errorCode an error code indicating the type of failure.
+         * @param detail a Throwable object with additional detail regarding the failure if
+         *     available, otherwise null.
+         */
+        public void onError(@CellInfoCallbackError int errorCode, @Nullable Throwable detail) {
+            // By default, simply invoke the success callback with an empty list.
+            onCellInfo(new ArrayList<CellInfo>());
+        }
+    };
+
+    /**
+     * Used for checking if the target SDK version for the current process is S or above.
+     *
+     * <p> Applies to the following methods:
+     * {@link #requestCellInfoUpdate},
+     * {@link #setPreferredOpportunisticDataSubscription},
+     * {@link #updateAvailableNetworks},
+     * requestNumberVerification(),
+     * setSimPowerStateForSlot(),
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    private static final long NULL_TELEPHONY_THROW_NO_CB = 182185642L;
+
+    /**
+     * Requests all available cell information from the current subscription for observed
+     * camped/registered, serving, and neighboring cells.
+     *
+     * <p>Any available results from this request will be provided by calls to
+     * {@link TelephonyCallback.CellInfoListener#onCellInfoChanged(List) onCellInfoChanged()}
+     * for each active subscription.
+     *
+     * <p>This method returns valid data for devices with
+     * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices
+     * that do not implement this feature, the behavior is not reliable.
+     *
+     * @param executor the executor on which callback will be invoked.
+     * @param callback a callback to receive CellInfo.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void requestCellInfoUpdate(
+            @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) {
+                    throw new IllegalStateException("Telephony is null");
+                } else {
+                    return;
+                }
+            }
+
+            telephony.requestCellInfoUpdate(
+                    getSubId(),
+                    new ICellInfoCallback.Stub() {
+                        @Override
+                        public void onCellInfo(List<CellInfo> cellInfo) {
+                            final long identity = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onCellInfo(cellInfo));
+                            } finally {
+                                Binder.restoreCallingIdentity(identity);
+                            }
+                        }
+
+                        @Override
+                        public void onError(int errorCode, String exceptionName, String message) {
+                            final long identity = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onError(
+                                        errorCode,
+                                        createThrowableByClassName(exceptionName, message)));
+                            } finally {
+                                Binder.restoreCallingIdentity(identity);
+                            }
+                        }
+                    }, getOpPackageName(), getAttributionTag());
+        } catch (RemoteException ex) {
+            runOnBackgroundThread(() -> executor.execute(
+                    () -> callback.onError(CellInfoCallback.ERROR_MODEM_ERROR, ex)));
+        }
+    }
+
+    /**
+     * Requests all available cell information from the current subscription for observed
+     * camped/registered, serving, and neighboring cells.
+     *
+     * <p>Any available results from this request will be provided by calls to
+     * {@link TelephonyCallback.CellInfoListener#onCellInfoChanged(List) onCellInfoChanged()}
+     * for each active subscription.
+     *
+     * <p>This method returns valid data for devices with
+     * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices
+     * that do not implement this feature, the behavior is not reliable.
+     *
+     * @param workSource the requestor to whom the power consumption for this should be attributed.
+     * @param executor the executor on which callback will be invoked.
+     * @param callback a callback to receive CellInfo.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_FINE_LOCATION,
+            android.Manifest.permission.MODIFY_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void requestCellInfoUpdate(@NonNull WorkSource workSource,
+            @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) {
+                    throw new IllegalStateException("Telephony is null");
+                } else {
+                    return;
+                }
+            }
+
+            telephony.requestCellInfoUpdateWithWorkSource(
+                    getSubId(),
+                    new ICellInfoCallback.Stub() {
+                        @Override
+                        public void onCellInfo(List<CellInfo> cellInfo) {
+                            final long identity = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onCellInfo(cellInfo));
+                            } finally {
+                                Binder.restoreCallingIdentity(identity);
+                            }
+
+                        }
+
+                        @Override
+                        public void onError(int errorCode, String exceptionName, String message) {
+                            final long identity = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onError(
+                                        errorCode,
+                                        createThrowableByClassName(exceptionName, message)));
+                            } finally {
+                                Binder.restoreCallingIdentity(identity);
+                            }
+                        }
+                    }, getOpPackageName(), getAttributionTag(), workSource);
+        } catch (RemoteException ex) {
+            runOnBackgroundThread(() -> executor.execute(
+                    () -> callback.onError(CellInfoCallback.ERROR_MODEM_ERROR, ex)));
+        }
+    }
+
+    private static Throwable createThrowableByClassName(String className, String message) {
+        if (className == null) {
+            return null;
+        }
+        try {
+            Class<?> c = Class.forName(className);
+            return (Throwable) c.getConstructor(String.class).newInstance(message);
+        } catch (ReflectiveOperationException | ClassCastException e) {
+        }
+        return new RuntimeException(className + ": " + message);
+    }
+
+    /**
+     * Sets the minimum time in milli-seconds between {@link
+     * TelephonyCallback.CellInfoListener#onCellInfoChanged(List)} will be invoked.
+     *<p>
+     * The default, 0, means invoke onCellInfoChanged when any of the reported
+     * information changes. Setting the value to INT_MAX(0x7fffffff) means never issue
+     * A onCellInfoChanged.
+     *<p>
+     * @param rateInMillis the rate
+     *
+     * @hide
+     */
+    public void setCellInfoListRate(int rateInMillis, int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.setCellInfoListRate(rateInMillis, subId);
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+    }
+
+    /**
+     * Returns the MMS user agent.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public String getMmsUserAgent() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getMmsUserAgent(getSubId());
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return null;
+    }
+
+    /**
+     * Returns the MMS user agent profile URL.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public String getMmsUAProfUrl() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getMmsUAProfUrl(getSubId());
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return null;
+    }
+
+    /**
+     * Get the first active portIndex from the corresponding physical slot index.
+     * @param physicalSlotIndex physical slot index
+     * @return first active port index or INVALID_PORT_INDEX if no port is active
+     */
+    private int getFirstActivePortIndex(int physicalSlotIndex) {
+        UiccSlotInfo[] slotInfos = getUiccSlotsInfo();
+        if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.length
+                && slotInfos[physicalSlotIndex] != null) {
+            Optional<UiccPortInfo> result =  slotInfos[physicalSlotIndex].getPorts().stream()
+                    .filter(portInfo -> portInfo.isActive()).findFirst();
+            if (result.isPresent()) {
+                return result.get().getPortIndex();
+            }
+        }
+        return INVALID_PORT_INDEX;
+    }
+
+    /**
+     * Opens a logical channel to the ICC card.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CCHO command.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param AID Application id. See ETSI 102.221 and 101.220.
+     * @return an IccOpenLogicalChannelResponse object.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @deprecated Replaced by {@link #iccOpenLogicalChannel(String, int)}
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @Deprecated
+    public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID) {
+        return iccOpenLogicalChannel(getSubId(), AID, -1);
+    }
+
+    /**
+     * Opens a logical channel to the ICC card using the physical slot index.
+     *
+     * Use this method when no subscriptions are available on the SIM and the operation must be
+     * performed using the physical slot index.
+     *
+     * This operation wraps two APDU instructions:
+     * <ul>
+     *     <li>MANAGE CHANNEL to open a logical channel</li>
+     *     <li>SELECT the given {@code AID} using the given {@code p2}</li>
+     * </ul>
+     *
+     * Per Open Mobile API Specification v3.2 section 6.2.7.h, only p2 values of 0x00, 0x04, 0x08,
+     * and 0x0C are guaranteed to be supported.
+     *
+     * If the SELECT command's status word is not '9000', '62xx', or '63xx', the status word will be
+     * considered an error and the channel shall not be opened.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CCHO command.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param slotIndex the physical slot index of the ICC card
+     * @param aid Application id. See ETSI 102.221 and 101.220.
+     * @param p2 P2 parameter (described in ISO 7816-4).
+     * @return an IccOpenLogicalChannelResponse object.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
+     * instead use {@link #iccOpenLogicalChannelByPort(int, int, String, int)}
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @SystemApi
+    @Nullable
+    @Deprecated
+    public IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int slotIndex,
+            @Nullable String aid, int p2) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IccLogicalChannelRequest request = new IccLogicalChannelRequest();
+                request.slotIndex = slotIndex;
+                request.portIndex = getFirstActivePortIndex(slotIndex);
+                request.aid = aid;
+                request.p2 = p2;
+                request.callingPackage = getOpPackageName();
+                request.binder = new Binder();
+                return telephony.iccOpenLogicalChannel(request);
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return null;
+    }
+
+    /**
+     * Opens a logical channel to the ICC card using the physical slot index and port index.
+     *
+     * Use this method when no subscriptions are available on the SIM and the operation must be
+     * performed using the physical slot index and port index.
+     *
+     * This operation wraps two APDU instructions:
+     * <ul>
+     *     <li>MANAGE CHANNEL to open a logical channel</li>
+     *     <li>SELECT the given {@code AID} using the given {@code p2}</li>
+     * </ul>
+     *
+     * Per Open Mobile API Specification v3.2 section 6.2.7.h, only p2 values of 0x00, 0x04, 0x08,
+     * and 0x0C are guaranteed to be supported.
+     *
+     * If the SELECT command's status word is not '9000', '62xx', or '63xx', the status word will be
+     * considered an error and the channel shall not be opened.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CCHO command.
+     *
+     * @param slotIndex the physical slot index of the ICC card
+     * @param portIndex The port index is an enumeration of the ports available on the UICC.
+     *                  Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+     * @param aid Application id. See ETSI 102.221 and 101.220.
+     * @param p2 P2 parameter (described in ISO 7816-4).
+     * @return an IccOpenLogicalChannelResponse object.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @SystemApi
+    @NonNull
+    public IccOpenLogicalChannelResponse iccOpenLogicalChannelByPort(int slotIndex,
+            int portIndex, @Nullable String aid, int p2) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IccLogicalChannelRequest request = new IccLogicalChannelRequest();
+                request.slotIndex = slotIndex;
+                request.portIndex = portIndex;
+                request.aid = aid;
+                request.p2 = p2;
+                request.callingPackage = getOpPackageName();
+                request.binder = new Binder();
+                return telephony.iccOpenLogicalChannel(request);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Opens a logical channel to the ICC card.
+     *
+     * This operation wraps two APDU instructions:
+     * <ul>
+     *     <li>MANAGE CHANNEL to open a logical channel</li>
+     *     <li>SELECT the given {@code AID} using the given {@code p2}</li>
+     * </ul>
+     *
+     * Per Open Mobile API Specification v3.2 section 6.2.7.h, only p2 values of 0x00, 0x04, 0x08,
+     * and 0x0C are guaranteed to be supported.
+     *
+     * If the SELECT command's status word is not '9000', '62xx', or '63xx', the status word will be
+     * considered an error and the channel shall not be opened.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CCHO command.
+     *
+     * It is strongly recommended that callers of this should firstly create a new TelephonyManager
+     * instance by calling {@link TelephonyManager#createForSubscriptionId(int)}. Failure to do so
+     * can result in unpredictable and detrimental behavior like callers can end up talking to the
+     * wrong SIM card.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param AID Application id. See ETSI 102.221 and 101.220.
+     * @param p2 P2 parameter (described in ISO 7816-4).
+     * @return an IccOpenLogicalChannelResponse object.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) {
+        return iccOpenLogicalChannel(getSubId(), AID, p2);
+    }
+
+    /**
+     * Opens a logical channel to the ICC card.
+     *
+     * This operation wraps two APDU instructions:
+     * <ul>
+     *     <li>MANAGE CHANNEL to open a logical channel</li>
+     *     <li>SELECT the given {@code AID} using the given {@code p2}</li>
+     * </ul>
+     *
+     * Per Open Mobile API Specification v3.2 section 6.2.7.h, only p2 values of 0x00, 0x04, 0x08,
+     * and 0x0C are guaranteed to be supported.
+     *
+     * If the SELECT command's status word is not '9000', '62xx', or '63xx', the status word will be
+     * considered an error and the channel shall not be opened.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CCHO command.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param subId The subscription to use.
+     * @param AID Application id. See ETSI 102.221 and 101.220.
+     * @param p2 P2 parameter (described in ISO 7816-4).
+     * @return an IccOpenLogicalChannelResponse object.
+     * @hide
+     */
+    public IccOpenLogicalChannelResponse iccOpenLogicalChannel(int subId, String AID, int p2) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IccLogicalChannelRequest request = new IccLogicalChannelRequest();
+                request.subId = subId;
+                request.callingPackage = getOpPackageName();
+                request.aid = AID;
+                request.p2 = p2;
+                request.binder = new Binder();
+                return telephony.iccOpenLogicalChannel(request);
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return null;
+    }
+
+    /**
+     * Closes a previously opened logical channel to the ICC card using the physical slot index.
+     *
+     * Use this method when no subscriptions are available on the SIM and the operation must be
+     * performed using the physical slot index.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CCHC command.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param slotIndex the physical slot index of the ICC card
+     * @param channel is the channel id to be closed as returned by a successful
+     *            iccOpenLogicalChannel.
+     * @return true if the channel was closed successfully.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
+     * instead use {@link #iccCloseLogicalChannelByPort(int, int, int)}
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @SystemApi
+    @Deprecated
+    public boolean iccCloseLogicalChannelBySlot(int slotIndex, int channel) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IccLogicalChannelRequest request = new IccLogicalChannelRequest();
+                request.slotIndex = slotIndex;
+                request.portIndex = getFirstActivePortIndex(slotIndex);
+                request.channel = channel;
+                return telephony.iccCloseLogicalChannel(request);
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        } catch (IllegalStateException ex) {
+            Rlog.e(TAG, "iccCloseLogicalChannel IllegalStateException", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Closes a previously opened logical channel to the ICC card using the physical slot index and
+     * port index.
+     *
+     * Use this method when no subscriptions are available on the SIM and the operation must be
+     * performed using the physical slot index and port index.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CCHC command.
+     *
+     * @param slotIndex the physical slot index of the ICC card
+     * @param portIndex The port index is an enumeration of the ports available on the UICC.
+     *                  Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+     * @param channel is the channel id to be closed as returned by a successful
+     *            iccOpenLogicalChannel.
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available or modem
+     *                               currently can't process this command.
+     * @throws IllegalArgumentException if invalid arguments are passed.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @SystemApi
+    public void iccCloseLogicalChannelByPort(int slotIndex, int portIndex, int channel) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IccLogicalChannelRequest request = new IccLogicalChannelRequest();
+                request.slotIndex = slotIndex;
+                request.portIndex = portIndex;
+                request.channel = channel;
+                telephony.iccCloseLogicalChannel(request);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Closes a previously opened logical channel to the ICC card.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CCHC command.
+     * It is strongly recommended that callers of this API should firstly create
+     * new TelephonyManager instance by calling
+     * {@link TelephonyManager#createForSubscriptionId(int)}. Failure to do so can result in
+     * unpredictable and detrimental behavior like callers can end up talking to the wrong SIM card.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param channel is the channel id to be closed as returned by a successful
+     *            iccOpenLogicalChannel.
+     * @return true if the channel was closed successfully.
+     * @throws IllegalArgumentException if input parameters are wrong. e.g., invalid channel
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public boolean iccCloseLogicalChannel(int channel) {
+        try {
+            return iccCloseLogicalChannel(getSubId(), channel);
+        } catch (IllegalStateException ex) {
+            Rlog.e(TAG, "iccCloseLogicalChannel IllegalStateException", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Closes a previously opened logical channel to the ICC card.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CCHC command.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param subId The subscription to use.
+     * @param channel is the channel id to be closed as returned by a successful
+     *            iccOpenLogicalChannel.
+     * @return true if the channel was closed successfully.
+     * @hide
+     */
+    public boolean iccCloseLogicalChannel(int subId, int channel) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IccLogicalChannelRequest request = new IccLogicalChannelRequest();
+                request.subId = subId;
+                request.channel = channel;
+                return telephony.iccCloseLogicalChannel(request);
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        } catch (IllegalStateException ex) {
+            Rlog.e(TAG, "iccCloseLogicalChannel IllegalStateException", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Transmit an APDU to the ICC card over a logical channel using the physical slot index.
+     *
+     * Use this method when no subscriptions are available on the SIM and the operation must be
+     * performed using the physical slot index.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CGLA command.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param slotIndex the physical slot index of the ICC card
+     * @param channel is the channel id to be closed as returned by a successful
+     *            iccOpenLogicalChannel.
+     * @param cla Class of the APDU command.
+     * @param instruction Instruction of the APDU command.
+     * @param p1 P1 value of the APDU command.
+     * @param p2 P2 value of the APDU command.
+     * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+     *            is sent to the SIM.
+     * @param data Data to be sent with the APDU.
+     * @return The APDU response from the ICC card with the status appended at the end, or null if
+     * there is an issue connecting to the Telephony service.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
+     * instead use
+     * {@link #iccTransmitApduLogicalChannelByPort(int, int, int, int, int, int, int, int, String)}
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @SystemApi
+    @Nullable
+    @Deprecated
+    public String iccTransmitApduLogicalChannelBySlot(int slotIndex, int channel, int cla,
+            int instruction, int p1, int p2, int p3, @Nullable String data) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.iccTransmitApduLogicalChannelByPort(slotIndex,
+                        getFirstActivePortIndex(slotIndex), channel, cla, instruction,
+                        p1, p2, p3, data);
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return null;
+    }
+
+    /**
+     * Transmit an APDU to the ICC card over a logical channel using the physical slot index.
+     *
+     * Use this method when no subscriptions are available on the SIM and the operation must be
+     * performed using the physical slot index.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CGLA command.
+     *
+     * @param slotIndex the physical slot index of the ICC card
+     * @param portIndex The port index is an enumeration of the ports available on the UICC.
+     *                  Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+     * @param channel is the channel id to be closed as returned by a successful
+     *            iccOpenLogicalChannel.
+     * @param cla Class of the APDU command.
+     * @param instruction Instruction of the APDU command.
+     * @param p1 P1 value of the APDU command.
+     * @param p2 P2 value of the APDU command.
+     * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+     *            is sent to the SIM.
+     * @param data Data to be sent with the APDU.
+     * @return The APDU response from the ICC card with the status appended at the end, or null if
+     * there is an issue connecting to the Telephony service.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @SystemApi
+    @NonNull
+    public String iccTransmitApduLogicalChannelByPort(int slotIndex, int portIndex, int channel,
+            int cla, int instruction, int p1, int p2, int p3, @Nullable String data) {
+        String response;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                response = telephony.iccTransmitApduLogicalChannelByPort(slotIndex, portIndex,
+                        channel, cla, instruction, p1, p2, p3, data);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowAsRuntimeException();
+        }
+        return response;
+    }
+
+    /**
+     * Transmit an APDU to the ICC card over a logical channel.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CGLA command.
+     *
+     * It is strongly recommended that callers of this API should firstly create a new
+     * TelephonyManager instance by calling
+     * {@link TelephonyManager#createForSubscriptionId(int)}. Failure to do so can result in
+     * unpredictable and detrimental behavior like callers can end up talking to the wrong SIM card.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param channel is the channel id to be closed as returned by a successful
+     *            iccOpenLogicalChannel.
+     * @param cla Class of the APDU command.
+     * @param instruction Instruction of the APDU command.
+     * @param p1 P1 value of the APDU command.
+     * @param p2 P2 value of the APDU command.
+     * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+     *            is sent to the SIM.
+     * @param data Data to be sent with the APDU.
+     * @return The APDU response from the ICC card with the status appended at
+     *            the end.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String iccTransmitApduLogicalChannel(int channel, int cla,
+            int instruction, int p1, int p2, int p3, String data) {
+        return iccTransmitApduLogicalChannel(getSubId(), channel, cla,
+                    instruction, p1, p2, p3, data);
+    }
+
+    /**
+     * Transmit an APDU to the ICC card over a logical channel.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CGLA command.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param subId The subscription to use.
+     * @param channel is the channel id to be closed as returned by a successful
+     *            iccOpenLogicalChannel.
+     * @param cla Class of the APDU command.
+     * @param instruction Instruction of the APDU command.
+     * @param p1 P1 value of the APDU command.
+     * @param p2 P2 value of the APDU command.
+     * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+     *            is sent to the SIM.
+     * @param data Data to be sent with the APDU.
+     * @return The APDU response from the ICC card with the status appended at
+     *            the end.
+     * @hide
+     */
+    public String iccTransmitApduLogicalChannel(int subId, int channel, int cla,
+            int instruction, int p1, int p2, int p3, String data) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.iccTransmitApduLogicalChannel(subId, channel, cla,
+                    instruction, p1, p2, p3, data);
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return "";
+    }
+
+    /**
+     * Transmit an APDU to the ICC card over the basic channel using the physical slot index.
+     *
+     * Use this method when no subscriptions are available on the SIM and the operation must be
+     * performed using the physical slot index.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CSIM command.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param slotIndex the physical slot index of the ICC card to target
+     * @param cla Class of the APDU command.
+     * @param instruction Instruction of the APDU command.
+     * @param p1 P1 value of the APDU command.
+     * @param p2 P2 value of the APDU command.
+     * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+     *            is sent to the SIM.
+     * @param data Data to be sent with the APDU.
+     * @return The APDU response from the ICC card with the status appended at
+     *            the end.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
+     * instead use
+     * {@link #iccTransmitApduBasicChannelByPort(int, int, int, int, int, int, int, String)}
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @SystemApi
+    @NonNull
+    @Deprecated
+    public String iccTransmitApduBasicChannelBySlot(int slotIndex, int cla, int instruction, int p1,
+            int p2, int p3, @Nullable String data) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.iccTransmitApduBasicChannelByPort(slotIndex,
+                        getFirstActivePortIndex(slotIndex), getOpPackageName(),
+                        cla, instruction, p1, p2, p3, data);
+            }
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return null;
+    }
+
+    /**
+     * Transmit an APDU to the ICC card over the basic channel using the physical slot index.
+     *
+     * Use this method when no subscriptions are available on the SIM and the operation must be
+     * performed using the physical slot index.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CSIM command.
+     *
+     * @param slotIndex the physical slot index of the ICC card to target
+     * @param portIndex The port index is an enumeration of the ports available on the UICC.
+     *                  Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+     * @param cla Class of the APDU command.
+     * @param instruction Instruction of the APDU command.
+     * @param p1 P1 value of the APDU command.
+     * @param p2 P2 value of the APDU command.
+     * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+     *            is sent to the SIM.
+     * @param data Data to be sent with the APDU.
+     * @return The APDU response from the ICC card with the status appended at
+     *            the end.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @SystemApi
+    @NonNull
+    public String iccTransmitApduBasicChannelByPort(int slotIndex, int portIndex, int cla,
+            int instruction, int p1, int p2, int p3, @Nullable String data) {
+        String response;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                response = telephony.iccTransmitApduBasicChannelByPort(slotIndex, portIndex,
+                        getOpPackageName(), cla, instruction, p1, p2, p3, data);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowAsRuntimeException();
+        }
+        return response;
+    }
+    /**
+     * Transmit an APDU to the ICC card over the basic channel.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CSIM command.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param cla Class of the APDU command.
+     * @param instruction Instruction of the APDU command.
+     * @param p1 P1 value of the APDU command.
+     * @param p2 P2 value of the APDU command.
+     * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+     *            is sent to the SIM.
+     * @param data Data to be sent with the APDU.
+     * @return The APDU response from the ICC card with the status appended at
+     *            the end.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String iccTransmitApduBasicChannel(int cla,
+            int instruction, int p1, int p2, int p3, String data) {
+        return iccTransmitApduBasicChannel(getSubId(), cla,
+                    instruction, p1, p2, p3, data);
+    }
+
+    /**
+     * Transmit an APDU to the ICC card over the basic channel.
+     *
+     * Input parameters equivalent to TS 27.007 AT+CSIM command.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param subId The subscription to use.
+     * @param cla Class of the APDU command.
+     * @param instruction Instruction of the APDU command.
+     * @param p1 P1 value of the APDU command.
+     * @param p2 P2 value of the APDU command.
+     * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+     *            is sent to the SIM.
+     * @param data Data to be sent with the APDU.
+     * @return The APDU response from the ICC card with the status appended at
+     *            the end.
+     * @hide
+     */
+    public String iccTransmitApduBasicChannel(int subId, int cla,
+            int instruction, int p1, int p2, int p3, String data) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.iccTransmitApduBasicChannel(subId, getOpPackageName(), cla,
+                    instruction, p1, p2, p3, data);
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return "";
+    }
+
+    /**
+     * Returns the response APDU for a command APDU sent through SIM_IO.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param fileID
+     * @param command
+     * @param p1 P1 value of the APDU command.
+     * @param p2 P2 value of the APDU command.
+     * @param p3 P3 value of the APDU command.
+     * @param filePath
+     * @return The APDU response.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
+            String filePath) {
+        return iccExchangeSimIO(getSubId(), fileID, command, p1, p2, p3, filePath);
+    }
+
+    /**
+     * Returns the response APDU for a command APDU sent through SIM_IO.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param subId The subscription to use.
+     * @param fileID
+     * @param command
+     * @param p1 P1 value of the APDU command.
+     * @param p2 P2 value of the APDU command.
+     * @param p3 P3 value of the APDU command.
+     * @param filePath
+     * @return The APDU response.
+     * @hide
+     */
+    public byte[] iccExchangeSimIO(int subId, int fileID, int command, int p1, int p2,
+            int p3, String filePath) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.iccExchangeSimIO(subId, fileID, command, p1, p2, p3, filePath);
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return null;
+    }
+
+    /**
+     * Send ENVELOPE to the SIM and return the response.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param content String containing SAT/USAT response in hexadecimal
+     *                format starting with command tag. See TS 102 223 for
+     *                details.
+     * @return The APDU response from the ICC card in hexadecimal format
+     *         with the last 4 bytes being the status word. If the command fails,
+     *         returns an empty string.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String sendEnvelopeWithStatus(String content) {
+        return sendEnvelopeWithStatus(getSubId(), content);
+    }
+
+    /**
+     * Send ENVELOPE to the SIM and return the response.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param subId The subscription to use.
+     * @param content String containing SAT/USAT response in hexadecimal
+     *                format starting with command tag. See TS 102 223 for
+     *                details.
+     * @return The APDU response from the ICC card in hexadecimal format
+     *         with the last 4 bytes being the status word. If the command fails,
+     *         returns an empty string.
+     * @hide
+     */
+    public String sendEnvelopeWithStatus(int subId, String content) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.sendEnvelopeWithStatus(subId, content);
+        } catch (RemoteException ex) {
+        } catch (NullPointerException ex) {
+        }
+        return "";
+    }
+
+    /**
+     * Read one of the NV items defined in com.android.internal.telephony.RadioNVItems.
+     * Used for device configuration by some CDMA operators.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param itemID the ID of the item to read.
+     * @return the NV item as a String, or null on any failure.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public String nvReadItem(int itemID) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.nvReadItem(itemID);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "nvReadItem RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "nvReadItem NPE", ex);
+        }
+        return "";
+    }
+
+    /**
+     * Write one of the NV items defined in com.android.internal.telephony.RadioNVItems.
+     * Used for device configuration by some CDMA operators.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param itemID the ID of the item to read.
+     * @param itemValue the value to write, as a String.
+     * @return true on success; false on any failure.
+     *
+     * @hide
+     */
+    public boolean nvWriteItem(int itemID, String itemValue) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.nvWriteItem(itemID, itemValue);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "nvWriteItem RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "nvWriteItem NPE", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
+     * Used for device configuration by some CDMA operators.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param preferredRoamingList byte array containing the new PRL.
+     * @return true on success; false on any failure.
+     *
+     * @hide
+     */
+    public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.nvWriteCdmaPrl(preferredRoamingList);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "nvWriteCdmaPrl RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "nvWriteCdmaPrl NPE", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Perform the specified type of NV config reset. The radio will be taken offline
+     * and the device must be rebooted after the operation. Used for device
+     * configuration by some CDMA operators.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * TODO: remove this one. use {@link #rebootModem()} for reset type 1 and
+     * {@link #resetRadioConfig()} for reset type 3 (b/116476729)
+     *
+     * @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
+     * @return true on success; false on any failure.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public boolean nvResetConfig(int resetType) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (resetType == 1 /*1: reload NV reset */) {
+                    return telephony.rebootModem(getSlotIndex());
+                } else if (resetType == 3 /*3: factory NV reset */) {
+                    return telephony.resetModemConfig(getSlotIndex());
+                } else {
+                    Rlog.e(TAG, "nvResetConfig unsupported reset type");
+                }
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "nvResetConfig RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "nvResetConfig NPE", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Rollback modem configurations to factory default except some config which are in allowlist.
+     * Used for device configuration by some carriers.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return {@code true} on success; {@code false} on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean resetRadioConfig() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.resetModemConfig(getSlotIndex());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "resetRadioConfig RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "resetRadioConfig NPE", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Generate a radio modem reset. Used for device configuration by some carriers.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return {@code true} on success; {@code false} on any failure.
+     *
+     * @deprecated  Using {@link #rebootModem()} instead.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean rebootRadio() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.rebootModem(getSlotIndex());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "rebootRadio RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "rebootRadio NPE", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Generate a radio modem reset. Used for device configuration by some carriers.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws RuntimeException
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void rebootModem() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                throw new IllegalStateException("telephony service is null.");
+            }
+            telephony.rebootModem(getSlotIndex());
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "rebootRadio RemoteException", ex);
+            throw ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Return an appropriate subscription ID for any situation.
+     *
+     * If this object has been created with {@link #createForSubscriptionId}, then the provided
+     * subscription ID is returned. Otherwise, the default subscription ID will be returned.
+     *
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public int getSubscriptionId() {
+        return getSubId();
+    }
+
+    /**
+     * Return an appropriate subscription ID for any situation.
+     *
+     * If this object has been created with {@link #createForSubscriptionId}, then the provided
+     * subscription ID is returned. Otherwise, the default subscription ID will be returned.
+     *
+     */
+    private int getSubId() {
+      if (SubscriptionManager.isUsableSubIdValue(mSubId)) {
+        return mSubId;
+      }
+      return SubscriptionManager.getDefaultSubscriptionId();
+    }
+
+    /**
+     * Return an appropriate subscription ID for any situation.
+     *
+     * If this object has been created with {@link #createForSubscriptionId}, then the provided
+     * subId is returned. Otherwise, the preferred subId which is based on caller's context is
+     * returned.
+     * {@see SubscriptionManager#getDefaultDataSubscriptionId()}
+     * {@see SubscriptionManager#getDefaultVoiceSubscriptionId()}
+     * {@see SubscriptionManager#getDefaultSmsSubscriptionId()}
+     */
+    @UnsupportedAppUsage
+    private int getSubId(int preferredSubId) {
+        if (SubscriptionManager.isUsableSubIdValue(mSubId)) {
+            return mSubId;
+        }
+        return preferredSubId;
+    }
+
+    /**
+     * Return an appropriate phone ID for any situation.
+     *
+     * If this object has been created with {@link #createForSubscriptionId}, then the phoneId
+     * associated with the provided subId is returned. Otherwise, the default phoneId associated
+     * with the default subId will be returned.
+     */
+    private int getPhoneId() {
+        return SubscriptionManager.getPhoneId(getSubId());
+    }
+
+    /**
+     * Return an appropriate phone ID for any situation.
+     *
+     * If this object has been created with {@link #createForSubscriptionId}, then the phoneId
+     * associated with the provided subId is returned. Otherwise, return the phoneId associated
+     * with the preferred subId based on caller's context.
+     * {@see SubscriptionManager#getDefaultDataSubscriptionId()}
+     * {@see SubscriptionManager#getDefaultVoiceSubscriptionId()}
+     * {@see SubscriptionManager#getDefaultSmsSubscriptionId()}
+     */
+    @UnsupportedAppUsage
+    private int getPhoneId(int preferredSubId) {
+        return SubscriptionManager.getPhoneId(getSubId(preferredSubId));
+    }
+
+    /**
+     * Return an appropriate slot index for any situation.
+     *
+     * if this object has been created with {@link #createForSubscriptionId}, then the slot index
+     * associated with the provided subId is returned. Otherwise, return the slot index associated
+     * with the default subId.
+     * If SIM is not inserted, return default SIM slot index.
+     *
+     * {@hide}
+     */
+    @VisibleForTesting
+    @UnsupportedAppUsage
+    public int getSlotIndex() {
+        int slotIndex = SubscriptionManager.getSlotIndex(getSubId());
+        if (slotIndex == SubscriptionManager.SIM_NOT_INSERTED) {
+            slotIndex = SubscriptionManager.DEFAULT_SIM_SLOT_INDEX;
+        }
+        return slotIndex;
+    }
+
+    /**
+     * Request that the next incoming call from a number matching {@code range} be intercepted.
+     *
+     * This API is intended for OEMs to provide a service for apps to verify the device's phone
+     * number. When called, the Telephony stack will store the provided {@link PhoneNumberRange} and
+     * intercept the next incoming call from a number that lies within the range, within a timeout
+     * specified by {@code timeoutMillis}.
+     *
+     * If such a phone call is received, the caller will be notified via
+     * {@link NumberVerificationCallback#onCallReceived(String)} on the provided {@link Executor}.
+     * If verification fails for any reason, the caller will be notified via
+     * {@link NumberVerificationCallback#onVerificationFailed(int)}
+     * on the provided {@link Executor}.
+     *
+     * In addition to the {@link Manifest.permission#MODIFY_PHONE_STATE} permission, callers of this
+     * API must also be listed in the device configuration as an authorized app in
+     * {@code packages/services/Telephony/res/values/config.xml} under the
+     * {@code platform_number_verification_package} key.
+     *
+     * @hide
+     * @param range The range of phone numbers the caller expects a phone call from.
+     * @param timeoutMillis The amount of time to wait for such a call, or the value of
+     *                      {@link #getMaxNumberVerificationTimeoutMillis()}, whichever is lesser.
+     * @param executor The {@link Executor} that callbacks should be executed on.
+     * @param callback The callback to use for delivering results.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void requestNumberVerification(@NonNull PhoneNumberRange range, long timeoutMillis,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull NumberVerificationCallback callback) {
+        if (executor == null) {
+            throw new NullPointerException("Executor must be non-null");
+        }
+        if (callback == null) {
+            throw new NullPointerException("Callback must be non-null");
+        }
+
+        INumberVerificationCallback internalCallback = new INumberVerificationCallback.Stub() {
+            @Override
+            public void onCallReceived(String phoneNumber) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() ->
+                            callback.onCallReceived(phoneNumber));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+
+            @Override
+            public void onVerificationFailed(int reason) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() ->
+                            callback.onVerificationFailed(reason));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        };
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) {
+                    throw new IllegalStateException("Telephony is null");
+                } else {
+                    return;
+                }
+            }
+
+            telephony.requestNumberVerification(range, timeoutMillis, internalCallback,
+                    getOpPackageName());
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "requestNumberVerification RemoteException", ex);
+            runOnBackgroundThread(() -> executor.execute(
+                    () -> callback.onVerificationFailed(
+                            NumberVerificationCallback.REASON_UNSPECIFIED)));
+        }
+    }
+
+    /**
+     * Inserts or updates a list property. Expands the list if its length is not enough.
+     */
+    private static <T> List<T> updateTelephonyProperty(List<T> prop, int phoneId, T value) {
+        List<T> ret = new ArrayList<>(prop);
+        while (ret.size() <= phoneId) ret.add(null);
+        ret.set(phoneId, value);
+        return ret;
+    }
+    /**
+     * Convenience function for retrieving a value from the secure settings
+     * value list as an integer.  Note that internally setting values are
+     * always stored as strings; this function converts the string to an
+     * integer for you.
+     * <p>
+     * This version does not take a default value.  If the setting has not
+     * been set, or the string value is not a number,
+     * it throws {@link SettingNotFoundException}.
+     *
+     * @param cr The ContentResolver to access.
+     * @param name The name of the setting to retrieve.
+     * @param index The index of the list
+     *
+     * @throws SettingNotFoundException Thrown if a setting by the given
+     * name can't be found or the setting value is not an integer.
+     *
+     * @return The value at the given index of settings.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static int getIntAtIndex(android.content.ContentResolver cr,
+            String name, int index)
+            throws android.provider.Settings.SettingNotFoundException {
+        String v = android.provider.Settings.Global.getString(cr, name);
+        if (v != null) {
+            String valArray[] = v.split(",");
+            if ((index >= 0) && (index < valArray.length) && (valArray[index] != null)) {
+                try {
+                    return Integer.parseInt(valArray[index]);
+                } catch (NumberFormatException e) {
+                    //Log.e(TAG, "Exception while parsing Integer: ", e);
+                }
+            }
+        }
+        throw new android.provider.Settings.SettingNotFoundException(name);
+    }
+
+    /**
+     * Convenience function for updating settings value as coma separated
+     * integer values. This will either create a new entry in the table if the
+     * given name does not exist, or modify the value of the existing row
+     * with that name.  Note that internally setting values are always
+     * stored as strings, so this function converts the given value to a
+     * string before storing it.
+     *
+     * @param cr The ContentResolver to access.
+     * @param name The name of the setting to modify.
+     * @param index The index of the list
+     * @param value The new value for the setting to be added to the list.
+     * @return true if the value was set, false on database errors
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static boolean putIntAtIndex(android.content.ContentResolver cr,
+            String name, int index, int value) {
+        String data = "";
+        String valArray[] = null;
+        String v = android.provider.Settings.Global.getString(cr, name);
+
+        if (index == Integer.MAX_VALUE) {
+            throw new IllegalArgumentException("putIntAtIndex index == MAX_VALUE index=" + index);
+        }
+        if (index < 0) {
+            throw new IllegalArgumentException("putIntAtIndex index < 0 index=" + index);
+        }
+        if (v != null) {
+            valArray = v.split(",");
+        }
+
+        // Copy the elements from valArray till index
+        for (int i = 0; i < index; i++) {
+            String str = "";
+            if ((valArray != null) && (i < valArray.length)) {
+                str = valArray[i];
+            }
+            data = data + str + ",";
+        }
+
+        data = data + value;
+
+        // Copy the remaining elements from valArray if any.
+        if (valArray != null) {
+            for (int i = index+1; i < valArray.length; i++) {
+                data = data + "," + valArray[i];
+            }
+        }
+        return android.provider.Settings.Global.putString(cr, name, data);
+    }
+
+    /**
+     * Gets a per-phone telephony property from a property name.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static String getTelephonyProperty(int phoneId, String property, String defaultVal) {
+        String propVal = null;
+        String prop = SystemProperties.get(property);
+        if ((prop != null) && (prop.length() > 0)) {
+            String values[] = prop.split(",");
+            if ((phoneId >= 0) && (phoneId < values.length) && (values[phoneId] != null)) {
+                propVal = values[phoneId];
+            }
+        }
+        return propVal == null ? defaultVal : propVal;
+    }
+
+    /**
+     * Gets a typed per-phone telephony property from a schematized list property.
+     */
+    private static <T> T getTelephonyProperty(int phoneId, List<T> prop, T defaultValue) {
+        T ret = null;
+        if (phoneId >= 0 && phoneId < prop.size()) ret = prop.get(phoneId);
+        return ret != null ? ret : defaultValue;
+    }
+
+    /**
+     * Gets a global telephony property.
+     *
+     * See also getTelephonyProperty(phoneId, property, defaultVal). Most telephony properties are
+     * per-phone.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static String getTelephonyProperty(String property, String defaultVal) {
+        String propVal = SystemProperties.get(property);
+        return TextUtils.isEmpty(propVal) ? defaultVal : propVal;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage
+    public int getSimCount() {
+        // FIXME Need to get it from Telephony Dev Controller when that gets implemented!
+        // and then this method shouldn't be used at all!
+        return getPhoneCount();
+    }
+
+    /**
+     * Returns the IMS Service Table (IST) that was loaded from the ISIM.
+     *
+     * See 3GPP TS 31.103 (Section 4.2.7) for the definition and more information on this table.
+     *
+     * @return IMS Service Table or null if not present or not loaded
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getIsimIst() {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            //get the Isim Ist based on subId
+            return info.getIsimIst(getSubId());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /**
+     * Returns the IMS Proxy Call Session Control Function(PCSCF) that were loaded from the ISIM.
+     * @return an array of PCSCF strings with one PCSCF per string, or null if
+     *         not present or not loaded
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public String[] getIsimPcscf() {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            //get the Isim Pcscf based on subId
+            return info.getIsimPcscf(getSubId());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return null;
+        }
+    }
+
+    /** UICC application type is unknown or not specified */
+    public static final int APPTYPE_UNKNOWN = PhoneConstants.APPTYPE_UNKNOWN;
+    /** UICC application type is SIM */
+    public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM;
+    /** UICC application type is USIM */
+    public static final int APPTYPE_USIM = PhoneConstants.APPTYPE_USIM;
+    /** UICC application type is RUIM */
+    public static final int APPTYPE_RUIM = PhoneConstants.APPTYPE_RUIM;
+    /** UICC application type is CSIM */
+    public static final int APPTYPE_CSIM = PhoneConstants.APPTYPE_CSIM;
+    /** UICC application type is ISIM */
+    public static final int APPTYPE_ISIM = PhoneConstants.APPTYPE_ISIM;
+
+    // authContext (parameter P2) when doing UICC challenge,
+    // per 3GPP TS 31.102 (Section 7.1.2)
+    /** Authentication type for UICC challenge is EAP SIM. See RFC 4186 for details. */
+    public static final int AUTHTYPE_EAP_SIM = PhoneConstants.AUTH_CONTEXT_EAP_SIM;
+    /** Authentication type for UICC challenge is EAP AKA. See RFC 4187 for details. */
+    public static final int AUTHTYPE_EAP_AKA = PhoneConstants.AUTH_CONTEXT_EAP_AKA;
+    /**
+     * Authentication type for GBA Bootstrap Challenge.
+     * Pass this authentication type into the {@link #getIccAuthentication} API to perform a GBA
+     * Bootstrap challenge (BSF), with {@code data} (generated according to the procedure defined in
+     * 3GPP 33.220 Section 5.3.2 step.4) in base64 encoding.
+     * This method will return the Bootstrapping response in base64 encoding when ICC authentication
+     * is completed.
+     * Ref 3GPP 33.220 Section 5.3.2.
+     */
+    public static final int AUTHTYPE_GBA_BOOTSTRAP = PhoneConstants.AUTH_CONTEXT_GBA_BOOTSTRAP;
+    /**
+     * Authentication type for GBA Network Application Functions (NAF) key External Challenge.
+     * Pass this authentication type into the {@link #getIccAuthentication} API to perform a GBA
+     * Network Applications Functions (NAF) key External challenge using the NAF_ID parameter
+     * as the {@code data} in base64 encoding.
+     * This method will return the Ks_Ext_Naf key in base64 encoding when ICC authentication
+     * is completed.
+     * Ref 3GPP 33.220 Section 5.3.2.
+     */
+    public static final int AUTHTYPE_GBA_NAF_KEY_EXTERNAL =
+            PhoneConstants.AUTHTYPE_GBA_NAF_KEY_EXTERNAL;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            AUTHTYPE_EAP_SIM,
+            AUTHTYPE_EAP_AKA,
+            AUTHTYPE_GBA_BOOTSTRAP,
+            AUTHTYPE_GBA_NAF_KEY_EXTERNAL
+    })
+    public @interface AuthType {}
+
+    /**
+     * Returns the response of authentication for the default subscription.
+     * Returns null if the authentication hasn't been successful
+     *
+     * <p>Requires one of the following permissions:
+     * <ul>
+     *     <li>READ_PRIVILEGED_PHONE_STATE
+     *     <li>the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *     <li>the calling app has been granted the
+     *     {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} permission.
+     * </ul>
+     *
+     * @param appType the icc application type, like {@link #APPTYPE_USIM}
+     * @param authType the authentication type, any one of {@link #AUTHTYPE_EAP_AKA} or
+     * {@link #AUTHTYPE_EAP_SIM} or {@link #AUTHTYPE_GBA_BOOTSTRAP} or
+     * {@link #AUTHTYPE_GBA_NAF_KEY_EXTERNAL}
+     * @param data authentication challenge data, base64 encoded.
+     * See 3GPP TS 31.102 7.1.2 for more details.
+     * @return the response of authentication. This value will be null in the following cases:
+     *   Authentication error, incorrect MAC
+     *   Authentication error, security context not supported
+     *   Key freshness failure
+     *   Authentication error, no memory space available
+     *   Authentication error, no memory space available in EFMUK
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
+    // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
+    // it's not public API.
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getIccAuthentication(int appType,@AuthType int authType, String data) {
+        return getIccAuthentication(getSubId(), appType, authType, data);
+    }
+
+    /**
+     * Returns the response of USIM Authentication for specified subId.
+     * Returns null if the authentication hasn't been successful
+     *
+     * <p>See {@link #getIccAuthentication(int, int, String)} for details on the required
+     * permissions.
+     *
+     * @param subId subscription ID used for authentication
+     * @param appType the icc application type, like {@link #APPTYPE_USIM}
+     * @param authType the authentication type, any one of {@link #AUTHTYPE_EAP_AKA} or
+     * {@link #AUTHTYPE_EAP_SIM} or {@link #AUTHTYPE_GBA_BOOTSTRAP} or
+     * {@link #AUTHTYPE_GBA_NAF_KEY_EXTERNAL}
+     * @param data authentication challenge data, base64 encoded.
+     * See 3GPP TS 31.102 7.1.2 for more details.
+     * @return the response of authentication. This value will be null in the following cases only
+     * (see 3GPP TS 31.102 7.3.1):
+     *   Authentication error, incorrect MAC
+     *   Authentication error, security context not supported
+     *   Key freshness failure
+     *   Authentication error, no memory space available
+     *   Authentication error, no memory space available in EFMUK
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public String getIccAuthentication(int subId, int appType,@AuthType int authType, String data) {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null)
+                return null;
+            return info.getIccSimChallengeResponse(subId, appType, authType, data,
+                    getOpPackageName(), getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone starts
+            return null;
+        }
+    }
+
+    /**
+     * Returns an array of Forbidden PLMNs from the USIM App
+     * Returns null if the query fails.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return an array of forbidden PLMNs or null if not available
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String[] getForbiddenPlmns() {
+      return getForbiddenPlmns(getSubId(), APPTYPE_USIM);
+    }
+
+    /**
+     * Returns an array of Forbidden PLMNs from the specified SIM App
+     * Returns null if the query fails.
+     *
+     * @param subId subscription ID used for authentication
+     * @param appType the icc application type, like {@link #APPTYPE_USIM}
+     * @return fplmns an array of forbidden PLMNs
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public String[] getForbiddenPlmns(int subId, int appType) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return null;
+            return telephony.getForbiddenPlmns(subId, appType, mContext.getOpPackageName(),
+                    getAttributionTag());
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone starts
+            return null;
+        }
+    }
+
+    /**
+     * Replace the contents of the forbidden PLMN SIM file with the provided values.
+     * Passing an empty list will clear the contents of the EFfplmn file.
+     * If the provided list is shorter than the size of EFfplmn, then the list will be padded
+     * up to the file size with 'FFFFFF'. (required by 3GPP TS 31.102 spec 4.2.16)
+     * If the list is longer than the size of EFfplmn, then the file will be written from the
+     * beginning of the list up to the file size.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE
+     * MODIFY_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param fplmns a list of PLMNs to be forbidden.
+     *
+     * @return number of PLMNs that were successfully written to the SIM FPLMN list.
+     * This may be less than the number of PLMNs passed in where the SIM file does not have enough
+     * room for all of the values passed in. Return -1 in the event of an unexpected failure
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public int setForbiddenPlmns(@NonNull List<String> fplmns) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) return -1;
+            return telephony.setForbiddenPlmns(
+                    getSubId(), APPTYPE_USIM, fplmns, getOpPackageName(), getAttributionTag());
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setForbiddenPlmns RemoteException: " + ex.getMessage());
+        } catch (NullPointerException ex) {
+            // This could happen before phone starts
+            Rlog.e(TAG, "setForbiddenPlmns NullPointerException: " + ex.getMessage());
+        }
+        return -1;
+    }
+
+    /**
+     * Fetches the sim service table from the EFUST/EFIST based on the application type
+     * {@link #APPTYPE_USIM} or {@link #APPTYPE_ISIM}. The return value is hexaString format
+     * representing X bytes (x >= 1). Each bit of every byte indicates which optional services
+     * are available for the given application type.
+     * The USIM service table EF is described in as per Section 4.2.8 of 3GPP TS 31.102.
+     * The ISIM service table EF is described in as per Section 4.2.7 of 3GPP TS 31.103.
+     * The list of services mapped to the exact nth byte of response as mentioned in Section 4.2
+     * .7 of 3GPP TS 31.103. Eg. Service n°1: Local Phone Book, Service n°2: Fixed Dialling
+     * Numbers (FDN) - Bit 1 and 2 of the 1st Byte represent service Local Phone Book and Fixed
+     * Dialling Numbers (FDN)respectively. The coding format for each service type  should be
+     * interpreted as bit = 1: service available;bit = 0:service not available.
+     *
+     * @param appType of type int of either {@link #APPTYPE_USIM} or {@link #APPTYPE_ISIM}.
+     * @return HexString represents sim service table else null.
+     * @throws SecurityException if the caller does not have the required permission/privileges
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+
+    @Nullable
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getSimServiceTable(int appType) {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null) {
+                Rlog.e(TAG, "getSimServiceTable(): IPhoneSubInfo is null");
+                return null;
+            }
+            //Fetches the sim service table based on subId and appType
+            if (appType == APPTYPE_ISIM) {
+                return info.getIsimIst(getSubId());
+            } else if ((appType == APPTYPE_USIM)) {
+                return info.getSimServiceTable(getSubId(), APPTYPE_USIM);
+            } else {
+                return null;
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getSimServiceTable(): RemoteException=" + ex.getMessage());
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "getSimServiceTable(): NullPointerException=" + ex.getMessage());
+        }
+        return null;
+    }
+
+    /**
+     * Resets the {@link android.telephony.ims.ImsService} associated with the specified sim slot.
+     * Used by diagnostic apps to force the IMS stack to be disabled and re-enabled in an effort to
+     * recover from scenarios where the {@link android.telephony.ims.ImsService} gets in to a bad
+     * state.
+     *
+     * @param slotIndex the sim slot to reset the IMS stack on.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide */
+    @SystemApi
+    @WorkerThread
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
+    public void resetIms(int slotIndex) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.resetIms(slotIndex);
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "toggleImsOnOff, RemoteException: "
+                    + e.getMessage());
+        }
+    }
+
+    /**
+     * Enables IMS for the framework. This will trigger IMS registration and ImsFeature capability
+     * status updates, if not already enabled.
+     * @hide
+     */
+    public void enableIms(int slotId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.enableIms(slotId);
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "enableIms, RemoteException: "
+                    + e.getMessage());
+        }
+    }
+
+    /**
+     * Disables IMS for the framework. This will trigger IMS de-registration and trigger ImsFeature
+     * status updates to disabled.
+     * @hide
+     */
+    public void disableIms(int slotId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.disableIms(slotId);
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "disableIms, RemoteException: "
+                    + e.getMessage());
+        }
+    }
+
+    /**
+     * @return the {@IImsRegistration} interface that corresponds with the slot index and feature.
+     * @param slotIndex The SIM slot corresponding to the ImsService ImsRegistration is active for.
+     * @param feature An integer indicating the feature that we wish to get the ImsRegistration for.
+     * Corresponds to features defined in ImsFeature.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public @Nullable IImsRegistration getImsRegistration(int slotIndex, int feature) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getImsRegistration(slotIndex, feature);
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "getImsRegistration, RemoteException: " + e.getMessage());
+        }
+        return null;
+    }
+
+    /**
+     * @return the {@IImsConfig} interface that corresponds with the slot index and feature.
+     * @param slotIndex The SIM slot corresponding to the ImsService ImsConfig is active for.
+     * @param feature An integer indicating the feature that we wish to get the ImsConfig for.
+     * Corresponds to features defined in ImsFeature.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public @Nullable IImsConfig getImsConfig(int slotIndex, int feature) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getImsConfig(slotIndex, feature);
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "getImsRegistration, RemoteException: " + e.getMessage());
+        }
+        return null;
+    }
+
+    /**
+     * Set IMS registration state on all active subscriptions.
+     * <p/>
+     * Use {@link android.telephony.ims.stub.ImsRegistrationImplBase#onRegistered} and
+     * {@link android.telephony.ims.stub.ImsRegistrationImplBase#onDeregistered} to set Ims
+     * registration state instead.
+     *
+     * @param registered whether ims is registered
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void setImsRegistrationState(final boolean registered) {
+        try {
+            final ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.setImsRegistrationState(registered);
+        } catch (final RemoteException e) {
+        }
+    }
+
+    /** @hide */
+    @IntDef(prefix = { "NETWORK_MODE_" }, value = {
+            NETWORK_MODE_WCDMA_PREF,
+            NETWORK_MODE_GSM_ONLY,
+            NETWORK_MODE_WCDMA_ONLY,
+            NETWORK_MODE_GSM_UMTS,
+            NETWORK_MODE_CDMA_EVDO,
+            NETWORK_MODE_CDMA_NO_EVDO,
+            NETWORK_MODE_EVDO_NO_CDMA,
+            NETWORK_MODE_GLOBAL,
+            NETWORK_MODE_LTE_CDMA_EVDO,
+            NETWORK_MODE_LTE_GSM_WCDMA,
+            NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA,
+            NETWORK_MODE_LTE_ONLY,
+            NETWORK_MODE_LTE_WCDMA,
+            NETWORK_MODE_TDSCDMA_ONLY,
+            NETWORK_MODE_TDSCDMA_WCDMA,
+            NETWORK_MODE_LTE_TDSCDMA,
+            NETWORK_MODE_TDSCDMA_GSM,
+            NETWORK_MODE_LTE_TDSCDMA_GSM,
+            NETWORK_MODE_TDSCDMA_GSM_WCDMA,
+            NETWORK_MODE_LTE_TDSCDMA_WCDMA,
+            NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA,
+            NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
+            NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
+            NETWORK_MODE_NR_ONLY,
+            NETWORK_MODE_NR_LTE,
+            NETWORK_MODE_NR_LTE_CDMA_EVDO,
+            NETWORK_MODE_NR_LTE_GSM_WCDMA,
+            NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA,
+            NETWORK_MODE_NR_LTE_WCDMA,
+            NETWORK_MODE_NR_LTE_TDSCDMA,
+            NETWORK_MODE_NR_LTE_TDSCDMA_GSM,
+            NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA,
+            NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA,
+            NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PrefNetworkMode{}
+
+    /**
+     * Preferred network mode is GSM/WCDMA (WCDMA preferred).
+     * @hide
+     */
+    public static final int NETWORK_MODE_WCDMA_PREF = RILConstants.NETWORK_MODE_WCDMA_PREF;
+
+    /**
+     * Preferred network mode is GSM only.
+     * @hide
+     */
+    public static final int NETWORK_MODE_GSM_ONLY = RILConstants.NETWORK_MODE_GSM_ONLY;
+
+    /**
+     * Preferred network mode is WCDMA only.
+     * @hide
+     */
+    public static final int NETWORK_MODE_WCDMA_ONLY = RILConstants.NETWORK_MODE_WCDMA_ONLY;
+
+    /**
+     * Preferred network mode is GSM/WCDMA (auto mode, according to PRL).
+     * @hide
+     */
+    public static final int NETWORK_MODE_GSM_UMTS = RILConstants.NETWORK_MODE_GSM_UMTS;
+
+    /**
+     * Preferred network mode is CDMA and EvDo (auto mode, according to PRL).
+     * @hide
+     */
+    public static final int NETWORK_MODE_CDMA_EVDO = RILConstants.NETWORK_MODE_CDMA;
+
+    /**
+     * Preferred network mode is CDMA only.
+     * @hide
+     */
+    public static final int NETWORK_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
+
+    /**
+     * Preferred network mode is EvDo only.
+     * @hide
+     */
+    public static final int NETWORK_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
+
+    /**
+     * Preferred network mode is GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL).
+     * @hide
+     */
+    public static final int NETWORK_MODE_GLOBAL = RILConstants.NETWORK_MODE_GLOBAL;
+
+    /**
+     * Preferred network mode is LTE, CDMA and EvDo.
+     * @hide
+     */
+    public static final int NETWORK_MODE_LTE_CDMA_EVDO = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
+
+    /**
+     * Preferred network mode is LTE, GSM/WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_LTE_GSM_WCDMA = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
+
+    /**
+     * Preferred network mode is LTE, CDMA, EvDo, GSM/WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
+
+    /**
+     * Preferred network mode is LTE Only.
+     * @hide
+     */
+    public static final int NETWORK_MODE_LTE_ONLY = RILConstants.NETWORK_MODE_LTE_ONLY;
+
+    /**
+     * Preferred network mode is LTE/WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_LTE_WCDMA = RILConstants.NETWORK_MODE_LTE_WCDMA;
+
+    /**
+     * Preferred network mode is TD-SCDMA only.
+     * @hide
+     */
+    public static final int NETWORK_MODE_TDSCDMA_ONLY = RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
+
+    /**
+     * Preferred network mode is TD-SCDMA and WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_TDSCDMA_WCDMA = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
+
+    /**
+     * Preferred network mode is TD-SCDMA and LTE.
+     * @hide
+     */
+    public static final int NETWORK_MODE_LTE_TDSCDMA = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
+
+    /**
+     * Preferred network mode is TD-SCDMA and GSM.
+     * @hide
+     */
+    public static final int NETWORK_MODE_TDSCDMA_GSM = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
+
+    /**
+     * Preferred network mode is TD-SCDMA,GSM and LTE.
+     * @hide
+     */
+    public static final int NETWORK_MODE_LTE_TDSCDMA_GSM =
+            RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
+
+    /**
+     * Preferred network mode is TD-SCDMA, GSM/WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
+
+    /**
+     * Preferred network mode is TD-SCDMA, WCDMA and LTE.
+     * @hide
+     */
+    public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA =
+            RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
+
+    /**
+     * Preferred network mode is TD-SCDMA, GSM/WCDMA and LTE.
+     * @hide
+     */
+    public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
+
+    /**
+     * Preferred network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+    /**
+     * Preferred network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo.
+     * @hide
+     */
+    public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+
+    /**
+     * Preferred network mode is NR 5G only.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_ONLY = RILConstants.NETWORK_MODE_NR_ONLY;
+
+    /**
+     * Preferred network mode is NR 5G, LTE.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE = RILConstants.NETWORK_MODE_NR_LTE;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, CDMA and EvDo.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO =
+            RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, GSM and WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, CDMA, EvDo, GSM and WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA;
+
+    /**
+     * Preferred network mode is NR 5G, LTE and WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_WCDMA = RILConstants.NETWORK_MODE_NR_LTE_WCDMA;
+
+    /**
+     * Preferred network mode is NR 5G, LTE and TDSCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_TDSCDMA = RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, TD-SCDMA and GSM.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_TDSCDMA_GSM =
+            RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, TD-SCDMA, WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA =
+            RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, TD-SCDMA, GSM and WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA;
+
+    /**
+     * Preferred network mode is NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA.
+     * @hide
+     */
+    public static final int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+
+    /**
+     * The default preferred network mode constant.
+     *
+     * <p> This constant is used in case of nothing is set in
+     * TelephonyProperties#default_network().
+     *
+     * @hide
+     */
+    public static final int DEFAULT_PREFERRED_NETWORK_MODE =
+            RILConstants.PREFERRED_NETWORK_MODE;
+
+    /**
+     * Get the preferred network type.
+     * Used for device configuration by some CDMA operators.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return the preferred network type.
+     * @hide
+     * @deprecated Use {@link #getAllowedNetworkTypesBitmask} instead.
+     */
+    @Deprecated
+    @RequiresPermission((android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE))
+    @UnsupportedAppUsage
+    public @PrefNetworkMode int getPreferredNetworkType(int subId) {
+        return RadioAccessFamily.getNetworkTypeFromRaf((int) getAllowedNetworkTypesBitmask());
+    }
+
+    /**
+     * Get the preferred network type bitmask.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return The bitmask of preferred network types.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     * @deprecated Use {@link #getAllowedNetworkTypesBitmask} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @NetworkTypeBitMask long getPreferredNetworkTypeBitmask() {
+        return getAllowedNetworkTypesBitmask();
+    }
+
+    /**
+     * Get the allowed network type bitmask.
+     * Note that the device can only register on the network of {@link NetworkTypeBitmask}
+     * (except for emergency call cases).
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return The bitmask of allowed network types.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @NetworkTypeBitMask long getAllowedNetworkTypesBitmask() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return (long) telephony.getAllowedNetworkTypesBitmask(getSubId());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getAllowedNetworkTypesBitmask RemoteException", ex);
+        }
+        return 0;
+    }
+
+    /**
+     * Get the allowed network types by carriers.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return the allowed network type bitmask
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     * @deprecated Use {@link #getAllowedNetworkTypesForReason} instead.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    @SystemApi
+    @Deprecated
+    public @NetworkTypeBitMask long getAllowedNetworkTypes() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getAllowedNetworkTypesForReason(getSubId(),
+                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getAllowedNetworkTypes RemoteException", ex);
+        }
+        return -1;
+    }
+
+    /**
+     * Sets the network selection mode to automatic.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void setNetworkSelectionModeAutomatic() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setNetworkSelectionModeAutomatic(getSubId());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setNetworkSelectionModeAutomatic RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "setNetworkSelectionModeAutomatic NPE", ex);
+        }
+    }
+
+    /**
+     * Perform a radio scan and return the list of available networks.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p> Note that this scan can take a long time (sometimes minutes) to happen.
+     *
+     * <p>Requires Permissions:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * @return {@link CellNetworkScanResult} with the status
+     * {@link CellNetworkScanResult#STATUS_SUCCESS} and a list of
+     * {@link com.android.internal.telephony.OperatorInfo} if it's available. Otherwise, the failure
+     * caused will be included in the result.
+     *
+     * @hide
+     */
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    public CellNetworkScanResult getAvailableNetworks() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getCellNetworkScanResults(getSubId(), getOpPackageName(),
+                        getAttributionTag());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getAvailableNetworks RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "getAvailableNetworks NPE", ex);
+        }
+        return new CellNetworkScanResult(
+                CellNetworkScanResult.STATUS_UNKNOWN_ERROR, null /* OperatorInfo */);
+    }
+
+    /**
+     * Request a network scan.
+     *
+     * This method is asynchronous, so the network scan results will be returned by callback.
+     * The returned NetworkScan will contain a callback method which can be used to stop the scan.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
+     *
+     * If the system-wide location switch is off, apps may still call this API, with the
+     * following constraints:
+     * <ol>
+     *     <li>The app must hold the {@code android.permission.NETWORK_SCAN} permission.</li>
+     *     <li>The app must not supply any specific bands or channels to scan.</li>
+     *     <li>The app must only specify MCC/MNC pairs that are
+     *     associated to a SIM in the device.</li>
+     *     <li>Returned results will have no meaningful info other than signal strength
+     *     and MCC/MNC info.</li>
+     * </ol>
+     *
+     * @param request Contains all the RAT with bands/channels that need to be scanned.
+     * @param executor The executor through which the callback should be invoked. Since the scan
+     *        request may trigger multiple callbacks and they must be invoked in the same order as
+     *        they are received by the platform, the user should provide an executor which executes
+     *        tasks one at a time in serial order.
+     * @param callback Returns network scan results or errors.
+     * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public NetworkScan requestNetworkScan(
+            NetworkScanRequest request, Executor executor,
+            TelephonyScanManager.NetworkScanCallback callback) {
+        return requestNetworkScan(INCLUDE_LOCATION_DATA_FINE, request, executor, callback);
+    }
+
+    /**
+     * Request a network scan.
+     *
+     * This method is asynchronous, so the network scan results will be returned by callback.
+     * The returned NetworkScan will contain a callback method which can be used to stop the scan.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_FINE_LOCATION} if includeLocationData is
+     * set to {@link #INCLUDE_LOCATION_DATA_FINE}.
+     *
+     * If the system-wide location switch is off, apps may still call this API, with the
+     * following constraints:
+     * <ol>
+     *     <li>The app must hold the {@code android.permission.NETWORK_SCAN} permission.</li>
+     *     <li>The app must not supply any specific bands or channels to scan.</li>
+     *     <li>The app must only specify MCC/MNC pairs that are
+     *     associated to a SIM in the device.</li>
+     *     <li>Returned results will have no meaningful info other than signal strength
+     *     and MCC/MNC info.</li>
+     * </ol>
+     *
+     * @param includeLocationData Specifies if the caller would like to receive
+     * location related information. If this parameter is set to
+     * {@link #INCLUDE_LOCATION_DATA_FINE} then the application will be checked for
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission and available
+     * location related information received during network scan will be sent to the caller.
+     * @param request Contains all the RAT with bands/channels that need to be scanned.
+     * @param executor The executor through which the callback should be invoked. Since the scan
+     *        request may trigger multiple callbacks and they must be invoked in the same order as
+     *        they are received by the platform, the user should provide an executor which executes
+     *        tasks one at a time in serial order.
+     * @param callback Returns network scan results or errors.
+     * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @Nullable NetworkScan requestNetworkScan(
+            @IncludeLocationData int includeLocationData,
+            @NonNull NetworkScanRequest request,
+            @NonNull Executor executor,
+            @NonNull TelephonyScanManager.NetworkScanCallback callback) {
+        synchronized (sCacheLock) {
+            if (mTelephonyScanManager == null) {
+                mTelephonyScanManager = new TelephonyScanManager();
+            }
+        }
+        return mTelephonyScanManager.requestNetworkScan(getSubId(),
+                includeLocationData != INCLUDE_LOCATION_DATA_FINE,
+                request, executor, callback,
+                getOpPackageName(), getAttributionTag());
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     *
+     * @deprecated
+     * Use {@link
+     * #requestNetworkScan(NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}
+     * @removed
+     */
+    @Deprecated
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public NetworkScan requestNetworkScan(
+        NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
+        return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback);
+    }
+
+    /**
+     * Ask the radio to connect to the input network and change selection mode to manual.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param operatorNumeric the PLMN ID of the network to select.
+     * @param persistSelection whether the selection will persist until reboot. If true, only allows
+     * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
+     * normal network selection next time.
+     * @return {@code true} on success; {@code false} on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean setNetworkSelectionModeManual(String operatorNumeric, boolean persistSelection) {
+        return setNetworkSelectionModeManual(
+                new OperatorInfo(
+                        "" /* operatorAlphaLong */, "" /* operatorAlphaShort */, operatorNumeric),
+                persistSelection);
+    }
+
+    /**
+     * Ask the radio to connect to the input network and change selection mode to manual.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param operatorNumeric the PLMN ID of the network to select.
+     * @param persistSelection whether the selection will persist until reboot.
+     *         If true, only allows attaching to the selected PLMN until reboot; otherwise,
+     *         attach to the chosen PLMN and resume normal network selection next time.
+     * @param ran the initial suggested radio access network type.
+     *         If registration fails, the RAN is not available after, the RAN is not within the
+     *         network types specified by the preferred network types, or the value is
+     *         {@link AccessNetworkConstants.AccessNetworkType#UNKNOWN}, modem will select
+     *         the next best RAN for network registration.
+     * @return {@code true} on success; {@code false} on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean setNetworkSelectionModeManual(@NonNull String operatorNumeric,
+            boolean persistSelection, @AccessNetworkConstants.RadioAccessNetworkType int ran) {
+        return setNetworkSelectionModeManual(new OperatorInfo("" /* operatorAlphaLong */,
+                "" /* operatorAlphaShort */, operatorNumeric, ran), persistSelection);
+    }
+
+    /**
+     * Ask the radio to connect to the input network and change selection mode to manual.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param operatorInfo included the PLMN id, long name, short name of the operator to attach to.
+     * @param persistSelection whether the selection will persist until reboot. If true, only allows
+     * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
+     * normal network selection next time.
+     * @return {@code true} on success; {@code true} on any failure.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public boolean setNetworkSelectionModeManual(
+            OperatorInfo operatorInfo, boolean persistSelection) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.setNetworkSelectionModeManual(
+                        getSubId(), operatorInfo, persistSelection);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setNetworkSelectionModeManual RemoteException", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Get the network selection mode.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *  <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return the network selection mode.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @SuppressAutoDoc // No support for carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @NetworkSelectionMode int getNetworkSelectionMode() {
+        int mode = NETWORK_SELECTION_MODE_UNKNOWN;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                mode = telephony.getNetworkSelectionMode(getSubId());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getNetworkSelectionMode RemoteException", ex);
+        }
+        return mode;
+    }
+
+    /**
+     * Get the PLMN chosen for Manual Network Selection if active.
+     * Return empty string if in automatic selection.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link #hasCarrierPrivileges})
+     *
+     * @return manually selected network info on success or empty string on failure
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @SuppressAutoDoc // No support carrier privileges (b/72967236).
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @NonNull String getManualNetworkSelectionPlmn() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null && isManualNetworkSelectionAllowed()) {
+                return telephony.getManualNetworkSelectionPlmn(getSubId());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getManualNetworkSelectionPlmn RemoteException", ex);
+        }
+        return "";
+    }
+
+    /**
+     * Query Telephony to see if there has recently been an emergency SMS sent to the network by the
+     * user and we are still within the time interval after the emergency SMS was sent that we are
+     * considered in Emergency SMS mode.
+     *
+     * <p>This mode is used by other applications to allow them to perform special functionality,
+     * such as allow the GNSS service to provide user location to the carrier network for emergency
+     * when an emergency SMS is sent. This interval is set by
+     * {@link CarrierConfigManager#KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT}. If
+     * the carrier does not support this mode, this function will always return false.
+     *
+     * @return {@code true} if this device is in emergency SMS mode, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public boolean isInEmergencySmsMode() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isInEmergencySmsMode();
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "isInEmergencySmsMode RemoteException", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Set the preferred network type.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * <p>
+     * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+     * ({@link TelephonyManager#CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK}) returns true, then
+     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
+     * setPreferredNetworkTypesBitmap is used instead.
+     *
+     * @param subId the id of the subscription to set the preferred network type for.
+     * @param networkType the preferred network type
+     * @return true on success; false on any failure.
+     * @hide
+     * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead.
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public boolean setPreferredNetworkType(int subId, @PrefNetworkMode int networkType) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.setAllowedNetworkTypesForReason(subId,
+                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER,
+                        RadioAccessFamily.getRafFromNetworkType(networkType));
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Set the preferred network type bitmask but if {@link #setAllowedNetworkTypes} has been set,
+     * only the allowed network type will set to the modem.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * <p>
+     * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+     * ({@link TelephonyManager#CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK}) returns true, then
+     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
+     * setPreferredNetworkTypesBitmap is used instead.
+     *
+     * @param networkTypeBitmask The bitmask of preferred network types.
+     * @return true on success; false on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    @SystemApi
+    public boolean setPreferredNetworkTypeBitmask(@NetworkTypeBitMask long networkTypeBitmask) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                networkTypeBitmask = checkNetworkTypeBitmask(networkTypeBitmask);
+                return telephony.setAllowedNetworkTypesForReason(getSubId(),
+                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, networkTypeBitmask);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setPreferredNetworkTypeBitmask RemoteException", ex);
+        }
+        return false;
+    }
+
+    /**
+     * If {@link #NETWORK_TYPE_BITMASK_LTE_CA} bit is set, convert it to NETWORK_TYPE_BITMASK_LTE.
+     *
+     * @param networkTypeBitmask The networkTypeBitmask being checked
+     * @return The checked/converted networkTypeBitmask
+     */
+    private long checkNetworkTypeBitmask(@NetworkTypeBitMask long networkTypeBitmask) {
+        if ((networkTypeBitmask & NETWORK_TYPE_BITMASK_LTE_CA) != 0) {
+            networkTypeBitmask ^= NETWORK_TYPE_BITMASK_LTE_CA;
+            networkTypeBitmask |= NETWORK_TYPE_BITMASK_LTE;
+        }
+        return networkTypeBitmask;
+    }
+
+    /**
+     * Set the allowed network types of the device. This is for carrier or privileged apps to
+     * enable/disable certain network types on the device. The user preferred network types should
+     * be set through {@link #setPreferredNetworkTypeBitmask}.
+     * <p>
+     * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+     * ({@link TelephonyManager#CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK}) returns true, then
+     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
+     * setPreferredNetworkTypesBitmap is used instead.
+     *
+     * @param allowedNetworkTypes The bitmask of allowed network types.
+     * @return true on success; false on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     *
+     * @hide
+     * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead with reason
+     * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER}.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK)
+    @SystemApi
+    public boolean setAllowedNetworkTypes(@NetworkTypeBitMask long allowedNetworkTypes) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                allowedNetworkTypes = checkNetworkTypeBitmask(allowedNetworkTypes);
+                return telephony.setAllowedNetworkTypesForReason(getSubId(),
+                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER, allowedNetworkTypes);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setAllowedNetworkTypes RemoteException", ex);
+        }
+        return false;
+    }
+
+    /** @hide */
+    @IntDef({
+            ALLOWED_NETWORK_TYPES_REASON_USER,
+            ALLOWED_NETWORK_TYPES_REASON_POWER,
+            ALLOWED_NETWORK_TYPES_REASON_CARRIER,
+            ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AllowedNetworkTypesReason {
+    }
+
+    /**
+     * To indicate allowed network type change is requested by user.
+     */
+    public static final int ALLOWED_NETWORK_TYPES_REASON_USER = 0;
+
+    /**
+     * To indicate allowed network type change is requested by power manager.
+     * Power Manger configuration won't affect the settings configured through
+     * other reasons and will result in allowing network types that are in both
+     * configurations (i.e intersection of both sets).
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ALLOWED_NETWORK_TYPES_REASON_POWER = 1;
+
+    /**
+     * To indicate allowed network type change is requested by carrier.
+     * Carrier configuration won't affect the settings configured through
+     * other reasons and will result in allowing network types that are in both
+     * configurations (i.e intersection of both sets).
+     */
+    public static final int ALLOWED_NETWORK_TYPES_REASON_CARRIER = 2;
+
+    /**
+     * To indicate allowed network type change is requested by the user via the 2G toggle.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3;
+
+    /**
+     * Set the allowed network types of the device and provide the reason triggering the allowed
+     * network change.
+     * <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or
+     * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * This can be called for following reasons:
+     * <ol>
+     * <li>Allowed network types control by USER
+     * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER}
+     * <li>Allowed network types control by carrier {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER}
+     * </ol>
+     * This API will result in allowing an intersection of allowed network types for all reasons,
+     * including the configuration done through other reasons.
+     *
+     * @param reason the reason the allowed network type change is taking place
+     * @param allowedNetworkTypes The bitmask of allowed network type
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
+     * @throws SecurityException if the caller does not have the required privileges or if the
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * caller tries to use one of the following security-based reasons without
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE} permissions.
+     * <ol>
+     *     <li>{@code TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}</li>
+     * </ol>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK)
+    public void setAllowedNetworkTypesForReason(@AllowedNetworkTypesReason int reason,
+            @NetworkTypeBitMask long allowedNetworkTypes) {
+        if (!isValidAllowedNetworkTypesReason(reason)) {
+            throw new IllegalArgumentException("invalid AllowedNetworkTypesReason.");
+        }
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                allowedNetworkTypes = checkNetworkTypeBitmask(allowedNetworkTypes);
+                telephony.setAllowedNetworkTypesForReason(getSubId(), reason,
+                        allowedNetworkTypes);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setAllowedNetworkTypesForReason RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the allowed network types for certain reason.
+     *
+     * {@link #getAllowedNetworkTypesForReason} returns allowed network type for a
+     * specific reason.
+     * <p>Requires permission: android.Manifest.READ_PRIVILEGED_PHONE_STATE or
+     * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param reason the reason the allowed network type change is taking place
+     * @return the allowed network type bitmask
+     * @throws IllegalStateException    if the Telephony process is not currently available.
+     * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
+     * @throws SecurityException if the caller does not have the required permission/privileges
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK)
+    public @NetworkTypeBitMask long getAllowedNetworkTypesForReason(
+            @AllowedNetworkTypesReason int reason) {
+        if (!isValidAllowedNetworkTypesReason(reason)) {
+            throw new IllegalArgumentException("invalid AllowedNetworkTypesReason.");
+        }
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getAllowedNetworkTypesForReason(getSubId(), reason);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getAllowedNetworkTypesForReason RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return -1;
+    }
+    /**
+     * Verifies that the reason provided is valid.
+     * @hide
+     */
+    public static boolean isValidAllowedNetworkTypesReason(@AllowedNetworkTypesReason int reason) {
+        switch (reason) {
+            case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER:
+            case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER:
+            case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER:
+            case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
+                return true;
+        }
+        return false;
+    }
+    /**
+     * Get bit mask of all network types.
+     *
+     * @return bit mask of all network types
+     * @hide
+     */
+    public static @NetworkTypeBitMask long getAllNetworkTypesBitmask() {
+        return NETWORK_STANDARDS_FAMILY_BITMASK_3GPP | NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2;
+    }
+
+    /**
+     * Returns a string representation of the allowed network types{@link NetworkTypeBitMask}.
+     *
+     * @param networkTypeBitmask The bitmask of allowed network types.
+     * @return the name of the allowed network types
+     * @hide
+     */
+    public static String convertNetworkTypeBitmaskToString(
+            @NetworkTypeBitMask long networkTypeBitmask) {
+        String networkTypeName = IntStream.rangeClosed(NETWORK_TYPE_GPRS, NETWORK_TYPE_NR)
+                .filter(x -> {
+                    return (networkTypeBitmask & getBitMaskForNetworkType(x))
+                            == getBitMaskForNetworkType(x);
+                })
+                .mapToObj(x -> getNetworkTypeName(x))
+                .collect(Collectors.joining("|"));
+        return TextUtils.isEmpty(networkTypeName) ? "UNKNOWN" : networkTypeName;
+    }
+
+    /**
+     * Set the preferred network type to global mode which includes NR, LTE, CDMA, EvDo
+     * and GSM/WCDMA.
+     *
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return true on success; false on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean setPreferredNetworkTypeToGlobal() {
+        return setPreferredNetworkTypeToGlobal(getSubId());
+    }
+
+    /**
+     * Set the preferred network type to global mode which includes NR, LTE, CDMA, EvDo
+     * and GSM/WCDMA.
+     *
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return true on success; false on any failure.
+     * @hide
+     */
+    public boolean setPreferredNetworkTypeToGlobal(int subId) {
+        return setPreferredNetworkType(subId, RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA);
+    }
+
+    /**
+     * Check whether DUN APN is required for tethering.
+     * <p>
+     * Requires Permission: MODIFY_PHONE_STATE.
+     *
+     * @return {@code true} if DUN APN is required for tethering.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean isTetheringApnRequired() {
+        return isTetheringApnRequired(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
+    }
+
+    /**
+     * Check whether DUN APN is required for tethering with subId.
+     *
+     * @param subId the id of the subscription to require tethering.
+     * @return {@code true} if DUN APN is required for tethering.
+     * @hide
+     */
+    public boolean isTetheringApnRequired(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.isTetheringApnRequiredForSubscriber(subId);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "hasMatchedTetherApnSetting RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "hasMatchedTetherApnSetting NPE", ex);
+        }
+        return false;
+    }
+
+
+    /**
+     * Values used to return status for hasCarrierPrivileges call.
+     */
+    /** @hide */ @SystemApi
+    public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1;
+    /** @hide */ @SystemApi
+    public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0;
+    /** @hide */ @SystemApi
+    public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1;
+    /** @hide */ @SystemApi
+    public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2;
+
+    /**
+     * Has the calling application been granted carrier privileges by the carrier.
+     *
+     * If any of the packages in the calling UID has carrier privileges, the
+     * call will return true. This access is granted by the owner of the UICC
+     * card and does not depend on the registered carrier.
+     *
+     * Note that this API applies to both physical and embedded subscriptions and
+     * is a superset of the checks done in SubscriptionManager#canManageSubscription.
+     *
+     * @return true if the app has carrier privileges.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public boolean hasCarrierPrivileges() {
+        return hasCarrierPrivileges(getSubId());
+    }
+
+    /**
+     * Has the calling application been granted carrier privileges by the carrier.
+     *
+     * If any of the packages in the calling UID has carrier privileges, the
+     * call will return true. This access is granted by the owner of the UICC
+     * card and does not depend on the registered carrier.
+     *
+     * Note that this API applies to both physical and embedded subscriptions and
+     * is a superset of the checks done in SubscriptionManager#canManageSubscription.
+     *
+     * @param subId The subscription to use.
+     * @return true if the app has carrier privileges.
+     * @hide
+     */
+    public boolean hasCarrierPrivileges(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getCarrierPrivilegeStatus(subId)
+                        == CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "hasCarrierPrivileges RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "hasCarrierPrivileges NPE", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Override the branding for the current ICCID.
+     *
+     * Once set, whenever the SIM is present in the device, the service
+     * provider name (SPN) and the operator name will both be replaced by the
+     * brand value input. To unset the value, the same function should be
+     * called with a null brand value.
+     *
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param brand The brand name to display/set.
+     * @return true if the operation was executed correctly.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public boolean setOperatorBrandOverride(String brand) {
+        return setOperatorBrandOverride(getSubId(), brand);
+    }
+
+    /**
+     * Override the branding for the current ICCID.
+     *
+     * Once set, whenever the SIM is present in the device, the service
+     * provider name (SPN) and the operator name will both be replaced by the
+     * brand value input. To unset the value, the same function should be
+     * called with a null brand value.
+     *
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param subId The subscription to use.
+     * @param brand The brand name to display/set.
+     * @return true if the operation was executed correctly.
+     * @hide
+     */
+    public boolean setOperatorBrandOverride(int subId, String brand) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.setOperatorBrandOverride(subId, brand);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setOperatorBrandOverride RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "setOperatorBrandOverride NPE", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Override the roaming preference for the current ICCID.
+     *
+     * Using this call, the carrier app (see #hasCarrierPrivileges) can override
+     * the platform's notion of a network operator being considered roaming or not.
+     * The change only affects the ICCID that was active when this call was made.
+     *
+     * If null is passed as any of the input, the corresponding value is deleted.
+     *
+     * <p>Requires that the caller have carrier privilege. See #hasCarrierPrivileges.
+     *
+     * @param gsmRoamingList - List of MCCMNCs to be considered roaming for 3GPP RATs.
+     * @param gsmNonRoamingList - List of MCCMNCs to be considered not roaming for 3GPP RATs.
+     * @param cdmaRoamingList - List of SIDs to be considered roaming for 3GPP2 RATs.
+     * @param cdmaNonRoamingList - List of SIDs to be considered not roaming for 3GPP2 RATs.
+     * @return true if the operation was executed correctly.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public boolean setRoamingOverride(List<String> gsmRoamingList,
+            List<String> gsmNonRoamingList, List<String> cdmaRoamingList,
+            List<String> cdmaNonRoamingList) {
+        return setRoamingOverride(getSubId(), gsmRoamingList, gsmNonRoamingList,
+                cdmaRoamingList, cdmaNonRoamingList);
+    }
+
+    /**
+     * Override the roaming preference for the current ICCID.
+     *
+     * Using this call, the carrier app (see #hasCarrierPrivileges) can override
+     * the platform's notion of a network operator being considered roaming or not.
+     * The change only affects the ICCID that was active when this call was made.
+     *
+     * If null is passed as any of the input, the corresponding value is deleted.
+     *
+     * <p>Requires that the caller have carrier privilege. See #hasCarrierPrivileges.
+     *
+     * @param subId for which the roaming overrides apply.
+     * @param gsmRoamingList - List of MCCMNCs to be considered roaming for 3GPP RATs.
+     * @param gsmNonRoamingList - List of MCCMNCs to be considered not roaming for 3GPP RATs.
+     * @param cdmaRoamingList - List of SIDs to be considered roaming for 3GPP2 RATs.
+     * @param cdmaNonRoamingList - List of SIDs to be considered not roaming for 3GPP2 RATs.
+     * @return true if the operation was executed correctly.
+     *
+     * @hide
+     */
+    public boolean setRoamingOverride(int subId, List<String> gsmRoamingList,
+            List<String> gsmNonRoamingList, List<String> cdmaRoamingList,
+            List<String> cdmaNonRoamingList) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.setRoamingOverride(subId, gsmRoamingList, gsmNonRoamingList,
+                        cdmaRoamingList, cdmaNonRoamingList);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setRoamingOverride RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "setRoamingOverride NPE", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Expose the rest of ITelephony to @SystemApi
+     */
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    public String getCdmaMdn() {
+        return getCdmaMdn(getSubId());
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    public String getCdmaMdn(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return null;
+            return telephony.getCdmaMdn(subId);
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    public String getCdmaMin() {
+        return getCdmaMin(getSubId());
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    public String getCdmaMin(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return null;
+            return telephony.getCdmaMin(subId);
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public int checkCarrierPrivilegesForPackage(String pkgName) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.checkCarrierPrivilegesForPackage(getSubId(), pkgName);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "checkCarrierPrivilegesForPackage RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "checkCarrierPrivilegesForPackage NPE", ex);
+        }
+        return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.checkCarrierPrivilegesForPackageAnyPhone(pkgName);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "checkCarrierPrivilegesForPackageAnyPhone RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "checkCarrierPrivilegesForPackageAnyPhone NPE", ex);
+        }
+        return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public List<String> getCarrierPackageNamesForIntent(Intent intent) {
+        return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId());
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public List<String> getCarrierPackageNamesForIntentAndPhone(Intent intent, int phoneId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.getCarrierPackageNamesForIntentAndPhone(intent, phoneId);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getCarrierPackageNamesForIntentAndPhone RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "getCarrierPackageNamesForIntentAndPhone NPE", ex);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the package name that provides the {@link CarrierService} implementation for the
+     * current subscription, or {@code null} if no package with carrier privileges declares one.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, then the provided
+     * subscription ID is used. Otherwise, the default subscription ID will be used.
+     *
+     * @return The system-selected package that provides the {@link CarrierService} implementation
+     * for the current subscription, or {@code null} if none is resolved
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @Nullable String getCarrierServicePackageName() {
+        return getCarrierServicePackageNameForLogicalSlot(getPhoneId());
+    }
+
+    /**
+     * Returns the package name that provides the {@link CarrierService} implementation for the
+     * specified {@code logicalSlotIndex}, or {@code null} if no package with carrier privileges
+     * declares one.
+     *
+     * @param logicalSlotIndex The slot index to fetch the {@link CarrierService} package for
+     * @return The system-selected package that provides the {@link CarrierService} implementation
+     * for the slot, or {@code null} if none is resolved
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @Nullable String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getCarrierServicePackageNameForLogicalSlot(logicalSlotIndex);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getCarrierServicePackageNameForLogicalSlot RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "getCarrierServicePackageNameForLogicalSlot NPE", ex);
+        }
+        return null;
+    }
+
+    /** @hide */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public List<String> getPackagesWithCarrierPrivileges() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getPackagesWithCarrierPrivileges(getPhoneId());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getPackagesWithCarrierPrivileges RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "getPackagesWithCarrierPrivileges NPE", ex);
+        }
+        return Collections.EMPTY_LIST;
+    }
+
+    /**
+     * Get the names of packages with carrier privileges for all the active subscriptions.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @NonNull
+    public List<String> getCarrierPrivilegedPackagesForAllActiveSubscriptions() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getPackagesWithCarrierPrivilegesForAllPhones();
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getCarrierPrivilegedPackagesForAllActiveSubscriptions RemoteException",
+                    ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "getCarrierPrivilegedPackagesForAllActiveSubscriptions NPE", ex);
+        }
+        return Collections.EMPTY_LIST;
+    }
+
+    /**
+     * Call composer status <b>OFF</b> from user setting.
+     */
+    public static final int CALL_COMPOSER_STATUS_OFF = 0;
+
+    /**
+     * Call composer status <b>ON</b> from user setting.
+     */
+    public static final int CALL_COMPOSER_STATUS_ON = 1;
+
+    /**
+     * Call composer status <b>Business Only</b> from user setting.
+     */
+    @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_BUSINESS_CALL_COMPOSER)
+    public static final int CALL_COMPOSER_STATUS_BUSINESS_ONLY = 2;
+
+    /** @hide */
+    @IntDef(prefix = {"CALL_COMPOSER_STATUS_"},
+            value = {
+                CALL_COMPOSER_STATUS_ON,
+                CALL_COMPOSER_STATUS_OFF,
+                CALL_COMPOSER_STATUS_BUSINESS_ONLY
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallComposerStatus {}
+
+    /**
+     * Set the user-set status for enriched calling with call composer.
+     *
+     * @param status user-set status for enriched calling with call composer.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * @throws IllegalArgumentException if requested state is invalid.
+     * @throws SecurityException if the caller does not have the permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void setCallComposerStatus(@CallComposerStatus int status) {
+        if (com.android.server.telecom.flags.Flags.businessCallComposer()) {
+            if (status > CALL_COMPOSER_STATUS_BUSINESS_ONLY
+                    || status < CALL_COMPOSER_STATUS_OFF) {
+                throw new IllegalArgumentException("requested status is invalid");
+            }
+        } else {
+            if (status > CALL_COMPOSER_STATUS_ON
+                    || status < CALL_COMPOSER_STATUS_OFF) {
+                throw new IllegalArgumentException("requested status is invalid");
+            }
+        }
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setCallComposerStatus(getSubId(), status);
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Error calling ITelephony#setCallComposerStatus", ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the user-set status for enriched calling with call composer.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * @throws SecurityException if the caller does not have the permission.
+     *
+     * @return the user-set status for enriched calling with call composer, either of
+     * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public @CallComposerStatus int getCallComposerStatus() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getCallComposerStatus(getSubId());
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Error calling ITelephony#getCallComposerStatus", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return CALL_COMPOSER_STATUS_OFF;
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("RequiresPermission")
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void dial(String number) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.dial(number);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#dial", e);
+        }
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     *
+     * @deprecated Use  {@link android.telecom.TelecomManager#placeCall(Uri address,
+     * Bundle extras)} instead.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void call(String callingPackage, String number) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.call(callingPackage, number);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#call", e);
+        }
+    }
+
+    /**
+     * @removed Use {@link android.telecom.TelecomManager#endCall()} instead.
+     * @hide
+     * @removed
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+    public boolean endCall() {
+        return false;
+    }
+
+    /**
+     * @removed Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
+     * @hide
+     * @removed
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void answerRingingCall() {
+        // No-op
+    }
+
+    /**
+     * @removed Use {@link android.telecom.TelecomManager#silenceRinger} instead
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @SuppressLint("RequiresPermission")
+    public void silenceRinger() {
+        // No-op
+    }
+
+    /**
+   * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE
+    })
+    public boolean isOffhook() {
+        TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
+        return tm.isInCall();
+    }
+
+    /**
+     * @deprecated Use {@link android.telecom.TelecomManager#isRinging} instead
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE
+    })
+    public boolean isRinging() {
+        TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
+        return tm.isRinging();
+    }
+
+    /**
+     * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE
+    })
+    public boolean isIdle() {
+        TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
+        return !tm.isInCall();
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @deprecated Use {@link android.telephony.TelephonyManager#getServiceState} instead
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean isRadioOn() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.isRadioOnWithFeature(getOpPackageName(), getAttributionTag());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isRadioOn", e);
+        }
+        return false;
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public boolean supplyPin(String pin) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.supplyPinForSubscriber(getSubId(), pin);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#supplyPinForSubscriber", e);
+        }
+        return false;
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public boolean supplyPuk(String puk, String pin) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.supplyPukForSubscriber(getSubId(), puk, pin);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#supplyPukForSubscriber", e);
+        }
+        return false;
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
+     * @deprecated use {@link #supplyIccLockPin(String)} instead.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @Deprecated
+    public int[] supplyPinReportResult(String pin) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.supplyPinReportResultForSubscriber(getSubId(), pin);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#supplyPinReportResultForSubscriber", e);
+        }
+        return new int[0];
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
+     * @deprecated use {@link #supplyIccLockPuk(String, String)} instead.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @Deprecated
+    public int[] supplyPukReportResult(String puk, String pin) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.supplyPukReportResultForSubscriber(getSubId(), puk, pin);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#supplyPukReportResultForSubscriber", e);
+        }
+        return new int[0];
+    }
+
+    /**
+     * Supplies a PIN to unlock the ICC and returns the corresponding {@link PinResult}.
+     * Used when the user enters their ICC unlock PIN to attempt an unlock.
+     *
+     * @param pin The user entered PIN.
+     * @return The result of the PIN.
+     * @throws SecurityException if the caller doesn't have the permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public PinResult supplyIccLockPin(@NonNull String pin) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                int[] result = telephony.supplyPinReportResultForSubscriber(getSubId(), pin);
+                return new PinResult(result[0], result[1]);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#supplyIccLockPin", e);
+            e.rethrowFromSystemServer();
+        }
+        return PinResult.getDefaultFailedResult();
+    }
+
+    /**
+     * Supplies a PUK and PIN to unlock the ICC and returns the corresponding {@link PinResult}.
+     * Used when the user enters their ICC unlock PUK and PIN to attempt an unlock.
+     *
+     * @param puk The product unlocking key.
+     * @param pin The user entered PIN.
+     * @return The result of the PUK and PIN.
+     * @throws SecurityException if the caller doesn't have the permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public PinResult supplyIccLockPuk(@NonNull String puk, @NonNull String pin) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                int[] result = telephony.supplyPukReportResultForSubscriber(getSubId(), puk, pin);
+                return new PinResult(result[0], result[1]);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#supplyIccLockPuk", e);
+            e.rethrowFromSystemServer();
+        }
+        return PinResult.getDefaultFailedResult();
+    }
+
+    /**
+     * Used to notify callers of
+     * {@link TelephonyManager#sendUssdRequest(String, UssdResponseCallback, Handler)} when the
+     * network either successfully executes a USSD request, or if there was a failure while
+     * executing the request.
+     * <p>
+     * {@link #onReceiveUssdResponse(TelephonyManager, String, CharSequence)} will be called if the
+     * USSD request has succeeded.
+     * {@link #onReceiveUssdResponseFailed(TelephonyManager, String, int)} will be called if the
+     * USSD request has failed.
+     */
+    public static abstract class UssdResponseCallback {
+       /**
+        * Called when a USSD request has succeeded.  The {@code response} contains the USSD
+        * response received from the network.  The calling app can choose to either display the
+        * response to the user or perform some operation based on the response.
+        * <p>
+        * USSD responses are unstructured text and their content is determined by the mobile network
+        * operator.
+        *
+        * @param telephonyManager the TelephonyManager the callback is registered to.
+        * @param request the USSD request sent to the mobile network.
+        * @param response the response to the USSD request provided by the mobile network.
+        **/
+       public void onReceiveUssdResponse(final TelephonyManager telephonyManager,
+                                         String request, CharSequence response) {};
+
+       /**
+        * Called when a USSD request has failed to complete.
+        *
+        * @param telephonyManager the TelephonyManager the callback is registered to.
+        * @param request the USSD request sent to the mobile network.
+        * @param failureCode failure code indicating why the request failed.  Will be either
+        *        {@link TelephonyManager#USSD_RETURN_FAILURE} or
+        *        {@link TelephonyManager#USSD_ERROR_SERVICE_UNAVAIL}.
+        **/
+       public void onReceiveUssdResponseFailed(final TelephonyManager telephonyManager,
+                                               String request, int failureCode) {};
+    }
+
+    /**
+     * Sends an Unstructured Supplementary Service Data (USSD) request to the mobile network and
+     * informs the caller of the response via the supplied {@code callback}.
+     * <p>Carriers define USSD codes which can be sent by the user to request information such as
+     * the user's current data balance or minutes balance.
+     * <p>Requires permission:
+     * {@link android.Manifest.permission#CALL_PHONE}
+     * @param ussdRequest the USSD command to be executed.
+     * @param callback called by the framework to inform the caller of the result of executing the
+     *                 USSD request (see {@link UssdResponseCallback}).
+     * @param handler the {@link Handler} to run the request on.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void sendUssdRequest(String ussdRequest,
+                                final UssdResponseCallback callback, Handler handler) {
+        checkNotNull(callback, "UssdResponseCallback cannot be null.");
+        final TelephonyManager telephonyManager = this;
+
+        ResultReceiver wrappedCallback = new ResultReceiver(handler) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle ussdResponse) {
+                Rlog.d(TAG, "USSD:" + resultCode);
+                checkNotNull(ussdResponse, "ussdResponse cannot be null.");
+                UssdResponse response = ussdResponse.getParcelable(USSD_RESPONSE, android.telephony.UssdResponse.class);
+
+                if (resultCode == USSD_RETURN_SUCCESS) {
+                    callback.onReceiveUssdResponse(telephonyManager, response.getUssdRequest(),
+                            response.getReturnMessage());
+                } else {
+                    callback.onReceiveUssdResponseFailed(telephonyManager,
+                            response.getUssdRequest(), resultCode);
+                }
+            }
+        };
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.handleUssdRequest(getSubId(), ussdRequest, wrappedCallback);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#sendUSSDCode", e);
+            UssdResponse response = new UssdResponse(ussdRequest, "");
+            Bundle returnData = new Bundle();
+            returnData.putParcelable(USSD_RESPONSE, response);
+            wrappedCallback.send(USSD_ERROR_SERVICE_UNAVAIL, returnData);
+        }
+    }
+
+    /**
+     * Whether the device is currently on a technology (e.g. UMTS or LTE) which can support
+     * voice and data simultaneously. This can change based on location or network condition.
+     *
+     * @return {@code true} if simultaneous voice and data supported, and {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean isConcurrentVoiceAndDataSupported() {
+        try {
+            ITelephony telephony = getITelephony();
+            return (telephony == null ? false : telephony.isConcurrentVoiceAndDataAllowed(
+                    getSubId()));
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isConcurrentVoiceAndDataAllowed", e);
+        }
+        return false;
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public boolean handlePinMmi(String dialString) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.handlePinMmi(dialString);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#handlePinMmi", e);
+        }
+        return false;
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public boolean handlePinMmiForSubscriber(int subId, String dialString) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.handlePinMmiForSubscriber(subId, dialString);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#handlePinMmi", e);
+        }
+        return false;
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void toggleRadioOnOff() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.toggleRadioOnOff();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#toggleRadioOnOff", e);
+        }
+    }
+
+    /**
+     * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and
+     * {@link clearRadioPowerOffForReason}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean setRadio(boolean turnOn) {
+        boolean result = true;
+        try {
+            if (turnOn) {
+                clearRadioPowerOffForReason(RADIO_POWER_REASON_USER);
+            } else {
+                requestRadioPowerOffForReason(RADIO_POWER_REASON_USER);
+            }
+        } catch (Exception e) {
+            String calledFunction =
+                    turnOn ? "clearRadioPowerOffForReason" : "requestRadioPowerOffForReason";
+            Log.e(TAG, "Error calling " + calledFunction, e);
+            result = false;
+        }
+        return result;
+    }
+
+    /**
+     * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and
+     * {@link clearRadioPowerOffForReason}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean setRadioPower(boolean turnOn) {
+        boolean result = true;
+        try {
+            if (turnOn) {
+                clearRadioPowerOffForReason(RADIO_POWER_REASON_USER);
+            } else {
+                requestRadioPowerOffForReason(RADIO_POWER_REASON_USER);
+            }
+        } catch (Exception e) {
+            String calledFunction =
+                    turnOn ? "clearRadioPowerOffForReason" : "requestRadioPowerOffForReason";
+            Log.e(TAG, "Error calling " + calledFunction, e);
+            result = false;
+        }
+        return result;
+    }
+
+    /**
+     * Vote on powering off the radio for a reason. The radio will be turned on only when there is
+     * no reason to power it off. When any of the voters want to power it off, it will be turned
+     * off. In case of emergency, the radio will be turned on even if there are some reasons for
+     * powering it off, and these radio off votes will be cleared.
+     * <p>
+     * Each API call is for one reason. However, an app can call the API multiple times for multiple
+     * reasons. Multiple apps can vote for the same reason but the vote of one app does not affect
+     * the vote of another app.
+     * <p>
+     * Each app is responsible for its vote. A powering-off vote for a reason of an app will be
+     * maintained until it is cleared by calling {@link #clearRadioPowerOffForReason(int)} for that
+     * reason by the app, or an emergency call is made, or the device is rebooted. When an app
+     * comes backup from a crash, it needs to make sure if its vote is as expected. An app can use
+     * the API {@link #getRadioPowerOffReasons()} to check its votes. Votes won't be removed when
+     * an app crashes.
+     * <p>
+     * User setting for power state is persistent across device reboots. This applies to all users,
+     * callers must be careful to update the off reasons when the current user changes.
+     *
+     * @param reason The reason for powering off radio.
+     * @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission.
+     * @throws IllegalStateException if the Telephony service is not currently available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void requestRadioPowerOffForReason(@RadioPowerReason int reason) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (!telephony.requestRadioPowerOffForReason(getSubId(), reason)) {
+                    throw new IllegalStateException("Telephony service is not available.");
+                }
+            } else {
+                throw new IllegalStateException("Telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#requestRadioPowerOffForReason", e);
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Remove the vote on powering off the radio for a reason, as requested by
+     * {@link requestRadioPowerOffForReason}.
+     *
+     * @param reason The reason for powering off radio.
+     * @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission.
+     * @throws IllegalStateException if the Telephony service is not currently available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void clearRadioPowerOffForReason(@RadioPowerReason int reason) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (!telephony.clearRadioPowerOffForReason(getSubId(), reason)) {
+                    throw new IllegalStateException("Telephony service is not available.");
+                }
+            } else {
+                throw new IllegalStateException("Telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#clearRadioPowerOffForReason", e);
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Get reasons for powering off radio of the calling app, as requested by
+     * {@link #requestRadioPowerOffForReason(int)}.
+     *
+     * @return Set of reasons for powering off radio of the calling app.
+     * @throws SecurityException if the caller does not have READ_PRIVILEGED_PHONE_STATE permission.
+     * @throws IllegalStateException if the Telephony service is not currently available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    @NonNull
+    public Set<Integer> getRadioPowerOffReasons() {
+        Set<Integer> result = new HashSet<>();
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                result.addAll(telephony.getRadioPowerOffReasons(getSubId(),
+                        mContext.getOpPackageName(), mContext.getAttributionTag()));
+            } else {
+                throw new IllegalStateException("Telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getRadioPowerOffReasons", e);
+            e.rethrowAsRuntimeException();
+        }
+        return result;
+    }
+
+    /**
+     * Shut down all the live radios over all the slot indexes.
+     *
+     * <p>To know when the radio has completed powering off, use
+     * {@link TelephonyCallback.ServiceStateListener}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void shutdownAllRadios() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.shutdownMobileRadios();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#shutdownAllRadios", e);
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Check if any radio is on over all the slot indexes.
+     *
+     * @return {@code true} if any radio is on over any slot index.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean isAnyRadioPoweredOn() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.needMobileRadioShutdown();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isAnyRadioPoweredOn", e);
+            e.rethrowAsRuntimeException();
+        }
+        return false;
+    }
+
+    /**
+     * Radio explicitly powered off (e.g, airplane mode).
+     * @hide
+     */
+    @SystemApi
+    public static final int RADIO_POWER_OFF = 0;
+
+    /**
+     * Radio power is on.
+     * @hide
+     */
+    @SystemApi
+    public static final int RADIO_POWER_ON = 1;
+
+    /**
+     * Radio power unavailable (eg, modem resetting or not booted).
+     * @hide
+     */
+    @SystemApi
+    public static final int RADIO_POWER_UNAVAILABLE = 2;
+
+    /**
+     * @return current modem radio state.
+     *
+     * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
+     * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @RadioPowerState int getRadioPowerState() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getRadioPowerState(getSlotIndex(), mContext.getOpPackageName(),
+                        mContext.getAttributionTag());
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return RADIO_POWER_UNAVAILABLE;
+    }
+
+    /**
+     * This method should not be used due to privacy and stability concerns.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void updateServiceLocation() {
+        Log.e(TAG, "Do not call TelephonyManager#updateServiceLocation()");
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean enableDataConnectivity() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.enableDataConnectivity(getOpPackageName());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#enableDataConnectivity", e);
+        }
+        return false;
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean disableDataConnectivity() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.disableDataConnectivity(getOpPackageName());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#disableDataConnectivity", e);
+        }
+        return false;
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean isDataConnectivityPossible() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.isDataConnectivityPossible(getSubId(SubscriptionManager
+                        .getActiveDataSubscriptionId()));
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isDataAllowed", e);
+        }
+        return false;
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean needsOtaServiceProvisioning() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.needsOtaServiceProvisioning();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#needsOtaServiceProvisioning", e);
+        }
+        return false;
+    }
+
+    /**
+     * Get the mobile provisioning url that is used to launch a browser to allow users to manage
+     * their mobile plan.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}.
+     *
+     * TODO: The legacy design only supports single sim design. Ideally, this should support
+     * multi-sim design in current world.
+     *
+     * {@hide}
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @Nullable String getMobileProvisioningUrl() {
+        try {
+            final ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getMobileProvisioningUrl();
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Telephony#getMobileProvisioningUrl RemoteException" + ex);
+        }
+        return null;
+    }
+
+    /**
+     * Turns mobile data on or off.
+     * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+     * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param enable Whether to enable mobile data.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @deprecated use setDataEnabledForReason with reason DATA_ENABLED_REASON_USER instead.
+     *
+     */
+    @Deprecated
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public void setDataEnabled(boolean enable) {
+        setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     * @deprecated use {@link #setDataEnabledForReason(int, boolean)} instead.
+    */
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public void setDataEnabled(int subId, boolean enable) {
+        try {
+            setDataEnabledForReason(subId, DATA_ENABLED_REASON_USER, enable);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Error calling setDataEnabledForReason e:" + e);
+        }
+    }
+
+    /**
+     * @deprecated use {@link #isDataEnabled()} instead.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean getDataEnabled() {
+        return isDataEnabled();
+    }
+
+    /**
+     * Returns whether mobile data is enabled or not per user setting. There are other factors
+     * that could disable mobile data, but they are not considered here.
+     *
+     * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+     * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p>Requires one of the following permissions:
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE},
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE}, or
+     * {@link android.Manifest.permission#READ_BASIC_PHONE_STATE
+     * READ_BASIC_PHONE_STATE} or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * <p>Note that this does not take into account any data restrictions that may be present on the
+     * calling app. Such restrictions may be inspected with
+     * {@link ConnectivityManager#getRestrictBackgroundStatus}.
+     *
+     * @return true if mobile data is enabled.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     */
+    @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_BASIC_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean isDataEnabled() {
+        try {
+            return isDataEnabledForReason(DATA_ENABLED_REASON_USER);
+        } catch (IllegalStateException ise) {
+            // TODO(b/176163590): Remove this catch once TelephonyManager is booting safely.
+            Log.e(TAG, "Error calling #isDataEnabled, returning default (false).", ise);
+            return false;
+        }
+    }
+
+    /**
+     * Returns whether mobile data roaming is enabled on the subscription.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p>Requires one of the following permissions:
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE},
+     * {@link android.Manifest.permission#READ_PHONE_STATE} or
+     * {@link android.Manifest.permission#READ_BASIC_PHONE_STATE
+     * READ_BASIC_PHONE_STATE} or that the calling app
+     * has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return {@code true} if the data roaming is enabled on the subscription, otherwise return
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * {@code false}.
+     */
+    @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_BASIC_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean isDataRoamingEnabled() {
+        boolean isDataRoamingEnabled = false;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                isDataRoamingEnabled = telephony.isDataRoamingEnabled(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isDataRoamingEnabled", e);
+        }
+        return isDataRoamingEnabled;
+    }
+
+    /**
+     * Gets the roaming mode for CDMA phone.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * @return the CDMA roaming mode.
+     * @throws SecurityException if the caller does not have the permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     *
+     * @see #CDMA_ROAMING_MODE_RADIO_DEFAULT
+     * @see #CDMA_ROAMING_MODE_HOME
+     * @see #CDMA_ROAMING_MODE_AFFILIATED
+     * @see #CDMA_ROAMING_MODE_ANY
+     *
+     * <p>Requires permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    public @CdmaRoamingMode int getCdmaRoamingMode() {
+        int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                mode = telephony.getCdmaRoamingMode(getSubId());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Error calling ITelephony#getCdmaRoamingMode", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return mode;
+    }
+
+    /**
+     * Sets the roaming mode for CDMA phone to the given mode {@code mode}. If the phone is not
+     * CDMA capable, this method throws an IllegalStateException.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * @param mode CDMA roaming mode.
+     * @throws SecurityException if the caller does not have the permission.
+     * @throws IllegalStateException if the Telephony process or radio is not currently available,
+     *         the device is not CDMA capable, or the request fails.
+     *
+     * @see #CDMA_ROAMING_MODE_RADIO_DEFAULT
+     * @see #CDMA_ROAMING_MODE_HOME
+     * @see #CDMA_ROAMING_MODE_AFFILIATED
+     * @see #CDMA_ROAMING_MODE_ANY
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    public void setCdmaRoamingMode(@CdmaRoamingMode int mode) {
+        if (getPhoneType() != PHONE_TYPE_CDMA) {
+            throw new IllegalStateException("Phone does not support CDMA.");
+        }
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                boolean result = telephony.setCdmaRoamingMode(getSubId(), mode);
+                if (!result) throw new IllegalStateException("radio is unavailable.");
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Error calling ITelephony#setCdmaRoamingMode", ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    @IntDef(prefix = { "CDMA_SUBSCRIPTION_" }, value = {
+            CDMA_SUBSCRIPTION_UNKNOWN,
+            CDMA_SUBSCRIPTION_RUIM_SIM,
+            CDMA_SUBSCRIPTION_NV
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CdmaSubscription{}
+
+    /**
+     * Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source.
+     * @hide
+     */
+    @SystemApi
+    public static final int CDMA_SUBSCRIPTION_UNKNOWN  = -1;
+
+    /**
+     * Used for CDMA subscription mode: RUIM/SIM (default)
+     * @hide
+     */
+    @SystemApi
+    public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0;
+
+    /**
+     * Used for CDMA subscription mode: NV -> non-volatile memory
+     * @hide
+     */
+    @SystemApi
+    public static final int CDMA_SUBSCRIPTION_NV       = 1;
+
+    /**
+     * Gets the subscription mode for CDMA phone.
+     *
+     * @return the CDMA subscription mode.
+     * @throws SecurityException if the caller does not have the permission.
+     * @throws IllegalStateException if the Telephony process or radio is not currently available.
+     *
+     * @see #CDMA_SUBSCRIPTION_UNKNOWN
+     * @see #CDMA_SUBSCRIPTION_RUIM_SIM
+     * @see #CDMA_SUBSCRIPTION_NV
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    public @CdmaSubscription int getCdmaSubscriptionMode() {
+        int mode = CDMA_SUBSCRIPTION_RUIM_SIM;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                mode = telephony.getCdmaSubscriptionMode(getSubId());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Error calling ITelephony#getCdmaSubscriptionMode", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return mode;
+    }
+
+    /**
+     * Sets the subscription mode for CDMA phone to the given mode {@code mode}. If the phone is not
+     * CDMA capable, this method throws an IllegalStateException.
+     *
+     * @param mode CDMA subscription mode.
+     * @throws SecurityException if the caller does not have the permission.
+     * @throws IllegalStateException if the Telephony process or radio is not currently available,
+     *         the device is not CDMA capable, or the request fails.
+     *
+     * @see #CDMA_SUBSCRIPTION_UNKNOWN
+     * @see #CDMA_SUBSCRIPTION_RUIM_SIM
+     * @see #CDMA_SUBSCRIPTION_NV
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    public void setCdmaSubscriptionMode(@CdmaSubscription int mode) {
+        if (getPhoneType() != PHONE_TYPE_CDMA) {
+            throw new IllegalStateException("Phone does not support CDMA.");
+        }
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                boolean result = telephony.setCdmaSubscriptionMode(getSubId(), mode);
+                if (!result) throw new IllegalStateException("radio is unavailable.");
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Error calling ITelephony#setCdmaSubscriptionMode", ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Enables/Disables the data roaming on the subscription.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p> Requires permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param isEnabled {@code true} to enable mobile data roaming, otherwise disable it.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public void setDataRoamingEnabled(boolean isEnabled) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setDataRoamingEnabled(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), isEnabled);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setDataRoamingEnabled", e);
+        }
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     *
+     * @deprecated use {@link #isDataEnabled()} instead.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean getDataEnabled(int subId) {
+        try {
+            return isDataEnabledForReason(subId, DATA_ENABLED_REASON_USER);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Error calling isDataEnabledForReason e:" + e);
+        }
+        return false;
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @deprecated Use {@link android.telephony.ims.ImsMmTelManager#setVtSettingEnabled(boolean)}
+     * instead.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
+    public void enableVideoCalling(boolean enable) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.enableVideoCalling(enable);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#enableVideoCalling", e);
+        }
+    }
+
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @deprecated Use {@link ImsMmTelManager#isVtSettingEnabled()} instead to check if the user
+     * has enabled the Video Calling setting, {@link ImsMmTelManager#isAvailable(int, int)} to
+     * determine if video calling is available, or {@link ImsMmTelManager#isCapable(int, int)} to
+     * determine if video calling is capable.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
+    public boolean isVideoCallingEnabled() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                return telephony.isVideoCallingEnabled(getOpPackageName(), getAttributionTag());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isVideoCallingEnabled", e);
+        }
+        return false;
+    }
+
+    /**
+     * Whether the device supports configuring the DTMF tone length.
+     *
+     * @return {@code true} if the DTMF tone length can be changed, and {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public boolean canChangeDtmfToneLength() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.canChangeDtmfToneLength(mSubId, getOpPackageName(),
+                        getAttributionTag());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#canChangeDtmfToneLength", e);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Permission error calling ITelephony#canChangeDtmfToneLength", e);
+        }
+        return false;
+    }
+
+    /**
+     * Whether the device is a world phone.
+     *
+     * @return {@code true} if the device is a world phone, and {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
+    public boolean isWorldPhone() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isWorldPhone(mSubId, getOpPackageName(), getAttributionTag());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isWorldPhone", e);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Permission error calling ITelephony#isWorldPhone", e);
+        }
+        return false;
+    }
+
+    /**
+     * @deprecated Use {@link TelecomManager#isTtySupported()} instead
+     * Whether the phone supports TTY mode.
+     *
+     * @return {@code true} if the device supports TTY mode, and {@code false} otherwise.
+     *
+     */
+    @Deprecated
+    public boolean isTtyModeSupported() {
+        try {
+            TelecomManager telecomManager = null;
+            if (mContext != null) {
+                telecomManager = mContext.getSystemService(TelecomManager.class);
+            }
+            if (telecomManager != null) {
+                return telecomManager.isTtySupported();
+            }
+        } catch (SecurityException e) {
+            Log.e(TAG, "Permission error calling TelecomManager#isTtySupported", e);
+        }
+        return false;
+    }
+
+    /**
+     * Determines whether the device currently supports RTT (Real-time text). Based both on carrier
+     * support for the feature and device firmware support.
+     *
+     * @return {@code true} if the device and carrier both support RTT, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
+    public boolean isRttSupported() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isRttSupported(mSubId);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isRttSupported", e);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Permission error calling ITelephony#isWorldPhone", e);
+        }
+        return false;
+    }
+    /**
+     * Whether the phone supports hearing aid compatibility.
+     *
+     * @return {@code true} if the device supports hearing aid compatibility, and {@code false}
+     * otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public boolean isHearingAidCompatibilitySupported() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isHearingAidCompatibilitySupported();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isHearingAidCompatibilitySupported", e);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Permission error calling ITelephony#isHearingAidCompatibilitySupported", e);
+        }
+        return false;
+    }
+
+    /**
+     * Returns the IMS Registration Status for a particular Subscription ID.
+     *
+     * @param subId Subscription ID
+     * @return true if IMS status is registered, false if the IMS status is not registered or a
+     * RemoteException occurred.
+     * Use {@link ImsMmTelManager.RegistrationCallback} instead.
+     * @hide
+     */
+    public boolean isImsRegistered(int subId) {
+        try {
+            return getITelephony().isImsRegistered(subId);
+        } catch (RemoteException | NullPointerException ex) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the IMS Registration Status for a particular Subscription ID, which is determined
+     * when the TelephonyManager is created using {@link #createForSubscriptionId(int)}. If an
+     * invalid subscription ID is used during creation, will the default subscription ID will be
+     * used.
+     *
+     * @return true if IMS status is registered, false if the IMS status is not registered or a
+     * RemoteException occurred.
+     * @see SubscriptionManager#getDefaultSubscriptionId()
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public boolean isImsRegistered() {
+       try {
+           return getITelephony().isImsRegistered(getSubId());
+       } catch (RemoteException | NullPointerException ex) {
+           return false;
+       }
+    }
+
+    /**
+     * The current status of Voice over LTE for the subscription associated with this instance when
+     * it was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was
+     * used during creation, the default subscription ID will be used.
+     * @return true if Voice over LTE is available or false if it is unavailable or unknown.
+     * @see SubscriptionManager#getDefaultSubscriptionId()
+     * <p>
+     * Use {@link ImsMmTelManager#isAvailable(int, int)} instead.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean isVolteAvailable() {
+        try {
+            return getITelephony().isAvailable(getSubId(),
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        } catch (RemoteException | NullPointerException ex) {
+            return false;
+        }
+    }
+
+    /**
+     * The availability of Video Telephony (VT) for the subscription ID specified when this instance
+     * was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was
+     * used during creation, the default subscription ID will be used. To query the
+     * underlying technology that VT is available on, use {@link #getImsRegTechnologyForMmTel}.
+     * @return true if VT is available, or false if it is unavailable or unknown.
+     * Use {@link ImsMmTelManager#isAvailable(int, int)} instead.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean isVideoTelephonyAvailable() {
+        try {
+            return getITelephony().isVideoTelephonyAvailable(getSubId());
+        } catch (RemoteException | NullPointerException ex) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the Status of Wi-Fi calling (Voice over WiFi) for the subscription ID specified.
+     * @param subId the subscription ID.
+     * @return true if VoWiFi is available, or false if it is unavailable or unknown.
+     * Use {@link ImsMmTelManager#isAvailable(int, int)} instead.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean isWifiCallingAvailable() {
+       try {
+           return getITelephony().isWifiCallingAvailable(getSubId());
+       } catch (RemoteException | NullPointerException ex) {
+           return false;
+       }
+   }
+
+    /**
+     * The technology that IMS is registered for for the MMTEL feature.
+     * @param subId subscription ID to get IMS registration technology for.
+     * @return The IMS registration technology that IMS is registered to for the MMTEL feature.
+     * Valid return results are:
+     *  - {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} for LTE registration,
+     *  - {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} for IWLAN registration, or
+     *  - {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} for registration over
+     *  other sim's internet, or
+     *  - {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE} if we are not registered or the
+     *  result is unavailable.
+     *  Use {@link ImsMmTelManager.RegistrationCallback} instead.
+     *  @hide
+     */
+    public @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegTechnologyForMmTel() {
+        try {
+            return getITelephony().getImsRegTechnologyForMmTel(getSubId());
+        } catch (RemoteException ex) {
+            return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+        }
+    }
+
+   /**
+    * Set TelephonyProperties.icc_operator_numeric for the given phone.
+    *
+    * @hide
+    */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void setSimOperatorNumericForPhone(int phoneId, String numeric) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            List<String> newList = updateTelephonyProperty(
+                    TelephonyProperties.icc_operator_numeric(), phoneId, numeric);
+            TelephonyProperties.icc_operator_numeric(newList);
+        }
+    }
+
+    /**
+     * Set TelephonyProperties.icc_operator_alpha for the given phone.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setSimOperatorNameForPhone(int phoneId, String name) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            List<String> newList = updateTelephonyProperty(
+                    TelephonyProperties.icc_operator_alpha(), phoneId, name);
+            TelephonyProperties.icc_operator_alpha(newList);
+        }
+    }
+
+   /**
+    * Set TelephonyProperties.icc_operator_iso_country for the default phone.
+    *
+    * @hide
+    */
+    public void setSimCountryIso(String iso) {
+        int phoneId = getPhoneId();
+        setSimCountryIsoForPhone(phoneId, iso);
+    }
+
+   /**
+    * Set TelephonyProperties.icc_operator_iso_country for the given phone.
+    *
+    * @hide
+    */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setSimCountryIsoForPhone(int phoneId, String iso) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            List<String> newList = updateTelephonyProperty(
+                    TelephonyProperties.icc_operator_iso_country(), phoneId, iso);
+            TelephonyProperties.icc_operator_iso_country(newList);
+        }
+    }
+
+    /**
+     * Set TelephonyProperties.sim_state for the default phone.
+     *
+     * @hide
+     */
+    public void setSimState(String state) {
+        int phoneId = getPhoneId();
+        setSimStateForPhone(phoneId, state);
+    }
+
+    /**
+     * Set TelephonyProperties.sim_state for the given phone.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setSimStateForPhone(int phoneId, String state) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            List<String> newList = updateTelephonyProperty(
+                    TelephonyProperties.sim_state(), phoneId, state);
+            TelephonyProperties.sim_state(newList);
+        }
+    }
+
+    /**
+     * Powers down the SIM. SIM must be up prior.
+     * @hide
+     */
+    public static final int CARD_POWER_DOWN = 0;
+
+    /**
+     * Powers up the SIM normally. SIM must be down prior.
+     * @hide
+     */
+    public static final int CARD_POWER_UP = 1;
+
+    /**
+     * Powers up the SIM in PASS_THROUGH mode. SIM must be down prior.
+     * When SIM is powered up in PASS_THOUGH mode, the modem does not send
+     * any command to it (for example SELECT of MF, or TERMINAL CAPABILITY),
+     * and the SIM card is controlled completely by Telephony sending APDUs
+     * directly. The SIM card state will be RIL_CARDSTATE_PRESENT and the
+     * number of card apps will be 0.
+     * No new error code is generated. Emergency calls are supported in the
+     * same way as if the SIM card is absent.
+     * The PASS_THROUGH mode is valid only for the specific card session where it
+     * is activated, and normal behavior occurs at the next SIM initialization,
+     * unless PASS_THROUGH mode is requested again. Hence, the last power-up mode
+     * is NOT persistent across boots. On reboot, SIM will power up normally.
+     * @hide
+     */
+    public static final int CARD_POWER_UP_PASS_THROUGH = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CARD_POWER"},
+            value = {
+                    CARD_POWER_DOWN,
+                    CARD_POWER_UP,
+                    CARD_POWER_UP_PASS_THROUGH,
+            })
+    public @interface SimPowerState {}
+
+    /**
+     * Set SIM card power state.
+     *
+     * @param state  State of SIM (power down, power up, pass through)
+     * @see #CARD_POWER_DOWN
+     * @see #CARD_POWER_UP
+     * @see #CARD_POWER_UP_PASS_THROUGH
+     * Callers should monitor for {@link TelephonyIntents#ACTION_SIM_STATE_CHANGED}
+     * broadcasts to determine success or failure and timeout if needed.
+     *
+     * @deprecated prefer {@link setSimPowerState(int, Executor, Consumer<Integer>)}.
+     * There is no guarantee that SIM power changes will trigger ACTION_SIM_STATE_CHANGED on new
+     * devices.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * {@hide}
+     **/
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public void setSimPowerState(int state) {
+        setSimPowerStateForSlot(getSlotIndex(), state);
+    }
+
+    /**
+     * Set SIM card power state.
+     *
+     * @param slotIndex SIM slot id
+     * @param state  State of SIM (power down, power up, pass through)
+     * @see #CARD_POWER_DOWN
+     * @see #CARD_POWER_UP
+     * @see #CARD_POWER_UP_PASS_THROUGH
+     * Callers should monitor for {@link TelephonyIntents#ACTION_SIM_STATE_CHANGED}
+     * broadcasts to determine success or failure and timeout if needed.
+     *
+     * @deprecated prefer {@link setSimPowerStateForSlot(int, int, Executor, Consumer<Integer>)}.
+     * changes will trigger ACTION_SIM_STATE_CHANGED on new devices.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * {@hide}
+     **/
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public void setSimPowerStateForSlot(int slotIndex, int state) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setSimPowerStateForSlot(slotIndex, state);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e);
+        } catch (SecurityException e) {
+            Log.e(TAG, "Permission error calling ITelephony#setSimPowerStateForSlot", e);
+        }
+    }
+
+    /**
+     * Set SIM card power state.
+     *
+     * @param state  State of SIM (power down, power up, pass through)
+     * @see #CARD_POWER_DOWN
+     * @see #CARD_POWER_UP
+     * @see #CARD_POWER_UP_PASS_THROUGH
+     * @param executor The executor of where the callback will execute.
+     * @param callback Callback will be triggered once it succeeds or failed.
+     * @see #SET_SIM_POWER_STATE_SUCCESS
+     * @see #SET_SIM_POWER_STATE_ALREADY_IN_STATE
+     * @see #SET_SIM_POWER_STATE_MODEM_ERROR
+     * @see #SET_SIM_POWER_STATE_SIM_ERROR
+     * @see #SET_SIM_POWER_STATE_NOT_SUPPORTED
+     * @throws IllegalArgumentException if requested SIM state is invalid
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * {@hide}
+     **/
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public void setSimPowerState(@SimPowerState int state, @NonNull Executor executor,
+            @NonNull @SetSimPowerStateResult Consumer<Integer> callback) {
+        setSimPowerStateForSlot(getSlotIndex(), state, executor, callback);
+    }
+
+    /**
+     * Set SIM card power state.
+     *
+     * @param slotIndex SIM slot id
+     * @param state  State of SIM (power down, power up, pass through)
+     * @see #CARD_POWER_DOWN
+     * @see #CARD_POWER_UP
+     * @see #CARD_POWER_UP_PASS_THROUGH
+     * @param executor The executor of where the callback will execute.
+     * @param callback Callback will be triggered once it succeeds or failed.
+     * @see #SET_SIM_POWER_STATE_SUCCESS
+     * @see #SET_SIM_POWER_STATE_ALREADY_IN_STATE
+     * @see #SET_SIM_POWER_STATE_MODEM_ERROR
+     * @see #SET_SIM_POWER_STATE_SIM_ERROR
+     * @see #SET_SIM_POWER_STATE_NOT_SUPPORTED
+     * @throws IllegalArgumentException if requested SIM state is invalid
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * {@hide}
+     **/
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public void setSimPowerStateForSlot(int slotIndex, @SimPowerState int state,
+            @NonNull Executor executor,
+            @NonNull @SetSimPowerStateResult Consumer<Integer> callback) {
+        if (state != CARD_POWER_DOWN && state != CARD_POWER_UP
+                && state != CARD_POWER_UP_PASS_THROUGH) {
+            throw new IllegalArgumentException("requested SIM state is invalid");
+        }
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) throw new IllegalStateException("Telephony is null.");
+
+            IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+                @Override
+                public void accept(int result) {
+                    executor.execute(() ->
+                            Binder.withCleanCallingIdentity(() -> callback.accept(result)));
+                }
+            };
+            if (telephony == null) {
+                if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) {
+                    throw new IllegalStateException("Telephony is null");
+                } else {
+                    return;
+                }
+            }
+            telephony.setSimPowerStateForSlotWithCallback(slotIndex, state, internalCallback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e);
+            runOnBackgroundThread(() -> executor.execute(
+                    () -> callback.accept(SET_SIM_POWER_STATE_MODEM_ERROR)));
+        } catch (SecurityException e) {
+            Log.e(TAG, "Permission error calling ITelephony#setSimPowerStateForSlot",
+                    e);
+        }
+    }
+
+    /**
+     * Set baseband version by phone id.
+     *
+     * @param phoneId for which baseband version is set
+     * @param version baseband version
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setBasebandVersionForPhone(int phoneId, String version) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            List<String> newList = updateTelephonyProperty(
+                    TelephonyProperties.baseband_version(), phoneId, version);
+            TelephonyProperties.baseband_version(newList);
+        }
+    }
+
+    /**
+     * Get baseband version for the default phone.
+     *
+     * @return baseband version.
+     * @hide
+     */
+    public String getBasebandVersion() {
+        int phoneId = getPhoneId();
+        return getBasebandVersionForPhone(phoneId);
+    }
+
+    /**
+     * Get baseband version by phone id.
+     *
+     * @return baseband version.
+     * @hide
+     */
+    public String getBasebandVersionForPhone(int phoneId) {
+        return getTelephonyProperty(phoneId, TelephonyProperties.baseband_version(), "");
+    }
+
+    /**
+     * Set phone type by phone id.
+     *
+     * @param phoneId for which phone type is set
+     * @param type phone type
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setPhoneType(int phoneId, int type) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            List<Integer> newList = updateTelephonyProperty(
+                    TelephonyProperties.current_active_phone(), phoneId, type);
+            TelephonyProperties.current_active_phone(newList);
+        }
+    }
+
+    /**
+     * Get OTASP number schema by phone id.
+     *
+     * @param phoneId for which OTA SP number schema is get
+     * @param defaultValue default value
+     * @return OTA SP number schema
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public String getOtaSpNumberSchemaForPhone(int phoneId, String defaultValue) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            return getTelephonyProperty(
+                    phoneId, TelephonyProperties.otasp_num_schema(), defaultValue);
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Get SMS receive capable from system property by phone id.
+     *
+     * @param phoneId for which SMS receive capable is get
+     * @param defaultValue default value
+     * @return SMS receive capable
+     *
+     * @hide
+     */
+    public boolean getSmsReceiveCapableForPhone(int phoneId, boolean defaultValue) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            return getTelephonyProperty(phoneId, TelephonyProperties.sms_receive(), defaultValue);
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Get SMS send capable from system property by phone id.
+     *
+     * @param phoneId for which SMS send capable is get
+     * @param defaultValue default value
+     * @return SMS send capable
+     *
+     * @hide
+     */
+    public boolean getSmsSendCapableForPhone(int phoneId, boolean defaultValue) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            return getTelephonyProperty(phoneId, TelephonyProperties.sms_send(), defaultValue);
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Get the component name of the default app to direct respond-via-message intent for the
+     * user associated with this subscription, update the cache if there is no respond-via-message
+     * application currently configured for this user.
+     * @return component name of the app and class to direct Respond Via Message intent to, or
+     * {@code null} if the functionality is not supported.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public @Nullable ComponentName getAndUpdateDefaultRespondViaMessageApplication() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getDefaultRespondViaMessageApplication(getSubId(), true);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in getAndUpdateDefaultRespondViaMessageApplication: " + e);
+        }
+        return null;
+    }
+
+    /**
+     * Get the component name of the default app to direct respond-via-message intent for the
+     * user associated with this subscription.
+     * @return component name of the app and class to direct Respond Via Message intent to, or
+     * {@code null} if the functionality is not supported.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public @Nullable ComponentName getDefaultRespondViaMessageApplication() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getDefaultRespondViaMessageApplication(getSubId(), false);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in getDefaultRespondViaMessageApplication: " + e);
+        }
+        return null;
+    }
+
+    /**
+     * Set the alphabetic name of current registered operator.
+     * @param phoneId which phone you want to set
+     * @param name the alphabetic name of current registered operator.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void setNetworkOperatorNameForPhone(int phoneId, String name) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            List<String> newList = updateTelephonyProperty(
+                    TelephonyProperties.operator_alpha(), phoneId, name);
+            try {
+                TelephonyProperties.operator_alpha(newList);
+            } catch (IllegalArgumentException e) { //property value is longer than the byte limit
+                Log.e(TAG, "setNetworkOperatorNameForPhone: ", e);
+
+                int numberOfEntries = newList.size();
+                int maxOperatorLength = //save 1 byte for joiner " , "
+                        (SystemProperties.PROP_VALUE_MAX - numberOfEntries) / numberOfEntries;
+
+                //examine and truncate every operator and retry
+                for (int i = 0; i < newList.size(); i++) {
+                    if (newList.get(i) != null) {
+                        newList.set(i, TextUtils
+                                .truncateStringForUtf8Storage(newList.get(i), maxOperatorLength));
+                    }
+                }
+                TelephonyProperties.operator_alpha(newList);
+                Log.e(TAG, "successfully truncated operator_alpha: " + newList);
+            }
+        }
+    }
+
+    /**
+     * Set the numeric name (MCC+MNC) of current registered operator.
+     * @param phoneId for which phone type is set
+     * @param operator the numeric name (MCC+MNC) of current registered operator
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void setNetworkOperatorNumericForPhone(int phoneId, String numeric) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            List<String> newList = updateTelephonyProperty(
+                    TelephonyProperties.operator_numeric(), phoneId, numeric);
+            TelephonyProperties.operator_numeric(newList);
+        }
+    }
+
+    /**
+     * Set roaming state of the current network, for GSM purposes.
+     * @param phoneId which phone you want to set
+     * @param isRoaming is network in romaing state or not
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void setNetworkRoamingForPhone(int phoneId, boolean isRoaming) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            List<Boolean> newList = updateTelephonyProperty(
+                    TelephonyProperties.operator_is_roaming(), phoneId, isRoaming);
+            TelephonyProperties.operator_is_roaming(newList);
+        }
+    }
+
+    /**
+     * Set the network type currently in use on the device for data transmission.
+     *
+     * If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * phoneId associated with the given subId. Otherwise, applies to the phoneId associated with
+     * {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * @param type the network type currently in use on the device for data transmission
+     * @hide
+     */
+    public void setDataNetworkType(int type) {
+        int phoneId = getPhoneId(SubscriptionManager.getDefaultDataSubscriptionId());
+        setDataNetworkTypeForPhone(phoneId, type);
+    }
+
+    /**
+     * Set the network type currently in use on the device for data transmission.
+     * @param phoneId which phone you want to set
+     * @param type the network type currently in use on the device for data transmission
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void setDataNetworkTypeForPhone(int phoneId, int type) {
+        if (SubscriptionManager.isValidPhoneId(phoneId)) {
+            List<String> newList = updateTelephonyProperty(
+                    TelephonyProperties.data_network_type(), phoneId,
+                    ServiceState.rilRadioTechnologyToString(type));
+            TelephonyProperties.data_network_type(newList);
+        }
+    }
+
+    /**
+     * Returns the subscription ID for the given phone account.
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public int getSubIdForPhoneAccount(@Nullable PhoneAccount phoneAccount) {
+        int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        if (phoneAccount != null
+                && phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+            retval = getSubscriptionId(phoneAccount.getAccountHandle());
+        }
+        return retval;
+    }
+
+    /**
+     * Determines the {@link PhoneAccountHandle} associated with this TelephonyManager.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges})
+     *
+     * @return The {@link PhoneAccountHandle} associated with the TelphonyManager, or {@code null}
+     * if there is no associated {@link PhoneAccountHandle}; this can happen if the subscription is
+     * data-only or an opportunistic subscription.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public @Nullable PhoneAccountHandle getPhoneAccountHandle() {
+        return getPhoneAccountHandleForSubscriptionId(getSubId());
+    }
+
+    /**
+     * Determines the {@link PhoneAccountHandle} associated with a subscription Id.
+     *
+     * @param subscriptionId The subscription Id to check.
+     * @return The {@link PhoneAccountHandle} associated with a subscription Id, or {@code null} if
+     * there is no associated {@link PhoneAccountHandle}.
+     * @hide
+     */
+    public @Nullable PhoneAccountHandle getPhoneAccountHandleForSubscriptionId(int subscriptionId) {
+        PhoneAccountHandle returnValue = null;
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                returnValue = service.getPhoneAccountHandleForSubscriptionId(subscriptionId);
+            }
+        } catch (RemoteException e) {
+        }
+
+        return returnValue;
+    }
+
+    /**
+     * Returns the subscription ID for the given phone account handle.
+     *
+     * @param phoneAccountHandle the phone account handle for outgoing calls
+     * @return subscription ID for the given phone account handle; or
+     *         {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
+     *         if not available; or throw a SecurityException if the caller doesn't have the
+     *         permission.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public int getSubscriptionId(@NonNull PhoneAccountHandle phoneAccountHandle) {
+        return mPhoneAccountHandleToSubIdCache.query(phoneAccountHandle);
+    }
+
+    /**
+     * Resets telephony manager settings back to factory defaults.
+     *
+     * @hide
+     */
+    public void factoryReset(int subId) {
+        try {
+            Log.d(TAG, "factoryReset: subId=" + subId);
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.factoryReset(subId, getOpPackageName());
+            }
+        } catch (RemoteException e) {
+        }
+    }
+
+
+    /**
+     * Resets Telephony and IMS settings back to factory defaults only for the subscription
+     * associated with this instance.
+     * @see #createForSubscriptionId(int)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
+    public void resetSettings() {
+        try {
+            Log.d(TAG, "resetSettings: subId=" + getSubId());
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.factoryReset(getSubId(), getOpPackageName());
+            }
+        } catch (RemoteException e) {
+        }
+    }
+
+
+    /**
+     * Returns a locale based on the country and language from the SIM. Returns {@code null} if
+     * no locale could be derived from subscriptions.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @see Locale#toLanguageTag()
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @Nullable public Locale getSimLocale() {
+        try {
+            final ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                String languageTag = telephony.getSimLocaleForSubscriber(getSubId());
+                if (!TextUtils.isEmpty(languageTag)) {
+                    return Locale.forLanguageTag(languageTag);
+                }
+            }
+        } catch (RemoteException ex) {
+        }
+        return null;
+    }
+
+    /**
+     * Exception that may be supplied to the callback provided in {@link #requestModemActivityInfo}.
+     * @hide
+     */
+    @SystemApi
+    public static class ModemActivityInfoException extends Exception {
+        /** Indicates that an unknown error occurred */
+        public static final int ERROR_UNKNOWN = 0;
+
+        /**
+         * Indicates that the modem or phone processes are not available (such as when the device
+         * is in airplane mode).
+         */
+        public static final int ERROR_PHONE_NOT_AVAILABLE = 1;
+
+        /**
+         * Indicates that the modem supplied an invalid instance of {@link ModemActivityInfo}
+         */
+        public static final int ERROR_INVALID_INFO_RECEIVED = 2;
+
+        /**
+         * Indicates that the modem encountered an internal failure when processing the request
+         * for activity info.
+         */
+        public static final int ERROR_MODEM_RESPONSE_ERROR = 3;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"ERROR_"},
+                value = {
+                        ERROR_UNKNOWN,
+                        ERROR_PHONE_NOT_AVAILABLE,
+                        ERROR_INVALID_INFO_RECEIVED,
+                        ERROR_MODEM_RESPONSE_ERROR,
+                })
+        public @interface ModemActivityInfoError {}
+
+        private final int mErrorCode;
+
+        /**
+         * An exception with ModemActivityInfo specific error codes.
+         *
+         * @param errorCode a ModemActivityInfoError code.
+         */
+        public ModemActivityInfoException(@ModemActivityInfoError int errorCode) {
+            mErrorCode = errorCode;
+        }
+
+        public @ModemActivityInfoError int getErrorCode() {
+            return mErrorCode;
+        }
+
+        @Override
+        public String toString() {
+            switch (mErrorCode) {
+                case ERROR_UNKNOWN: return "ERROR_UNKNOWN";
+                case ERROR_PHONE_NOT_AVAILABLE: return "ERROR_PHONE_NOT_AVAILABLE";
+                case ERROR_INVALID_INFO_RECEIVED: return "ERROR_INVALID_INFO_RECEIVED";
+                case ERROR_MODEM_RESPONSE_ERROR: return "ERROR_MODEM_RESPONSE_ERROR";
+                default: return "UNDEFINED";
+            }
+        }
+    }
+
+    /**
+     * Requests the current modem activity info.
+     *
+     * The provided instance of {@link ModemActivityInfo} represents the cumulative activity since
+     * the last restart of the phone process.
+     *
+     * @param callback A callback object to which the result will be delivered. If there was an
+     *                 error processing the request, {@link OutcomeReceiver#onError} will be called
+     *                 with more details about the error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
+    public void requestModemActivityInfo(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<ModemActivityInfo, ModemActivityInfoException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        // Pass no handler into the receiver, since we're going to be trampolining the call to the
+        // listener onto the provided executor.
+        ResultReceiver wrapperResultReceiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle data) {
+                if (data == null) {
+                    Log.w(TAG, "requestModemActivityInfo: received null bundle");
+                    sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+                    return;
+                }
+                data.setDefusable(true);
+                if (data.containsKey(EXCEPTION_RESULT_KEY)) {
+                    int receivedErrorCode = data.getInt(EXCEPTION_RESULT_KEY);
+                    sendErrorToListener(receivedErrorCode);
+                    return;
+                }
+
+                if (!data.containsKey(MODEM_ACTIVITY_RESULT_KEY)) {
+                    Log.w(TAG, "requestModemActivityInfo: Bundle did not contain expected key");
+                    sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+                    return;
+                }
+                Parcelable receivedResult = data.getParcelable(MODEM_ACTIVITY_RESULT_KEY);
+                if (!(receivedResult instanceof ModemActivityInfo)) {
+                    Log.w(TAG, "requestModemActivityInfo: Bundle contained something that wasn't "
+                            + "a ModemActivityInfo.");
+                    sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+                    return;
+                }
+                ModemActivityInfo modemActivityInfo = (ModemActivityInfo) receivedResult;
+                if (!modemActivityInfo.isValid()) {
+                    Log.w(TAG, "requestModemActivityInfo: Received an invalid ModemActivityInfo");
+                    sendErrorToListener(ModemActivityInfoException.ERROR_INVALID_INFO_RECEIVED);
+                    return;
+                }
+                Log.d(TAG, "requestModemActivityInfo: Sending result to app: " + modemActivityInfo);
+                sendResultToListener(modemActivityInfo);
+            }
+
+            private void sendResultToListener(ModemActivityInfo info) {
+                Binder.withCleanCallingIdentity(() ->
+                        executor.execute(() ->
+                                callback.onResult(info)));
+            }
+
+            private void sendErrorToListener(int code) {
+                ModemActivityInfoException e = new ModemActivityInfoException(code);
+                Binder.withCleanCallingIdentity(() ->
+                        executor.execute(() ->
+                                callback.onError(e)));
+            }
+        };
+
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.requestModemActivityInfo(wrapperResultReceiver);
+                return;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e);
+        }
+        executor.execute(() -> callback.onError(
+                new ModemActivityInfoException(
+                        ModemActivityInfoException.ERROR_PHONE_NOT_AVAILABLE)));
+    }
+
+    /**
+     * Returns the current {@link ServiceState} information.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * If you want continuous updates of service state info, register a {@link TelephonyCallback}
+     * that implements {@link TelephonyCallback.ServiceStateListener} through {@link
+     * #registerTelephonyCallback}.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     * May return {@code null} when the subscription is inactive or when there was an error
+     * communicating with the phone process.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @Nullable ServiceState getServiceState() {
+        return getServiceState(getLocationData());
+    }
+
+    /**
+     * Returns the current {@link ServiceState} information.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * If you want continuous updates of service state info, register a {@link TelephonyCallback}
+     * that implements {@link TelephonyCallback.ServiceStateListener} through {@link
+     * #registerTelephonyCallback}.
+     *
+     * There's another way to renounce permissions with a custom context
+     * {@code AttributionSource.Builder#setRenouncedPermissions(Set<String>)} but only for system
+     * apps. To avoid confusion, calling this method supersede renouncing permissions with a
+     * custom context.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     * @param includeLocationData Specifies if the caller would like to receive
+     * location related information.
+     * May return {@code null} when the subscription is inactive or when there was an error
+     * communicating with the phone process.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @Nullable ServiceState getServiceState(@IncludeLocationData int includeLocationData) {
+        return getServiceStateForSlot(SubscriptionManager.getSlotIndex(getSubId()),
+                includeLocationData != INCLUDE_LOCATION_DATA_FINE,
+                includeLocationData == INCLUDE_LOCATION_DATA_NONE);
+    }
+
+    /**
+     * Returns the service state information on specified SIM slot.
+     *
+     * May return {@code null} when the {@code slotIndex} is invalid or when there was an error
+     * communicating with the phone process.
+     *
+     * @param slotIndex of phone whose service state is returned
+     * @param renounceFineLocationAccess Set this to true if the caller would not like to receive
+     * location related information which will be sent if the caller already possess
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the permission
+     * @param renounceCoarseLocationAccess Set this to true if the caller would not like to
+     * receive location related information which will be sent if the caller already possess
+     * {@link Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the permissions.
+     * @return Service state on specified SIM slot.
+     */
+    private ServiceState getServiceStateForSlot(int slotIndex, boolean renounceFineLocationAccess,
+            boolean renounceCoarseLocationAccess) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getServiceStateForSlot(slotIndex,
+                        renounceFineLocationAccess, renounceCoarseLocationAccess,
+                        getOpPackageName(), getAttributionTag());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getServiceStateForSlot", e);
+        } catch (NullPointerException e) {
+            AnomalyReporter.reportAnomaly(
+                    UUID.fromString("e2bed88e-def9-476e-bd71-3e572a8de6d1"),
+                    "getServiceStateForSlot " + slotIndex + " NPE");
+        }
+        return null;
+    }
+
+    /**
+     * Returns the service state information on specified subscription. Callers require
+     * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
+     *
+     * May return {@code null} when the subscription is inactive or when there was an error
+     * communicating with the phone process.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public ServiceState getServiceStateForSubscriber(int subId) {
+        return getServiceStateForSlot(
+                SubscriptionManager.getSlotIndex(subId), false, false);
+    }
+
+    /**
+     * Returns the service state information on specified SIM slot.
+     *
+     * If you want continuous updates of service state info, register a {@link TelephonyCallback}
+     * that implements {@link TelephonyCallback.ServiceStateListener} through
+     * {@link #registerTelephonyCallback}.
+     *
+     * May return {@code null} when the {@code slotIndex} is invalid or when there was an error
+     * communicating with the phone process
+     *
+     * See {@link #getActiveModemCount()} to get the total number of slots
+     * that are active on the device.
+     *
+     * @param slotIndex of phone whose service state is returned
+     * @return ServiceState on specified SIM slot.
+     *
+     * @hide
+     */
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_COARSE_LOCATION
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @Nullable ServiceState getServiceStateForSlot(int slotIndex) {
+        return getServiceStateForSlot(slotIndex, false, false);
+    }
+
+    /**
+     * Returns the URI for the per-account voicemail ringtone set in Phone settings.
+     *
+     * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
+     * voicemail ringtone.
+     * @return The URI for the ringtone to play when receiving a voicemail from a specific
+     * PhoneAccount. May be {@code null} if no ringtone is set.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public @Nullable Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getVoicemailRingtoneUri(accountHandle);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getVoicemailRingtoneUri", e);
+        }
+        return null;
+    }
+
+    /**
+     * Sets the per-account voicemail ringtone.
+     *
+     * <p>Requires that the calling app is the default dialer, or has carrier privileges (see
+     * {@link #hasCarrierPrivileges}, or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the
+     * voicemail ringtone.
+     * @param uri The URI for the ringtone to play when receiving a voicemail from a specific
+     * PhoneAccount.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
+     * instead.
+     */
+    @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void setVoicemailRingtoneUri(PhoneAccountHandle phoneAccountHandle, Uri uri) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.setVoicemailRingtoneUri(getOpPackageName(), phoneAccountHandle, uri);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setVoicemailRingtoneUri", e);
+        }
+    }
+
+    /**
+     * Returns whether vibration is set for voicemail notification in Phone settings.
+     *
+     * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
+     * voicemail vibration setting.
+     * @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public boolean isVoicemailVibrationEnabled(PhoneAccountHandle accountHandle) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.isVoicemailVibrationEnabled(accountHandle);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isVoicemailVibrationEnabled", e);
+        }
+        return false;
+    }
+
+    /**
+     * Sets the per-account preference whether vibration is enabled for voicemail notifications.
+     *
+     * <p>Requires that the calling app is the default dialer, or has carrier privileges (see
+     * {@link #hasCarrierPrivileges}, or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the
+     * voicemail vibration setting.
+     * @param enabled Whether to enable or disable vibration for voicemail notifications from a
+     * specific PhoneAccount.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
+     * instead.
+     */
+    @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void setVoicemailVibrationEnabled(PhoneAccountHandle phoneAccountHandle,
+            boolean enabled) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.setVoicemailVibrationEnabled(getOpPackageName(), phoneAccountHandle,
+                        enabled);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isVoicemailVibrationEnabled", e);
+        }
+    }
+
+    /**
+     * Returns carrier id of the current subscription.
+     * <p>To recognize a carrier (including MVNO) as a first-class identity, Android assigns each
+     * carrier with a canonical integer a.k.a. carrier id. The carrier ID is an Android
+     * platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
+     * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
+     *
+     * <p>Apps which have carrier-specific configurations or business logic can use the carrier id
+     * as an Android platform-wide identifier for carriers.
+     *
+     * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
+     * subscription is unavailable or the carrier cannot be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public int getSimCarrierId() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getSubscriptionCarrierId(getSubId());
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return UNKNOWN_CARRIER_ID;
+    }
+
+    /**
+     * Returns carrier id name of the current subscription.
+     * <p>Carrier id name is a user-facing name of carrier id returned by
+     * {@link #getSimCarrierId()}, usually the brand name of the subsidiary
+     * (e.g. T-Mobile). Each carrier could configure multiple {@link #getSimOperatorName() SPN} but
+     * should have a single carrier name. Carrier name is not a canonical identity,
+     * use {@link #getSimCarrierId()} instead.
+     * <p>The returned carrier name is unlocalized.
+     *
+     * @return Carrier name of the current subscription. Return {@code null} if the subscription is
+     * unavailable or the carrier cannot be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @Nullable CharSequence getSimCarrierIdName() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getSubscriptionCarrierName(getSubId());
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return null;
+    }
+
+    /**
+     * Returns fine-grained carrier ID of the current subscription.
+     *
+     * A specific carrier ID can represent the fact that a carrier may be in effect an aggregation
+     * of other carriers (ie in an MVNO type scenario) where each of these specific carriers which
+     * are used to make up the actual carrier service may have different carrier configurations.
+     * A specific carrier ID could also be used, for example, in a scenario where a carrier requires
+     * different carrier configuration for different service offering such as a prepaid plan.
+     *
+     * the specific carrier ID would be used for configuration purposes, but apps wishing to know
+     * about the carrier itself should use the regular carrier ID returned by
+     * {@link #getSimCarrierId()}.
+     *
+     * e.g, Tracfone SIMs could return different specific carrier ID based on IMSI from current
+     * subscription while carrier ID remains the same.
+     *
+     * <p>For carriers without fine-grained specific carrier ids, return {@link #getSimCarrierId()}
+     * <p>Specific carrier ids are defined in the same way as carrier id
+     * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
+     * except each with a "parent" id linking to its top-level carrier id.
+     *
+     * @return Returns fine-grained carrier id of the current subscription.
+     * Return {@link #UNKNOWN_CARRIER_ID} if the subscription is unavailable or the carrier cannot
+     * be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public int getSimSpecificCarrierId() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getSubscriptionSpecificCarrierId(getSubId());
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return UNKNOWN_CARRIER_ID;
+    }
+
+    /**
+     * Similar like {@link #getSimCarrierIdName()}, returns user-facing name of the
+     * specific carrier id returned by {@link #getSimSpecificCarrierId()}.
+     *
+     * The specific carrier ID would be used for configuration purposes, but apps wishing to know
+     * about the carrier itself should use the regular carrier ID returned by
+     * {@link #getSimCarrierIdName()}.
+     *
+     * <p>The returned name is unlocalized.
+     *
+     * @return user-facing name of the subscription specific carrier id. Return {@code null} if the
+     * subscription is unavailable or the carrier cannot be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @Nullable CharSequence getSimSpecificCarrierIdName() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getSubscriptionSpecificCarrierName(getSubId());
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return null;
+    }
+
+    /**
+     * Returns carrier id based on sim MCCMNC (returned by {@link #getSimOperator()}) only.
+     * This is used for fallback when configurations/logic for exact carrier id
+     * {@link #getSimCarrierId()} are not found.
+     *
+     * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
+     * can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier
+     * was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id
+     * by default. After carrier id table update, a new carrier id was assigned. If apps don't
+     * take the update with the new id, it might be helpful to always fallback by using carrier
+     * id based on MCCMNC if there is no match.
+     *
+     * @return matching carrier id from sim MCCMNC. Return {@link #UNKNOWN_CARRIER_ID} if the
+     * subscription is unavailable or the carrier cannot be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public int getCarrierIdFromSimMccMnc() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getCarrierIdFromMccMnc(getSlotIndex(), getSimOperator(), true);
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return UNKNOWN_CARRIER_ID;
+    }
+
+     /**
+      * Returns carrier id based on MCCMNC (returned by {@link #getSimOperator()}) only. This is
+      * used for fallback when configurations/logic for exact carrier id {@link #getSimCarrierId()}
+      * are not found.
+      *
+      * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
+      * can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier
+      * was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id
+      * by default. After carrier id table update, a new carrier id was assigned. If apps don't
+      * take the update with the new id, it might be helpful to always fallback by using carrier
+      * id based on MCCMNC if there is no match.
+      *
+      * @return matching carrier id from passing MCCMNC. Return {@link #UNKNOWN_CARRIER_ID} if the
+      * subscription is unavailable or the carrier cannot be identified.
+      * @hide
+      */
+     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+     public int getCarrierIdFromMccMnc(String mccmnc) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getCarrierIdFromMccMnc(getSlotIndex(), mccmnc, false);
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return UNKNOWN_CARRIER_ID;
+    }
+
+    /**
+     * Return a list of certs as hex strings from loaded carrier privileges access rules.
+     *
+     * @return a list of certificates as hex strings, or an empty list if there are no certs or
+     *     privilege rules are not loaded yet.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @NonNull
+    public List<String> getCertsFromCarrierPrivilegeAccessRules() {
+        List<String> certs = null;
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                certs = service.getCertsFromCarrierPrivilegeAccessRules(getSubId());
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return certs == null ? Collections.emptyList() : certs;
+    }
+
+    /**
+     * Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}.
+     * All uicc applications are uniquely identified by application ID, represented by the hex
+     * string. e.g, A00000015141434C00. See ETSI 102.221 and 101.220
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @param appType the uicc app type.
+     * @return Application ID for specified app type or {@code null} if no uicc or error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public String getAidForAppType(@UiccAppType int appType) {
+        return getAidForAppType(getSubId(), appType);
+    }
+
+    /**
+     * same as {@link #getAidForAppType(int)}
+     * @hide
+     */
+    public String getAidForAppType(int subId, int appType) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getAidForAppType(subId, appType);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getAidForAppType", e);
+        }
+        return null;
+    }
+
+    /**
+     * Return the Electronic Serial Number.
+     *
+     * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+     *
+     * @return ESN or null if error.
+     * @hide
+     */
+    public String getEsn() {
+        return getEsn(getSubId());
+    }
+
+    /**
+     * Return the Electronic Serial Number.
+     *
+     * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+     *
+     * @param subId the subscription ID that this request applies to.
+     * @return ESN or null if error.
+     * @hide
+     */
+    public String getEsn(int subId) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getEsn(subId);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getEsn", e);
+        }
+        return null;
+    }
+
+    /**
+     * Return the Preferred Roaming List Version
+     *
+     * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+     *
+     * @return PRLVersion or null if error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
+    public String getCdmaPrlVersion() {
+        return getCdmaPrlVersion(getSubId());
+    }
+
+    /**
+     * Return the Preferred Roaming List Version
+     *
+     * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+     *
+     * @param subId the subscription ID that this request applies to.
+     * @return PRLVersion or null if error.
+     * @hide
+     */
+    public String getCdmaPrlVersion(int subId) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getCdmaPrlVersion(subId);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getCdmaPrlVersion", e);
+        }
+        return null;
+    }
+
+    /**
+     * Get snapshot of Telephony histograms
+     * @return List of Telephony histograms
+     * Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * Or the calling app has carrier privileges.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public List<TelephonyHistogram> getTelephonyHistograms() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getTelephonyHistograms();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getTelephonyHistograms", e);
+        }
+        return null;
+    }
+
+    /**
+     * Set the allowed carrier list for slotIndex
+     * Require system privileges. In the future we may add this to carrier APIs.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     *
+     * <p>This method works only on devices with {@link
+     * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
+     *
+     * @deprecated use setCarrierRestrictionRules instead
+     *
+     * @return The number of carriers set successfully. Should be length of
+     * carrierList on success; -1 if carrierList null or on error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)
+    public int setAllowedCarriers(int slotIndex, List<CarrierIdentifier> carriers) {
+        if (carriers == null || !SubscriptionManager.isValidPhoneId(slotIndex)) {
+            return -1;
+        }
+        // Execute the method setCarrierRestrictionRules with an empty excluded list.
+        // If the allowed list is empty, it means that all carriers are allowed (default allowed),
+        // otherwise it means that only specified carriers are allowed (default not allowed).
+        CarrierRestrictionRules carrierRestrictionRules = CarrierRestrictionRules.newBuilder()
+                .setAllowedCarriers(carriers)
+                .setDefaultCarrierRestriction(
+                    carriers.isEmpty()
+                        ? CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_ALLOWED
+                        : CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED)
+                .build();
+
+        int result = setCarrierRestrictionRules(carrierRestrictionRules);
+
+        // Convert result into int, as required by this method.
+        if (result == SET_CARRIER_RESTRICTION_SUCCESS) {
+            return carriers.size();
+        } else {
+            return -1;
+        }
+    }
+
+    /**
+     * The carrier restrictions were successfully set.
+     * @hide
+     */
+    @SystemApi
+    public static final int SET_CARRIER_RESTRICTION_SUCCESS = 0;
+
+    /**
+     * The carrier restrictions were not set due to lack of support in the modem. This can happen
+     * if the modem does not support setting the carrier restrictions or if the configuration
+     * passed in the {@code setCarrierRestrictionRules} is not supported by the modem.
+     * @hide
+     */
+    @SystemApi
+    public static final int SET_CARRIER_RESTRICTION_NOT_SUPPORTED = 1;
+
+    /**
+     * The setting of carrier restrictions failed.
+     * @hide
+     */
+    @SystemApi
+    public static final int SET_CARRIER_RESTRICTION_ERROR = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"SET_CARRIER_RESTRICTION_"},
+            value = {
+                    SET_CARRIER_RESTRICTION_SUCCESS,
+                    SET_CARRIER_RESTRICTION_NOT_SUPPORTED,
+                    SET_CARRIER_RESTRICTION_ERROR
+            })
+    public @interface SetCarrierRestrictionResult {}
+
+    /**
+     * The SIM power state was successfully set.
+     * @hide
+     */
+    @SystemApi
+    public static final int SET_SIM_POWER_STATE_SUCCESS = 0;
+
+    /**
+     * The SIM is already in the requested power state.
+     * @hide
+     */
+    @SystemApi
+    public static final int SET_SIM_POWER_STATE_ALREADY_IN_STATE = 1;
+
+    /**
+     * Failed to connect to the modem to make the power state request. This may happen if the
+     * modem has an error. The user may want to make the request again later.
+     * @hide
+     */
+    @SystemApi
+    public static final int SET_SIM_POWER_STATE_MODEM_ERROR = 2;
+
+    /**
+     * Failed to connect to the SIM to make the power state request. This may happen if the
+     * SIM has been removed. The user may want to make the request again later.
+     * @hide
+     */
+    @SystemApi
+    public static final int SET_SIM_POWER_STATE_SIM_ERROR = 3;
+
+    /**
+     * The modem version does not support synchronous power.
+     * @hide
+     */
+    @SystemApi
+    public static final int SET_SIM_POWER_STATE_NOT_SUPPORTED = 4;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"SET_SIM_POWER_STATE_"},
+            value = {
+                    SET_SIM_POWER_STATE_SUCCESS,
+                    SET_SIM_POWER_STATE_ALREADY_IN_STATE,
+                    SET_SIM_POWER_STATE_MODEM_ERROR,
+                    SET_SIM_POWER_STATE_SIM_ERROR,
+                    SET_SIM_POWER_STATE_NOT_SUPPORTED
+            })
+    public @interface SetSimPowerStateResult {}
+
+    /**
+     * Set the allowed carrier list and the excluded carrier list indicating the priority between
+     * the two lists.
+     * Requires system privileges.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     *
+     * <p>This method works only on devices with {@link
+     * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
+     *
+     * @return {@link #SET_CARRIER_RESTRICTION_SUCCESS} in case of success.
+     * {@link #SET_CARRIER_RESTRICTION_NOT_SUPPORTED} if the modem does not support the
+     * configuration. {@link #SET_CARRIER_RESTRICTION_ERROR} in all other error cases.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @SetCarrierRestrictionResult
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)
+    public int setCarrierRestrictionRules(@NonNull CarrierRestrictionRules rules) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.setAllowedCarriers(rules);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e);
+        } catch (NullPointerException e) {
+            Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e);
+        }
+        return SET_CARRIER_RESTRICTION_ERROR;
+    }
+
+    /**
+     * Get the allowed carrier list for slotIndex.
+     * Requires system privileges.
+     *
+     * <p>This method returns valid data on devices with {@link
+     * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
+     *
+     * @deprecated Apps should use {@link #getCarrierRestrictionRules} to retrieve the list of
+     * allowed and excliuded carriers, as the result of this API is valid only when the excluded
+     * list is empty. This API could return an empty list, even if some restrictions are present.
+     *
+     * @return List of {@link android.service.carrier.CarrierIdentifier}; empty list
+     * means all carriers are allowed.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)
+    public List<CarrierIdentifier> getAllowedCarriers(int slotIndex) {
+        if (SubscriptionManager.isValidPhoneId(slotIndex)) {
+            CarrierRestrictionRules carrierRestrictionRule = getCarrierRestrictionRules();
+            if (carrierRestrictionRule != null) {
+                return carrierRestrictionRule.getAllowedCarriers();
+            }
+        }
+        return new ArrayList<CarrierIdentifier>(0);
+    }
+
+    /**
+     * Get the allowed carrier list and the excluded carrier list indicating the priority between
+     * the two lists.
+     * Require system privileges. In the future we may add this to carrier APIs.
+     *
+     * <p>This method returns valid data on devices with {@link
+     * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
+     *
+     * @return {@link CarrierRestrictionRules} which contains the allowed carrier list and the
+     * excluded carrier list with the priority between the two lists. Returns {@code null}
+     * in case of error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)
+    @Nullable
+    public CarrierRestrictionRules getCarrierRestrictionRules() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getAllowedCarriers();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getAllowedCarriers", e);
+        } catch (NullPointerException e) {
+            Log.e(TAG, "Error calling ITelephony#getAllowedCarriers", e);
+        }
+        return null;
+    }
+
+    /**
+     * Carrier restriction status value is unknown, in case modem did not provide any
+     * information about carrier restriction status.
+     */
+    public static final int CARRIER_RESTRICTION_STATUS_UNKNOWN = 0;
+
+    /** The device is not restricted to a carrier */
+    public static final int CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED = 1;
+
+    /** The device is restricted to a carrier. */
+    public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED = 2;
+
+    /** The device is restricted to the carrier of the calling application. */
+    public static final int CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER = 3;
+
+    /** @hide */
+    @IntDef(prefix = {"CARRIER_RESTRICTION_STATUS_"}, value = {
+            CARRIER_RESTRICTION_STATUS_UNKNOWN,
+            CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED,
+            CARRIER_RESTRICTION_STATUS_RESTRICTED,
+            CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CarrierRestrictionStatus {
+    }
+
+    /**
+     * Get the carrier restriction status of the device.
+     * <p>To fetch the carrier restriction status of the device the calling application needs to be
+     * allowlisted to Android at <a href="https://android.googlesource.com/platform/packages/services/Telephony/+/master/assets/CarrierRestrictionOperatorDetails.json">here</a>.
+     * The calling application also needs the READ_PHONE_STATE permission.
+     * The return value of the API is as follows.
+     * <ul>
+     *      <li>return {@link #CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER} if the caller
+     *      and the device locked by the network are same</li>
+     *      <li>return {@link #CARRIER_RESTRICTION_STATUS_RESTRICTED} if the caller and the
+     *      device locked by the network are different</li>
+     *      <li>return {@link #CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED} if the device is
+     *      not locked</li>
+     *      <li>return {@link #CARRIER_RESTRICTION_STATUS_UNKNOWN} if the device locking
+     *      state is unavailable or radio does not supports the feature</li>
+     * </ul>
+     *
+     * @param executor The executor on which the result listener will be called.
+     * @param resultListener {@link Consumer} that will be called with the carrier restriction
+     *                       status result fetched from the radio
+     * @throws SecurityException if the caller does not have the required permission/privileges or
+     *                           if the caller is not pre-registered.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public void getCarrierRestrictionStatus(@NonNull Executor executor,
+            @NonNull @CarrierRestrictionStatus
+                    Consumer<Integer> resultListener) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(resultListener);
+
+        IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+            @Override
+            public void accept(@CarrierRestrictionStatus int result) {
+                executor.execute(() -> Binder.withCleanCallingIdentity(
+                        () -> resultListener.accept(result)));
+            }
+        };
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.getCarrierRestrictionStatus(internalCallback, getOpPackageName());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getCarrierRestrictionStatus: RemoteException = " + ex);
+            throw ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Test API to verify carrier restriction status allow list i.e.
+     * packages/services/Telephony/assets/CarrierRestrictionOperatorDetails.json.
+     *
+     * @param pkgName : packaga name of the entry to verify
+     * @param carrierId : carrier Id of the entry
+     * @return {@code List<String>} : list of registered shaIds
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public List<String> getShaIdFromAllowList(String pkgName, int carrierId) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getShaIdFromAllowList(pkgName, carrierId);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getShaIdFromAllowList: RemoteException = " + ex);
+            throw ex.rethrowAsRuntimeException();
+        }
+        return Collections.EMPTY_LIST;
+    }
+
+    /**
+     * Used to enable or disable carrier data by the system based on carrier signalling or
+     * carrier privileged apps. Different from {@link #setDataEnabled(boolean)} which is linked to
+     * user settings, carrier data on/off won't affect user settings but will bypass the
+     * settings and turns off data internally if set to {@code false}.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param enabled control enable or disable carrier data.
+     * @see #resetAllCarrierActions()
+     * @deprecated use {@link #setDataEnabledForReason(int, boolean) with
+     * reason {@link #DATA_ENABLED_REASON_CARRIER}} instead.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public void setCarrierDataEnabled(boolean enabled) {
+        try {
+            setDataEnabledForReason(DATA_ENABLED_REASON_CARRIER, enabled);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Error calling setDataEnabledForReason e:" + e);
+        }
+    }
+
+    /**
+     * Carrier action to enable or disable the radio.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param enabled control enable or disable radio.
+     * @see #resetAllCarrierActions()
+     *
+     * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and
+     * {@link clearRadioPowerOffForReason}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void setRadioEnabled(boolean enabled) {
+        if (enabled) {
+            clearRadioPowerOffForReason(RADIO_POWER_REASON_CARRIER);
+        } else {
+            requestRadioPowerOffForReason(RADIO_POWER_REASON_CARRIER);
+        }
+    }
+
+    /**
+     * No error. Operation succeeded.
+     * @hide
+     */
+    public static final int ENABLE_VONR_SUCCESS = 0;
+
+    /**
+     * Radio is not available.
+     * @hide
+     */
+    public static final int ENABLE_VONR_RADIO_NOT_AVAILABLE = 2;
+
+    /**
+     * Internal Radio error.
+     * @hide
+     */
+    public static final int ENABLE_VONR_RADIO_ERROR = 3;
+
+    /**
+     * Voice over NR enable/disable request is received when system is in invalid state.
+     * @hide
+     */
+    public static final int ENABLE_VONR_RADIO_INVALID_STATE = 4;
+
+    /**
+     * Voice over NR enable/disable request is not supported.
+     * @hide
+     */
+    public static final int ENABLE_VONR_REQUEST_NOT_SUPPORTED = 5;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"EnableVoNrResult"}, value = {
+            ENABLE_VONR_SUCCESS,
+            ENABLE_VONR_RADIO_NOT_AVAILABLE,
+            ENABLE_VONR_RADIO_ERROR,
+            ENABLE_VONR_RADIO_INVALID_STATE,
+            ENABLE_VONR_REQUEST_NOT_SUPPORTED})
+    public @interface EnableVoNrResult {}
+
+    /**
+     * Enable or disable Voice over NR (VoNR)
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param enabled  enable or disable VoNR.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public @EnableVoNrResult int setVoNrEnabled(boolean enabled) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.setVoNrEnabled(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enabled);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setVoNrEnabled", e);
+        }
+
+        return ENABLE_VONR_RADIO_INVALID_STATE;
+    }
+
+    /**
+     * Is Voice over NR (VoNR) enabled.
+     * @return true if Voice over NR (VoNR) is enabled else false. Enabled state does not mean
+     *  voice call over NR is active or voice ove NR is available. It means the device is allowed to
+     *  register IMS over NR.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isVoNrEnabled() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isVoNrEnabled(getSubId());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "isVoNrEnabled RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    /**
+     * Carrier action to start or stop reporting default network available events.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param report control start/stop reporting network status.
+     * @see #resetAllCarrierActions()
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void reportDefaultNetworkStatus(boolean report) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.carrierActionReportDefaultNetworkStatus(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), report);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#carrierActionReportDefaultNetworkStatus", e);
+        }
+    }
+
+    /**
+     * Reset all carrier actions previously set by {@link #setRadioEnabled},
+     * {@link #reportDefaultNetworkStatus} and {@link #setCarrierDataEnabled}.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public void resetAllCarrierActions() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.carrierActionResetAll(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#carrierActionResetAll", e);
+        }
+    }
+
+    /**
+     * Policy control of data connection. Usually used when data limit is passed.
+     * @param enabled True if enabling the data, otherwise disabling.
+     * @deprecated use {@link #setDataEnabledForReason(int, boolean) with
+     * reason {@link #DATA_ENABLED_REASON_POLICY}} instead.
+     * @hide
+     */
+    @Deprecated
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setPolicyDataEnabled(boolean enabled) {
+        try {
+            setDataEnabledForReason(DATA_ENABLED_REASON_POLICY, enabled);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Error calling setDataEnabledForReason e:" + e);
+        }
+    }
+
+    /** @hide */
+    @IntDef({
+            DATA_ENABLED_REASON_USER,
+            DATA_ENABLED_REASON_POLICY,
+            DATA_ENABLED_REASON_CARRIER,
+            DATA_ENABLED_REASON_THERMAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DataEnabledReason{}
+
+    /** @hide */
+    @IntDef({
+            DATA_ENABLED_REASON_UNKNOWN,
+            DATA_ENABLED_REASON_USER,
+            DATA_ENABLED_REASON_POLICY,
+            DATA_ENABLED_REASON_CARRIER,
+            DATA_ENABLED_REASON_THERMAL,
+            DATA_ENABLED_REASON_OVERRIDE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DataEnabledChangedReason{}
+
+    /**
+     * To indicate that data was enabled or disabled due to an unknown reason.
+     * Note that this is not a valid reason for {@link #setDataEnabledForReason(int, boolean)} and
+     * is only used to indicate that data enabled was changed.
+     */
+    public static final int DATA_ENABLED_REASON_UNKNOWN = -1;
+
+    /**
+     * To indicate that user enabled or disabled data.
+     */
+    public static final int DATA_ENABLED_REASON_USER = 0;
+
+    /**
+     * To indicate that data control due to policy. Usually used when data limit is passed.
+     * Policy data on/off won't affect user settings but will bypass the
+     * settings and turns off data internally if set to {@code false}.
+     */
+    public static final int DATA_ENABLED_REASON_POLICY = 1;
+
+    /**
+     * To indicate enable or disable carrier data by the system based on carrier signalling or
+     * carrier privileged apps. Carrier data on/off won't affect user settings but will bypass the
+     * settings and turns off data internally if set to {@code false}.
+     */
+    public static final int DATA_ENABLED_REASON_CARRIER = 2;
+
+    /**
+     * To indicate enable or disable data by thermal service.
+     * Thermal data on/off won't affect user settings but will bypass the
+     * settings and turns off data internally if set to {@code false}.
+     */
+    public static final int DATA_ENABLED_REASON_THERMAL = 3;
+
+    /**
+     * To indicate data was enabled or disabled due to mobile data policy overrides.
+     * Note that this is not a valid reason for {@link #setDataEnabledForReason(int, boolean)} and
+     * is only used to indicate that data enabled was changed due to an override.
+     */
+    public static final int DATA_ENABLED_REASON_OVERRIDE = 4;
+
+    /**
+     * Control of data connection and provide the reason triggering the data connection control.
+     * This can be called for following reasons
+     * <ol>
+     * <li>data limit is passed {@link #DATA_ENABLED_REASON_POLICY}
+     * <li>data disabled by carrier {@link #DATA_ENABLED_REASON_CARRIER}
+     * <li>data disabled by user {@link #DATA_ENABLED_REASON_USER}
+     * <li>data disabled due to thermal {@link #DATA_ENABLED_REASON_THERMAL}
+     * </ol>
+     * If any of the reason is off, then it will result in
+     * bypassing user preference and result in data to be turned off.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies
+     *      to the given subId. Otherwise, applies to
+     * {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     *
+     * @param reason the reason the data enable change is taking place
+     * @param enabled True if enabling the data, otherwise disabling.
+     *
+     * <p>Requires Permission:
+     * The calling app has carrier privileges (see {@link #hasCarrierPrivileges}) if the reason is
+     * {@link #DATA_ENABLED_REASON_USER} or {@link #DATA_ENABLED_REASON_CARRIER} or the call app
+     * has {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} irrespective of
+     * the reason.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public void setDataEnabledForReason(@DataEnabledReason int reason, boolean enabled) {
+        setDataEnabledForReason(getSubId(), reason, enabled);
+    }
+
+    private void setDataEnabledForReason(int subId, @DataEnabledReason int reason,
+            boolean enabled) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.setDataEnabledForReason(subId, reason, enabled, getOpPackageName());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Telephony#setDataEnabledForReason RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return whether data is enabled for certain reason .
+     *
+     * If {@link #isDataEnabledForReason} returns false, it means in data enablement for a
+     * specific reason is turned off. If any of the reason is off, then it will result in
+     * bypassing user preference and result in data to be turned off. Call
+     * {@link #isDataConnectionAllowed} in order to know whether
+     * data connection is allowed on the device.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies
+     *      to the given subId. Otherwise, applies to
+     * {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * @param reason the reason the data enable change is taking place
+     * @return whether data is enabled for a reason.
+     * <p>Requires Permission:
+     * The calling app has carrier privileges (see {@link #hasCarrierPrivileges}) or
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} or
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     * {@link android.Manifest.permission#READ_BASIC_PHONE_STATE}
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     */
+    @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            android.Manifest.permission.READ_BASIC_PHONE_STATE
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean isDataEnabledForReason(@DataEnabledReason int reason) {
+        return isDataEnabledForReason(getSubId(), reason);
+    }
+
+    private boolean isDataEnabledForReason(int subId, @DataEnabledReason int reason) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.isDataEnabledForReason(subId, reason);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Telephony#isDataEnabledForReason RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    /**
+     * Get Client request stats which will contain statistical information
+     * on each request made by client.
+     * Callers require either READ_PRIVILEGED_PHONE_STATE or
+     * READ_PHONE_STATE to retrieve the information.
+     * @param subId sub id
+     * @return List of Client Request Stats
+     * @hide
+     */
+    public List<ClientRequestStats> getClientRequestStats(int subId) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getClientRequestStats(getOpPackageName(), getAttributionTag(),
+                        subId);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getClientRequestStats", e);
+        }
+
+        return null;
+    }
+
+    /**
+     * Checks if phone is in emergency callback mode.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * @return true if phone is in emergency callback mode.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public boolean getEmergencyCallbackMode() {
+        return getEmergencyCallbackMode(getSubId());
+    }
+
+    /**
+     * Check if phone is in emergency callback mode
+     * @return true if phone is in emergency callback mode
+     * @param subId the subscription ID that this action applies to.
+     * @hide
+     */
+    public boolean getEmergencyCallbackMode(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                return false;
+            }
+            return telephony.getEmergencyCallbackMode(subId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getEmergencyCallbackMode", e);
+        }
+        return false;
+    }
+
+    /**
+     * Checks if manual network selection is allowed.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link #hasCarrierPrivileges})
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @return {@code true} if manual network selection is allowed, otherwise return {@code false}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @SuppressAutoDoc // No support carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {android.Manifest.permission.READ_PRECISE_PHONE_STATE,
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean isManualNetworkSelectionAllowed() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isManualNetworkSelectionAllowed(getSubId());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isManualNetworkSelectionAllowed", e);
+        }
+        return true;
+    }
+
+    /**
+     * Get the most recently available signal strength information.
+     *
+     * Get the most recent SignalStrength information reported by the modem. Due
+     * to power saving this information may not always be current.
+     * @return the most recent cached signal strength info from the modem
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @Nullable
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public SignalStrength getSignalStrength() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getSignalStrength(getSubId());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getSignalStrength", e);
+        }
+        return null;
+    }
+
+    /**
+     * Checks whether cellular data connection is allowed in the device.
+     *
+     * <p>Whether cellular data connection is allowed considers all factors below:
+     * <UL>
+     *   <LI>User turned on data setting {@link #isDataEnabled}.</LI>
+     *   <LI>Carrier allows data to be on.</LI>
+     *   <LI>Network policy.</LI>
+     *   <LI>And possibly others.</LI>
+     * </UL>
+     * @return {@code true} if the overall data connection is allowed; {@code false} if not.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     */
+    @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_BASIC_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean isDataConnectionAllowed() {
+        boolean retVal = false;
+        try {
+            int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                retVal = telephony.isDataEnabled(subId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error isDataConnectionAllowed", e);
+        }
+        return retVal;
+    }
+
+    /**
+     * @return true if the current device is "data capable" over a radio on the device.
+     * <p>
+     * "Data capable" means that this device supports packet-switched
+     * data connections over the telephony network.
+     * <p>
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean isDataCapable() {
+        if (mContext == null) return true;
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_mobile_data_capable);
+    }
+
+    /**
+     * The indication for signal strength update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_SIGNAL_STRENGTH               = 0x1;
+
+    /**
+     * The indication for full network state update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_FULL_NETWORK_STATE            = 0x2;
+
+    /**
+     * The indication for data call dormancy changed update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED    = 0x4;
+
+    /**
+     * The indication for link capacity estimate update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_LINK_CAPACITY_ESTIMATE        = 0x8;
+
+    /**
+     * The indication for physical channel config update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG       = 0x10;
+
+    /**
+     * A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2,
+     * plmn and spn. This would be handy for, eg, forcing a particular carrier id, carrier's config
+     * (also any country or carrier overlays) to be loaded when using a test SIM with a call box.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     *
+     *
+     * @deprecated
+     * @hide
+     */
+    @Deprecated
+    @TestApi
+    public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
+            String gid2, String plmn, String spn) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setCarrierTestOverride(
+                        getSubId(), mccmnc, imsi, iccid, gid1, gid2, plmn, spn,
+                        null, null);
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+    }
+
+    /**
+     * A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2,
+     * plmn, spn, apn and carrier priviledge. This would be handy for, eg, forcing a particular
+     * carrier id, carrier's config (also any country or carrier overlays) to be loaded when using
+     * a test SIM with a call box.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     *
+     * @hide
+     */
+    @TestApi
+    public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
+                                       String gid2, String plmn, String spn,
+                                       String carrierPriviledgeRules, String apn) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setCarrierTestOverride(
+                        getSubId(), mccmnc, imsi, iccid, gid1, gid2, plmn, spn,
+                        carrierPriviledgeRules, apn);
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+    }
+
+    /**
+     * A test API to return installed carrier id list version
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public int getCarrierIdListVersion() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getCarrierIdListVersion(getSubId());
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return UNKNOWN_CARRIER_ID_LIST_VERSION;
+    }
+
+    /**
+     * How many modems can have simultaneous data connections.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public int getNumberOfModemsWithSimultaneousDataConnections() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getNumberOfModemsWithSimultaneousDataConnections(
+                        getSubId(), getOpPackageName(), getAttributionTag());
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return 0;
+    }
+
+    /**
+     * Enable or disable OpportunisticNetworkService.
+     *
+     * This method should be called to enable or disable
+     * OpportunisticNetwork service on the device.
+     *
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     *
+     * @param enable enable(True) or disable(False)
+     * @return returns true if successfully set.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean setOpportunisticNetworkState(boolean enable) {
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        boolean ret = false;
+        try {
+            IOns iOpportunisticNetworkService = getIOns();
+            if (iOpportunisticNetworkService != null) {
+                ret = iOpportunisticNetworkService.setEnable(enable, pkgForDebug);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "enableOpportunisticNetwork RemoteException", ex);
+        }
+
+        return ret;
+    }
+
+    /**
+     * is OpportunisticNetworkService enabled
+     *
+     * This method should be called to determine if the OpportunisticNetworkService is
+     * enabled
+     *
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean isOpportunisticNetworkEnabled() {
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        boolean isEnabled = false;
+
+        try {
+            IOns iOpportunisticNetworkService = getIOns();
+            if (iOpportunisticNetworkService != null) {
+                isEnabled = iOpportunisticNetworkService.isEnabled(pkgForDebug);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "enableOpportunisticNetwork RemoteException", ex);
+        }
+
+        return isEnabled;
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @LongDef(flag = true, prefix = {"NETWORK_TYPE_BITMASK_"},
+            value = {NETWORK_TYPE_BITMASK_UNKNOWN,
+                    NETWORK_TYPE_BITMASK_GSM,
+                    NETWORK_TYPE_BITMASK_GPRS,
+                    NETWORK_TYPE_BITMASK_EDGE,
+                    NETWORK_TYPE_BITMASK_CDMA,
+                    NETWORK_TYPE_BITMASK_1xRTT,
+                    NETWORK_TYPE_BITMASK_EVDO_0,
+                    NETWORK_TYPE_BITMASK_EVDO_A,
+                    NETWORK_TYPE_BITMASK_EVDO_B,
+                    NETWORK_TYPE_BITMASK_EHRPD,
+                    NETWORK_TYPE_BITMASK_HSUPA,
+                    NETWORK_TYPE_BITMASK_HSDPA,
+                    NETWORK_TYPE_BITMASK_HSPA,
+                    NETWORK_TYPE_BITMASK_HSPAP,
+                    NETWORK_TYPE_BITMASK_UMTS,
+                    NETWORK_TYPE_BITMASK_TD_SCDMA,
+                    NETWORK_TYPE_BITMASK_LTE,
+                    NETWORK_TYPE_BITMASK_LTE_CA,
+                    NETWORK_TYPE_BITMASK_NR,
+                    NETWORK_TYPE_BITMASK_IWLAN,
+                    NETWORK_TYPE_BITMASK_IDEN
+            })
+    public @interface NetworkTypeBitMask {}
+
+    // 2G
+    /**
+     * network type bitmask unknown.
+     */
+    public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L;
+    /**
+     * network type bitmask indicating the support of radio tech GSM.
+     */
+    public static final long NETWORK_TYPE_BITMASK_GSM = (1 << (NETWORK_TYPE_GSM -1));
+    /**
+     * network type bitmask indicating the support of radio tech GPRS.
+     */
+    public static final long NETWORK_TYPE_BITMASK_GPRS = (1 << (NETWORK_TYPE_GPRS -1));
+    /**
+     * network type bitmask indicating the support of radio tech EDGE.
+     */
+    public static final long NETWORK_TYPE_BITMASK_EDGE = (1 << (NETWORK_TYPE_EDGE -1));
+    /**
+     * network type bitmask indicating the support of radio tech CDMA(IS95A/IS95B).
+     */
+    public static final long NETWORK_TYPE_BITMASK_CDMA = (1 << (NETWORK_TYPE_CDMA -1));
+    /**
+     * network type bitmask indicating the support of radio tech 1xRTT.
+     */
+    @SuppressLint("AllUpper")
+    public static final long NETWORK_TYPE_BITMASK_1xRTT = (1 << (NETWORK_TYPE_1xRTT - 1));
+    // 3G
+    /**
+     * network type bitmask indicating the support of radio tech EVDO 0.
+     */
+    public static final long NETWORK_TYPE_BITMASK_EVDO_0 = (1 << (NETWORK_TYPE_EVDO_0 -1));
+    /**
+     * network type bitmask indicating the support of radio tech EVDO A.
+     */
+    public static final long NETWORK_TYPE_BITMASK_EVDO_A = (1 << (NETWORK_TYPE_EVDO_A - 1));
+    /**
+     * network type bitmask indicating the support of radio tech EVDO B.
+     */
+    public static final long NETWORK_TYPE_BITMASK_EVDO_B = (1 << (NETWORK_TYPE_EVDO_B -1));
+    /**
+     * network type bitmask indicating the support of radio tech EHRPD.
+     */
+    public static final long NETWORK_TYPE_BITMASK_EHRPD = (1 << (NETWORK_TYPE_EHRPD -1));
+    /**
+     * network type bitmask indicating the support of radio tech HSUPA.
+     */
+    public static final long NETWORK_TYPE_BITMASK_HSUPA = (1 << (NETWORK_TYPE_HSUPA -1));
+    /**
+     * network type bitmask indicating the support of radio tech HSDPA.
+     */
+    public static final long NETWORK_TYPE_BITMASK_HSDPA = (1 << (NETWORK_TYPE_HSDPA -1));
+    /**
+     * network type bitmask indicating the support of radio tech HSPA.
+     */
+    public static final long NETWORK_TYPE_BITMASK_HSPA = (1 << (NETWORK_TYPE_HSPA -1));
+    /**
+     * network type bitmask indicating the support of radio tech iDen.
+     * @hide
+     */
+    public static final long NETWORK_TYPE_BITMASK_IDEN = (1 << (NETWORK_TYPE_IDEN - 1));
+    /**
+     * network type bitmask indicating the support of radio tech HSPAP.
+     */
+    public static final long NETWORK_TYPE_BITMASK_HSPAP = (1 << (NETWORK_TYPE_HSPAP -1));
+    /**
+     * network type bitmask indicating the support of radio tech UMTS.
+     */
+    public static final long NETWORK_TYPE_BITMASK_UMTS = (1 << (NETWORK_TYPE_UMTS -1));
+    /**
+     * network type bitmask indicating the support of radio tech TD_SCDMA.
+     */
+    public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = (1 << (NETWORK_TYPE_TD_SCDMA -1));
+    // 4G
+    /**
+     * network type bitmask indicating the support of radio tech LTE.
+     */
+    public static final long NETWORK_TYPE_BITMASK_LTE = (1 << (NETWORK_TYPE_LTE -1));
+    /**
+     * NOT USED; this bitmask is exposed accidentally.
+     * If used, will be converted to {@link #NETWORK_TYPE_BITMASK_LTE}.
+     * network type bitmask indicating the support of radio tech LTE CA (carrier aggregation).
+     *
+     * @deprecated Please use {@link #NETWORK_TYPE_BITMASK_LTE} instead. Deprecated in Android U.
+     */
+    @Deprecated
+    public static final long NETWORK_TYPE_BITMASK_LTE_CA = (1 << (NETWORK_TYPE_LTE_CA -1));
+
+    /**
+     * network type bitmask indicating the support of radio tech NR(New Radio) 5G.
+     */
+    public static final long NETWORK_TYPE_BITMASK_NR = (1 << (NETWORK_TYPE_NR -1));
+
+    /**
+     * network type bitmask indicating the support of radio tech IWLAN.
+     */
+    public static final long NETWORK_TYPE_BITMASK_IWLAN = (1 << (NETWORK_TYPE_IWLAN -1));
+
+    /** @hide */
+    public static final long NETWORK_CLASS_BITMASK_2G = NETWORK_TYPE_BITMASK_GSM
+                | NETWORK_TYPE_BITMASK_GPRS
+                | NETWORK_TYPE_BITMASK_EDGE
+                | NETWORK_TYPE_BITMASK_CDMA
+                | NETWORK_TYPE_BITMASK_1xRTT;
+
+    /** @hide */
+    public static final long NETWORK_CLASS_BITMASK_3G = NETWORK_TYPE_BITMASK_EVDO_0
+            | NETWORK_TYPE_BITMASK_EVDO_A
+            | NETWORK_TYPE_BITMASK_EVDO_B
+            | NETWORK_TYPE_BITMASK_EHRPD
+            | NETWORK_TYPE_BITMASK_HSUPA
+            | NETWORK_TYPE_BITMASK_HSDPA
+            | NETWORK_TYPE_BITMASK_HSPA
+            | NETWORK_TYPE_BITMASK_HSPAP
+            | NETWORK_TYPE_BITMASK_UMTS
+            | NETWORK_TYPE_BITMASK_TD_SCDMA;
+
+    /** @hide */
+    public static final long NETWORK_CLASS_BITMASK_4G = NETWORK_TYPE_BITMASK_LTE
+            | NETWORK_TYPE_BITMASK_LTE_CA
+            | NETWORK_TYPE_BITMASK_IWLAN;
+
+    /** @hide */
+    public static final long NETWORK_CLASS_BITMASK_5G = NETWORK_TYPE_BITMASK_NR;
+
+    /** @hide */
+    public static final long NETWORK_STANDARDS_FAMILY_BITMASK_3GPP = NETWORK_TYPE_BITMASK_GSM
+            | NETWORK_TYPE_BITMASK_GPRS
+            | NETWORK_TYPE_BITMASK_EDGE
+            | NETWORK_TYPE_BITMASK_HSUPA
+            | NETWORK_TYPE_BITMASK_HSDPA
+            | NETWORK_TYPE_BITMASK_HSPA
+            | NETWORK_TYPE_BITMASK_HSPAP
+            | NETWORK_TYPE_BITMASK_UMTS
+            | NETWORK_TYPE_BITMASK_TD_SCDMA
+            | NETWORK_TYPE_BITMASK_LTE
+            | NETWORK_TYPE_BITMASK_LTE_CA
+            | NETWORK_TYPE_BITMASK_NR;
+
+    /** @hide */
+    public static final long NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2 = NETWORK_TYPE_BITMASK_CDMA
+            | NETWORK_TYPE_BITMASK_1xRTT
+            | NETWORK_TYPE_BITMASK_EVDO_0
+            | NETWORK_TYPE_BITMASK_EVDO_A
+            | NETWORK_TYPE_BITMASK_EVDO_B
+            | NETWORK_TYPE_BITMASK_EHRPD;
+
+    /**
+     * @return Modem supported radio access family bitmask
+     *
+     * <p>Requires permission: android.Manifest.READ_PRIVILEGED_PHONE_STATE or
+     * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws SecurityException if the caller does not have the required permission
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @NetworkTypeBitMask long getSupportedRadioAccessFamily() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName());
+            } else {
+                // This can happen when the ITelephony interface is not up yet.
+                return NETWORK_TYPE_BITMASK_UNKNOWN;
+            }
+        } catch (RemoteException ex) {
+            // This shouldn't happen in the normal case
+            return NETWORK_TYPE_BITMASK_UNKNOWN;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return NETWORK_TYPE_BITMASK_UNKNOWN;
+        }
+    }
+
+    /**
+     * Indicates Emergency number database version is invalid.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1;
+
+    /**
+     * Notify Telephony for OTA emergency number database installation complete.
+     *
+     * <p> Requires permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @SystemApi
+    public void notifyOtaEmergencyNumberDbInstalled() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.notifyOtaEmergencyNumberDbInstalled();
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "notifyOtaEmergencyNumberDatabaseInstalled RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Override the file path for OTA emergency number database in a file partition.
+     *
+     * @param otaParcelFileDescriptor parcelable file descriptor for OTA emergency number database.
+     *
+     * <p> Requires permission:
+     * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @SystemApi
+    public void updateOtaEmergencyNumberDbFilePath(
+            @NonNull ParcelFileDescriptor otaParcelFileDescriptor) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.updateOtaEmergencyNumberDbFilePath(otaParcelFileDescriptor);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "updateOtaEmergencyNumberDbFilePath RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Reset the file path to default for OTA emergency number database in a file partition.
+     *
+     * <p> Requires permission:
+     * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    @SystemApi
+    public void resetOtaEmergencyNumberDbFilePath() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.resetOtaEmergencyNumberDbFilePath();
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "resetOtaEmergencyNumberDbFilePath RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Returns whether {@link TelephonyManager#ACTION_EMERGENCY_ASSISTANCE emergency assistance} is
+     * available on the device.
+     * <p>
+     * Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @return {@code true} if emergency assistance is available, {@code false} otherwise
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SuppressWarnings("AndroidFrameworkClientSidePermissionCheck")
+    @SystemApi
+    public boolean isEmergencyAssistanceEnabled() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+                "isEmergencyAssistanceEnabled");
+        return EMERGENCY_ASSISTANCE_ENABLED;
+    }
+
+    /**
+     * Get the emergency assistance package name.
+     *
+     * @return the package name of the emergency assistance app, or {@code null} if no app
+     * supports emergency assistance.
+     * @throws IllegalStateException if emergency assistance is not enabled or the device is
+     * not voice capable.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @FlaggedApi(android.permission.flags.Flags.FLAG_GET_EMERGENCY_ROLE_HOLDER_API_ENABLED)
+    @Nullable
+    @SystemApi
+    public String getEmergencyAssistancePackageName() {
+        if (!isEmergencyAssistanceEnabled() || !isVoiceCapable()) {
+            throw new IllegalStateException("isEmergencyAssistanceEnabled() is false or device"
+                + " not voice capable.");
+        }
+        return mContext.getSystemService(RoleManager.class)
+                .getEmergencyRoleHolder(mContext.getUserId());
+    }
+
+    /**
+     * Get the emergency number list based on current locale, sim, default, modem and network.
+     *
+     * <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher
+     * priority sources will be located at the smaller index; the priority order of sources are:
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG}
+     *
+     * <p>The subscriptions which the returned list would be based on, are all the active
+     * subscriptions, no matter which subscription could be used to create TelephonyManager.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return Map including the keys as the active subscription IDs (Note: if there is no active
+     * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
+     * as the list of {@link EmergencyNumber}; empty Map if this information is not available;
+     * or throw a SecurityException if the caller does not have the permission.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @NonNull
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList() {
+        Map<Integer, List<EmergencyNumber>> emergencyNumberList = new HashMap<>();
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getEmergencyNumberList(mContext.getOpPackageName(),
+                        mContext.getAttributionTag());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getEmergencyNumberList RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return emergencyNumberList;
+    }
+
+    /**
+     * Get the per-category emergency number list based on current locale, sim, default, modem
+     * and network.
+     *
+     * <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher
+     * priority sources will be located at the smaller index; the priority order of sources are:
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG}
+     *
+     * <p>The subscriptions which the returned list would be based on, are all the active
+     * subscriptions, no matter which subscription could be used to create TelephonyManager.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param categories the emergency service categories which are the bitwise-OR combination of
+     * the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+     * </ol>
+     * @return Map including the keys as the active subscription IDs (Note: if there is no active
+     * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
+     * as the list of {@link EmergencyNumber}; empty Map if this information is not available;
+     * or throw a SecurityException if the caller does not have the permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @NonNull
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList(
+            @EmergencyServiceCategories int categories) {
+        Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>();
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                Map<Integer, List<EmergencyNumber>> emergencyNumberList =
+                        telephony.getEmergencyNumberList(mContext.getOpPackageName(),
+                                mContext.getAttributionTag());
+                emergencyNumberListForCategories =
+                        filterEmergencyNumbersByCategories(emergencyNumberList, categories);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getEmergencyNumberList with Categories RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return emergencyNumberListForCategories;
+    }
+
+    /**
+     * Filter emergency numbers with categories.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public Map<Integer, List<EmergencyNumber>> filterEmergencyNumbersByCategories(
+            Map<Integer, List<EmergencyNumber>> emergencyNumberList,
+                    @EmergencyServiceCategories int categories) {
+        Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>();
+        if (emergencyNumberList != null) {
+            for (Integer subscriptionId : emergencyNumberList.keySet()) {
+                List<EmergencyNumber> allNumbersForSub = emergencyNumberList.get(
+                        subscriptionId);
+                List<EmergencyNumber> numbersForCategoriesPerSub = new ArrayList<>();
+                for (EmergencyNumber number : allNumbersForSub) {
+                    if (number.isInEmergencyServiceCategories(categories)) {
+                        numbersForCategoriesPerSub.add(number);
+                    }
+                }
+                emergencyNumberListForCategories.put(
+                        subscriptionId, numbersForCategoriesPerSub);
+            }
+        }
+        return emergencyNumberListForCategories;
+    }
+
+    /**
+     * Identifies if the supplied phone number is an emergency number that matches a known
+     * emergency number based on current locale, SIM card(s), Android database, modem, network,
+     * or defaults.
+     *
+     * <p>This method assumes that only dialable phone numbers are passed in; non-dialable
+     * numbers are not considered emergency numbers. A dialable phone number consists only
+     * of characters/digits identified by {@link PhoneNumberUtils#isDialable(char)}.
+     *
+     * <p>The subscriptions which the identification would be based on, are all the active
+     * subscriptions, no matter which subscription could be used to create TelephonyManager.
+     *
+     * @param number - the number to look up
+     * @return {@code true} if the given number is an emergency number based on current locale,
+     * SIM card(s), Android database, modem, network or defaults; {@code false} otherwise.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public boolean isEmergencyNumber(@NonNull String number) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isEmergencyNumber(number, true);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "isEmergencyNumber RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return false;
+    }
+
+    /**
+     * Checks if the supplied number is an emergency number based on current locale, sim, default,
+     * modem and network.
+     *
+     * <p> Specifically, this method will return {@code true} if the specified number is an
+     * emergency number, *or* if the number simply starts with the same digits as any current
+     * emergency number.
+     *
+     * <p>The subscriptions which the identification would be based on, are all the active
+     * subscriptions, no matter which subscription could be used to create TelephonyManager.
+     *
+     * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
+     * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param number - the number to look up
+     * @return {@code true} if the given number is an emergency number or it simply starts with
+     * the same digits of any current emergency number based on current locale, sim, modem and
+     * network; {@code false} if it is not; or throw an SecurityException if the caller does not
+     * have the required permission/privileges
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     *
+     * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)} instead.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public boolean isPotentialEmergencyNumber(@NonNull String number) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isEmergencyNumber(number, false);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "isEmergencyNumber RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return false;
+    }
+
+    /**
+     * Returns the emergency number database version.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public int getEmergencyNumberDbVersion() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getEmergencyNumberDbVersion(getSubId());
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getEmergencyNumberDbVersion RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return INVALID_EMERGENCY_NUMBER_DB_VERSION;
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"SET_OPPORTUNISTIC_SUB"}, value = {
+            SET_OPPORTUNISTIC_SUB_SUCCESS,
+            SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED,
+            SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION,
+            SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE,
+            SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION})
+    public @interface SetOpportunisticSubscriptionResult {}
+
+    /**
+     * No error. Operation succeeded.
+     */
+    public static final int SET_OPPORTUNISTIC_SUB_SUCCESS = 0;
+
+    /**
+     * Validation failed when trying to switch to preferred subscription.
+     */
+    public static final int SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED = 1;
+
+    /**
+     * The subscription is not valid. It must be an active opportunistic subscription.
+     */
+    public static final int SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION = 2;
+
+    /**
+     * The subscription is not valid. It must be an opportunistic subscription.
+     */
+    public static final int SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE = 3;
+
+    /**
+     * Subscription service happened remote exception.
+     */
+    public static final int SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION = 4;
+
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"UPDATE_AVAILABLE_NETWORKS"}, value = {
+            UPDATE_AVAILABLE_NETWORKS_SUCCESS,
+            UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE,
+            UPDATE_AVAILABLE_NETWORKS_ABORTED,
+            UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS,
+            UPDATE_AVAILABLE_NETWORKS_NO_CARRIER_PRIVILEGE,
+            UPDATE_AVAILABLE_NETWORKS_DISABLE_MODEM_FAIL,
+            UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL,
+            UPDATE_AVAILABLE_NETWORKS_MULTIPLE_NETWORKS_NOT_SUPPORTED,
+            UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE,
+            UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION,
+            UPDATE_AVAILABLE_NETWORKS_SERVICE_IS_DISABLED,
+            UPDATE_AVAILABLE_NETWORKS_SIM_PORT_NOT_AVAILABLE})
+    public @interface UpdateAvailableNetworksResult {}
+
+    /**
+     * No error. Operation succeeded.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_SUCCESS = 0;
+
+    /**
+     * There is a unknown failure happened.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE = 1;
+
+    /**
+     * The request is aborted.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_ABORTED = 2;
+
+    /**
+     * The parameter passed in is invalid.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS = 3;
+
+    /**
+     * No carrier privilege.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_NO_CARRIER_PRIVILEGE = 4;
+
+    /**
+     * Disable modem fail.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_DISABLE_MODEM_FAIL = 5;
+
+    /**
+     * Enable modem fail.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_ENABLE_MODEM_FAIL = 6;
+
+    /**
+     * Carrier app does not support multiple available networks.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_MULTIPLE_NETWORKS_NOT_SUPPORTED = 7;
+
+    /**
+     * The subscription is not valid. It must be an opportunistic subscription.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE = 8;
+
+    /**
+     * There is no OpportunisticNetworkService.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION = 9;
+
+    /**
+     * OpportunisticNetworkService is disabled.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_SERVICE_IS_DISABLED = 10;
+
+    /**
+     * SIM port is not available to switch to opportunistic subscription.
+     * @hide
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_SIM_PORT_NOT_AVAILABLE = 11;
+
+    /**
+     * Set preferred opportunistic data subscription id.
+     *
+     * Switch internet data to preferred opportunistic data subscription id. This api
+     * can result in lose of internet connectivity for short period of time while internet data
+     * is handed over.
+     * <p>Requires that the calling app has carrier privileges on both primary and
+     * secondary subscriptions (see
+     * {@link #hasCarrierPrivileges}), or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param subId which opportunistic subscription
+     * {@link SubscriptionManager#getOpportunisticSubscriptions} is preferred for cellular data.
+     * Pass {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} to unset the preference
+     * @param needValidation whether validation is needed before switch happens.
+     * @param executor The executor of where the callback will execute.
+     * @param callback Callback will be triggered once it succeeds or failed.
+     *                 See the {@code SET_OPPORTUNISTIC_SUB_*} constants
+     *                 for more details. Pass null if don't care about the result.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public void setPreferredOpportunisticDataSubscription(int subId, boolean needValidation,
+            @Nullable @CallbackExecutor Executor executor, @Nullable Consumer<Integer> callback) {
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        try {
+            IOns iOpportunisticNetworkService = getIOns();
+            if (iOpportunisticNetworkService == null) {
+                if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) {
+                    throw new IllegalStateException("Opportunistic Network Service is null");
+                } else {
+                    // Let the general remote exception handling catch this.
+                    throw new RemoteException("Null Opportunistic Network Service!");
+                }
+            }
+            ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() {
+                @Override
+                public void onComplete(int result) {
+                    if (executor == null || callback == null) {
+                        return;
+                    }
+                    final long identity = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> {
+                            callback.accept(result);
+                        });
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
+                }
+            };
+
+            iOpportunisticNetworkService
+                    .setPreferredDataSubscriptionId(subId, needValidation, callbackStub,
+                            pkgForDebug);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setPreferredOpportunisticDataSubscription RemoteException", ex);
+            if (executor == null || callback == null) {
+                return;
+            }
+            runOnBackgroundThread(() -> executor.execute(() -> {
+                if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                    callback.accept(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
+                } else {
+                    callback.accept(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
+                }
+            }));
+        }
+    }
+
+    /**
+     * Get preferred opportunistic data subscription Id
+     *
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}),
+     * or has either READ_PRIVILEGED_PHONE_STATE
+     * or {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission.
+     * @return subId preferred opportunistic subscription id or
+     * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred
+     * subscription id
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE
+    })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public int getPreferredOpportunisticDataSubscription() {
+        String packageName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        String attributionTag = mContext != null ? mContext.getAttributionTag() : null;
+        int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        try {
+            IOns iOpportunisticNetworkService = getIOns();
+            if (iOpportunisticNetworkService != null) {
+                subId = iOpportunisticNetworkService.getPreferredDataSubscriptionId(
+                        packageName, attributionTag);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getPreferredDataSubscriptionId RemoteException", ex);
+        }
+        return subId;
+    }
+
+    /**
+     * Update availability of a list of networks in the current location.
+     *
+     * This api should be called to inform OpportunisticNetwork Service about the availability
+     * of a network at the current location. This information will be used by OpportunisticNetwork
+     * service to enable modem stack and to attach to the network. If an empty list is passed,
+     * it is assumed that no network is available and will result in disabling the modem stack
+     * to save power. This api do not switch internet data once network attach is completed.
+     * Use {@link TelephonyManager#setPreferredOpportunisticDataSubscription}
+     * to switch internet data after network attach is complete.
+     * Requires that the calling app has carrier privileges on both primary and
+     * secondary subscriptions (see {@link #hasCarrierPrivileges}), or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * @param availableNetworks is a list of available network information.
+     * @param executor The executor of where the callback will execute.
+     * @param callback Callback will be triggered once it succeeds or failed.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void updateAvailableNetworks(@NonNull List<AvailableNetworkInfo> availableNetworks,
+            @Nullable @CallbackExecutor Executor executor,
+            @UpdateAvailableNetworksResult @Nullable Consumer<Integer> callback) {
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        Objects.requireNonNull(availableNetworks, "availableNetworks must not be null.");
+        try {
+            IOns iOpportunisticNetworkService = getIOns();
+            if (iOpportunisticNetworkService == null) {
+                if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) {
+                    throw new IllegalStateException("Opportunistic Network Service is null");
+                } else {
+                    // Let the general remote exception handling catch this.
+                    throw new RemoteException("Null Opportunistic Network Service!");
+                }
+            }
+
+            IUpdateAvailableNetworksCallback callbackStub =
+                    new IUpdateAvailableNetworksCallback.Stub() {
+                        @Override
+                        public void onComplete(int result) {
+                            if (executor == null || callback == null) {
+                                return;
+                            }
+                            Binder.withCleanCallingIdentity(() -> {
+                                executor.execute(() -> callback.accept(result));
+                            });
+                        }
+                    };
+            iOpportunisticNetworkService
+                    .updateAvailableNetworks(availableNetworks, callbackStub, pkgForDebug);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "updateAvailableNetworks RemoteException", ex);
+            if (executor == null || callback == null) {
+                return;
+            }
+            runOnBackgroundThread(() -> executor.execute(() -> {
+                if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                    callback.accept(UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION);
+                } else {
+                    callback.accept(UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE);
+                }
+            }));
+        }
+    }
+
+    /**
+     * Enable or disable a logical modem stack. When a logical modem is disabled, the corresponding
+     * SIM will still be visible to the user but its mapping modem will not have any radio activity.
+     * For example, we will disable a modem when user or system believes the corresponding SIM
+     * is temporarily not needed (e.g. out of coverage), and will enable it back on when needed.
+     *
+     * Requires that the calling app has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * @param slotIndex which corresponding modem will operate on.
+     * @param enable whether to enable or disable the modem stack.
+     * @return whether the operation is successful.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
+    public boolean enableModemForSlot(int slotIndex, boolean enable) {
+        boolean ret = false;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ret = telephony.enableModemForSlot(slotIndex, enable);
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "enableModem RemoteException", ex);
+        }
+        return ret;
+    }
+
+    /**
+     * Indicates whether or not there is a modem stack enabled for the given SIM slot.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE},
+     * READ_PRIVILEGED_PHONE_STATE or that the calling app has carrier privileges (see
+     * {@link #hasCarrierPrivileges()}).
+     *
+     * @param slotIndex which slot it's checking.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
+    public boolean isModemEnabledForSlot(int slotIndex) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isModemEnabledForSlot(slotIndex, mContext.getOpPackageName(),
+                        mContext.getAttributionTag());
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "enableModem RemoteException", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Broadcast intent action for network country code changes.
+     *
+     * <p>
+     * The {@link #EXTRA_NETWORK_COUNTRY} extra indicates the country code of the current
+     * network returned by {@link #getNetworkCountryIso()}.
+     *
+     * <p>There may be a delay of several minutes before reporting that no country is detected.
+     *
+     * @see #EXTRA_NETWORK_COUNTRY
+     * @see #getNetworkCountryIso()
+     */
+    public static final String ACTION_NETWORK_COUNTRY_CHANGED =
+            "android.telephony.action.NETWORK_COUNTRY_CHANGED";
+
+    /**
+     * The extra used with an {@link #ACTION_NETWORK_COUNTRY_CHANGED} to specify the
+     * the country code in ISO-3166-1 alpha-2 format. This is the same country code returned by
+     * {@link #getNetworkCountryIso()}. This might be an empty string when the country code is not
+     * available.
+     *
+     * <p class="note">
+     * Retrieve with {@link android.content.Intent#getStringExtra(String)}.
+     */
+    public static final String EXTRA_NETWORK_COUNTRY =
+            "android.telephony.extra.NETWORK_COUNTRY";
+
+    /**
+     * The extra used with an {@link #ACTION_NETWORK_COUNTRY_CHANGED} to specify the
+     * last known the country code in ISO-3166-1 alpha-2 format. This might be an empty string when
+     * the country code was never available. The last known country code persists across reboot.
+     *
+     * <p class="note">
+     * Retrieve with {@link android.content.Intent#getStringExtra(String)}.
+     */
+    public static final String EXTRA_LAST_KNOWN_NETWORK_COUNTRY =
+            "android.telephony.extra.LAST_KNOWN_NETWORK_COUNTRY";
+
+    /**
+     * Indicate if the user is allowed to use multiple SIM cards at the same time to register
+     * on the network (e.g. Dual Standby or Dual Active) when the device supports it, or if the
+     * usage is restricted. This API is used to prevent usage of multiple SIM card, based on
+     * policies of the carrier.
+     * <p>Note: the API does not prevent access to the SIM cards for operations that don't require
+     * access to the network.
+     *
+     * @param isMultiSimCarrierRestricted true if usage of multiple SIMs is restricted, false
+     * otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)
+    public void setMultiSimCarrierRestriction(boolean isMultiSimCarrierRestricted) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.setMultiSimCarrierRestriction(isMultiSimCarrierRestricted);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "setMultiSimCarrierRestriction RemoteException", e);
+        }
+    }
+
+    /**
+     * The usage of multiple SIM cards at the same time to register on the network (e.g. Dual
+     * Standby or Dual Active) is supported.
+     */
+    public static final int MULTISIM_ALLOWED = 0;
+
+    /**
+     * The usage of multiple SIM cards at the same time to register on the network (e.g. Dual
+     * Standby or Dual Active) is not supported by the hardware.
+     */
+    public static final int MULTISIM_NOT_SUPPORTED_BY_HARDWARE = 1;
+
+    /**
+     * The usage of multiple SIM cards at the same time to register on the network (e.g. Dual
+     * Standby or Dual Active) is supported by the hardware, but restricted by the carrier.
+     */
+    public static final int MULTISIM_NOT_SUPPORTED_BY_CARRIER = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"MULTISIM_"},
+            value = {
+                    MULTISIM_ALLOWED,
+                    MULTISIM_NOT_SUPPORTED_BY_HARDWARE,
+                    MULTISIM_NOT_SUPPORTED_BY_CARRIER
+            })
+    public @interface IsMultiSimSupportedResult {}
+
+    /**
+     * Returns if the usage of multiple SIM cards at the same time to register on the network
+     * (e.g. Dual Standby or Dual Active) is supported by the device and by the carrier.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return {@link #MULTISIM_ALLOWED} if the device supports multiple SIMs.
+     * {@link #MULTISIM_NOT_SUPPORTED_BY_HARDWARE} if the device does not support multiple SIMs.
+     * {@link #MULTISIM_NOT_SUPPORTED_BY_CARRIER} in the device supports multiple SIMs, but the
+     * functionality is restricted by the carrier.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @IsMultiSimSupportedResult
+    public int isMultiSimSupported() {
+        if (getSupportedModemCount() < 2) {
+            return TelephonyManager.MULTISIM_NOT_SUPPORTED_BY_HARDWARE;
+        }
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.isMultiSimSupported(getOpPackageName(), getAttributionTag());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "isMultiSimSupported RemoteException", e);
+        }
+        return MULTISIM_NOT_SUPPORTED_BY_HARDWARE;
+    }
+
+    /**
+     * Switch configs to enable multi-sim or switch back to single-sim
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * Note: with only carrier privileges, it is not allowed to switch from multi-sim
+     * to single-sim
+     *
+     * @param numOfSims number of live SIMs we want to switch to
+     * @throws android.os.RemoteException
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public void switchMultiSimConfig(int numOfSims) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.switchMultiSimConfig(numOfSims);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "switchMultiSimConfig RemoteException", ex);
+        }
+    }
+
+    /**
+     * Get whether making changes to modem configurations by {@link #switchMultiSimConfig(int)} will
+     * trigger device reboot.
+     * The modem configuration change refers to switching from single SIM configuration to DSDS
+     * or the other way around.
+     *
+     *  <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return {@code true} if reboot will be triggered after making changes to modem
+     * configurations, otherwise return {@code false}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public boolean doesSwitchMultiSimConfigTriggerReboot() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.doesSwitchMultiSimConfigTriggerReboot(getSubId(),
+                        getOpPackageName(), getAttributionTag());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "doesSwitchMultiSimConfigTriggerReboot RemoteException", e);
+        }
+        return false;
+    }
+
+    /**
+     * Retrieve the Radio HAL Version for this device.
+     *
+     * Get the HAL version for the IRadio interface for test purposes.
+     *
+     * @return a Pair of (major version, minor version) or (-1,-1) if unknown.
+     *
+     * @hide
+     *
+     * @deprecated Use {@link #getHalVersion} instead.
+     */
+    @Deprecated
+    @UnsupportedAppUsage
+    @TestApi
+    public Pair<Integer, Integer> getRadioHalVersion() {
+        return getHalVersion(HAL_SERVICE_RADIO);
+    }
+
+    /** @hide */
+    public static final int HAL_SERVICE_RADIO = 0;
+
+    /**
+     * HAL service type that supports the HAL APIs implementation of IRadioData
+     * {@link RadioDataProxy}
+     * @hide
+     */
+    @TestApi
+    public static final int HAL_SERVICE_DATA = 1;
+
+    /**
+     * HAL service type that supports the HAL APIs implementation of IRadioMessaging
+     * {@link RadioMessagingProxy}
+     * @hide
+     */
+    @TestApi
+    public static final int HAL_SERVICE_MESSAGING = 2;
+
+    /**
+     * HAL service type that supports the HAL APIs implementation of IRadioModem
+     * {@link RadioModemProxy}
+     * @hide
+     */
+    @TestApi
+    public static final int HAL_SERVICE_MODEM = 3;
+
+    /**
+     * HAL service type that supports the HAL APIs implementation of IRadioNetwork
+     * {@link RadioNetworkProxy}
+     * @hide
+     */
+    @TestApi
+    public static final int HAL_SERVICE_NETWORK = 4;
+
+    /**
+     * HAL service type that supports the HAL APIs implementation of IRadioSim
+     * {@link RadioSimProxy}
+     * @hide
+     */
+    @TestApi
+    public static final int HAL_SERVICE_SIM = 5;
+
+    /**
+     * HAL service type that supports the HAL APIs implementation of IRadioVoice
+     * {@link RadioVoiceProxy}
+     * @hide
+     */
+    @TestApi
+    public static final int HAL_SERVICE_VOICE = 6;
+
+    /**
+     * HAL service type that supports the HAL APIs implementation of IRadioIms
+     * {@link RadioImsProxy}
+     * @hide
+     */
+    @TestApi
+    public static final int HAL_SERVICE_IMS = 7;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"HAL_SERVICE_"},
+            value = {
+                    HAL_SERVICE_RADIO,
+                    HAL_SERVICE_DATA,
+                    HAL_SERVICE_MESSAGING,
+                    HAL_SERVICE_MODEM,
+                    HAL_SERVICE_NETWORK,
+                    HAL_SERVICE_SIM,
+                    HAL_SERVICE_VOICE,
+                    HAL_SERVICE_IMS,
+            })
+    public @interface HalService {}
+
+    /**
+     * The HAL Version indicating that the version is unknown or invalid.
+     * @hide
+     */
+    @TestApi
+    public static final Pair HAL_VERSION_UNKNOWN = new Pair(-1, -1);
+
+    /**
+     * The HAL Version indicating that the version is unsupported.
+     * @hide
+     */
+    @TestApi
+    public static final Pair HAL_VERSION_UNSUPPORTED = new Pair(-2, -2);
+
+    /**
+     * Retrieve the HAL Version of a specific service for this device.
+     *
+     * Get the HAL version for a specific HAL interface for test purposes.
+     *
+     * @param halService the service id to query.
+     * @return a Pair of (major version, minor version), HAL_VERSION_UNKNOWN if unknown
+     * or HAL_VERSION_UNSUPPORTED if unsupported.
+     *
+     * @hide
+     */
+    @TestApi
+    public @NonNull Pair<Integer, Integer> getHalVersion(@HalService int halService) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                int version = service.getHalVersion(halService);
+                if (version != -1) {
+                    return new Pair<Integer, Integer>(version / 100, version % 100);
+                }
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "getHalVersion() RemoteException", e);
+            e.rethrowAsRuntimeException();
+        }
+        return HAL_VERSION_UNKNOWN;
+    }
+
+    /**
+     * Get the calling application status about carrier privileges for the subscription created
+     * in TelephonyManager. Used by Telephony Module for permission checking.
+     *
+     * @param uid Uid to check.
+     * @return any value of {@link #CARRIER_PRIVILEGE_STATUS_HAS_ACCESS},
+     *         {@link #CARRIER_PRIVILEGE_STATUS_NO_ACCESS},
+     *         {@link #CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED}, or
+     *         {@link #CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @CarrierPrivilegeStatus int getCarrierPrivilegeStatus(int uid) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getCarrierPrivilegeStatusForUid(getSubId(), uid);
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getCarrierPrivilegeStatus RemoteException", ex);
+        }
+        return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+    }
+
+    /**
+     * Returns a list of APNs set as overrides by the device policy manager via
+     * {@link #addDevicePolicyOverrideApn}.
+     * This method must only be called from the system or phone processes.
+     *
+     * @param context Context to use.
+     * @return {@link List} of APNs that have been set as overrides.
+     * @throws {@link SecurityException} if the caller is not the system or phone process.
+     * @hide
+     */
+    @TestApi
+    // TODO: add new permission tag indicating that this is system-only.
+    public @NonNull List<ApnSetting> getDevicePolicyOverrideApns(@NonNull Context context) {
+        try (Cursor cursor = context.getContentResolver().query(DPC_URI, null, null, null, null)) {
+            if (cursor == null) {
+                return Collections.emptyList();
+            }
+            List<ApnSetting> apnList = new ArrayList<ApnSetting>();
+            cursor.moveToPosition(-1);
+            while (cursor.moveToNext()) {
+                ApnSetting apn = ApnSetting.makeApnSetting(cursor);
+                apnList.add(apn);
+            }
+            return apnList;
+        }
+    }
+
+    /**
+     * Used by the device policy manager to add a new override APN.
+     * This method must only be called from the system or phone processes.
+     *
+     * @param context Context to use.
+     * @param apnSetting The {@link ApnSetting} describing the new APN.
+     * @return An integer, corresponding to a primary key in a database, that allows the caller to
+     *         modify the APN in the future via {@link #modifyDevicePolicyOverrideApn}, or
+     *         {@link android.provider.Telephony.Carriers.INVALID_APN_ID} if the override operation
+     *         failed.
+     * @throws {@link SecurityException} if the caller is not the system or phone process.
+     * @hide
+     */
+    @TestApi
+    // TODO: add new permission tag indicating that this is system-only.
+    public int addDevicePolicyOverrideApn(@NonNull Context context,
+            @NonNull ApnSetting apnSetting) {
+        Uri resultUri = context.getContentResolver().insert(DPC_URI, apnSetting.toContentValues());
+
+        int resultId = INVALID_APN_ID;
+        if (resultUri != null) {
+            try {
+                resultId = Integer.parseInt(resultUri.getLastPathSegment());
+            } catch (NumberFormatException e) {
+                Rlog.e(TAG, "Failed to parse inserted override APN id: "
+                        + resultUri.getLastPathSegment());
+            }
+        }
+        return resultId;
+    }
+
+    /**
+     * Used by the device policy manager to modify an override APN.
+     * This method must only be called from the system or phone processes.
+     *
+     * @param context Context to use.
+     * @param apnId The integer key of the APN to modify, as returned by
+     *              {@link #addDevicePolicyOverrideApn}
+     * @param apnSetting The {@link ApnSetting} describing the updated APN.
+     * @return {@code true} if successful, {@code false} otherwise.
+     * @throws {@link SecurityException} if the caller is not the system or phone process.
+     * @hide
+     */
+    @TestApi
+    // TODO: add new permission tag indicating that this is system-only.
+    public boolean modifyDevicePolicyOverrideApn(@NonNull Context context, int apnId,
+            @NonNull ApnSetting apnSetting) {
+        return context.getContentResolver().update(
+                Uri.withAppendedPath(DPC_URI, Integer.toString(apnId)),
+                apnSetting.toContentValues(), null, null) > 0;
+    }
+
+    /**
+     * Return whether data is enabled for certain APN type. This will tell if framework will accept
+     * corresponding network requests on a subId.
+     *
+     * {@link #isDataEnabled()} is directly associated with users' Mobile data toggle on / off. If
+     * {@link #isDataEnabled()} returns false, it means in general all meter-ed data are disabled.
+     *
+     * This per APN type API gives a better idea whether data is allowed on a specific APN type.
+     * It will return true if:
+     *
+     *  1) User data is turned on, or
+     *  2) APN is un-metered for this subscription, or
+     *  3) APN type is allowlisted. E.g. MMS is allowlisted if
+     *  {@link #MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED} is enabled.
+     *
+     * @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
+     * @return whether data is enabled for a apn type.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean isDataEnabledForApn(@ApnType int apnType) {
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.isDataEnabledForApn(apnType, getSubId(), pkgForDebug);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Telephony#isDataEnabledForApn RemoteException" + ex);
+        }
+        return false;
+    }
+
+    /**
+     * Whether an APN type is metered or not. It will be evaluated with the subId associated
+     * with the TelephonyManager instance.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean isApnMetered(@ApnType int apnType) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.isApnMetered(apnType, getSubId());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Telephony#isApnMetered RemoteException" + ex);
+        }
+        return true;
+    }
+
+    /**
+     * Specify which bands modem's background scan must act on.
+     * If {@code specifiers} is non-empty, the scan will be restricted to the bands specified.
+     * Otherwise, it scans all bands.
+     *
+     * For example, CBRS is only on LTE band 48. By specifying this band,
+     * modem saves more power.
+     *
+     * @param specifiers which bands to scan.
+     * @param executor The executor to execute the callback on
+     * @param callback The callback that gets invoked when the radio responds to the request. Called
+     *                 with {@code true} if the request succeeded, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void setSystemSelectionChannels(@NonNull List<RadioAccessSpecifier> specifiers,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Boolean> callback) {
+        Objects.requireNonNull(specifiers, "Specifiers must not be null.");
+        Objects.requireNonNull(executor, "Executor must not be null.");
+        Objects.requireNonNull(callback, "Callback must not be null.");
+        setSystemSelectionChannelsInternal(specifiers, executor, callback);
+    }
+
+    /**
+     * Same as {@link #setSystemSelectionChannels(List, Executor, Consumer<Boolean>)}, but to be
+     * used when the caller does not need feedback on the results of the operation.
+     * @param specifiers which bands to scan.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void setSystemSelectionChannels(@NonNull List<RadioAccessSpecifier> specifiers) {
+        Objects.requireNonNull(specifiers, "Specifiers must not be null.");
+        setSystemSelectionChannelsInternal(specifiers, null, null);
+    }
+
+
+    private void setSystemSelectionChannelsInternal(@NonNull List<RadioAccessSpecifier> specifiers,
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable Consumer<Boolean> callback) {
+        IBooleanConsumer aidlConsumer = callback == null ? null : new IBooleanConsumer.Stub() {
+            @Override
+            public void accept(boolean result) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> callback.accept(result));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        };
+
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.setSystemSelectionChannels(specifiers, getSubId(), aidlConsumer);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Telephony#setSystemSelectionChannels RemoteException" + ex);
+        }
+    }
+
+    /**
+     * Get which bands the modem's background scan is acting on, specified by
+     * {@link #setSystemSelectionChannels}.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return a list of {@link RadioAccessSpecifier}, or an empty list if no bands are specified.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public @NonNull List<RadioAccessSpecifier> getSystemSelectionChannels() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getSystemSelectionChannels(getSubId());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Telephony#getSystemSelectionChannels RemoteException" + ex);
+        }
+        return new ArrayList<>();
+    }
+
+    /**
+     * Verifies whether the input MCC/MNC and MVNO correspond to the current carrier.
+     *
+     * @param mccmnc the carrier's mccmnc that you want to match
+     * @param mvnoType the mvnoType that defined in {@link ApnSetting}
+     * @param mvnoMatchData the MVNO match data
+     * @return {@code true} if input mccmnc and mvno matches with data from sim operator.
+     * {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * {@hide}
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public boolean matchesCurrentSimOperator(@NonNull String mccmnc, @MvnoType int mvnoType,
+            @Nullable String mvnoMatchData) {
+        try {
+            if (!mccmnc.equals(getSimOperator())) {
+                return false;
+            }
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.isMvnoMatched(getSlotIndex(), mvnoType, mvnoMatchData);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Telephony#matchesCurrentSimOperator RemoteException" + ex);
+        }
+        return false;
+    }
+
+    /**
+     * Callback to be used with {@link #getCallForwarding}
+     * @hide
+     */
+    @SystemApi
+    public interface CallForwardingInfoCallback {
+        /**
+         * Indicates that the operation was successful.
+         */
+        int RESULT_SUCCESS = 0;
+
+        /**
+         * Indicates that setting or retrieving the call forwarding info failed with an unknown
+         * error.
+         */
+        int RESULT_ERROR_UNKNOWN = 1;
+
+        /**
+         * Indicates that call forwarding is not enabled because the recipient is not on a
+         * Fixed Dialing Number (FDN) list.
+         */
+        int RESULT_ERROR_FDN_CHECK_FAILURE = 2;
+
+        /**
+         * Indicates that call forwarding is not supported on the network at this time.
+         */
+        int RESULT_ERROR_NOT_SUPPORTED = 3;
+
+        /**
+         * Call forwarding errors
+         * @hide
+         */
+        @IntDef(prefix = { "RESULT_ERROR_" }, value = {
+                RESULT_ERROR_UNKNOWN,
+                RESULT_ERROR_NOT_SUPPORTED,
+                RESULT_ERROR_FDN_CHECK_FAILURE
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface CallForwardingError{
+        }
+        /**
+         * Called when the call forwarding info is successfully retrieved from the network.
+         * @param info information about how calls are forwarded
+         */
+        void onCallForwardingInfoAvailable(@NonNull CallForwardingInfo info);
+
+        /**
+         * Called when there was an error retrieving the call forwarding information.
+         * @param error
+         */
+        void onError(@CallForwardingError int error);
+    }
+
+    /**
+     * Gets the voice call forwarding info for a given call forwarding reason.
+     *
+     * This method queries the network for the currently set call forwarding configuration for the
+     * provided call forwarding reason. When the network has provided its response, the result will
+     * be supplied via the provided {@link Executor} on the provided
+     * {@link CallForwardingInfoCallback}.
+     *
+     * @param callForwardingReason the call forwarding reason to query.
+     * @param executor The executor on which to execute the callback once the result is ready.
+     * @param callback The callback the results should be delivered on.
+     *
+     * @throws IllegalArgumentException if callForwardingReason is not any of
+     * {@link CallForwardingInfo#REASON_UNCONDITIONAL}, {@link CallForwardingInfo#REASON_BUSY},
+     * {@link CallForwardingInfo#REASON_NO_REPLY}, {@link CallForwardingInfo#REASON_NOT_REACHABLE},
+     * {@link CallForwardingInfo#REASON_ALL}, or {@link CallForwardingInfo#REASON_ALL_CONDITIONAL}
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void getCallForwarding(@CallForwardingReason int callForwardingReason,
+            @NonNull Executor executor, @NonNull CallForwardingInfoCallback callback) {
+        if (callForwardingReason < CallForwardingInfo.REASON_UNCONDITIONAL
+                || callForwardingReason > CallForwardingInfo.REASON_ALL_CONDITIONAL) {
+            throw new IllegalArgumentException("callForwardingReason is out of range");
+        }
+
+        ICallForwardingInfoCallback internalCallback = new ICallForwardingInfoCallback.Stub() {
+            @Override
+            public void onCallForwardingInfoAvailable(CallForwardingInfo info) {
+                executor.execute(() ->
+                        Binder.withCleanCallingIdentity(() ->
+                                callback.onCallForwardingInfoAvailable(info)));
+            }
+
+            @Override
+            public void onError(int error) {
+                executor.execute(() ->
+                        Binder.withCleanCallingIdentity(() ->
+                                callback.onError(error)));
+            }
+        };
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.getCallForwarding(getSubId(), callForwardingReason, internalCallback);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getCallForwarding RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Sets voice call forwarding behavior as described by the provided {@link CallForwardingInfo}.
+     *
+     * This method will enable call forwarding if the provided {@link CallForwardingInfo} returns
+     * {@code true} from its {@link CallForwardingInfo#isEnabled()} method, and disables call
+     * forwarding otherwise.
+     *
+     * If you wish to be notified about the results of this operation, provide an {@link Executor}
+     * and {@link Consumer<Integer>} to be notified asynchronously when the operation completes.
+     *
+     * @param callForwardingInfo Info about whether calls should be forwarded and where they
+     *                           should be forwarded to.
+     * @param executor The executor on which the listener will be called. Must be non-null if
+     *                 {@code listener} is non-null.
+     * @param resultListener Asynchronous listener that'll be called when the operation completes.
+     *                      Called with {@link CallForwardingInfoCallback#RESULT_SUCCESS} if the
+     *                      operation succeeded and an error code from
+     *                      {@link CallForwardingInfoCallback} it failed.
+     *
+     * @throws IllegalArgumentException if any of the following are true for the parameter
+     * callForwardingInfo:
+     * <ul>
+     * <li>it is {@code null}.</li>
+     * <li>{@link CallForwardingInfo#getReason()} is not any of:
+     *     <ul>
+     *         <li>{@link CallForwardingInfo#REASON_UNCONDITIONAL}</li>
+     *         <li>{@link CallForwardingInfo#REASON_BUSY}</li>
+     *         <li>{@link CallForwardingInfo#REASON_NO_REPLY}</li>
+     *         <li>{@link CallForwardingInfo#REASON_NOT_REACHABLE}</li>
+     *         <li>{@link CallForwardingInfo#REASON_ALL}</li>
+     *         <li>{@link CallForwardingInfo#REASON_ALL_CONDITIONAL}</li>
+     *     </ul>
+     * <li>{@link CallForwardingInfo#getNumber()} returns {@code null} when enabling call
+     * forwarding</li>
+     * <li>{@link CallForwardingInfo#getTimeoutSeconds()} returns a non-positive value when
+     * enabling call forwarding</li>
+     * </ul>
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void setCallForwarding(@NonNull CallForwardingInfo callForwardingInfo,
+            @Nullable @CallbackExecutor Executor executor,
+            @Nullable @CallForwardingInfoCallback.CallForwardingError
+                    Consumer<Integer> resultListener) {
+        if (callForwardingInfo == null) {
+            throw new IllegalArgumentException("callForwardingInfo is null");
+        }
+        int callForwardingReason = callForwardingInfo.getReason();
+        if (callForwardingReason < CallForwardingInfo.REASON_UNCONDITIONAL
+                || callForwardingReason > CallForwardingInfo.REASON_ALL_CONDITIONAL) {
+            throw new IllegalArgumentException("callForwardingReason is out of range");
+        }
+        if (callForwardingInfo.isEnabled()) {
+            if (callForwardingInfo.getNumber() == null) {
+                throw new IllegalArgumentException("callForwarding number is null");
+            }
+            if (callForwardingReason == CallForwardingInfo.REASON_NO_REPLY
+                        && callForwardingInfo.getTimeoutSeconds() <= 0) {
+                throw new IllegalArgumentException("callForwarding timeout isn't positive");
+            }
+        }
+        if (resultListener != null) {
+            Objects.requireNonNull(executor);
+        }
+
+        IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+            @Override
+            public void accept(int result) {
+                executor.execute(() ->
+                        Binder.withCleanCallingIdentity(() -> resultListener.accept(result)));
+            }
+        };
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setCallForwarding(getSubId(), callForwardingInfo, internalCallback);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setCallForwarding RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "setCallForwarding NPE", ex);
+            throw ex;
+        }
+    }
+
+    /**
+     * Indicates that call waiting is enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int CALL_WAITING_STATUS_ENABLED = 1;
+
+    /**
+     * Indicates that call waiting is disabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int CALL_WAITING_STATUS_DISABLED = 2;
+
+    /**
+     * Indicates there was an unknown error retrieving the call waiting status.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3;
+
+    /**
+     * Indicates the call waiting is not supported on the current network.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4;
+
+    /**
+     * Indicates the call waiting status could not be set or queried because the Fixed Dialing
+     * Numbers (FDN) feature is enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int CALL_WAITING_STATUS_FDN_CHECK_FAILURE = 5;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = {
+            CALL_WAITING_STATUS_ENABLED,
+            CALL_WAITING_STATUS_DISABLED,
+            CALL_WAITING_STATUS_UNKNOWN_ERROR,
+            CALL_WAITING_STATUS_NOT_SUPPORTED,
+            CALL_WAITING_STATUS_FDN_CHECK_FAILURE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallWaitingStatus {
+    }
+
+    /**
+     * Retrieves the call waiting status of this device from the network.
+     *
+     * When call waiting is enabled, an incoming call that arrives when the user is already on
+     * an active call will be held in a waiting state while the user is notified instead of being
+     * rejected with a busy signal.
+     *
+     * @param executor The executor on which the result listener will be called.
+     * @param resultListener A {@link Consumer} that will be called with the result fetched
+     *                       from the network. The result will be one of:
+     *                       <ul>
+     *                          <li>{@link #CALL_WAITING_STATUS_ENABLED}}</li>
+     *                          <li>{@link #CALL_WAITING_STATUS_DISABLED}}</li>
+     *                          <li>{@link #CALL_WAITING_STATUS_UNKNOWN_ERROR}}</li>
+     *                          <li>{@link #CALL_WAITING_STATUS_NOT_SUPPORTED}}</li>
+     *                          <li>{@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE}}</li>
+     *                       </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void getCallWaitingStatus(@NonNull Executor executor,
+            @NonNull @CallWaitingStatus Consumer<Integer> resultListener) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(resultListener);
+
+        IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+            @Override
+            public void accept(int result) {
+                executor.execute(() -> Binder.withCleanCallingIdentity(
+                        () -> resultListener.accept(result)));
+            }
+        };
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.getCallWaitingStatus(getSubId(), internalCallback);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getCallWaitingStatus RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "getCallWaitingStatus NPE", ex);
+            throw ex;
+        }
+    }
+
+    /**
+     * Sets the call waiting status of this device with the network.
+     *
+     * If you wish to be notified about the results of this operation, provide an {@link Executor}
+     * and {@link Consumer<Integer>} to be notified asynchronously when the operation completes.
+     *
+     * @see #getCallWaitingStatus for a description of the call waiting functionality.
+     *
+     * @param enabled {@code true} to enable; {@code false} to disable.
+     * @param executor The executor on which the listener will be called. Must be non-null if
+     *                 {@code listener} is non-null.
+     * @param resultListener Asynchronous listener that'll be called when the operation completes.
+     *                       Called with the new call waiting status (either
+     *                       {@link #CALL_WAITING_STATUS_ENABLED} or
+     *                       {@link #CALL_WAITING_STATUS_DISABLED} if the operation succeeded and
+     *                       {@link #CALL_WAITING_STATUS_NOT_SUPPORTED} or
+     *                       {@link #CALL_WAITING_STATUS_UNKNOWN_ERROR} or
+     *                       {@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE} if it failed.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public void setCallWaitingEnabled(boolean enabled, @Nullable Executor executor,
+            @Nullable Consumer<Integer> resultListener) {
+        if (resultListener != null) {
+            Objects.requireNonNull(executor);
+        }
+
+        IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+            @Override
+            public void accept(int result) {
+                executor.execute(() ->
+                        Binder.withCleanCallingIdentity(() -> resultListener.accept(result)));
+            }
+        };
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setCallWaitingStatus(getSubId(), enabled, internalCallback);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setCallWaitingStatus RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "setCallWaitingStatus NPE", ex);
+            throw ex;
+        }
+    }
+
+    /**
+     * Controls whether mobile data  on the non-default SIM is allowed during a voice call.
+     *
+     * This is used for allowing data on the non-default data SIM when a voice call is placed on
+     * the non-default data SIM on DSDS devices. If this policy is disabled, users will not be able
+     * to use mobile data via the non-default data SIM during the call, which may mean no mobile
+     * data at all since some modem implementations disallow mobile data via the default data SIM
+     * during voice calls.
+     * If this policy is enabled, data will be temporarily enabled on the non-default data SIM
+     * during any voice calls.
+     *
+     * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}.
+     * @hide
+     */
+    @SystemApi
+    public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1;
+
+    /**
+     * Controls whether MMS messages bypass the user-specified "mobile data" toggle.
+     *
+     * When enabled, requests for connections to the MMS APN will be accepted by telephony even if
+     * the user has turned "mobile data" off on this specific sim card. {@link #isDataEnabledForApn}
+     * will also return true for {@link ApnSetting#TYPE_MMS}.
+     * When disabled, the MMS APN will be governed by the same rules as all other APNs.
+     *
+     * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}.
+     * @hide
+     */
+    @SystemApi
+    public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2;
+
+    /**
+     * Allow switching mobile data to the non-default SIM if the non-default SIM has better
+     * availability.
+     *
+     * This is used for temporarily allowing data on the non-default data SIM when on-default SIM
+     * has better availability on DSDS devices, where better availability means strong
+     * signal/connectivity.
+     * If this policy is enabled, data will be temporarily enabled on the non-default data SIM,
+     * including during any voice calls(equivalent to enabling
+     * {@link #MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL}).
+     *
+     * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}.
+     * @hide
+     */
+    @SystemApi
+    public static final int MOBILE_DATA_POLICY_AUTO_DATA_SWITCH = 3;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "MOBILE_DATA_POLICY_" }, value = {
+            MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL,
+            MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED,
+            MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MobileDataPolicy { }
+
+    /**
+     * Enables or disables a piece of mobile data policy.
+     *
+     * Enables or disables the mobile data policy specified in {@code policy}. See the detailed
+     * description of each policy constant for what they do.
+     *
+     * @param policy The data policy to enable.
+     * @param enabled Whether to enable or disable the policy.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public void setMobileDataPolicyEnabled(@MobileDataPolicy int policy, boolean enabled) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.setMobileDataPolicyEnabled(getSubId(), policy, enabled);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Telephony#setMobileDataPolicyEnabled RemoteException" + ex);
+        }
+    }
+
+    /**
+     * Fetches the status of a piece of mobile data policy.
+     *
+     * @param policy The data policy that you want the status for.
+     * @return {@code true} if enabled, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean isMobileDataPolicyEnabled(@MobileDataPolicy int policy) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.isMobileDataPolicyEnabled(getSubId(), policy);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Telephony#isMobileDataPolicyEnabled RemoteException" + ex);
+        }
+        return false;
+    }
+
+    /**
+     * Indicates that the ICC PIN lock state or PIN was changed successfully.
+     * @hide
+     */
+    public static final int CHANGE_ICC_LOCK_SUCCESS = Integer.MAX_VALUE;
+
+    /**
+     * Check whether ICC PIN lock is enabled.
+     * This is a sync call which returns the cached PIN enabled state.
+     *
+     * @return {@code true} if ICC PIN lock enabled, {@code false} if disabled.
+     * @throws SecurityException if the caller doesn't have the permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @WorkerThread
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @SystemApi
+    public boolean isIccLockEnabled() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isIccLockEnabled(getSubId());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "isIccLockEnabled RemoteException", e);
+            e.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    /**
+     * Enable or disable the ICC PIN lock.
+     *
+     * @param enabled "true" for locked, "false" for unlocked.
+     * @param pin needed to change the ICC PIN lock, aka. Pin1.
+     * @return the result of enabling or disabling the ICC PIN lock.
+     * @throws SecurityException if the caller doesn't have the permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public PinResult setIccLockEnabled(boolean enabled, @NonNull String pin) {
+        checkNotNull(pin, "setIccLockEnabled pin can't be null.");
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                int result = telephony.setIccLockEnabled(getSubId(), enabled, pin);
+                if (result == CHANGE_ICC_LOCK_SUCCESS) {
+                    return new PinResult(PinResult.PIN_RESULT_TYPE_SUCCESS, 0);
+                } else if (result < 0) {
+                    return PinResult.getDefaultFailedResult();
+                } else {
+                    return new PinResult(PinResult.PIN_RESULT_TYPE_INCORRECT, result);
+                }
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "setIccLockEnabled RemoteException", e);
+            e.rethrowFromSystemServer();
+        }
+        return PinResult.getDefaultFailedResult();
+    }
+
+    /**
+     * Change the ICC lock PIN.
+     *
+     * @param oldPin is the old PIN
+     * @param newPin is the new PIN
+     * @return The result of changing the ICC lock PIN.
+     * @throws SecurityException if the caller doesn't have the permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public PinResult changeIccLockPin(@NonNull String oldPin, @NonNull String newPin) {
+        checkNotNull(oldPin, "changeIccLockPin oldPin can't be null.");
+        checkNotNull(newPin, "changeIccLockPin newPin can't be null.");
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                int result = telephony.changeIccLockPassword(getSubId(), oldPin, newPin);
+                if (result == CHANGE_ICC_LOCK_SUCCESS) {
+                    return new PinResult(PinResult.PIN_RESULT_TYPE_SUCCESS, 0);
+                } else if (result < 0) {
+                    return PinResult.getDefaultFailedResult();
+                } else {
+                    return new PinResult(PinResult.PIN_RESULT_TYPE_INCORRECT, result);
+                }
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "changeIccLockPin RemoteException", e);
+            e.rethrowFromSystemServer();
+        }
+        return PinResult.getDefaultFailedResult();
+    }
+
+    /**
+     * Called when userActivity is signalled in the power manager.
+     * This should only be called from system Uid.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void notifyUserActivity() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.userActivity();
+            }
+        } catch (RemoteException e) {
+            // one-way notification, if telephony is not available, it is okay to not throw
+            // exception here.
+            Log.w(TAG, "notifyUserActivity exception: " + e.getMessage());
+        }
+    }
+
+    /**
+     * No error. Operation succeeded.
+     * @hide
+     */
+    @SystemApi
+    public static final int ENABLE_NR_DUAL_CONNECTIVITY_SUCCESS = 0;
+
+    /**
+     * NR Dual connectivity enablement is not supported.
+     * @hide
+     */
+    @SystemApi
+    public static final int ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED = 1;
+
+    /**
+     * Radio is not available.
+     * @hide
+     */
+    @SystemApi
+    public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE = 2;
+
+    /**
+     * Internal Radio error.
+     * @hide
+     */
+    @SystemApi
+    public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR = 3;
+
+    /**
+     * Currently in invalid state. Not able to process the request.
+     * @hide
+     */
+    @SystemApi
+    public static final int ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE = 4;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"ENABLE_NR_DUAL_CONNECTIVITY"}, value = {
+            ENABLE_NR_DUAL_CONNECTIVITY_SUCCESS,
+            ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED,
+            ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE,
+            ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE,
+            ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR})
+    public @interface EnableNrDualConnectivityResult {}
+
+    /**
+     * Enable NR dual connectivity. Enabled state does not mean dual connectivity
+     * is active. It means device is allowed to connect to both primary and secondary.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int NR_DUAL_CONNECTIVITY_ENABLE = 1;
+
+    /**
+     * Disable NR dual connectivity. Disabled state does not mean the secondary cell is released.
+     * Modem will release it only if the current bearer is released to avoid radio link failure.
+     * @hide
+     */
+    @SystemApi
+    public static final int NR_DUAL_CONNECTIVITY_DISABLE = 2;
+
+    /**
+     * Disable NR dual connectivity and force the secondary cell to be released if dual connectivity
+     * was active. This will result in radio link failure.
+     * @hide
+     */
+    @SystemApi
+    public static final int NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE = 3;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "NR_DUAL_CONNECTIVITY_" }, value = {
+            NR_DUAL_CONNECTIVITY_ENABLE,
+            NR_DUAL_CONNECTIVITY_DISABLE,
+            NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NrDualConnectivityState {
+    }
+
+    /**
+     * Enable/Disable E-UTRA-NR Dual Connectivity.
+     *
+     * This api is supported only if
+     * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+     * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE})
+     * returns true.
+     * @param nrDualConnectivityState expected NR dual connectivity state
+     * This can be passed following states
+     * <ol>
+     * <li>Enable NR dual connectivity {@link #NR_DUAL_CONNECTIVITY_ENABLE}
+     * <li>Disable NR dual connectivity {@link #NR_DUAL_CONNECTIVITY_DISABLE}
+     * <li>Disable NR dual connectivity and force secondary cell to be released
+     * {@link #NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE}
+     * </ol>
+     * @return operation result.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)
+    public @EnableNrDualConnectivityResult int setNrDualConnectivityState(
+            @NrDualConnectivityState int nrDualConnectivityState) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.setNrDualConnectivityState(getSubId(), nrDualConnectivityState);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setNrDualConnectivityState RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+
+        return ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE;
+    }
+
+    /**
+     * Is E-UTRA-NR Dual Connectivity enabled.
+     * This api is supported only if
+     * {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+     * ({@link TelephonyManager#CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE})
+     * returns true.
+     * @return true if dual connectivity is enabled else false. Enabled state does not mean dual
+     * connectivity is active. It means the device is allowed to connect to both primary and
+     * secondary cell.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)
+    public boolean isNrDualConnectivityEnabled() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isNrDualConnectivityEnabled(getSubId());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "isNRDualConnectivityEnabled RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    private static class DeathRecipient implements IBinder.DeathRecipient {
+        @Override
+        public void binderDied() {
+            resetServiceCache();
+        }
+    }
+
+   /**
+    * Reset everything in the service cache; if one handle died then they are
+    * all probably broken.
+    * @hide
+    */
+    private static void resetServiceCache() {
+        synchronized (sCacheLock) {
+            if (sITelephony != null) {
+                sITelephony.asBinder().unlinkToDeath(sServiceDeath, 0);
+                sITelephony = null;
+            }
+            if (sISub != null) {
+                sISub.asBinder().unlinkToDeath(sServiceDeath, 0);
+                sISub = null;
+                SubscriptionManager.clearCaches();
+            }
+            if (sISms != null) {
+                sISms.asBinder().unlinkToDeath(sServiceDeath, 0);
+                sISms = null;
+            }
+            if (sIPhoneSubInfo != null) {
+                sIPhoneSubInfo.asBinder().unlinkToDeath(sServiceDeath, 0);
+                sIPhoneSubInfo = null;
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    static IPhoneSubInfo getSubscriberInfoService() {
+        // Keeps cache disabled until test fixes are checked into AOSP.
+        if (!sServiceHandleCacheEnabled) {
+            return IPhoneSubInfo.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getPhoneSubServiceRegisterer()
+                        .get());
+        }
+
+        if (sIPhoneSubInfo == null) {
+            IPhoneSubInfo temp = IPhoneSubInfo.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getPhoneSubServiceRegisterer()
+                        .get());
+            synchronized (sCacheLock) {
+                if (sIPhoneSubInfo == null && temp != null) {
+                    try {
+                        sIPhoneSubInfo = temp;
+                        sIPhoneSubInfo.asBinder().linkToDeath(sServiceDeath, 0);
+                    } catch (Exception e) {
+                        // something has gone horribly wrong
+                        sIPhoneSubInfo = null;
+                    }
+                }
+            }
+        }
+        return sIPhoneSubInfo;
+    }
+
+   /**
+    * @hide
+    */
+    static ISub getSubscriptionService() {
+        // Keeps cache disabled until test fixes are checked into AOSP.
+        if (!sServiceHandleCacheEnabled) {
+            return ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
+        }
+
+        if (sISub == null) {
+            ISub temp = ISub.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSubscriptionServiceRegisterer()
+                            .get());
+            synchronized (sCacheLock) {
+                if (sISub == null && temp != null) {
+                    try {
+                        sISub = temp;
+                        sISub.asBinder().linkToDeath(sServiceDeath, 0);
+                    } catch (Exception e) {
+                        // something has gone horribly wrong
+                        sISub = null;
+                    }
+                }
+            }
+        }
+        return sISub;
+    }
+
+    /**
+    * @hide
+    */
+    static ISms getSmsService() {
+        // Keeps cache disabled until test fixes are checked into AOSP.
+        if (!sServiceHandleCacheEnabled) {
+            return ISms.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSmsServiceRegisterer()
+                            .get());
+        }
+
+        if (sISms == null) {
+            ISms temp = ISms.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getSmsServiceRegisterer()
+                            .get());
+            synchronized (sCacheLock) {
+                if (sISms == null && temp != null) {
+                    try {
+                        sISms = temp;
+                        sISms.asBinder().linkToDeath(sServiceDeath, 0);
+                    } catch (Exception e) {
+                        // something has gone horribly wrong
+                        sISms = null;
+                    }
+                }
+            }
+        }
+        return sISms;
+    }
+
+    /**
+     * Disables service handle caching for tests that utilize mock services.
+     * @hide
+     */
+    @VisibleForTesting
+    public static void disableServiceHandleCaching() {
+        sServiceHandleCacheEnabled = false;
+    }
+
+    /**
+     * Reenables service handle caching.
+     * @hide
+     */
+    @VisibleForTesting
+    public static void enableServiceHandleCaching() {
+        sServiceHandleCacheEnabled = true;
+    }
+
+    /**
+     * Setup sITelephony for testing.
+     * @hide
+     */
+    @VisibleForTesting
+    public static void setupITelephonyForTest(ITelephony telephony) {
+        sITelephony = telephony;
+    }
+
+    /**
+     * Setup sIPhoneSubInfo for testing.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static void setupIPhoneSubInfoForTest(IPhoneSubInfo iPhoneSubInfo) {
+        synchronized (sCacheLock) {
+            sIPhoneSubInfo = iPhoneSubInfo;
+        }
+    }
+
+    /**
+     * Setup sISub for testing.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static void setupISubForTest(ISub iSub) {
+        synchronized (sCacheLock) {
+            sISub = iSub;
+        }
+    }
+
+    /**
+     * Whether device can connect to 5G network when two SIMs are active.
+     *
+     * @hide TODO b/153669716: remove or make system API.
+     */
+    public boolean canConnectTo5GInDsdsMode() {
+        ITelephony telephony = getITelephony();
+        if (telephony == null) return true;
+        try {
+            return telephony.canConnectTo5GInDsdsMode();
+        } catch (RemoteException ex) {
+            return true;
+        } catch (NullPointerException ex) {
+            return true;
+        }
+    }
+
+    /**
+     * Returns a list of the equivalent home PLMNs (EF_EHPLMN) from the USIM app.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return A list of equivalent home PLMNs. Returns an empty list if EF_EHPLMN is empty or
+     * does not exist on the SIM card.
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws SecurityException if the caller doesn't have the permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @NonNull List<String> getEquivalentHomePlmns() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getEquivalentHomePlmns(getSubId(), mContext.getOpPackageName(),
+                        getAttributionTag());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Telephony#getEquivalentHomePlmns RemoteException" + ex);
+        }
+
+        return Collections.emptyList();
+    }
+
+    /**
+     * Indicates whether {@link CarrierBandwidth#getSecondaryDownlinkCapacityKbps()} and
+     * {@link CarrierBandwidth#getSecondaryUplinkCapacityKbps()} are visible.  See comments
+     * on respective methods for more information.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE =
+            "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
+
+    /**
+     * Indicates whether {@link #setPreferredNetworkType}, {@link
+     * #setPreferredNetworkTypeBitmask}, {@link #setAllowedNetworkTypes} and
+     * {@link #setAllowedNetworkTypesForReason} rely on
+     * setAllowedNetworkTypesBitmap instead of setPreferredNetworkTypesBitmap on the radio
+     * interface.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK =
+            "CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK";
+
+    /**
+     * Indicates whether {@link #setNrDualConnectivityState()} and
+     * {@link #isNrDualConnectivityEnabled()} ()} are available.  See comments
+     * on respective methods for more information.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE =
+            "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE";
+
+    /**
+     * Indicates whether a data throttling request sent with {@link #sendThermalMitigationRequest}
+     * is supported. See comments on {@link #sendThermalMitigationRequest} for more information.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING =
+            "CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING";
+
+    /**
+     * Indicates whether {@link #getNetworkSlicingConfiguration} is supported. See comments on
+     * respective methods for more information.
+     */
+    public static final String CAPABILITY_SLICING_CONFIG_SUPPORTED =
+            "CAPABILITY_SLICING_CONFIG_SUPPORTED";
+
+    /**
+     * Indicates whether PHYSICAL_CHANNEL_CONFIG HAL1.6 is supported. See comments on
+     * respective methods for more information.
+     *
+     * @hide
+     */
+    public static final String CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED =
+            "CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED";
+
+    /**
+     * Indicates whether modem supports handling parsed SIM phonebook records through the RIL,
+     * both batched reads and individual writes.
+     *
+     * @hide
+     */
+    public static final String CAPABILITY_SIM_PHONEBOOK_IN_MODEM =
+            "CAPABILITY_SIM_PHONEBOOK_IN_MODEM";
+
+    /**
+     * A list of the radio interface capability values with public valid constants.
+     *
+     * Here is a related list for the systemapi-only valid constants:
+     *     CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE
+     *     CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK
+     *     CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE
+     *     CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING
+     *
+     * @hide
+     * TODO(b/185508047): Doc generation for mixed public/systemapi StringDefs formats badly.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef(prefix = "CAPABILITY_", value = {
+            CAPABILITY_SLICING_CONFIG_SUPPORTED,
+            CAPABILITY_SIM_PHONEBOOK_IN_MODEM,
+    })
+    public @interface RadioInterfaceCapability {}
+
+    /**
+     * Whether the device supports a given capability on the radio interface.
+     *
+     * If the capability is not in the set of radio interface capabilities, false is returned.
+     *
+     * @param capability the name of the capability to check for
+     * @return the availability of the capability
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public boolean isRadioInterfaceCapabilitySupported(
+            @NonNull @RadioInterfaceCapability String capability) {
+        try {
+            if (capability == null) return false;
+
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isRadioInterfaceCapabilitySupported(capability);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Telephony#isRadioInterfaceCapabilitySupported RemoteException" + ex);
+        }
+        return false;
+    }
+
+    /**
+     * Indicates that the thermal mitigation request was completed successfully.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_RESULT_SUCCESS = 0;
+
+    /**
+     * Indicates that the thermal mitigation request was not completed because of a modem error.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_RESULT_MODEM_ERROR = 1;
+
+    /**
+     * Indicates that the thermal mitigation request was not completed because the modem is not
+     * available.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE = 2;
+
+    /**
+     * Indicates that the thermal mitigation request could not power off the radio due to the device
+     * either being in an active emergency voice call, device pending an emergency call, or any
+     * other state that would disallow powering off of radio.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_RESULT_INVALID_STATE = 3;
+
+    /**
+     * Indicates that the thermal mitigation request resulted an unknown error.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR = 4;
+
+    /**
+     * Thermal mitigation request to control functionalities at modem. Thermal mitigation is done
+     * per-subscription. Caller must be sure to bind the TelephonyManager instance to subId by
+     * calling {@link #createForSubscriptionId(int)} if they want thermal mitigation on a specific
+     * subscription Id. Otherwise, TelephonyManager will use the default subscription.
+     *
+     * Calling this does not guarantee that the thermal mitigation action requested was done to
+     * completion. A thermal module should actively monitor the temperature levels and request an
+     * appropriate thermal mitigation action. Every action is assumed to be done 'on top of' the
+     * previous action, where the order of actions from least thermal mitigation to most is as
+     * follows:
+     * <ol>
+     *   <li>{@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_DATA_THROTTLING}</li>
+     *   <ol>
+     *      <li>{@link DataThrottlingRequest#DATA_THROTTLING_ACTION_NO_DATA_THROTTLING}</li>
+     *      <li>{@link DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}</li>
+     *      <li>{@link DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER}</li>
+     *   </ol>
+     *   <li>{@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY}</li>
+     *   <li>{@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF}</li>
+     * </ol>
+     *
+     * So, for example, requesting {@link
+     * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER} will ensure that the
+     * data on secondary carrier has been disabled before throttling on primary carrier. {@link
+     * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY} will ensure that data on both
+     * primary and secondary have been disabled. {@link
+     * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF} will ensure that voice is
+     * disabled and that data on both primary and secondary carriers are disabled before turning
+     * radio off. {@link DataThrottlingRequest#DATA_THROTTLING_ACTION_HOLD} is not part of the order
+     * and can be used at any time during data throttling to hold onto the current level of data
+     * throttling.
+     *
+     * <p> If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}({@link
+     * #CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING}) returns false, then sending a {@link
+     * DataThrottlingRequest#DATA_THROTTLING_ACTION_HOLD}, {@link
+     * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}, or {@link
+     * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER} will result in {@link
+     * IllegalArgumentException} being thrown. However, on devices that do not
+     * support data throttling, {@link
+     * DataThrottlingRequest#DATA_THROTTLING_ACTION_NO_DATA_THROTTLING} can still be requested in
+     * order to undo the mitigations above it (i.e {@link
+     * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY} and/or {@link
+     * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF}). </p>
+     *
+     * <p> In addition to the {@link Manifest.permission#MODIFY_PHONE_STATE} permission, callers of
+     * this API must also be listed in the device configuration as an authorized app in
+     * {@code packages/services/Telephony/res/values/config.xml} under the
+     * {@code thermal_mitigation_allowlisted_packages} key. </p>
+     *
+     * @param thermalMitigationRequest Thermal mitigation request. See {@link
+     * ThermalMitigationRequest} for details.
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws IllegalArgumentException if the thermalMitigationRequest had invalid parameters or
+     * if the device's modem does not support data throttling.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    @ThermalMitigationResult
+    public int sendThermalMitigationRequest(
+            @NonNull ThermalMitigationRequest thermalMitigationRequest) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.sendThermalMitigationRequest(getSubId(), thermalMitigationRequest,
+                        getOpPackageName());
+            }
+            throw new IllegalStateException("telephony service is null.");
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Telephony#thermalMitigationRequest RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR;
+    }
+
+    /**
+     * Registers a callback object to receive notification of changes in specified telephony states.
+     * <p>
+     * To register a callback, pass a {@link TelephonyCallback} which implements
+     * interfaces of events. For example,
+     * FakeServiceStateCallback extends {@link TelephonyCallback} implements
+     * {@link TelephonyCallback.ServiceStateListener}.
+     *
+     * At registration, and when a specified telephony state changes, the telephony manager invokes
+     * the appropriate callback method on the callback object and passes the current (updated)
+     * values.
+     * <p>
+     * Note: Be aware of the permission requirements stated on the {@link TelephonyCallback}
+     * listeners you implement.  Your application must be granted these permissions in order to
+     * register a {@link TelephonyCallback} which requires them; a {@link SecurityException} will be
+     * thrown if you do not hold the required permissions for all {@link TelephonyCallback}
+     * listeners you implement.
+     * <p>
+     * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
+     * applies to the given subId. Otherwise, applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}. To register events for multiple
+     * subIds, pass a separate callback object to each TelephonyManager object created with
+     * {@link #createForSubscriptionId}.
+     *
+     * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
+     * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
+     * {@link SecurityException} will be thrown otherwise.
+     *
+     * This API should be used sparingly -- large numbers of callbacks will cause system
+     * instability. If a process has registered too many callbacks without unregistering them, it
+     * may encounter an {@link IllegalStateException} when trying to register more callbacks.
+     *
+     * @param executor The executor of where the callback will execute.
+     * @param callback The {@link TelephonyCallback} object to register. The caller should hold a
+     * reference to the callback. The framework only holds a weak reference.
+     */
+    public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull TelephonyCallback callback) {
+        registerTelephonyCallback(getLocationData(), executor, callback);
+    }
+
+    private int getLocationData() {
+        boolean renounceCoarseLocation =
+                getRenouncedPermissions().contains(Manifest.permission.ACCESS_COARSE_LOCATION);
+        boolean renounceFineLocation =
+                getRenouncedPermissions().contains(Manifest.permission.ACCESS_FINE_LOCATION);
+        if (renounceCoarseLocation) {
+            return INCLUDE_LOCATION_DATA_NONE;
+        } else if (renounceFineLocation) {
+            return INCLUDE_LOCATION_DATA_COARSE;
+        } else {
+            return INCLUDE_LOCATION_DATA_FINE;
+        }
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"INCLUDE_LOCATION_DATA_"}, value = {
+            INCLUDE_LOCATION_DATA_NONE,
+            INCLUDE_LOCATION_DATA_COARSE,
+            INCLUDE_LOCATION_DATA_FINE})
+    public @interface IncludeLocationData {}
+
+    /**
+     * Specifies to not include any location related data.
+     *
+     * Indicates whether the caller would not like to receive
+     * location related information which will be sent if the caller already possess
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the
+     * permissions.
+     */
+    public static final int INCLUDE_LOCATION_DATA_NONE = 0;
+
+    /**
+     * Include coarse location data.
+     *
+     * Indicates whether the caller would not like to receive
+     * location related information which will be sent if the caller already possess
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and do not renounce the
+     * permissions.
+     */
+    public static final int INCLUDE_LOCATION_DATA_COARSE = 1;
+
+    /**
+     * Include fine location data.
+     *
+     * Indicates whether the caller would not like to receive
+     * location related information which will be sent if the caller already possess
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the
+     * permissions.
+     */
+    public static final int INCLUDE_LOCATION_DATA_FINE = 2;
+
+    /**
+     * Registers a callback object to receive notification of changes in specified telephony states.
+     * <p>
+     * To register a callback, pass a {@link TelephonyCallback} which implements
+     * interfaces of events. For example,
+     * FakeServiceStateCallback extends {@link TelephonyCallback} implements
+     * {@link TelephonyCallback.ServiceStateListener}.
+     *
+     * At registration, and when a specified telephony state changes, the telephony manager invokes
+     * the appropriate callback method on the callback object and passes the current (updated)
+     * values.
+     * <p>
+     *
+     * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
+     * applies to the given subId. Otherwise, applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}. To register events for multiple
+     * subIds, pass a separate callback object to each TelephonyManager object created with
+     * {@link #createForSubscriptionId}.
+     *
+     * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
+     * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
+     * {@link SecurityException} will be thrown otherwise.
+     *
+     * This API should be used sparingly -- large numbers of callbacks will cause system
+     * instability. If a process has registered too many callbacks without unregistering them, it
+     * may encounter an {@link IllegalStateException} when trying to register more callbacks.
+     *
+     * <p>
+     * There's another way to renounce permissions with a custom context
+     * {@code AttributionSource.Builder#setRenouncedPermissions(Set<String>)} but only for system
+     * apps. To avoid confusion, calling this method supersede renouncing permissions with a
+     * custom context.
+     *
+     * @param includeLocationData Specifies if the caller would like to receive
+     * location related information.
+     * @param executor The executor of where the callback will execute.
+     * @param callback The {@link TelephonyCallback} object to register. The caller should hold a
+     * reference to the callback. The framework only holds a weak reference.
+     */
+    public void registerTelephonyCallback(@IncludeLocationData int includeLocationData,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull TelephonyCallback callback) {
+        if (mContext == null) {
+            throw new IllegalStateException("telephony service is null.");
+        }
+
+        if (executor == null || callback == null) {
+            throw new IllegalArgumentException("TelephonyCallback and executor must be non-null");
+        }
+        mTelephonyRegistryMgr = (TelephonyRegistryManager)
+                mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+        if (mTelephonyRegistryMgr != null) {
+            mTelephonyRegistryMgr.registerTelephonyCallback(
+                    includeLocationData != INCLUDE_LOCATION_DATA_FINE,
+                    includeLocationData == INCLUDE_LOCATION_DATA_NONE,
+                    executor, mSubId, getOpPackageName(),
+                    getAttributionTag(), callback, getITelephony() != null);
+        } else {
+            throw new IllegalStateException("telephony service is null.");
+        }
+    }
+
+    /**
+     * Unregister an existing {@link TelephonyCallback}.
+     *
+     * @param callback The {@link TelephonyCallback} object to unregister.
+     */
+    public void unregisterTelephonyCallback(@NonNull TelephonyCallback callback) {
+
+        if (mContext == null) {
+            throw new IllegalStateException("telephony service is null.");
+        }
+
+        if (callback.callback == null) {
+            return;
+        }
+
+        mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+        if (mTelephonyRegistryMgr != null) {
+            mTelephonyRegistryMgr.unregisterTelephonyCallback(mSubId, getOpPackageName(),
+                    getAttributionTag(), callback, getITelephony() != null);
+        } else {
+            throw new IllegalStateException("telephony service is null.");
+        }
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"GBA_FAILURE_REASON_"}, value = {
+            GBA_FAILURE_REASON_UNKNOWN,
+            GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED,
+            GBA_FAILURE_REASON_FEATURE_NOT_READY,
+            GBA_FAILURE_REASON_NETWORK_FAILURE,
+            GBA_FAILURE_REASON_INCORRECT_NAF_ID,
+            GBA_FAILURE_REASON_SECURITY_PROTOCOL_NOT_SUPPORTED})
+    public @interface AuthenticationFailureReason {}
+
+    /**
+     * GBA Authentication has failed for an unknown reason.
+     *
+     * <p>The caller should retry a message that failed with this response.
+     * @hide
+     */
+    @SystemApi
+    public static final int GBA_FAILURE_REASON_UNKNOWN = 0;
+
+    /**
+     * GBA Authentication is not supported by the carrier, SIM or android.
+     *
+     * <p>Application should use other authentication mechanisms if possible.
+     * @hide
+     */
+    @SystemApi
+    public static final int GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED = 1;
+
+    /**
+     * GBA Authentication service is not ready for use.
+     *
+     * <p>Application could try again at a later time.
+     * @hide
+     */
+    @SystemApi
+    public static final int GBA_FAILURE_REASON_FEATURE_NOT_READY = 2;
+
+    /**
+     * GBA Authentication has been failed by the network.
+     * @hide
+     */
+    @SystemApi
+    public static final int GBA_FAILURE_REASON_NETWORK_FAILURE = 3;
+
+    /**
+     * GBA Authentication has failed due to incorrect NAF URL.
+     * @hide
+     */
+    @SystemApi
+    public static final int GBA_FAILURE_REASON_INCORRECT_NAF_ID = 4;
+
+    /**
+     * GBA Authentication has failed due to unsupported security protocol
+     * @hide
+     */
+    @SystemApi
+    public static final int GBA_FAILURE_REASON_SECURITY_PROTOCOL_NOT_SUPPORTED = 5;
+
+    /**
+     * The callback associated with a {@link #bootstrapAuthenticationRequest()}.
+     * @hide
+     */
+    @SystemApi
+    public static class BootstrapAuthenticationCallback {
+
+        /**
+         * Invoked when the previously requested GBA keys are available (@see
+         * bootstrapAuthenticationRequest()).
+         * @param gbaKey Ks_NAF/Ks_ext_NAF Response
+         * @param transactionId Bootstrapping Transaction Identifier
+         */
+        public void onKeysAvailable(@NonNull byte[] gbaKey, @NonNull String transactionId) {}
+
+        /**
+         * @param reason The reason for the authentication failure.
+         */
+        public void onAuthenticationFailure(@AuthenticationFailureReason int reason) {}
+    }
+
+    /**
+     * Used to get the Generic Bootstrapping Architecture authentication keys
+     * KsNAF/Ks_ext_NAF for a particular NAF as defined in 3GPP spec TS 33.220 for
+     * the specified sub id.
+     *
+     * <p>Application must be prepared to wait for receiving the Gba keys through the
+     * registered callback and not invoke the API on the main application thread.
+     * Application also must call the api to get the fresh key every time instead
+     * of caching the key.
+     *
+     * Following steps may be invoked on the API call depending on the state of the
+     * underlying GBA implementation:
+     * <ol>
+     *     <li>Resolve and bind to a Gba implementation.</li>
+     *     <li>Run bootstrapping if no valid keys are available or bootstrapping is forced.</li>
+     *     <li>Generate the ks_NAF/ ks_Ext_NAF to be returned via the callback.</li>
+     * </ol>
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE},</li>
+     *     <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
+     *     <li>or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     * @param appType icc application type, like {@link #APPTYPE_USIM} or {@link
+     * #APPTYPE_ISIM} or {@link#APPTYPE_UNKNOWN}
+     * @param nafId A URI to specify Network Application Function(NAF) fully qualified domain
+     * name (FQDN) and the selected GBA mode. The authority of the URI must contain two parts
+     * delimited by "@" sign. The first part is the constant string "3GPP-bootstrapping" (GBA_ME),
+     * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest).
+     * The second part shall be the FQDN of the NAF. The scheme of the URI is not actually used
+     * for the authentication, which may be set the same as the resource that the application is
+     * going to access. For example, the nafId can be
+     * "https://[email protected]",
+     * "https://[email protected]",
+     * "https://[email protected]",
+     * "ftps://[email protected]".
+     * @param securityProtocol Security protocol identifier between UE and NAF.  See
+     * 3GPP TS 33.220 Annex H. Application can use
+     * {@link UaSecurityProtocolIdentifier#createDefaultUaSpId},
+     * {@link UaSecurityProtocolIdentifier#create3GppUaSpId},
+     * to create the ua security protocol identifier as needed
+     * @param forceBootStrapping true=force bootstrapping, false=do not force
+     * bootstrapping. Bootstrapping shouldn't be forced unless the application sees
+     * authentication errors from the server.
+     * @param e The {@link Executor} that will be used to call the Gba callback.
+     * @param callback A callback called on the supplied {@link Executor} that will
+     * contain the GBA Ks_NAF/Ks_ext_NAF when available. If the NAF keys are
+     * available and valid at the time of call and bootstrapping is not requested,
+     * then the callback shall be invoked with the available keys.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @WorkerThread
+    @RequiresPermission(anyOf = {android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public void bootstrapAuthenticationRequest(
+            @UiccAppTypeExt int appType, @NonNull Uri nafId,
+            @NonNull UaSecurityProtocolIdentifier securityProtocol,
+            boolean forceBootStrapping, @NonNull Executor e,
+            @NonNull BootstrapAuthenticationCallback callback) {
+        try {
+            ITelephony service = getITelephony();
+            if (service == null) {
+                e.execute(() -> callback.onAuthenticationFailure(
+                        GBA_FAILURE_REASON_FEATURE_NOT_READY));
+                return;
+            }
+            service.bootstrapAuthenticationRequest(
+                    getSubId(), appType, nafId, securityProtocol, forceBootStrapping,
+                    new IBootstrapAuthenticationCallback.Stub() {
+                        @Override
+                        public void onKeysAvailable(int token, byte[] gbaKey,
+                                String transactionId) {
+                            final long identity = Binder.clearCallingIdentity();
+                            try {
+                                e.execute(() -> callback.onKeysAvailable(gbaKey, transactionId));
+                            } finally {
+                                Binder.restoreCallingIdentity(identity);
+                            }
+                        }
+
+                        @Override
+                        public void onAuthenticationFailure(int token, int reason) {
+                            final long identity = Binder.clearCallingIdentity();
+                            try {
+                                e.execute(() -> callback.onAuthenticationFailure(reason));
+                            } finally {
+                                Binder.restoreCallingIdentity(identity);
+                            }
+                        }
+                    });
+        } catch (RemoteException exception) {
+            Log.e(TAG, "Error calling ITelephony#bootstrapAuthenticationRequest", exception);
+            e.execute(() -> callback.onAuthenticationFailure(GBA_FAILURE_REASON_FEATURE_NOT_READY));
+        }
+    }
+
+    /**
+     * The network type is valid or not.
+     *
+     * @param networkType The network type {@link NetworkType}.
+     * @return {@code true} if valid, {@code false} otherwise.
+     *
+     * @hide
+     */
+    public static boolean isNetworkTypeValid(@NetworkType int networkType) {
+        return networkType >= TelephonyManager.NETWORK_TYPE_UNKNOWN &&
+                networkType <= TelephonyManager.NETWORK_TYPE_NR;
+    }
+
+    /**
+     * Set a {@link SignalStrengthUpdateRequest} to receive notification when signal quality
+     * measurements breach the specified thresholds.
+     *
+     * To be notified, set the signal strength update request and then register
+     * {@link TelephonyCallback} that implements {@link TelephonyCallback.SignalStrengthsListener}
+     * through {@link #registerTelephonyCallback}. The notification will arrive through
+     * {@link TelephonyCallback.SignalStrengthsListener#onSignalStrengthsChanged(SignalStrength)}.
+     *
+     * To stop receiving the notification over the specified thresholds, pass the same
+     * {@link SignalStrengthUpdateRequest} object to
+     * {@link #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}.
+     *
+     * System will clean up the {@link SignalStrengthUpdateRequest} if the caller process died
+     * without calling {@link #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}.
+     *
+     * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
+     * applies to the given subId. Otherwise, applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}. To request for multiple subIds,
+     * pass a request object to each TelephonyManager object created with
+     * {@link #createForSubscriptionId}.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * Note that the thresholds in the request will be used on a best-effort basis; the system may
+     * modify requests to multiplex various request sources or to optimize power consumption. The
+     * caller should not expect to be notified with the exactly the same thresholds.
+     *
+     * @see #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)
+     *
+     * @param request the SignalStrengthUpdateRequest to be set into the System
+     *
+     * @throws IllegalStateException if a new request is set with same subId from the same caller
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void setSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) {
+        Objects.requireNonNull(request, "request must not be null");
+
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.setSignalStrengthUpdateRequest(getSubId(), request, getOpPackageName());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setSignalStrengthUpdateRequest", e);
+        }
+    }
+
+    /**
+     * Clear a {@link SignalStrengthUpdateRequest} from the system.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * <p>If the given request was not set before, this operation is a no-op.
+     *
+     * @see #setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)
+     *
+     * @param request the SignalStrengthUpdateRequest to be cleared from the System
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+    public void clearSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) {
+        Objects.requireNonNull(request, "request must not be null");
+
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.clearSignalStrengthUpdateRequest(getSubId(), request, getOpPackageName());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#clearSignalStrengthUpdateRequest", e);
+        }
+    }
+
+    /**
+     * Gets the current phone capability.
+     *
+     * @return the PhoneCapability which describes the data connection capability of modem.
+     * It's used to evaluate possible phone config change, for example from single
+     * SIM device to multi-SIM device.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public @NonNull PhoneCapability getPhoneCapability() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getPhoneCapability();
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+        if (getActiveModemCount() > 1) {
+            return PhoneCapability.DEFAULT_DSDS_CAPABILITY;
+        } else {
+            return PhoneCapability.DEFAULT_SSSS_CAPABILITY;
+        }
+    }
+
+    /**
+     * The unattended reboot was prepared successfully.
+     * @hide
+     */
+    @SystemApi
+    public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0;
+
+    /**
+     * The unattended reboot was prepared, but the user will need to manually
+     * enter the PIN code of at least one SIM card present in the device.
+     * @hide
+     */
+    @SystemApi
+    public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1;
+
+    /**
+     * The unattended reboot was not prepared due to a non-recoverable error. After this error,
+     * the client that manages the unattended reboot should not try to invoke the API again
+     * until the next power cycle.
+     * @hide
+     */
+    @SystemApi
+    public static final int PREPARE_UNATTENDED_REBOOT_ERROR = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"PREPARE_UNATTENDED_REBOOT_"},
+            value = {
+                    PREPARE_UNATTENDED_REBOOT_SUCCESS,
+                    PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED,
+                    PREPARE_UNATTENDED_REBOOT_ERROR
+            })
+    public @interface PrepareUnattendedRebootResult {}
+
+    /**
+     * Prepare TelephonyManager for an unattended reboot. The reboot is required to be done
+     * shortly (e.g. within 15 seconds) after the API is invoked.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#REBOOT}
+     *
+     * @return {@link #PREPARE_UNATTENDED_REBOOT_SUCCESS} in case of success.
+     * {@link #PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED} if the device contains
+     * at least one SIM card for which the user needs to manually enter the PIN
+     * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case
+     * of error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.REBOOT)
+    @PrepareUnattendedRebootResult
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public int prepareForUnattendedReboot() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.prepareForUnattendedReboot();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Telephony#prepareForUnattendedReboot RemoteException", e);
+            e.rethrowFromSystemServer();
+        }
+        return PREPARE_UNATTENDED_REBOOT_ERROR;
+    }
+
+    /**
+     * Exception that may be supplied to the callback in {@link #getNetworkSlicingConfiguration} if
+     * something goes awry.
+     */
+    public static class NetworkSlicingException extends Exception {
+        /**
+         * Getting the current slicing configuration successfully. Used internally only.
+         * @hide
+         */
+        public static final int SUCCESS = 0;
+
+        /**
+         * The system timed out waiting for a response from the Radio.
+         * @hide
+         */
+        public static final int ERROR_TIMEOUT = 1;
+
+        /**
+         * The modem returned a failure.
+         * @hide
+         */
+        public static final int ERROR_MODEM_ERROR = 2;
+
+        /** @hide */
+        @IntDef(prefix = {"ERROR_"}, value = {
+                ERROR_TIMEOUT,
+                ERROR_MODEM_ERROR,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface NetworkSlicingError {}
+
+        private final int mErrorCode;
+
+        /** @hide */
+        public NetworkSlicingException(@NetworkSlicingError int errorCode) {
+            mErrorCode = errorCode;
+        }
+
+        @Override
+        public String toString() {
+            switch (mErrorCode) {
+                case ERROR_TIMEOUT: return "ERROR_TIMEOUT";
+                case ERROR_MODEM_ERROR: return "ERROR_MODEM_ERROR";
+                default: return "UNDEFINED";
+            }
+        }
+    }
+
+    /**
+     * Exception that is supplied to the callback in {@link #getNetworkSlicingConfiguration} if the
+     * system timed out waiting for a response from the Radio.
+     */
+    public class TimeoutException extends NetworkSlicingException {
+        /** @hide */
+        public TimeoutException(int errorCode) {
+            super(errorCode);
+        }
+    }
+
+    /**
+     * Exception that is supplied to the callback in {@link #getNetworkSlicingConfiguration} if the
+     * modem returned a failure.
+     */
+    public class ModemErrorException extends NetworkSlicingException {
+        /** @hide */
+        public ModemErrorException(int errorCode) {
+            super(errorCode);
+        }
+    }
+
+    /** @hide */
+    public static final String KEY_SLICING_CONFIG_HANDLE = "slicing_config_handle";
+
+    /**
+     * Request to get the current slicing configuration including URSP rules and
+     * NSSAIs (configured, allowed and rejected).
+     *
+     * This method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+     *     is a privileged permission that can only be granted to apps preloaded on the device.
+     *     <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * </ul>
+     *
+     * This will be invalid if the device does not support
+     * android.telephony.TelephonyManager#CAPABILITY_SLICING_CONFIG_SUPPORTED.
+     *
+     * @param executor the executor on which callback will be invoked.
+     * @param callback a callback to receive the current slicing configuration.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     */
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_SLICING_CONFIG_SUPPORTED)
+    @SuppressAutoDoc // No support for carrier privileges (b/72967236).
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void getNetworkSlicingConfiguration(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<NetworkSlicingConfig, NetworkSlicingException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                throw new IllegalStateException("telephony service is null.");
+            }
+            telephony.getSlicingConfig(new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle result) {
+                        if (resultCode == NetworkSlicingException.ERROR_TIMEOUT) {
+                            executor.execute(() -> callback.onError(
+                                    new TimeoutException(resultCode)));
+                            return;
+                        } else if (resultCode == NetworkSlicingException.ERROR_MODEM_ERROR) {
+                            executor.execute(() -> callback.onError(
+                                    new ModemErrorException(resultCode)));
+                            return;
+                        }
+
+                        NetworkSlicingConfig slicingConfig =
+                                result.getParcelable(KEY_SLICING_CONFIG_HANDLE, android.telephony.data.NetworkSlicingConfig.class);
+                        executor.execute(() -> callback.onResult(slicingConfig));
+                    }
+            });
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * A premium capability that boosts the network to allow for real-time interactive traffic
+     * by prioritizing low latency communication.
+     * Corresponds to {@link NetworkCapabilities#NET_CAPABILITY_PRIORITIZE_LATENCY}.
+     */
+    public static final int PREMIUM_CAPABILITY_PRIORITIZE_LATENCY =
+            NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY;
+
+    /**
+     * Purchasable premium capabilities.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "PREMIUM_CAPABILITY_" }, value = {
+            PREMIUM_CAPABILITY_PRIORITIZE_LATENCY})
+    public @interface PremiumCapability {}
+
+    /**
+     * Returns the premium capability {@link PremiumCapability} as a String.
+     *
+     * @param capability The premium capability.
+     * @return The premium capability as a String.
+     * @hide
+     */
+    public static String convertPremiumCapabilityToString(@PremiumCapability int capability) {
+        switch (capability) {
+            case PREMIUM_CAPABILITY_PRIORITIZE_LATENCY:
+                return "PRIORITIZE_LATENCY";
+            default:
+                return "UNKNOWN (" + capability + ")";
+        }
+    }
+
+    /**
+     * Check whether the given premium capability is available for purchase from the carrier.
+     * If this is {@code true}, the capability can be purchased from the carrier using
+     * {@link #purchasePremiumCapability(int, Executor, Consumer)}.
+     *
+     * @param capability The premium capability to check.
+     * @return Whether the given premium capability is available to purchase.
+     * @throws SecurityException if the caller does not hold permission READ_BASIC_PHONE_STATE.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_BASIC_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public boolean isPremiumCapabilityAvailableForPurchase(@PremiumCapability int capability) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                throw new IllegalStateException("telephony service is null.");
+            }
+            return telephony.isPremiumCapabilityAvailableForPurchase(capability, getSubId());
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+        return false;
+    }
+
+    /**
+     * Purchase premium capability request was successful.
+     * Once the purchase result is successful, the network must set up a slicing configuration
+     * for the purchased premium capability within the timeout specified by
+     * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG}.
+     * During the setup time, subsequent attempts will return
+     * {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP}.
+     * After setup is complete, subsequent attempts will return
+     * {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED} until the boost expires.
+     * The expiry time is determined by the type or duration of boost purchased from the carrier,
+     * provided at {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING}.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS = 1;
+
+    /**
+     * Purchase premium capability failed because the request is throttled.
+     * If purchasing premium capabilities is throttled, it will be for the amount of time
+     * specified by {@link CarrierConfigManager
+     * #KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}.
+     * If displaying the performance boost notification is throttled, it will be for the amount of
+     * time specified by {@link CarrierConfigManager
+     * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}.
+     * We will show the performance boost notification to the user up to the daily and monthly
+     * maximum number of times specified by
+     * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT} and
+     * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT}.
+     * Subsequent attempts will return the same error until the request is no longer throttled
+     * or throttling conditions change.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED = 2;
+
+    /**
+     * Purchase premium capability failed because it is already purchased and available.
+     * Subsequent attempts will return the same error until the performance boost expires.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED = 3;
+
+    /**
+     * Purchase premium capability failed because a request was already made and is in progress.
+     * This may have been requested by either the same app or another app.
+     * Subsequent attempts will return the same error until the previous request completes.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS = 4;
+
+    /**
+     * Purchase premium capability failed because the requesting application is not in the
+     * foreground. Subsequent attempts will return the same error until the requesting application
+     * moves to the foreground.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_FOREGROUND = 5;
+
+    /**
+     * Purchase premium capability failed because the user canceled the operation.
+     * Subsequent attempts will be throttled for the amount of time specified by
+     * {@link CarrierConfigManager
+     * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}
+     * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED = 6;
+
+    /**
+     * Purchase premium capability failed because the carrier disabled or does not support
+     * the capability, as specified in
+     * {@link CarrierConfigManager#KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY}.
+     * Subsequent attempts will return the same error until the carrier enables the feature.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED = 7;
+
+    /**
+     * Purchase premium capability failed because the carrier app did not indicate success.
+     * Subsequent attempts will be throttled for the amount of time specified by
+     * {@link CarrierConfigManager
+     * #KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}
+     * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR = 8;
+
+    /**
+     * Purchase premium capability failed because we did not receive a response from the user
+     * for the performance boost notification within the time specified by
+     * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG}.
+     * The performance boost notification will be automatically dismissed and subsequent attempts
+     * will be throttled for the amount of time specified by
+     * {@link CarrierConfigManager
+     * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}
+     * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT = 9;
+
+    /**
+     * Purchase premium capability failed because the device does not support the feature.
+     * Subsequent attempts will return the same error.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED = 10;
+
+    /**
+     * Purchase premium capability failed because the telephony service is unavailable
+     * or there was an error in the phone process.
+     * Subsequent attempts will return the same error until request conditions are satisfied.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED = 11;
+
+    /**
+     * Purchase premium capability failed because the network is not available.
+     * Subsequent attempts will return the same error until network conditions change.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE = 12;
+
+    /**
+     * Purchase premium capability failed because the entitlement check failed.
+     * Subsequent attempts will be throttled for the amount of time specified by
+     * {@link CarrierConfigManager
+     * #KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}
+     * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}.
+     * Throttling will be reevaluated when the network is no longer congested.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED = 13;
+
+    /**
+     * Purchase premium capability failed because the request was not made on the default data
+     * subscription, indicated by {@link SubscriptionManager#getDefaultDataSubscriptionId()}.
+     * Subsequent attempts will return the same error until the request is made on the default
+     * data subscription.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION = 14;
+
+    /**
+     * Purchase premium capability was successful and is waiting for the network to setup the
+     * slicing configuration. If the setup is complete within the time specified by
+     * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG},
+     * subsequent requests will return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED}
+     * until the purchase expires. If the setup is not complete within the time specified above,
+     * applications can request the premium capability again.
+     */
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP = 15;
+
+    /**
+     * Purchase premium capability failed because the user disabled the feature.
+     * Subsequent attempts will be throttled for the amount of time specified by
+     * {@link CarrierConfigManager
+     * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}
+     * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}.
+     */
+    @FlaggedApi(Flags.FLAG_SLICING_ADDITIONAL_ERROR_CODES)
+    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED = 16;
+
+    /**
+     * Results of the purchase premium capability request.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "PURCHASE_PREMIUM_CAPABILITY_RESULT_" }, value = {
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_FOREGROUND,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP,
+            PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED})
+    public @interface PurchasePremiumCapabilityResult {}
+
+    /**
+     * Returns the purchase result as a String.
+     *
+     * @param result The purchase premium capability result.
+     * @return The purchase result as a String.
+     * @hide
+     */
+    public static String convertPurchaseResultToString(
+            @PurchasePremiumCapabilityResult int result) {
+        switch (result) {
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS:
+                return "SUCCESS";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED:
+                return "THROTTLED";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED:
+                return "ALREADY_PURCHASED";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS:
+                return "ALREADY_IN_PROGRESS";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_FOREGROUND:
+                return "NOT_FOREGROUND";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED:
+                return "USER_CANCELED";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED:
+                return "CARRIER_DISABLED";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR:
+                return "CARRIER_ERROR";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT:
+                return "TIMEOUT";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED:
+                return "FEATURE_NOT_SUPPORTED";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED:
+                return "REQUEST_FAILED";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE:
+                return "NETWORK_NOT_AVAILABLE";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED:
+                return "ENTITLEMENT_CHECK_FAILED";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION:
+                return "NOT_DEFAULT_DATA_SUBSCRIPTION";
+            case PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP:
+                return "PENDING_NETWORK_SETUP";
+            default:
+                return "UNKNOWN (" + result + ")";
+        }
+    }
+
+    /**
+     * Purchase the given premium capability from the carrier.
+     * This requires user action to purchase the boost from the carrier.
+     * If this returns {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS} or
+     * {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED}, applications can request
+     * the premium capability via {@link ConnectivityManager#requestNetwork}.
+     *
+     * @param capability The premium capability to purchase.
+     * @param executor The callback executor for the response.
+     * @param callback The result of the purchase request.
+     * @throws SecurityException if the caller does not hold permissions
+     *         READ_BASIC_PHONE_STATE or INTERNET.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @see #isPremiumCapabilityAvailableForPurchase(int) to check whether the capability is valid.
+     */
+    @RequiresPermission(allOf = {android.Manifest.permission.READ_BASIC_PHONE_STATE,
+            android.Manifest.permission.INTERNET})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    public void purchasePremiumCapability(@PremiumCapability int capability,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull @PurchasePremiumCapabilityResult Consumer<Integer> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+            @Override
+            public void accept(int result) {
+                executor.execute(() -> callback.accept(result));
+            }
+        };
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                callback.accept(PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED);
+                return;
+            }
+            telephony.purchasePremiumCapability(capability, internalCallback, getSubId());
+        } catch (RemoteException ex) {
+            callback.accept(PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED);
+        }
+    }
+
+    /**
+     * Get last known cell identity.
+     *
+     * If there is current registered network this value will be same as the registered cell
+     * identity. If the device goes out of service the previous cell identity is cached and
+     * will be returned. If the cache age of the Cell identity is more than 24 hours
+     * it will be cleared and null will be returned.
+     * @return last known cell identity {@CellIdentity}.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_GET_LAST_KNOWN_CELL_IDENTITY)
+    @RequiresPermission(allOf = {Manifest.permission.ACCESS_FINE_LOCATION,
+            Manifest.permission.ACCESS_LAST_KNOWN_CELL_ID})
+    public @Nullable CellIdentity getLastKnownCellIdentity() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                throw new IllegalStateException("telephony service is null.");
+            }
+            return telephony.getLastKnownCellIdentity(getSubId(), getOpPackageName(),
+                    getAttributionTag());
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+        return null;
+    }
+
+    /**
+     * Callbacks to listen for when the set of packages with carrier privileges for a SIM changes.
+     *
+     * <p>Of note, when multiple callbacks are registered, they may be triggered one after another.
+     * The ordering of them is not guaranteed and thus should not be depend on.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface CarrierPrivilegesCallback {
+        /**
+         * Called when the set of packages with carrier privileges has changed.
+         *
+         * <p>Of note, this callback will <b>not</b> be fired if a carrier triggers a SIM profile
+         * switch and the same set of packages remains privileged after the switch.
+         *
+         * <p>At registration, the callback will receive the current set of privileged packages.
+         *
+         * @param privilegedPackageNames The updated set of package names that have carrier
+         *                               privileges
+         * @param privilegedUids         The updated set of UIDs that have carrier privileges
+         */
+        void onCarrierPrivilegesChanged(
+                @NonNull Set<String> privilegedPackageNames, @NonNull Set<Integer> privilegedUids);
+
+        /**
+         * Called when the {@link CarrierService} for the current user profile has changed.
+         *
+         * <p>This method does nothing by default. Clients that are interested in the carrier
+         * service change should override this method to get package name and UID info.
+         *
+         * <p>At registration, the callback will receive the current carrier service info.
+         *
+         * <p>Of note, this callback will <b>not</b> be fired if a carrier triggers a SIM profile
+         * switch and the same carrier service remains after switch.
+         *
+         * @param carrierServicePackageName package name of the {@link CarrierService}. May be
+         *                                  {@code null} when no carrier service is detected.
+         * @param carrierServiceUid         UID of the {@link CarrierService}. May be
+         *                                  {@link android.os.Process#INVALID_UID} if no carrier
+         *                                  service is detected.
+         */
+        default void onCarrierServiceChanged(
+                @Nullable String carrierServicePackageName, int carrierServiceUid) {
+            // do nothing by default
+        }
+    }
+
+    /**
+     * Sets a voice service state override from telecom based on the current {@link PhoneAccount}s
+     * registered. See {@link PhoneAccount#CAPABILITY_VOICE_CALLING_AVAILABLE}.
+     *
+     * <p>Currently, this API is only called to indicate over-the-top voice calling capability of
+     * the SIM call manager, which will get merged into {@link ServiceState#getState} and propagated
+     * to interested callers via {@link #getServiceState} and {@link
+     * TelephonyCallback.ServiceStateListener}.
+     *
+     * <p>If callers are truly interested in the actual device <-> tower connection status and not
+     * an overall "device can make voice calls" boolean, they can use {@link
+     * ServiceState#getNetworkRegistrationInfo} to check CS registration state.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+    @RequiresPermission(Manifest.permission.BIND_TELECOM_CONNECTION_SERVICE)
+    public void setVoiceServiceStateOverride(boolean hasService) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                throw new IllegalStateException("Telephony service is null");
+            }
+            telephony.setVoiceServiceStateOverride(getSubId(), hasService, getOpPackageName());
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Registers a {@link CarrierPrivilegesCallback} on the given {@code logicalSlotIndex} to
+     * receive callbacks when the set of packages with carrier privileges changes. The callback will
+     * immediately be called with the latest state.
+     *
+     * @param logicalSlotIndex The SIM slot to listen on
+     * @param executor The executor where {@code callback} will be invoked
+     * @param callback The callback to register
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void registerCarrierPrivilegesCallback(
+            int logicalSlotIndex,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull CarrierPrivilegesCallback callback) {
+        if (mContext == null) {
+            throw new IllegalStateException("Telephony service is null");
+        } else if (executor == null || callback == null) {
+            throw new IllegalArgumentException(
+                    "CarrierPrivilegesCallback and executor must be non-null");
+        }
+        mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+        if (mTelephonyRegistryMgr == null) {
+            throw new IllegalStateException("Telephony registry service is null");
+        }
+        mTelephonyRegistryMgr.addCarrierPrivilegesCallback(logicalSlotIndex, executor, callback);
+    }
+
+    /**
+     * Unregisters an existing {@link CarrierPrivilegesCallback}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void unregisterCarrierPrivilegesCallback(@NonNull CarrierPrivilegesCallback callback) {
+        if (mContext == null) {
+            throw new IllegalStateException("Telephony service is null");
+        } else if (callback == null) {
+            throw new IllegalArgumentException("CarrierPrivilegesCallback must be non-null");
+        }
+        mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+        if (mTelephonyRegistryMgr == null) {
+            throw new IllegalStateException("Telephony registry service is null");
+        }
+        mTelephonyRegistryMgr.removeCarrierPrivilegesCallback(callback);
+    }
+
+    /**
+     * set removable eSIM as default eUICC.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
+    public void setRemovableEsimAsDefaultEuicc(boolean isDefault) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setRemovableEsimAsDefaultEuicc(isDefault, getOpPackageName());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in setRemovableEsimAsDefault: " + e);
+        }
+    }
+
+    /**
+     * Returns whether the removable eSIM is default eUICC or not.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
+    public boolean isRemovableEsimDefaultEuicc() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isRemovableEsimDefaultEuicc(getOpPackageName());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in isRemovableEsimDefaultEuicc: " + e);
+        }
+        return false;
+    }
+
+    /**
+     * Returns a constant indicating the state of sim for the slot index.
+     *
+     * @param slotIndex Logical SIM slot index.
+     *
+     * @see TelephonyManager.SimState
+     *
+     * @hide
+     */
+    @SimState
+    public static int getSimStateForSlotIndex(int slotIndex) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getSimStateForSlotIndex(slotIndex);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in getSimStateForSlotIndex: " + e);
+        }
+        return TelephonyManager.SIM_STATE_UNKNOWN;
+    }
+
+    /**
+     * Captures parameters for collection of emergency
+     * call diagnostic data
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+    public static final class EmergencyCallDiagnosticData {
+        public static final class Builder {
+            private boolean mCollectTelecomDumpsys;
+            private boolean mCollectTelephonyDumpsys;
+
+            // If this is set to a value other than -1L, then the logcat collection is enabled.
+            // Logcat lines with this time or greater are collected how much is collected is
+            // dependent on internal implementation. Time represented as milliseconds since boot.
+            private long mLogcatStartTimeMillis = sUnsetLogcatStartTime;
+
+            /**
+             * Allows enabling of telecom dumpsys collection.
+             * @param collectTelecomDumpsys Determines whether telecom dumpsys should be collected.
+             * @return Builder instance corresponding to the configured call diagnostic params.
+             */
+            public @NonNull Builder setTelecomDumpsysCollectionEnabled(
+                    boolean collectTelecomDumpsys) {
+                mCollectTelecomDumpsys = collectTelecomDumpsys;
+                return this;
+            }
+
+            /**
+             * Allows enabling of telephony dumpsys collection.
+             * @param collectTelephonyDumpsys Determines if telephony dumpsys should be collected.
+             * @return Builder instance corresponding to the configured call diagnostic params.
+             */
+            public @NonNull Builder setTelephonyDumpsysCollectionEnabled(
+                    boolean collectTelephonyDumpsys) {
+                mCollectTelephonyDumpsys = collectTelephonyDumpsys;
+                return this;
+            }
+
+            /**
+             * Allows enabling of logcat (system,radio) collection.
+             * @param startTimeMillis Enables logcat collection as of the indicated timestamp.
+             * @return Builder instance corresponding to the configured call diagnostic params.
+             */
+            public @NonNull Builder setLogcatCollectionStartTimeMillis(
+                    @CurrentTimeMillisLong long startTimeMillis) {
+                mLogcatStartTimeMillis = startTimeMillis;
+                return this;
+            }
+
+            /**
+             * Build the EmergencyCallDiagnosticData from the provided Builder config.
+             * @return {@link EmergencyCallDiagnosticData} instance from provided builder.
+             */
+            public @NonNull EmergencyCallDiagnosticData build() {
+                return new EmergencyCallDiagnosticData(mCollectTelecomDumpsys,
+                        mCollectTelephonyDumpsys, mLogcatStartTimeMillis);
+            }
+        }
+
+        private boolean mCollectTelecomDumpsys;
+        private boolean mCollectTelephonyDumpsys;
+        private boolean mCollectLogcat;
+        private long mLogcatStartTimeMillis;
+
+        private static long sUnsetLogcatStartTime = -1L;
+
+        private EmergencyCallDiagnosticData(boolean collectTelecomDumpsys,
+                boolean collectTelephonyDumpsys, long logcatStartTimeMillis) {
+            mCollectTelecomDumpsys = collectTelecomDumpsys;
+            mCollectTelephonyDumpsys = collectTelephonyDumpsys;
+            mLogcatStartTimeMillis = logcatStartTimeMillis;
+            mCollectLogcat = logcatStartTimeMillis != sUnsetLogcatStartTime;
+        }
+
+        public boolean isTelecomDumpsysCollectionEnabled() {
+            return mCollectTelecomDumpsys;
+        }
+
+        public boolean isTelephonyDumpsysCollectionEnabled() {
+            return mCollectTelephonyDumpsys;
+        }
+
+        public boolean isLogcatCollectionEnabled() {
+            return mCollectLogcat;
+        }
+
+        public long getLogcatCollectionStartTimeMillis()
+        {
+            return mLogcatStartTimeMillis;
+        }
+
+        @Override
+        public String toString() {
+            return "EmergencyCallDiagnosticData{"
+                    + "mCollectTelecomDumpsys=" + mCollectTelecomDumpsys
+                    + ", mCollectTelephonyDumpsys=" + mCollectTelephonyDumpsys
+                    + ", mCollectLogcat=" + mCollectLogcat
+                    + ", mLogcatStartTimeMillis=" + mLogcatStartTimeMillis
+                    + '}';
+        }
+    }
+
+    /**
+     * Request telephony to persist state for debugging emergency call failures.
+     *
+     * @param dropboxTag Tag to use when persisting data to dropbox service.
+     * @param data Parameters controlling what is collected in the diagnostics.
+     *
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+    @RequiresPermission(android.Manifest.permission.READ_DROPBOX_DATA)
+    public void persistEmergencyCallDiagnosticData(@NonNull String dropboxTag,
+            @NonNull EmergencyCallDiagnosticData data) {
+        try {
+            ITelephony telephony = ITelephony.Stub.asInterface(
+                    TelephonyFrameworkInitializer
+                            .getTelephonyServiceManager()
+                            .getTelephonyServiceRegisterer()
+                            .get());
+            if (telephony != null) {
+                telephony.persistEmergencyCallDiagnosticData(dropboxTag,
+                        data.isLogcatCollectionEnabled(),
+                        data.getLogcatCollectionStartTimeMillis(),
+                        data.isTelecomDumpsysCollectionEnabled(),
+                        data.isTelephonyDumpsysCollectionEnabled());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error while persistEmergencyCallDiagnosticData: " + e);
+        }
+    }
+
+    /**
+     * Set the UE's ability to accept/reject null ciphered and null integrity-protected connections.
+     *
+     * The modem is required to ignore this in case of an emergency call.
+     *
+     * <p>Requires permission: android.Manifest.MODIFY_PHONE_STATE</p>
+     *
+     * @param enabled if null ciphered and null integrity protected connections are permitted
+     * @throws IllegalStateException if the Telephony process is not currently available
+     * @throws SecurityException if the caller does not have the required privileges
+     * @throws UnsupportedOperationException if the modem does not support disabling null ciphers.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setNullCipherAndIntegrityEnabled(boolean enabled) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setNullCipherAndIntegrityEnabled(enabled);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setNullCipherAndIntegrityEnabled RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the value of the global preference for null cipher and integriy enablement.
+     * Note: This does not return the state of the modem, only the persisted global preference.
+     *
+     * <p>Requires permission: android.Manifest.READ_PHONE_STATE</p>
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available
+     * @throws SecurityException if the caller does not have the required privileges
+     * @throws UnsupportedOperationException if the modem does not support disabling null ciphers.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public boolean isNullCipherAndIntegrityPreferenceEnabled() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isNullCipherAndIntegrityPreferenceEnabled();
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "isNullCipherAndIntegrityPreferenceEnabled RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return true;
+    }
+
+    /**
+     * Enable or disable notifications sent for cellular identifier disclosure events.
+     *
+     * Disclosure events are defined as instances where a device has sent a cellular identifier
+     * on the Non-access stratum (NAS) before a security context is established. As a result the
+     * identifier is sent in the clear, which has privacy implications for the user.
+     *
+     * @param enable if notifications about disclosure events should be enabled
+     * @throws IllegalStateException if the Telephony process is not currently available
+     * @throws SecurityException if the caller does not have the required privileges
+     * @throws UnsupportedOperationException if the modem does not support this feature.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY)
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    public void setEnableCellularIdentifierDisclosureNotifications(boolean enable) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setEnableCellularIdentifierDisclosureNotifications(enable);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setEnableCellularIdentifierDisclosureNotifications RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get whether or not cellular identifier disclosure notifications are enabled.
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available
+     * @throws SecurityException if the caller does not have the required privileges
+     * @throws UnsupportedOperationException if the modem does not support this feature.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY)
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    public boolean isCellularIdentifierDisclosureNotificationsEnabled() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isCellularIdentifierDisclosureNotificationsEnabled();
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "isCellularIdentifierDisclosureNotificationsEnabled RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    /**
+     * Enables or disables notifications sent when cellular null cipher or integrity algorithms
+     * are in use by the cellular modem.
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available
+     * @throws SecurityException if the caller does not have the required privileges
+     * @throws UnsupportedOperationException if the modem does not support reporting on ciphering
+     * and integrity algorithms in use
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY)
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    public void setNullCipherNotificationsEnabled(boolean enable) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setNullCipherNotificationsEnabled(enable);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setEnableNullCipherNotifications RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get whether notifications are enabled for null cipher or integrity algorithms in use by the
+     * cellular modem.
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available
+     * @throws SecurityException if the caller does not have the required privileges
+     * @throws UnsupportedOperationException if the modem does not support reporting on ciphering
+     * and integrity algorithms in use
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY)
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    public boolean isNullCipherNotificationsEnabled() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isNullCipherNotificationsEnabled();
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "isNullCipherNotificationsEnabled RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+
+    /**
+     * Get current cell broadcast message identifier ranges.
+     *
+     * @throws SecurityException if the caller does not have the required permission
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    @NonNull
+    public List<CellBroadcastIdRange> getCellBroadcastIdRanges() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getCellBroadcastIdRanges(getSubId());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+        }
+        return new ArrayList<>();
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CELL_BROADCAST_RESULT_"}, value = {
+            CELL_BROADCAST_RESULT_UNKNOWN,
+            CELL_BROADCAST_RESULT_SUCCESS,
+            CELL_BROADCAST_RESULT_UNSUPPORTED,
+            CELL_BROADCAST_RESULT_FAIL_CONFIG,
+            CELL_BROADCAST_RESULT_FAIL_ACTIVATION})
+    public @interface CellBroadcastResult {}
+
+    /**
+     * The result of the cell broadcast request is unknown
+     * @hide
+     */
+    @SystemApi
+    public static final int CELL_BROADCAST_RESULT_UNKNOWN = -1;
+
+    /**
+     * The cell broadcast request is successful.
+     * @hide
+     */
+    @SystemApi
+    public static final int CELL_BROADCAST_RESULT_SUCCESS = 0;
+
+    /**
+     * The cell broadcast request is not supported.
+     * @hide
+     */
+    @SystemApi
+    public static final int CELL_BROADCAST_RESULT_UNSUPPORTED = 1;
+
+    /**
+     * The cell broadcast request is failed due to the error to set config
+     * @hide
+     */
+    @SystemApi
+    public static final int CELL_BROADCAST_RESULT_FAIL_CONFIG = 2;
+
+    /**
+     * The cell broadcast request is failed due to the error to set activation
+     * @hide
+     */
+    @SystemApi
+    public static final int CELL_BROADCAST_RESULT_FAIL_ACTIVATION = 3;
+
+    /**
+     * Callback mode type
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"EMERGENCY_CALLBACK_MODE_"}, value = {
+            EMERGENCY_CALLBACK_MODE_CALL,
+            EMERGENCY_CALLBACK_MODE_SMS})
+    public @interface EmergencyCallbackModeType {}
+
+    /**
+     * The callback mode is due to emergency call.
+     * @hide
+     */
+    public static final int EMERGENCY_CALLBACK_MODE_CALL = 1;
+
+    /**
+     * The callback mode is due to emergency SMS.
+     * @hide
+     */
+    public static final int EMERGENCY_CALLBACK_MODE_SMS = 2;
+
+    /**
+     * The reason for changing callback mode.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"STOP_REASON_"},
+            value = {
+                    STOP_REASON_UNKNOWN,
+                    STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED,
+                    STOP_REASON_NORMAL_SMS_SENT,
+                    STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED,
+                    STOP_REASON_EMERGENCY_SMS_SENT,
+                    STOP_REASON_TIMER_EXPIRED,
+                    STOP_REASON_USER_ACTION,
+            })
+    public @interface EmergencyCallbackModeStopReason {}
+
+    /**
+     * unknown reason.
+     * @hide
+     */
+    public static final int STOP_REASON_UNKNOWN = 0;
+
+    /**
+     * The call back mode is exited due to a new normal call is originated.
+     * @hide
+     */
+    public static final int STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED = 1;
+
+    /**
+     * The call back mode is exited due to a new normal SMS is originated.
+     * @hide
+     */
+    public static final int STOP_REASON_NORMAL_SMS_SENT = 2;
+
+    /**
+     * The call back mode is exited due to a new emergency call is originated.
+     * @hide
+     */
+    public static final int STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED = 3;
+
+    /**
+     * The call back mode is exited due to a new emergency SMS is originated.
+     * @hide
+     */
+    public static final int STOP_REASON_EMERGENCY_SMS_SENT = 4;
+
+    /**
+     * The call back mode is exited due to timer expiry.
+     * @hide
+     */
+    public static final int STOP_REASON_TIMER_EXPIRED = 5;
+
+    /**
+     * The call back mode is exited due to user action.
+     * @hide
+     */
+    public static final int STOP_REASON_USER_ACTION = 6;
+
+    /**
+     * Set reception of cell broadcast messages with the list of the given ranges
+     *
+     * <p>The ranges set previously will be overridden by the new one. Empty list
+     * can be used to clear the ranges.
+     *
+     * @param ranges the list of {@link CellBroadcastIdRange} to be set.
+     * @param executor The {@link Executor} that will be used to call the callback.
+     * @param callback A callback called on the supplied {@link Executor} to notify
+     * the result when the operation completes.
+     * @throws SecurityException if the caller does not have the required permission
+     * @throws IllegalArgumentException when the ranges are invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    public void setCellBroadcastIdRanges(@NonNull List<CellBroadcastIdRange> ranges,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Integer> callback) {
+        IIntegerConsumer consumer = callback == null ? null : new IIntegerConsumer.Stub() {
+            @Override
+            public void accept(int result) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> callback.accept(result));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        };
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setCellBroadcastIdRanges(getSubId(), ranges, consumer);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the domain selection service is supported.
+     *
+     * @return {@code true} if the domain selection service is supported.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public boolean isDomainSelectionSupported() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isDomainSelectionSupported();
+            }
+        } catch (RemoteException ex) {
+            Rlog.w(TAG, "RemoteException", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether the AOSP domain selection service is supported.
+     *
+     * @return {@code true} if the AOSP domain selection service is supported.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
+    public boolean isAospDomainSelectionService() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isAospDomainSelectionService();
+            }
+        } catch (RemoteException ex) {
+            Rlog.w(TAG, "RemoteException", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Returns the primary IMEI (International Mobile Equipment Identity) of the device as
+     * mentioned in GSMA TS.37. {@link #getImei(int)} returns the IMEI that belongs to the selected
+     * slotID whereas this API {@link #getPrimaryImei()} returns primary IMEI of the device.
+     * A single SIM device with only one IMEI will be set by default as primary IMEI.
+     * A multi-SIM device with multiple IMEIs will have one of the IMEIs set as primary as
+     * mentioned in GSMA TS37_2.2_REQ_8.
+     *
+     * <p>Requires one of the following permissions
+     * <ul>
+     *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+     *     is a privileged permission that can only be granted to apps preloaded on the device.
+     *     <li>If the calling app is the device owner of a fully-managed device, a profile
+     *     owner of an organization-owned device, or their delegates (see {@link
+     *     android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
+     *     <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
+     *     active subscription.
+     *     <li>If the calling app is the default SMS role holder (see {@link
+     *     RoleManager#isRoleHeld(String)}).
+     *     <li>If the calling app has been granted the
+     *      {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} permission.
+     * </ul>
+     *
+     * @return Primary IMEI of type string
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
+     * @throws SecurityException if the caller does not have the required permission/privileges
+     */
+    @NonNull
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
+    public String getPrimaryImei() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                Rlog.e(TAG, "getPrimaryImei(): IPhoneSubInfo instance is NULL");
+                throw new IllegalStateException("Telephony service not available.");
+            }
+            return telephony.getPrimaryImei(getOpPackageName(), getAttributionTag());
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getPrimaryImei() RemoteException : " + ex);
+            throw ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Convert SIM state into string.
+     *
+     * @param state SIM state.
+     * @return SIM state in string format.
+     *
+     * @hide
+     */
+    @NonNull
+    public static String simStateToString(@SimState int state) {
+        switch (state) {
+            case TelephonyManager.SIM_STATE_UNKNOWN:
+                return "UNKNOWN";
+            case TelephonyManager.SIM_STATE_ABSENT:
+                return "ABSENT";
+            case TelephonyManager.SIM_STATE_PIN_REQUIRED:
+                return "PIN_REQUIRED";
+            case TelephonyManager.SIM_STATE_PUK_REQUIRED:
+                return "PUK_REQUIRED";
+            case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
+                return "NETWORK_LOCKED";
+            case TelephonyManager.SIM_STATE_READY:
+                return "READY";
+            case TelephonyManager.SIM_STATE_NOT_READY:
+                return "NOT_READY";
+            case TelephonyManager.SIM_STATE_PERM_DISABLED:
+                return "PERM_DISABLED";
+            case TelephonyManager.SIM_STATE_CARD_IO_ERROR:
+                return "CARD_IO_ERROR";
+            case TelephonyManager.SIM_STATE_CARD_RESTRICTED:
+                return "CARD_RESTRICTED";
+            case TelephonyManager.SIM_STATE_LOADED:
+                return "LOADED";
+            case TelephonyManager.SIM_STATE_PRESENT:
+                return "PRESENT";
+            default:
+                return "UNKNOWN(" + state + ")";
+        }
+    }
+}
diff --git a/android-35/android/telephony/TelephonyRegistryManager.java b/android-35/android/telephony/TelephonyRegistryManager.java
new file mode 100644
index 0000000..6160fdb
--- /dev/null
+++ b/android-35/android/telephony/TelephonyRegistryManager.java
@@ -0,0 +1,1725 @@
+/*
+ * Copyright (C) 2019 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;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.carrier.CarrierService;
+import android.telephony.Annotation.CallState;
+import android.telephony.Annotation.DataActivityType;
+import android.telephony.Annotation.DisconnectCauses;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.Annotation.PreciseDisconnectCauses;
+import android.telephony.Annotation.RadioPowerState;
+import android.telephony.Annotation.SimActivationState;
+import android.telephony.Annotation.SrvccState;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TelephonyManager.CarrierPrivilegesCallback;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.MediaQualityStatus;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.listeners.ListenerExecutor;
+import com.android.internal.telephony.ICarrierConfigChangeListener;
+import com.android.internal.telephony.ICarrierPrivilegesCallback;
+import com.android.internal.telephony.IOnSubscriptionsChangedListener;
+import com.android.internal.telephony.ITelephonyRegistry;
+import com.android.server.telecom.flags.Flags;
+
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
+
+/**
+ * A centralized place to notify telephony related status changes, e.g, {@link ServiceState} update
+ * or {@link PhoneCapability} changed. This might trigger callback from applications side through
+ * {@link android.telephony.PhoneStateListener}
+ *
+ * Limit API access to only carrier apps with certain permissions or apps running on
+ * privileged UID.
+ *
+ * TelephonyRegistryManager is intended for use on devices that implement
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices
+ * that do not implement this feature, the behavior is not reliable.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.TELEPHONY_REGISTRY_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY)
+@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+public class TelephonyRegistryManager {
+
+    private static final String TAG = "TelephonyRegistryManager";
+    private static ITelephonyRegistry sRegistry;
+    private final Context mContext;
+
+    /**
+     * A mapping between {@link SubscriptionManager.OnSubscriptionsChangedListener} and
+     * its callback IOnSubscriptionsChangedListener.
+     */
+    private final ConcurrentHashMap<SubscriptionManager.OnSubscriptionsChangedListener,
+            IOnSubscriptionsChangedListener>
+                    mSubscriptionChangedListenerMap = new ConcurrentHashMap<>();
+    /**
+     * A mapping between {@link SubscriptionManager.OnOpportunisticSubscriptionsChangedListener} and
+     * its callback IOnSubscriptionsChangedListener.
+     */
+    private final ConcurrentHashMap<SubscriptionManager.OnOpportunisticSubscriptionsChangedListener,
+            IOnSubscriptionsChangedListener>
+                    mOpportunisticSubscriptionChangedListenerMap = new ConcurrentHashMap<>();
+
+    /**
+     * A mapping between {@link CarrierConfigManager.CarrierConfigChangeListener} and its callback
+     * ICarrierConfigChangeListener.
+     */
+    private final ConcurrentHashMap<CarrierConfigManager.CarrierConfigChangeListener,
+                ICarrierConfigChangeListener>
+            mCarrierConfigChangeListenerMap = new ConcurrentHashMap<>();
+
+
+    /** @hide **/
+    public TelephonyRegistryManager(@NonNull Context context) {
+        mContext = context;
+        if (sRegistry == null) {
+            sRegistry = ITelephonyRegistry.Stub.asInterface(
+                ServiceManager.getService("telephony.registry"));
+        }
+    }
+
+    /**
+     * Register for changes to the list of {@link SubscriptionInfo} records or to the
+     * individual records (active or inactive) themselves. When a change occurs, the
+     * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged()} method of
+     * the listener will be invoked immediately. The
+     * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged()} method will also be invoked
+     * once initially when calling this method.
+     *
+     * @param listener an instance of {@link OnSubscriptionsChangedListener} with
+     * {@link OnSubscriptionsChangedListener#onSubscriptionsChanged()} overridden.
+     * @param executor the executor that will execute callbacks.
+     * @hide
+     */
+    public void addOnSubscriptionsChangedListener(
+            @NonNull SubscriptionManager.OnSubscriptionsChangedListener listener,
+            @NonNull Executor executor) {
+        if (mSubscriptionChangedListenerMap.get(listener) != null) {
+            Log.d(TAG, "addOnSubscriptionsChangedListener listener already present");
+            return;
+        }
+        IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
+            @Override
+            public void onSubscriptionsChanged () {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> listener.onSubscriptionsChanged());
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        };
+        mSubscriptionChangedListenerMap.put(listener, callback);
+        try {
+            sRegistry.addOnSubscriptionsChangedListener(mContext.getOpPackageName(),
+                    mContext.getAttributionTag(), callback);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregister the {@link SubscriptionManager.OnSubscriptionsChangedListener}. This is not
+     * strictly necessary as the listener will automatically be unregistered if an attempt to
+     * invoke the listener fails.
+     *
+     * @param listener that is to be unregistered.
+     * @hide
+     */
+    public void removeOnSubscriptionsChangedListener(
+            @NonNull SubscriptionManager.OnSubscriptionsChangedListener listener) {
+        if (mSubscriptionChangedListenerMap.get(listener) == null) {
+            return;
+        }
+        try {
+            sRegistry.removeOnSubscriptionsChangedListener(mContext.getOpPackageName(),
+                    mSubscriptionChangedListenerMap.get(listener));
+            mSubscriptionChangedListenerMap.remove(listener);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Register for changes to the list of opportunistic subscription records or to the
+     * individual records themselves. When a change occurs the onOpportunisticSubscriptionsChanged
+     * method of the listener will be invoked immediately if there has been a notification.
+     *
+     * @param listener an instance of
+     * {@link SubscriptionManager.OnOpportunisticSubscriptionsChangedListener} with
+     *                 onOpportunisticSubscriptionsChanged overridden.
+     * @param executor an Executor that will execute callbacks.
+     * @hide
+     */
+    public void addOnOpportunisticSubscriptionsChangedListener(
+            @NonNull SubscriptionManager.OnOpportunisticSubscriptionsChangedListener listener,
+            @NonNull Executor executor) {
+        if (mOpportunisticSubscriptionChangedListenerMap.get(listener) != null) {
+            Log.d(TAG, "addOnOpportunisticSubscriptionsChangedListener listener already present");
+            return;
+        }
+        /**
+         * The callback methods need to be called on the executor thread where
+         * this object was created.  If the binder did that for us it'd be nice.
+         */
+        IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
+            @Override
+            public void onSubscriptionsChanged() {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    Log.d(TAG, "onOpportunisticSubscriptionsChanged callback received.");
+                    executor.execute(() -> listener.onOpportunisticSubscriptionsChanged());
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        };
+        mOpportunisticSubscriptionChangedListenerMap.put(listener, callback);
+        try {
+            sRegistry.addOnOpportunisticSubscriptionsChangedListener(mContext.getOpPackageName(),
+                    mContext.getAttributionTag(), callback);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregister the {@link SubscriptionManager.OnOpportunisticSubscriptionsChangedListener}
+     * that is currently listening opportunistic subscriptions change. This is not strictly
+     * necessary as the listener will automatically be unregistered if an attempt to invoke the
+     * listener fails.
+     *
+     * @param listener that is to be unregistered.
+     * @hide
+     */
+    public void removeOnOpportunisticSubscriptionsChangedListener(
+            @NonNull SubscriptionManager.OnOpportunisticSubscriptionsChangedListener listener) {
+        if (mOpportunisticSubscriptionChangedListenerMap.get(listener) == null) {
+            return;
+        }
+        try {
+            sRegistry.removeOnSubscriptionsChangedListener(mContext.getOpPackageName(),
+                    mOpportunisticSubscriptionChangedListenerMap.get(listener));
+            mOpportunisticSubscriptionChangedListenerMap.remove(listener);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * To check the SDK version for {@code #listenFromListener}.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P)
+    private static final long LISTEN_CODE_CHANGE = 147600208L;
+
+    /**
+     * Listen for incoming subscriptions
+     * @param subId Subscription ID
+     * @param pkg Package name
+     * @param featureId Feature ID
+     * @param listener Listener providing callback
+     * @param events Events
+     * @param notifyNow Whether to notify instantly
+     * @hide
+     */
+    public void listenFromListener(int subId, @NonNull boolean renounceFineLocationAccess,
+            @NonNull boolean renounceCoarseLocationAccess, @NonNull String pkg,
+            @NonNull String featureId, @NonNull PhoneStateListener listener,
+            @NonNull int events, boolean notifyNow) {
+        if (listener == null) {
+            throw new IllegalStateException("telephony service is null.");
+        }
+
+        try {
+            int[] eventsList = getEventsFromBitmask(events).stream().mapToInt(i -> i).toArray();
+            // subId from PhoneStateListener is deprecated Q on forward, use the subId from
+            // TelephonyManager instance. Keep using subId from PhoneStateListener for pre-Q.
+            if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {
+                // Since mSubId in PhoneStateListener is deprecated from Q on forward, this is
+                // the only place to set mSubId and its for "informational" only.
+                listener.mSubId = (eventsList.length == 0)
+                        ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId;
+            } else if (listener.mSubId != null) {
+                subId = listener.mSubId;
+            }
+            sRegistry.listenWithEventList(renounceFineLocationAccess, renounceCoarseLocationAccess,
+                    subId, pkg, featureId, listener.callback, eventsList, notifyNow);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Listen for incoming subscriptions
+     * @param subId Subscription ID
+     * @param pkg Package name
+     * @param featureId Feature ID
+     * @param telephonyCallback Listener providing callback
+     * @param events List events
+     * @param notifyNow Whether to notify instantly
+     */
+    private void listenFromCallback(boolean renounceFineLocationAccess,
+            boolean renounceCoarseLocationAccess, int subId,
+            @NonNull String pkg, @NonNull String featureId,
+            @NonNull TelephonyCallback telephonyCallback, @NonNull int[] events,
+            boolean notifyNow) {
+        try {
+            sRegistry.listenWithEventList(renounceFineLocationAccess, renounceCoarseLocationAccess,
+                    subId, pkg, featureId, telephonyCallback.callback, events, notifyNow);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Informs the system of an intentional upcoming carrier network change by a carrier app.
+     * This call only used to allow the system to provide alternative UI while telephony is
+     * performing an action that may result in intentional, temporary network lack of connectivity.
+     * <p>
+     * Based on the active parameter passed in, this method will either show or hide the alternative
+     * UI. There is no timeout associated with showing this UX, so a carrier app must be sure to
+     * call with active set to false sometime after calling with it set to {@code true}.
+     * <p>
+     * This will apply to all subscriptions the carrier app has carrier privileges on.
+     * <p>
+     * Requires Permission: calling app has carrier privileges.
+     *
+     * @param active Whether the carrier network change is or shortly will be
+     * active. Set this value to true to begin showing alternative UI and false to stop.
+     * @see TelephonyManager#hasCarrierPrivileges
+     * @hide
+     */
+    public void notifyCarrierNetworkChange(boolean active) {
+        try {
+            sRegistry.notifyCarrierNetworkChange(active);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Informs the system of an intentional upcoming carrier network change by a carrier app on the
+     * given {@code subscriptionId}. This call only used to allow the system to provide alternative
+     * UI while telephony is performing an action that may result in intentional, temporary network
+     * lack of connectivity.
+     * <p>
+     * Based on the active parameter passed in, this method will either show or hide the
+     * alternative UI. There is no timeout associated with showing this UX, so a carrier app must be
+     * sure to call with active set to false sometime after calling with it set to {@code true}.
+     * <p>
+     * Requires Permission: calling app has carrier privileges.
+     *
+     * @param subscriptionId the subscription of the carrier network.
+     * @param active whether the carrier network change is or shortly will be active. Set this value
+     *              to true to begin showing alternative UI and false to stop.
+     * @see TelephonyManager#hasCarrierPrivileges
+     * @hide
+     */
+    public void notifyCarrierNetworkChange(int subscriptionId, boolean active) {
+        try {
+            sRegistry.notifyCarrierNetworkChangeWithSubId(subscriptionId, active);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify call state changed on certain subscription.
+     *
+     * @param slotIndex for which call state changed. Can be derived from subId except when subId is
+     * invalid.
+     * @param subId for which call state changed.
+     * @param state latest call state. e.g, offhook, ringing
+     * @param incomingNumber incoming phone number.
+     * @hide
+     */
+    public void notifyCallStateChanged(int slotIndex, int subId, @CallState int state,
+            @Nullable String incomingNumber) {
+        try {
+            sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify call state changed on all subscriptions, excluding over-the-top VOIP calls (otherwise
+     * known as self-managed calls in the Android Platform).
+     *
+     * @param state latest call state. e.g, offhook, ringing
+     * @param incomingNumber incoming phone number or null in the case for OTT VOIP calls
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void notifyCallStateChangedForAllSubscriptions(@CallState int state,
+            @Nullable String incomingNumber) {
+        try {
+            sRegistry.notifyCallStateForAllSubs(state, incomingNumber);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify {@link SubscriptionInfo} change.
+     * @hide
+     */
+    public void notifySubscriptionInfoChanged() {
+        try {
+            sRegistry.notifySubscriptionInfoChanged();
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify opportunistic {@link SubscriptionInfo} change.
+     * @hide
+     */
+    public void notifyOpportunisticSubscriptionInfoChanged() {
+        try {
+            sRegistry.notifyOpportunisticSubscriptionInfoChanged();
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify {@link ServiceState} update on certain subscription.
+     *
+     * @param slotIndex for which the service state changed. Can be derived from subId except
+     * subId is invalid.
+     * @param subId for which the service state changed.
+     * @param state service state e.g, in service, out of service or roaming status.
+     * @hide
+     */
+    public void notifyServiceStateChanged(int slotIndex, int subId, @NonNull ServiceState state) {
+        try {
+            sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify {@link SignalStrength} update on certain subscription.
+     *
+     * @param slotIndex for which the signalstrength changed. Can be derived from subId except when
+     * subId is invalid.
+     * @param subId for which the signalstrength changed.
+     * @param signalStrength e.g, signalstrength level {@see SignalStrength#getLevel()}
+     * @hide
+     */
+    public void notifySignalStrengthChanged(int slotIndex, int subId,
+            @NonNull SignalStrength signalStrength) {
+        try {
+            sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify changes to the message-waiting indicator on certain subscription. e.g, The status bar
+     * uses message waiting indicator to determine when to display the voicemail icon.
+     *
+     * @param slotIndex for which message waiting indicator changed. Can be derived from subId
+     * except when subId is invalid.
+     * @param subId for which message waiting indicator changed.
+     * @param msgWaitingInd {@code true} indicates there is message-waiting indicator, {@code false}
+     * otherwise.
+     * @hide
+     */
+    public void notifyMessageWaitingChanged(int slotIndex, int subId, boolean msgWaitingInd) {
+        try {
+            sRegistry.notifyMessageWaitingChangedForPhoneId(slotIndex, subId, msgWaitingInd);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify changes to the call-forwarding status on certain subscription.
+     *
+     * @param subId for which call forwarding status changed.
+     * @param callForwardInd {@code true} indicates there is call forwarding, {@code false}
+     * otherwise.
+     * @hide
+     */
+    public void notifyCallForwardingChanged(int subId, boolean callForwardInd) {
+        try {
+            sRegistry.notifyCallForwardingChangedForSubscriber(subId, callForwardInd);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify changes to activity state changes on certain subscription.
+     *
+     * @param subId for which data activity state changed.
+     * @param dataActivityType indicates the latest data activity type e.g. {@link
+     * TelephonyManager#DATA_ACTIVITY_IN}
+     * @hide
+     */
+    public void notifyDataActivityChanged(int subId, @DataActivityType int dataActivityType) {
+        try {
+            sRegistry.notifyDataActivityForSubscriber(subId, dataActivityType);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify changes to activity state changes on certain subscription.
+     *
+     * @param slotIndex for which data activity changed. Can be derived from subId except
+     * when subId is invalid.
+     * @param subId for which data activity state changed.
+     * @param dataActivityType indicates the latest data activity type e.g. {@link
+     * TelephonyManager#DATA_ACTIVITY_IN}
+     * @hide
+     */
+    public void notifyDataActivityChanged(int slotIndex, int subId,
+            @DataActivityType int dataActivityType) {
+        try {
+            sRegistry.notifyDataActivityForSubscriberWithSlot(slotIndex, subId, dataActivityType);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify changes to default (Internet) data connection state on certain subscription.
+     *
+     * @param slotIndex for which data connections state changed. Can be derived from subId except
+     * when subId is invalid.
+     * @param subId for which data connection state changed.
+     * @param preciseState the PreciseDataConnectionState
+     *
+     * @see PreciseDataConnectionState
+     * @see TelephonyManager#DATA_DISCONNECTED
+     * @hide
+     */
+    public void notifyDataConnectionForSubscriber(int slotIndex, int subId,
+            @NonNull PreciseDataConnectionState preciseState) {
+        try {
+            sRegistry.notifyDataConnectionForSubscriber(
+                    slotIndex, subId, preciseState);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify {@link CallQuality} change on certain subscription.
+     *
+     * @param slotIndex for which call quality state changed. Can be derived from subId except when
+     * subId is invalid.
+     * @param subId for which call quality state changed.
+     * @param callQuality Information about call quality e.g, call quality level
+     * @param networkType associated with this data connection. e.g, LTE
+     * @hide
+     */
+    public void notifyCallQualityChanged(int slotIndex, int subId, @NonNull CallQuality callQuality,
+        @NetworkType int networkType) {
+        try {
+            sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify change of media quality status {@link MediaQualityStatus} crosses media quality
+     * threshold
+     * <p/>
+     * Currently thresholds for this indication can be configurable by CARRIER_CONFIG
+     * {@link CarrierConfigManager#KEY_VOICE_RTP_THRESHOLDS_PACKET_LOSS_RATE_INT}
+     * {@link CarrierConfigManager#KEY_VOICE_RTP_THRESHOLDS_INACTIVITY_TIME_IN_MILLIS_INT}
+     * {@link CarrierConfigManager#KEY_VOICE_RTP_THRESHOLDS_JITTER_INT}
+     *
+     * @param status media quality status
+     * @hide
+     */
+    public void notifyMediaQualityStatusChanged(
+            int slotIndex, int subId, @NonNull MediaQualityStatus status) {
+        try {
+            sRegistry.notifyMediaQualityStatusChanged(slotIndex, subId, status);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify emergency number list changed on certain subscription.
+     *
+     * @param slotIndex for which emergency number list changed. Can be derived from subId except
+     * when subId is invalid.
+     * @param subId for which emergency number list changed.
+     * @hide
+     */
+    public void notifyEmergencyNumberList( int slotIndex, int subId) {
+        try {
+            sRegistry.notifyEmergencyNumberList(slotIndex, subId);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify outgoing emergency call to all applications that have registered a listener
+     * ({@link PhoneStateListener}) or a callback ({@link TelephonyCallback}) to monitor changes in
+     * telephony states.
+     * @param simSlotIndex Sender phone ID.
+     * @param subscriptionId Sender subscription ID.
+     * @param emergencyNumber Emergency number.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void notifyOutgoingEmergencyCall(int simSlotIndex, int subscriptionId,
+            @NonNull EmergencyNumber emergencyNumber) {
+        try {
+            sRegistry.notifyOutgoingEmergencyCall(simSlotIndex, subscriptionId, emergencyNumber);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify outgoing emergency SMS.
+     * @param phoneId Sender phone ID.
+     * @param subId Sender subscription ID.
+     * @param emergencyNumber Emergency number.
+     * @hide
+     */
+    public void notifyOutgoingEmergencySms(int phoneId, int subId,
+            @NonNull EmergencyNumber emergencyNumber) {
+        try {
+            sRegistry.notifyOutgoingEmergencySms(phoneId, subId, emergencyNumber);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify radio power state changed on certain subscription.
+     *
+     * @param slotIndex for which radio power state changed. Can be derived from subId except when
+     * subId is invalid.
+     * @param subId for which radio power state changed.
+     * @param radioPowerState the current modem radio state.
+     * @hide
+     */
+    public void notifyRadioPowerStateChanged(int slotIndex, int subId,
+            @RadioPowerState int radioPowerState) {
+        try {
+            sRegistry.notifyRadioPowerStateChanged(slotIndex, subId, radioPowerState);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify {@link PhoneCapability} changed.
+     *
+     * @param phoneCapability the capability of the modem group.
+     * @hide
+     */
+    public void notifyPhoneCapabilityChanged(@NonNull PhoneCapability phoneCapability) {
+        try {
+            sRegistry.notifyPhoneCapabilityChanged(phoneCapability);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sim activation type: voice
+     * @see #notifyVoiceActivationStateChanged
+     * @hide
+     */
+    public static final int SIM_ACTIVATION_TYPE_VOICE = 0;
+    /**
+     * Sim activation type: data
+     * @see #notifyDataActivationStateChanged
+     * @hide
+     */
+    public static final int SIM_ACTIVATION_TYPE_DATA = 1;
+
+    /**
+     * Notify data activation state changed on certain subscription.
+     * @see TelephonyManager#getDataActivationState()
+     *
+     * @param slotIndex for which data activation state changed. Can be derived from subId except
+     * when subId is invalid.
+     * @param subId for which data activation state changed.
+     * @param activationState sim activation state e.g, activated.
+     * @hide
+     */
+    public void notifyDataActivationStateChanged(int slotIndex, int subId,
+            @SimActivationState int activationState) {
+        try {
+            sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId,
+                    SIM_ACTIVATION_TYPE_DATA, activationState);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify voice activation state changed on certain subscription.
+     * @see TelephonyManager#getVoiceActivationState()
+     *
+     * @param slotIndex for which voice activation state changed. Can be derived from subId except
+     * subId is invalid.
+     * @param subId for which voice activation state changed.
+     * @param activationState sim activation state e.g, activated.
+     * @hide
+     */
+    public void notifyVoiceActivationStateChanged(int slotIndex, int subId,
+            @SimActivationState int activationState) {
+        try {
+            sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId,
+                    SIM_ACTIVATION_TYPE_VOICE, activationState);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify User mobile data state changed on certain subscription. e.g, mobile data is enabled
+     * or disabled.
+     *
+     * @param slotIndex for which mobile data state has changed. Can be derived from subId except
+     * when subId is invalid.
+     * @param subId for which mobile data state has changed.
+     * @param state {@code true} indicates mobile data is enabled/on. {@code false} otherwise.
+     * @hide
+     */
+    public void notifyUserMobileDataStateChanged(int slotIndex, int subId, boolean state) {
+        try {
+            sRegistry.notifyUserMobileDataStateChangedForPhoneId(slotIndex, subId, state);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify display info changed.
+     *
+     * @param slotIndex The SIM slot index for which display info has changed. Can be
+     * derived from {@code subscriptionId} except when {@code subscriptionId} is invalid, such as
+     * when the device is in emergency-only mode.
+     * @param subscriptionId Subscription id for which display network info has changed.
+     * @param telephonyDisplayInfo The display info.
+     * @hide
+     */
+    public void notifyDisplayInfoChanged(int slotIndex, int subscriptionId,
+            @NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
+        try {
+            sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, telephonyDisplayInfo);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify IMS call disconnect causes which contains {@link android.telephony.ims.ImsReasonInfo}.
+     *
+     * @param subId for which ims call disconnect.
+     * @param imsReasonInfo the reason for ims call disconnect.
+     * @hide
+     */
+    public void notifyImsDisconnectCause(int subId, @NonNull ImsReasonInfo imsReasonInfo) {
+        try {
+            sRegistry.notifyImsDisconnectCause(subId, imsReasonInfo);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify single Radio Voice Call Continuity (SRVCC) state change for the currently active call
+     * on certain subscription.
+     *
+     * @param subId for which srvcc state changed.
+     * @param state srvcc state
+     * @hide
+     */
+    public void notifySrvccStateChanged(int subId, @SrvccState int state) {
+        try {
+            sRegistry.notifySrvccStateChanged(subId, state);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify precise call state changed on certain subscription, including foreground, background
+     * and ringcall states.
+     *
+     * @param slotIndex for which precise call state changed. Can be derived from subId except when
+     * subId is invalid.
+     * @param subId for which precise call state changed.
+     * @param callStates Array of PreciseCallState of foreground, background & ringing calls.
+     * @param imsCallIds Array of IMS call session ID{@link ImsCallSession#getCallId} for
+     *                   ringing, foreground & background calls.
+     * @param imsServiceTypes Array of IMS call service type for ringing, foreground &
+     *                        background calls.
+     * @param imsCallTypes Array of IMS call type for ringing, foreground & background calls.
+     * @hide
+     */
+    public void notifyPreciseCallState(int slotIndex, int subId,
+            @Annotation.PreciseCallStates int[] callStates, String[] imsCallIds,
+            @Annotation.ImsCallServiceType int[] imsServiceTypes,
+            @Annotation.ImsCallType int[] imsCallTypes) {
+        try {
+            sRegistry.notifyPreciseCallState(slotIndex, subId, callStates,
+                    imsCallIds, imsServiceTypes, imsCallTypes);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify call disconnect causes which contains {@link DisconnectCause} and {@link
+     * android.telephony.PreciseDisconnectCause}.
+     *
+     * @param slotIndex for which call disconnected. Can be derived from subId except when subId is
+     * invalid.
+     * @param subId for which call disconnected.
+     * @param cause {@link DisconnectCause} for the disconnected call.
+     * @param preciseCause {@link android.telephony.PreciseDisconnectCause} for the disconnected
+     * call.
+     * @hide
+     */
+    public void notifyDisconnectCause(int slotIndex, int subId, @DisconnectCauses int cause,
+            @PreciseDisconnectCauses int preciseCause) {
+        try {
+            sRegistry.notifyDisconnectCause(slotIndex, subId, cause, preciseCause);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify {@link android.telephony.CellLocation} changed.
+     *
+     * <p>To be compatible with {@link TelephonyRegistry}, use {@link CellIdentity} which is
+     * parcelable, and convert to CellLocation in client code.
+     * @hide
+     */
+    public void notifyCellLocation(int subId, @NonNull CellIdentity cellLocation) {
+        try {
+            sRegistry.notifyCellLocationForSubscriber(subId, cellLocation);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify {@link CellInfo} changed on certain subscription. e.g, when an observed cell info has
+     * changed or new cells have been added or removed on the given subscription.
+     *
+     * @param subId for which cellinfo changed.
+     * @param cellInfo A list of cellInfo associated with the given subscription.
+     * @hide
+     */
+    public void notifyCellInfoChanged(int subId, @NonNull List<CellInfo> cellInfo) {
+        try {
+            sRegistry.notifyCellInfoForSubscriber(subId, cellInfo);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify that the active data subscription ID has changed.
+     * @param activeDataSubId The new subscription ID for active data
+     * @hide
+     */
+    public void notifyActiveDataSubIdChanged(int activeDataSubId) {
+        try {
+            sRegistry.notifyActiveDataSubIdChanged(activeDataSubId);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Report that Registration or a Location/Routing/Tracking Area update has failed.
+     *
+     * @param slotIndex for which call disconnected. Can be derived from subId except when subId is
+     * invalid.
+     * @param subId for which cellinfo changed.
+     * @param cellIdentity the CellIdentity, which must include the globally unique identifier
+     *        for the cell (for example, all components of the CGI or ECGI).
+     * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the
+     *         cell that was chosen for the failed registration attempt.
+     * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure.
+     * @param causeCode the primary failure cause code of the procedure.
+     *        For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
+     *        For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
+     *        For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+     *        For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
+     *        Integer.MAX_VALUE if this value is unused.
+     * @param additionalCauseCode the cause code of any secondary/combined procedure if appropriate.
+     *        For UMTS, if a combined attach succeeds for PS only, then the GMM cause code shall be
+     *        included as an additionalCauseCode. For LTE (ESM), cause codes are in
+     *        TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
+     * @hide
+     */
+    public void notifyRegistrationFailed(int slotIndex, int subId,
+            @NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
+            int domain, int causeCode, int additionalCauseCode) {
+        try {
+            sRegistry.notifyRegistrationFailed(slotIndex, subId, cellIdentity,
+                    chosenPlmn, domain, causeCode, additionalCauseCode);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify {@link BarringInfo} has changed for a specific subscription.
+     *
+     * @param slotIndex for the phone object that got updated barring info.
+     * @param subId for which the BarringInfo changed.
+     * @param barringInfo updated BarringInfo.
+     * @hide
+     */
+    public void notifyBarringInfoChanged(
+            int slotIndex, int subId, @NonNull BarringInfo barringInfo) {
+        try {
+            sRegistry.notifyBarringInfoChanged(slotIndex, subId, barringInfo);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify {@link PhysicalChannelConfig} has changed for a specific subscription.
+     *
+     * @param slotIndex for which physical channel configs changed.
+     * @param subId the subId
+     * @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel.
+     * @hide
+     */
+    public void notifyPhysicalChannelConfigForSubscriber(int slotIndex, int subId,
+            List<PhysicalChannelConfig> configs) {
+        try {
+            sRegistry.notifyPhysicalChannelConfigForSubscriber(slotIndex, subId, configs);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify that the data enabled has changed.
+     *
+     * @param enabled True if data is enabled, otherwise disabled.
+     * @param reason Reason for data enabled/disabled. See {@code REASON_*} in
+     * {@link TelephonyManager}.
+     * @hide
+     */
+    public void notifyDataEnabled(int slotIndex, int subId, boolean enabled,
+            @TelephonyManager.DataEnabledReason int reason) {
+        try {
+            sRegistry.notifyDataEnabled(slotIndex, subId, enabled, reason);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify the allowed network types has changed for a specific subscription and the specific
+     * reason.
+     * @param slotIndex for which allowed network types changed.
+     * @param subId for which allowed network types changed.
+     * @param reason an allowed network type reasons.
+     * @param allowedNetworkType an allowed network type bitmask value.
+     * @hide
+     */
+    public void notifyAllowedNetworkTypesChanged(int slotIndex, int subId,
+            int reason, long allowedNetworkType) {
+        try {
+            sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, reason,
+                    allowedNetworkType);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify that the link capacity estimate has changed.
+     * @param slotIndex for the phone object that gets the updated link capacity estimate
+     * @param subId for subscription that gets the updated link capacity estimate
+     * @param linkCapacityEstimateList a list of {@link  LinkCapacityEstimate}
+     * @hide
+     */
+    public void notifyLinkCapacityEstimateChanged(int slotIndex, int subId,
+            List<LinkCapacityEstimate> linkCapacityEstimateList) {
+        try {
+            sRegistry.notifyLinkCapacityEstimateChanged(slotIndex, subId, linkCapacityEstimateList);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify external listeners that the subscriptions supporting simultaneous cellular calling
+     * have changed.
+     * @param subIds The new set of subIds supporting simultaneous cellular calling.
+     * @hide
+     */
+    public void notifySimultaneousCellularCallingSubscriptionsChanged(
+            @NonNull Set<Integer> subIds) {
+        try {
+            sRegistry.notifySimultaneousCellularCallingSubscriptionsChanged(
+                    subIds.stream().mapToInt(i -> i).toArray());
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify external listeners that carrier roaming non-terrestrial network mode changed.
+     * @param subId subscription ID.
+     * @param active {@code true} If the device is connected to carrier roaming
+     *                           non-terrestrial network or was connected within the
+     *                           {CarrierConfigManager#KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT}
+     *                           duration, {code false} otherwise.
+     * @hide
+     */
+    public void notifyCarrierRoamingNtnModeChanged(int subId, boolean active) {
+        try {
+            sRegistry.notifyCarrierRoamingNtnModeChanged(subId, active);
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Processes potential event changes from the provided {@link TelephonyCallback}.
+     *
+     * @param telephonyCallback callback for monitoring callback changes to the telephony state.
+     * @hide
+     */
+    public @NonNull Set<Integer> getEventsFromCallback(
+            @NonNull TelephonyCallback telephonyCallback) {
+        Set<Integer> eventList = new ArraySet<>();
+
+        if (telephonyCallback instanceof TelephonyCallback.ServiceStateListener) {
+            eventList.add(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.MessageWaitingIndicatorListener) {
+            eventList.add(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.CallForwardingIndicatorListener) {
+            eventList.add(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.CellLocationListener) {
+            eventList.add(TelephonyCallback.EVENT_CELL_LOCATION_CHANGED);
+        }
+
+        // Note: Legacy PhoneStateListeners use EVENT_LEGACY_CALL_STATE_CHANGED
+        if (telephonyCallback instanceof TelephonyCallback.CallStateListener) {
+            eventList.add(TelephonyCallback.EVENT_CALL_STATE_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.DataConnectionStateListener) {
+            eventList.add(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.DataActivityListener) {
+            eventList.add(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.SignalStrengthsListener) {
+            eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.CellInfoListener) {
+            eventList.add(TelephonyCallback.EVENT_CELL_INFO_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.PreciseCallStateListener) {
+            eventList.add(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.CallDisconnectCauseListener) {
+            eventList.add(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.ImsCallDisconnectCauseListener) {
+            eventList.add(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.PreciseDataConnectionStateListener) {
+            eventList.add(TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.SrvccStateListener) {
+            eventList.add(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.VoiceActivationStateListener) {
+            eventList.add(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.DataActivationStateListener) {
+            eventList.add(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.UserMobileDataStateListener) {
+            eventList.add(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.DisplayInfoListener) {
+            eventList.add(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.EmergencyNumberListListener) {
+            eventList.add(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.OutgoingEmergencyCallListener) {
+            eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.OutgoingEmergencySmsListener) {
+            eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.PhoneCapabilityListener) {
+            eventList.add(TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.ActiveDataSubscriptionIdListener) {
+            eventList.add(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.RadioPowerStateListener) {
+            eventList.add(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.CarrierNetworkListener) {
+            eventList.add(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.RegistrationFailedListener) {
+            eventList.add(TelephonyCallback.EVENT_REGISTRATION_FAILURE);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.CallAttributesListener) {
+            eventList.add(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.BarringInfoListener) {
+            eventList.add(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.PhysicalChannelConfigListener) {
+            eventList.add(TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.DataEnabledListener) {
+            eventList.add(TelephonyCallback.EVENT_DATA_ENABLED_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.AllowedNetworkTypesListener) {
+            eventList.add(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.LinkCapacityEstimateChangedListener) {
+            eventList.add(TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.MediaQualityStatusChangedListener) {
+            eventList.add(TelephonyCallback.EVENT_MEDIA_QUALITY_STATUS_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.EmergencyCallbackModeListener) {
+            eventList.add(TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED);
+        }
+
+        if (telephonyCallback
+                instanceof TelephonyCallback.SimultaneousCellularCallingSupportListener) {
+            eventList.add(
+                    TelephonyCallback.EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED);
+        }
+
+        if (telephonyCallback instanceof TelephonyCallback.CarrierRoamingNtnModeListener) {
+            eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED);
+        }
+        return eventList;
+    }
+
+    private @NonNull Set<Integer> getEventsFromBitmask(int eventMask) {
+
+        Set<Integer> eventList = new ArraySet<>();
+
+        if ((eventMask & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
+            eventList.add(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
+            eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
+            eventList.add(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
+            eventList.add(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
+            eventList.add(TelephonyCallback.EVENT_CELL_LOCATION_CHANGED);
+        }
+
+        // Note: Legacy call state listeners can get the phone number which is not provided in the
+        // new version in TelephonyCallback.
+        if ((eventMask & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
+            eventList.add(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
+            eventList.add(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
+            eventList.add(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
+            eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_CELL_INFO) != 0) {
+            eventList.add(TelephonyCallback.EVENT_CELL_INFO_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
+            eventList.add(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
+            eventList.add(TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO) != 0) {
+            eventList.add(TelephonyCallback.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
+            eventList.add(TelephonyCallback.EVENT_OEM_HOOK_RAW);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) {
+            eventList.add(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) {
+            eventList.add(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) != 0) {
+            eventList.add(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) != 0) {
+            eventList.add(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
+            eventList.add(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
+            eventList.add(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) {
+            eventList.add(TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) {
+            eventList.add(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) {
+            eventList.add(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
+            eventList.add(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
+            eventList.add(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) {
+            eventList.add(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) {
+            eventList.add(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL) != 0) {
+            eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS) != 0) {
+            eventList.add(TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_REGISTRATION_FAILURE) != 0) {
+            eventList.add(TelephonyCallback.EVENT_REGISTRATION_FAILURE);
+        }
+
+        if ((eventMask & PhoneStateListener.LISTEN_BARRING_INFO) != 0) {
+            eventList.add(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
+        }
+        return eventList;
+
+    }
+
+    /**
+     * Registers a callback object to receive notification of changes in specified telephony states.
+     * <p>
+     * To register a callback, pass a {@link TelephonyCallback} which implements
+     * interfaces of events. For example,
+     * FakeServiceStateCallback extends {@link TelephonyCallback} implements
+     * {@link TelephonyCallback.ServiceStateListener}.
+     *
+     * At registration, and when a specified telephony state changes, the telephony manager invokes
+     * the appropriate callback method on the callback object and passes the current (updated)
+     * values.
+     * <p>
+     *
+     * If this TelephonyManager object has been created with
+     * {@link TelephonyManager#createForSubscriptionId}, applies to the given subId.
+     * Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     * To register events for multiple subIds, pass a separate callback object to
+     * each TelephonyManager object created with {@link TelephonyManager#createForSubscriptionId}.
+     *
+     * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
+     * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
+     * {@link SecurityException} will be thrown otherwise.
+     *
+     * This API should be used sparingly -- large numbers of callbacks will cause system
+     * instability. If a process has registered too many callbacks without unregistering them, it
+     * may encounter an {@link IllegalStateException} when trying to register more callbacks.
+     *
+     * @param callback The {@link TelephonyCallback} object to register.
+     * @hide
+     */
+    public void registerTelephonyCallback(boolean renounceFineLocationAccess,
+            boolean renounceCoarseLocationAccess,
+            @NonNull @CallbackExecutor Executor executor,
+            int subId, String pkgName, String attributionTag, @NonNull TelephonyCallback callback,
+            boolean notifyNow) {
+        if (callback == null) {
+            throw new IllegalStateException("telephony service is null.");
+        }
+        callback.init(executor);
+        listenFromCallback(renounceFineLocationAccess, renounceCoarseLocationAccess, subId,
+                pkgName, attributionTag, callback,
+                getEventsFromCallback(callback).stream().mapToInt(i -> i).toArray(), notifyNow);
+    }
+
+    /**
+     * Unregister an existing {@link TelephonyCallback}.
+     *
+     * @param callback The {@link TelephonyCallback} object to unregister.
+     * @hide
+     */
+    public void unregisterTelephonyCallback(int subId, String pkgName, String attributionTag,
+            @NonNull TelephonyCallback callback, boolean notifyNow) {
+        listenFromCallback(false, false, subId,
+                pkgName, attributionTag, callback, new int[0], notifyNow);
+    }
+
+    private static class CarrierPrivilegesCallbackWrapper extends ICarrierPrivilegesCallback.Stub
+            implements ListenerExecutor {
+        @NonNull private final WeakReference<CarrierPrivilegesCallback> mCallback;
+        @NonNull private final Executor mExecutor;
+
+        CarrierPrivilegesCallbackWrapper(
+                @NonNull CarrierPrivilegesCallback callback, @NonNull Executor executor) {
+            mCallback = new WeakReference<>(callback);
+            mExecutor = executor;
+        }
+
+        @Override
+        public void onCarrierPrivilegesChanged(
+                @NonNull List<String> privilegedPackageNames, @NonNull int[] privilegedUids) {
+            // AIDL interface does not support Set, keep the List/Array and translate them here
+            Set<String> privilegedPkgNamesSet = Set.copyOf(privilegedPackageNames);
+            Set<Integer> privilegedUidsSet = Arrays.stream(privilegedUids).boxed().collect(
+                    Collectors.toSet());
+            Binder.withCleanCallingIdentity(
+                    () ->
+                            executeSafely(
+                                    mExecutor,
+                                    mCallback::get,
+                                    cpc ->
+                                            cpc.onCarrierPrivilegesChanged(
+                                                    privilegedPkgNamesSet, privilegedUidsSet)));
+        }
+
+        @Override
+        public void onCarrierServiceChanged(@Nullable String packageName, int uid) {
+            Binder.withCleanCallingIdentity(
+                    () ->
+                            executeSafely(
+                                    mExecutor,
+                                    mCallback::get,
+                                    cpc -> cpc.onCarrierServiceChanged(packageName, uid)));
+        }
+    }
+
+    @NonNull
+    @GuardedBy("sCarrierPrivilegeCallbacks")
+    private static final WeakHashMap<CarrierPrivilegesCallback,
+            WeakReference<CarrierPrivilegesCallbackWrapper>>
+            sCarrierPrivilegeCallbacks = new WeakHashMap<>();
+
+    /**
+     * Registers a {@link CarrierPrivilegesCallback} on the given {@code logicalSlotIndex} to
+     * receive callbacks when the set of packages with carrier privileges changes. The callback will
+     * immediately be called with the latest state.
+     *
+     * @param logicalSlotIndex The SIM slot to listen on
+     * @param executor The executor where {@code listener} will be invoked
+     * @param callback The callback to register
+     * @hide
+     */
+    public void addCarrierPrivilegesCallback(
+            int logicalSlotIndex,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull CarrierPrivilegesCallback callback) {
+        if (callback == null || executor == null) {
+            throw new IllegalArgumentException("callback and executor must be non-null");
+        }
+        synchronized (sCarrierPrivilegeCallbacks) {
+            WeakReference<CarrierPrivilegesCallbackWrapper> existing =
+                    sCarrierPrivilegeCallbacks.get(callback);
+            if (existing != null && existing.get() != null) {
+                Log.d(TAG, "addCarrierPrivilegesCallback: callback already registered");
+                return;
+            }
+            CarrierPrivilegesCallbackWrapper wrapper =
+                    new CarrierPrivilegesCallbackWrapper(callback, executor);
+            sCarrierPrivilegeCallbacks.put(callback, new WeakReference<>(wrapper));
+            try {
+                sRegistry.addCarrierPrivilegesCallback(
+                        logicalSlotIndex,
+                        wrapper,
+                        mContext.getOpPackageName(),
+                        mContext.getAttributionTag());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Unregisters a {@link CarrierPrivilegesCallback}.
+     *
+     * @param callback The callback to unregister
+     * @hide
+     */
+    public void removeCarrierPrivilegesCallback(@NonNull CarrierPrivilegesCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("listener must be non-null");
+        }
+        synchronized (sCarrierPrivilegeCallbacks) {
+            WeakReference<CarrierPrivilegesCallbackWrapper> ref =
+                    sCarrierPrivilegeCallbacks.remove(callback);
+            if (ref == null) return;
+            CarrierPrivilegesCallbackWrapper wrapper = ref.get();
+            if (wrapper == null) return;
+            try {
+                sRegistry.removeCarrierPrivilegesCallback(wrapper, mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Notify listeners that the set of packages with carrier privileges has changed.
+     *
+     * @param logicalSlotIndex The SIM slot the change occurred on
+     * @param privilegedPackageNames The updated set of packages names with carrier privileges
+     * @param privilegedUids The updated set of UIDs with carrier privileges
+     * @hide
+     */
+    public void notifyCarrierPrivilegesChanged(
+            int logicalSlotIndex,
+            @NonNull Set<String> privilegedPackageNames,
+            @NonNull Set<Integer> privilegedUids) {
+        if (privilegedPackageNames == null || privilegedUids == null) {
+            throw new IllegalArgumentException(
+                    "privilegedPackageNames and privilegedUids must be non-null");
+        }
+        try {
+            // AIDL doesn't support Set yet. Convert Set to List/Array
+            List<String> pkgList = List.copyOf(privilegedPackageNames);
+            int[] uids = privilegedUids.stream().mapToInt(Number::intValue).toArray();
+            sRegistry.notifyCarrierPrivilegesChanged(logicalSlotIndex, pkgList, uids);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify listeners that the {@link CarrierService} for current user has changed.
+     *
+     * @param logicalSlotIndex the SIM slot the change occurred on
+     * @param packageName the package name of the changed {@link CarrierService}
+     * @param uid the UID of the changed {@link CarrierService}
+     * @hide
+     */
+    public void notifyCarrierServiceChanged(int logicalSlotIndex, @Nullable String packageName,
+            int uid) {
+        try {
+            sRegistry.notifyCarrierServiceChanged(logicalSlotIndex, packageName, uid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Register a {@link android.telephony.CarrierConfigManager.CarrierConfigChangeListener} to get
+     * notification when carrier configurations have changed.
+     *
+     * @param executor The executor on which the callback will be executed.
+     * @param listener The CarrierConfigChangeListener to be registered with.
+     * @hide
+     */
+    public void addCarrierConfigChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull CarrierConfigManager.CarrierConfigChangeListener listener) {
+        Objects.requireNonNull(executor, "Executor should be non-null.");
+        Objects.requireNonNull(listener, "Listener should be non-null.");
+        if (mCarrierConfigChangeListenerMap.get(listener) != null) {
+            Log.e(TAG, "registerCarrierConfigChangeListener: listener already present");
+            return;
+        }
+
+        ICarrierConfigChangeListener callback = new ICarrierConfigChangeListener.Stub() {
+            @Override
+            public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId,
+                    int specificCarrierId) {
+                Log.d(TAG, "onCarrierConfigChanged call in ICarrierConfigChangeListener callback");
+                final long identify = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> listener.onCarrierConfigChanged(slotIndex, subId,
+                            carrierId, specificCarrierId));
+                } finally {
+                    Binder.restoreCallingIdentity(identify);
+                }
+            }
+        };
+
+        try {
+            sRegistry.addCarrierConfigChangeListener(callback,
+                    mContext.getOpPackageName(), mContext.getAttributionTag());
+            mCarrierConfigChangeListenerMap.put(listener, callback);
+        } catch (RemoteException re) {
+            // system server crashes
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregister to stop the notification when carrier configurations changed.
+     *
+     * @param listener The CarrierConfigChangeListener to be unregistered with.
+     * @hide
+     */
+    public void removeCarrierConfigChangedListener(
+            @NonNull CarrierConfigManager.CarrierConfigChangeListener listener) {
+        Objects.requireNonNull(listener, "Listener should be non-null.");
+        if (mCarrierConfigChangeListenerMap.get(listener) == null) {
+            Log.e(TAG, "removeCarrierConfigChangedListener: listener was not present");
+            return;
+        }
+
+        try {
+            sRegistry.removeCarrierConfigChangeListener(
+                    mCarrierConfigChangeListenerMap.get(listener), mContext.getOpPackageName());
+            mCarrierConfigChangeListenerMap.remove(listener);
+        } catch (RemoteException re) {
+            // System sever crashes
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify the registrants the carrier configurations have changed.
+     *
+     * @param slotIndex         The SIM slot index on which to monitor and get notification.
+     * @param subId             The subscription on the SIM slot. May be
+     *                          {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+     * @param carrierId         The optional carrier Id, may be
+     *                          {@link TelephonyManager#UNKNOWN_CARRIER_ID}.
+     * @param specificCarrierId The optional specific carrier Id, may be {@link
+     *                          TelephonyManager#UNKNOWN_CARRIER_ID}.
+     * @hide
+     */
+    public void notifyCarrierConfigChanged(int slotIndex, int subId, int carrierId,
+            int specificCarrierId) {
+        // Only validate slotIndex, all others are optional and allowed to be invalid
+        if (!SubscriptionManager.isValidPhoneId(slotIndex)) {
+            Log.e(TAG, "notifyCarrierConfigChanged, ignored: invalid slotIndex " + slotIndex);
+            return;
+        }
+        try {
+            sRegistry.notifyCarrierConfigChanged(slotIndex, subId, carrierId, specificCarrierId);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify Callback Mode has been started.
+     * @param phoneId Sender phone ID.
+     * @param subId Sender subscription ID.
+     * @param type for callback mode entry.
+     *             See {@link TelephonyManager.EmergencyCallbackModeType}.
+     * @hide
+     */
+    public void notifyCallBackModeStarted(int phoneId, int subId,
+            @TelephonyManager.EmergencyCallbackModeType int type) {
+        try {
+            Log.d(TAG, "notifyCallBackModeStarted:type=" + type);
+            sRegistry.notifyCallbackModeStarted(phoneId, subId, type);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify Callback Mode has been stopped.
+     * @param phoneId Sender phone ID.
+     * @param subId Sender subscription ID.
+     * @param type for callback mode entry.
+     *             See {@link TelephonyManager.EmergencyCallbackModeType}.
+     * @param reason for changing callback mode.
+     *             See {@link TelephonyManager.EmergencyCallbackModeStopReason}.
+     * @hide
+     */
+    public void notifyCallbackModeStopped(int phoneId, int subId,
+            @TelephonyManager.EmergencyCallbackModeType int type,
+            @TelephonyManager.EmergencyCallbackModeStopReason int reason) {
+        try {
+            Log.d(TAG, "notifyCallbackModeStopped:type=" + type + ", reason=" + reason);
+            sRegistry.notifyCallbackModeStopped(phoneId, subId, type, reason);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/android-35/android/telephony/TelephonyScanManager.java b/android-35/android/telephony/TelephonyScanManager.java
new file mode 100644
index 0000000..b761709
--- /dev/null
+++ b/android-35/android/telephony/TelephonyScanManager.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2017 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;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.telephony.ITelephony;
+import com.android.telephony.Rlog;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages the radio access network scan requests and callbacks.
+ */
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
+public final class TelephonyScanManager {
+
+    private static final String TAG = "TelephonyScanManager";
+
+    /** @hide */
+    public static final String SCAN_RESULT_KEY = "scanResult";
+
+    /** @hide */
+    public static final int CALLBACK_SCAN_RESULTS = 1;
+    /** @hide */
+    public static final int CALLBACK_SCAN_ERROR = 2;
+    /** @hide */
+    public static final int CALLBACK_SCAN_COMPLETE = 3;
+    /** @hide */
+    public static final int CALLBACK_RESTRICTED_SCAN_RESULTS = 4;
+    /** @hide */
+    public static final int CALLBACK_TELEPHONY_DIED = 5;
+
+    /** @hide */
+    public static final int INVALID_SCAN_ID = -1;
+
+    /**
+     * The caller of
+     * {@link
+     * TelephonyManager#requestNetworkScan(NetworkScanRequest, Executor, NetworkScanCallback)}
+     * should implement and provide this callback so that the scan results or errors can be
+     * returned.
+     */
+    public static abstract class NetworkScanCallback {
+        /** Returns the scan results to the user, this callback will be called multiple times. */
+        public void onResults(List<CellInfo> results) {}
+
+        /**
+         * Informs the user that the scan has stopped.
+         *
+         * This callback will be called when the scan is finished or cancelled by the user.
+         * The related NetworkScanRequest will be deleted after this callback.
+         */
+        public void onComplete() {}
+
+        /**
+         * Informs the user that there is some error about the scan.
+         *
+         * This callback will be called whenever there is any error about the scan, and the scan
+         * will be terminated. onComplete() will NOT be called.
+         *
+         * @param error Error code when the scan is failed, as defined in {@link NetworkScan}.
+         */
+        public void onError(@NetworkScan.ScanErrorCode int error) {}
+    }
+
+    private static class NetworkScanInfo {
+        private final NetworkScanRequest mRequest;
+        private final Executor mExecutor;
+        private final NetworkScanCallback mCallback;
+
+        NetworkScanInfo(
+                NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
+            mRequest = request;
+            mExecutor = executor;
+            mCallback = callback;
+        }
+    }
+
+    private final Looper mLooper;
+    private final Handler mHandler;
+    private final Messenger mMessenger;
+    private final SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>();
+    private final Binder.DeathRecipient mDeathRecipient;
+
+    public TelephonyScanManager() {
+        HandlerThread thread = new HandlerThread(TAG);
+        thread.start();
+        mLooper = thread.getLooper();
+        mHandler = new Handler(mLooper) {
+            @Override
+            public void handleMessage(Message message) {
+                checkNotNull(message, "message cannot be null");
+                if (message.what == CALLBACK_TELEPHONY_DIED) {
+                    // If there are no objects in mScanInfo then binder death will simply return.
+                    synchronized (mScanInfo) {
+                        for (int i = 0; i < mScanInfo.size(); i++) {
+                            NetworkScanInfo nsi = mScanInfo.valueAt(i);
+                            // At this point we go into panic mode and ignore errors that would
+                            // normally stop the show in order to try and clean up as gracefully
+                            // as possible.
+                            if (nsi == null) continue; // shouldn't be possible
+                            Executor e = nsi.mExecutor;
+                            NetworkScanCallback cb = nsi.mCallback;
+                            if (e == null || cb == null) continue;
+                            try {
+                                e.execute(
+                                        () -> cb.onError(NetworkScan.ERROR_MODEM_UNAVAILABLE));
+                            } catch (java.util.concurrent.RejectedExecutionException ignore) {
+                                // ignore so that we can continue
+                            }
+                        }
+
+                        mScanInfo.clear();
+                    }
+                    return;
+                }
+
+                NetworkScanInfo nsi;
+                synchronized (mScanInfo) {
+                    nsi = mScanInfo.get(message.arg2);
+                }
+                if (nsi == null) {
+                    Rlog.e(TAG, "Unexpceted message " + message.what
+                            + " as there is no NetworkScanInfo with id " + message.arg2);
+                    return;
+                }
+
+                final NetworkScanCallback callback = nsi.mCallback;
+                final Executor executor = nsi.mExecutor;
+
+                switch (message.what) {
+                    case CALLBACK_RESTRICTED_SCAN_RESULTS:
+                    case CALLBACK_SCAN_RESULTS:
+                        try {
+                            final Bundle b = message.getData();
+                            final Parcelable[] parcelables = b.getParcelableArray(SCAN_RESULT_KEY);
+                            CellInfo[] ci = new CellInfo[parcelables.length];
+                            for (int i = 0; i < parcelables.length; i++) {
+                                ci[i] = (CellInfo) parcelables[i];
+                            }
+                            executor.execute(() -> {
+                                Rlog.d(TAG, "onResults: " + Arrays.toString(ci));
+                                callback.onResults(Arrays.asList(ci));
+                            });
+                        } catch (Exception e) {
+                            Rlog.e(TAG, "Exception in networkscan callback onResults", e);
+                        }
+                        break;
+                    case CALLBACK_SCAN_ERROR:
+                        try {
+                            final int errorCode = message.arg1;
+                            executor.execute(() -> {
+                                Rlog.d(TAG, "onError: " + errorCode);
+                                callback.onError(errorCode);
+                            });
+                            synchronized (mScanInfo) {
+                                mScanInfo.remove(message.arg2);
+                            }
+                        } catch (Exception e) {
+                            Rlog.e(TAG, "Exception in networkscan callback onError", e);
+                        }
+                        break;
+                    case CALLBACK_SCAN_COMPLETE:
+                        try {
+                            executor.execute(() -> {
+                                Rlog.d(TAG, "onComplete");
+                                callback.onComplete();
+                            });
+                            synchronized (mScanInfo) {
+                                mScanInfo.remove(message.arg2);
+                            }
+                        } catch (Exception e) {
+                            Rlog.e(TAG, "Exception in networkscan callback onComplete", e);
+                        }
+                        break;
+                    default:
+                        Rlog.e(TAG, "Unhandled message " + Integer.toHexString(message.what));
+                        break;
+                }
+            }
+        };
+        mMessenger = new Messenger(mHandler);
+        mDeathRecipient = new Binder.DeathRecipient() {
+            @Override
+            public void binderDied() {
+                mHandler.obtainMessage(CALLBACK_TELEPHONY_DIED).sendToTarget();
+            }
+        };
+    }
+
+    /**
+     * Request a network scan.
+     *
+     * This method is asynchronous, so the network scan results will be returned by callback.
+     * The returned NetworkScan will contain a callback method which can be used to stop the scan.
+     *
+     * <p>
+     * Requires Permission:
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * @param renounceFineLocationAccess Set this to true if the caller would not like to receive
+     * location related information which will be sent if the caller already possess
+     * {@link android.Manifest.permission.ACCESS_FINE_LOCATION} and do not renounce the permission
+     * @param request Contains all the RAT with bands/channels that need to be scanned.
+     * @param callback Returns network scan results or errors.
+     * @param callingPackage The package name of the caller
+     * @param callingFeatureId The feature id inside of the calling package
+     * @return A NetworkScan obj which contains a callback which can stop the scan.
+     * @hide
+     */
+    public NetworkScan requestNetworkScan(int subId,
+            boolean renounceFineLocationAccess,
+            NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
+            String callingPackage, @Nullable String callingFeatureId) {
+        try {
+            Objects.requireNonNull(request, "Request was null");
+            Objects.requireNonNull(callback, "Callback was null");
+            Objects.requireNonNull(executor, "Executor was null");
+            final ITelephony telephony = getITelephony();
+            if (telephony == null) return null;
+
+            // The lock must be taken before calling requestNetworkScan because the resulting
+            // scanId can be invoked asynchronously on another thread at any time after
+            // requestNetworkScan invoked, leaving a critical section between that call and adding
+            // the record to the ScanInfo cache.
+            synchronized (mScanInfo) {
+                int scanId = telephony.requestNetworkScan(
+                        subId, renounceFineLocationAccess, request, mMessenger,
+                        new Binder(), callingPackage,
+                        callingFeatureId);
+                if (scanId == INVALID_SCAN_ID) {
+                    Rlog.e(TAG, "Failed to initiate network scan");
+                    return null;
+                }
+                // We link to death whenever a scan is started to ensure that we are linked
+                // at the point that phone process death might matter.
+                // We never unlink because:
+                // - Duplicate links to death with the same callback do not result in
+                //   extraneous callbacks (the tracking de-dupes).
+                // - Receiving binderDeath() when no scans are active is a no-op.
+                telephony.asBinder().linkToDeath(mDeathRecipient, 0);
+                saveScanInfo(scanId, request, executor, callback);
+                return new NetworkScan(scanId, subId);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "requestNetworkScan RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "requestNetworkScan NPE", ex);
+        }
+        return null;
+    }
+
+    @GuardedBy("mScanInfo")
+    private void saveScanInfo(
+            int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
+        mScanInfo.put(id, new NetworkScanInfo(request, executor, callback));
+    }
+
+    private ITelephony getITelephony() {
+        return ITelephony.Stub.asInterface(
+            TelephonyFrameworkInitializer
+                    .getTelephonyServiceManager()
+                    .getTelephonyServiceRegisterer()
+                    .get());
+    }
+}
diff --git a/android-35/android/telephony/ThermalMitigationRequest.java b/android-35/android/telephony/ThermalMitigationRequest.java
new file mode 100644
index 0000000..a0676ea
--- /dev/null
+++ b/android-35/android/telephony/ThermalMitigationRequest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+/**
+ * Class stores information related to the type of data throttling request to be sent to {@link
+ * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)}.
+ * @hide
+ */
+@SystemApi
+public final class ThermalMitigationRequest implements Parcelable {
+    /**
+     * Sent as a thermal mititgation action to {@link
+     * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)} to start data
+     * throttling. {@link TelephonyManager#InvalidThermalMitigationRequestException} will be thrown
+     * if dataThrottlingRequest is {@code null} or if completion duration is < 0.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_ACTION_DATA_THROTTLING = 0;
+
+    /**
+     * Sent as a thermal mititgation action to {@link
+     * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)} to allow only voice
+     * calls and internet data will not be available. This attempts to enable radio if currently
+     * disabled for thermal mitigation with no guarantee of it actually turning on.
+     * dataThrottlingRequest must be {@code null} or {@link
+     * TelephonyManager#InvalidThermalMitigationRequestException} will be thrown.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_ACTION_VOICE_ONLY = 1;
+
+    /**
+     * Sent as a thermal mititgation action to {@link'
+     * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)} to turn radio off. If
+     * radio is not able to be powered off because of an ongoing voice call, pending emergency call,
+     * or any other state that wouldn't allow radio off, {@link
+     * TelephonyManager#THERMAL_MITIGATION_RESULT_INVALID_STATE}.
+     * dataThrottlingRequest must be {@code null} or
+     * {@link TelephonyManager#InvalidThermalMitigationRequestException} will be returned.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int THERMAL_MITIGATION_ACTION_RADIO_OFF = 2;
+
+    /**
+     * Type of thermal mitigation action.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "THERMAL_MITIGATION_ACTION_" }, value = {
+        THERMAL_MITIGATION_ACTION_DATA_THROTTLING,
+        THERMAL_MITIGATION_ACTION_VOICE_ONLY,
+        THERMAL_MITIGATION_ACTION_RADIO_OFF})
+    public @interface ThermalMitigationAction {}
+
+    private @ThermalMitigationAction int mThermalMitigationAction;
+    private DataThrottlingRequest mDataThrottlingRequest;
+
+    /**
+     * @param thermalMitigationAction thermal mitigation action.
+     * @param dataThrottlingRequest is the parameters for more fine-controlled data throttling. This
+     * is only applicable if thermalMitigationAction is
+     * {@link #THERMAL_MITIGATION_ACTION_DATA_THROTTLING}. Otherwise, it must be set to
+     * {@code null}. See {@link DataThrottlingRequest} for more details.
+     */
+    private ThermalMitigationRequest(@ThermalMitigationAction int thermalMitigationAction,
+            @Nullable DataThrottlingRequest dataThrottlingRequest) {
+        mThermalMitigationAction = thermalMitigationAction;
+        mDataThrottlingRequest = dataThrottlingRequest;
+    }
+
+    private ThermalMitigationRequest(Parcel in) {
+        mThermalMitigationAction = in.readInt();
+        mDataThrottlingRequest = in.readParcelable(DataThrottlingRequest.class.getClassLoader(), android.telephony.DataThrottlingRequest.class);
+    }
+
+     /**
+     * Implement the Parcelable interface
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mThermalMitigationAction);
+        dest.writeParcelable(mDataThrottlingRequest, 0);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "[ThermalMitigationRequest "
+            + ", thermalMitigationAction=" + mThermalMitigationAction
+            + ", dataThrottlingRequest=" + mDataThrottlingRequest
+            + "]";
+    }
+
+    /**
+     * @return the thermal mitigation action.
+     */
+    public @ThermalMitigationAction int getThermalMitigationAction() {
+        return mThermalMitigationAction;
+    }
+
+    /**
+     * @return the data throttling request.
+     */
+    @Nullable
+    public DataThrottlingRequest getDataThrottlingRequest() {
+        return mDataThrottlingRequest;
+    }
+
+    public static final @NonNull Parcelable.Creator<ThermalMitigationRequest> CREATOR =
+            new Parcelable.Creator<ThermalMitigationRequest>() {
+
+        @Override
+        public ThermalMitigationRequest createFromParcel(Parcel in) {
+            return new ThermalMitigationRequest(in);
+        }
+
+        @Override
+        public ThermalMitigationRequest[] newArray(int size) {
+            return new ThermalMitigationRequest[size];
+        }
+    };
+
+    /**
+     * Provides a convenient way to set the fields of a {@link ThermalMitigationRequest} when
+     * creating a new instance.
+     *
+     * <p>The example below shows how you might create a new {@code ThermalMitigationRequest}:
+     *
+     * <pre><code>
+     *
+     * ThermalMitigationRequest dp = new ThermalMitigationRequest.Builder()
+     *     .setThermalMitigationAction(
+     *          ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_DATA_THROTTLING)
+     *     .setDataThrottlingRequest(new DataThrottlingRequest.Builder()
+     *          .setDataThrottlingAction(
+     *              DataThrottlingRequest.DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER)
+     *          .setCompletionDurationMillis(10000L)
+     *          .build())
+     *     .build();
+     * </code></pre>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        private @ThermalMitigationAction int mThermalMitigationAction = -1;
+        private DataThrottlingRequest mDataThrottlingRequest;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {}
+
+        /**
+         * Set the thermal mitigation action.
+         *
+         * @param thermalMitigationAction thermal mitigation action. See {@link
+         *      #THERMAL_MITIGATION_ACTION_DATA_THROTTLING}, {@link
+         *      #THERMAL_MITIGATION_ACTION_VOICE_ONLY}, and {@link
+         *      #THERMAL_MITIGATION_ACTION_RADIO_OFF} for more details.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setThermalMitigationAction(
+                @ThermalMitigationAction int thermalMitigationAction) {
+            mThermalMitigationAction = thermalMitigationAction;
+            return this;
+        }
+
+        /**
+         * Set the data throttling request.
+         *
+         * @param dataThrottlingRequest is the parameters for more fine-controlled data throttling.
+         *      This is only applicable if thermalMitigationAction is {@link
+         *      #THERMAL_MITIGATION_ACTION_DATA_THROTTLING}. Otherwise, it should not be set and
+         *      will throw an IllegalArgumentException if it is. See {@link DataThrottlingRequest}
+         *      for more details.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setDataThrottlingRequest(
+                @NonNull DataThrottlingRequest dataThrottlingRequest) {
+            mDataThrottlingRequest = dataThrottlingRequest;
+            return this;
+        }
+
+        /**
+         * Build the ThermalMitigationRequest.
+         *
+         * @return the ThermalMitigationRequest object.
+         */
+        public @NonNull ThermalMitigationRequest build() {
+            if (mThermalMitigationAction < 0) {
+                throw new IllegalArgumentException("thermalMitigationAction was "
+                        + " not set");
+            }
+
+            if (mThermalMitigationAction == THERMAL_MITIGATION_ACTION_DATA_THROTTLING) {
+                if (mDataThrottlingRequest == null) {
+                    throw new IllegalArgumentException("dataThrottlingRequest  cannot be null for "
+                            + "THERMAL_MITIGATION_ACTION_DATA_THROTTLING");
+                }
+
+
+            } else if (mDataThrottlingRequest != null) {
+                throw new IllegalArgumentException("dataThrottlingRequest must be null for "
+                        + "THERMAL_MITIGATION_ACTION_VOICE_ONLY and "
+                        + "THERMAL_MITIGATION_ACTION_RADIO_OFF");
+            }
+
+            return new ThermalMitigationRequest(mThermalMitigationAction, mDataThrottlingRequest);
+        }
+    }
+}
diff --git a/android-35/android/telephony/TransportSelectorCallback.java b/android-35/android/telephony/TransportSelectorCallback.java
new file mode 100644
index 0000000..0f5dd27
--- /dev/null
+++ b/android-35/android/telephony/TransportSelectorCallback.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.telephony.Annotation.DisconnectCauses;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.function.Consumer;
+
+/**
+ * A callback class used by the domain selection module to notify the framework of the result of
+ * selecting a domain for a call.
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+public interface TransportSelectorCallback {
+    /**
+     * Notify that {@link DomainSelector} instance has been created for the selection request.
+     * <p>
+     * DomainSelector callbacks run using the executor specified in
+     * {@link DomainSelectionService#onCreateExecutor}.
+     *
+     * @param selector the {@link DomainSelector} instance created.
+     */
+    void onCreated(@NonNull DomainSelector selector);
+
+    /**
+     * Notify that WLAN transport has been selected.
+     *
+     * @param useEmergencyPdn Indicates whether Wi-Fi emergency services use emergency PDN or not.
+     */
+    void onWlanSelected(boolean useEmergencyPdn);
+
+    /**
+     * Notify that WWAN transport has been selected and the next phase of selecting
+     * the PS or CS domain is starting.
+     *
+     * @param consumer The callback used by the {@link DomainSelectionService} to optionally run
+     *        emergency network scans and notify the framework of the WWAN transport result.
+     */
+    void onWwanSelected(@NonNull Consumer<WwanSelectorCallback> consumer);
+
+    /**
+     * Notify that selection has terminated because there is no decision that can be made
+     * or a timeout has occurred. The call should be terminated when this method is called.
+     *
+     * @param cause indicates the reason.
+     */
+    void onSelectionTerminated(@DisconnectCauses int cause);
+}
diff --git a/android-35/android/telephony/UiccAccessRule.java b/android-35/android/telephony/UiccAccessRule.java
new file mode 100644
index 0000000..38b551b
--- /dev/null
+++ b/android-35/android/telephony/UiccAccessRule.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.pm.PackageInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.telephony.Rlog;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Describes a single UICC access rule according to the GlobalPlatform Secure Element Access Control
+ * specification.
+ *
+ * @hide
+ */
+@SystemApi
+public final class UiccAccessRule implements Parcelable {
+    private static final String TAG = "UiccAccessRule";
+
+    private static final int ENCODING_VERSION = 1;
+
+    /**
+     * Delimiter used to decode {@link CarrierConfigManager#KEY_CARRIER_CERTIFICATE_STRING_ARRAY}.
+     */
+    private static final String DELIMITER_CERTIFICATE_HASH_PACKAGE_NAMES = ":";
+
+    /**
+     * Delimiter used to decode {@link CarrierConfigManager#KEY_CARRIER_CERTIFICATE_STRING_ARRAY}.
+     */
+    private static final String DELIMITER_INDIVIDUAL_PACKAGE_NAMES = ",";
+
+    public static final @android.annotation.NonNull Creator<UiccAccessRule> CREATOR = new Creator<UiccAccessRule>() {
+        @Override
+        public UiccAccessRule createFromParcel(Parcel in) {
+            return new UiccAccessRule(in);
+        }
+
+        @Override
+        public UiccAccessRule[] newArray(int size) {
+            return new UiccAccessRule[size];
+        }
+    };
+
+    /**
+     * Encode these access rules as a byte array which can be parsed with {@link #decodeRules}.
+     * @hide
+     */
+    @Nullable
+    public static byte[] encodeRules(@Nullable UiccAccessRule[] accessRules) {
+        if (accessRules == null) {
+            return null;
+        }
+        try {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            DataOutputStream output = new DataOutputStream(baos);
+            output.writeInt(ENCODING_VERSION);
+            output.writeInt(accessRules.length);
+            for (UiccAccessRule accessRule : accessRules) {
+                output.writeInt(accessRule.mCertificateHash.length);
+                output.write(accessRule.mCertificateHash);
+                if (accessRule.mPackageName != null) {
+                    output.writeBoolean(true);
+                    output.writeUTF(accessRule.mPackageName);
+                } else {
+                    output.writeBoolean(false);
+                }
+                output.writeLong(accessRule.mAccessType);
+            }
+            output.close();
+            return baos.toByteArray();
+        } catch (IOException e) {
+            throw new IllegalStateException(
+                    "ByteArrayOutputStream should never lead to an IOException", e);
+        }
+    }
+
+    /**
+     * Decodes {@link CarrierConfigManager#KEY_CARRIER_CERTIFICATE_STRING_ARRAY} values.
+     * @hide
+     */
+    @Nullable
+    public static UiccAccessRule[] decodeRulesFromCarrierConfig(@Nullable String[] certs) {
+        if (certs == null) {
+            return null;
+        }
+        List<UiccAccessRule> carrierConfigAccessRulesArray = new ArrayList();
+        for (String cert : certs) {
+            String[] splitStr = cert.split(DELIMITER_CERTIFICATE_HASH_PACKAGE_NAMES);
+            byte[] certificateHash = IccUtils.hexStringToBytes(splitStr[0]);
+            if (splitStr.length == 1) {
+                // The value is a certificate hash, without any package name
+                carrierConfigAccessRulesArray.add(new UiccAccessRule(certificateHash, null, 0));
+            } else {
+                // The value is composed of the certificate hash followed by at least one
+                // package name
+                String[] packageNames = splitStr[1].split(DELIMITER_INDIVIDUAL_PACKAGE_NAMES);
+                for (String packageName : packageNames) {
+                    carrierConfigAccessRulesArray.add(
+                            new UiccAccessRule(certificateHash, packageName, 0));
+                }
+            }
+        }
+        return carrierConfigAccessRulesArray.toArray(
+            new UiccAccessRule[carrierConfigAccessRulesArray.size()]);
+    }
+
+    /**
+     * Decodes a byte array generated with {@link #encodeRules}.
+     * @hide
+     */
+    @Nullable
+    public static UiccAccessRule[] decodeRules(@Nullable byte[] encodedRules) {
+        if (encodedRules == null) {
+            return null;
+        }
+        ByteArrayInputStream bais = new ByteArrayInputStream(encodedRules);
+        try (DataInputStream input = new DataInputStream(bais)) {
+            input.readInt(); // version; currently ignored
+            int count = input.readInt();
+            UiccAccessRule[] accessRules = new UiccAccessRule[count];
+            for (int i = 0; i < count; i++) {
+                int certificateHashLength = input.readInt();
+                byte[] certificateHash = new byte[certificateHashLength];
+                input.readFully(certificateHash);
+                String packageName = input.readBoolean() ? input.readUTF() : null;
+                long accessType = input.readLong();
+                accessRules[i] = new UiccAccessRule(certificateHash, packageName, accessType);
+            }
+            input.close();
+            return accessRules;
+        } catch (IOException e) {
+            throw new IllegalStateException(
+                    "ByteArrayInputStream should never lead to an IOException", e);
+        }
+    }
+
+    private final byte[] mCertificateHash;
+    private final @Nullable String mPackageName;
+    // This bit is not currently used, but reserved for future use.
+    private final long mAccessType;
+
+    public UiccAccessRule(byte[] certificateHash, @Nullable String packageName, long accessType) {
+        this.mCertificateHash = certificateHash;
+        this.mPackageName = packageName;
+        this.mAccessType = accessType;
+    }
+
+    UiccAccessRule(Parcel in) {
+        mCertificateHash = in.createByteArray();
+        mPackageName = in.readString();
+        mAccessType = in.readLong();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByteArray(mCertificateHash);
+        dest.writeString(mPackageName);
+        dest.writeLong(mAccessType);
+    }
+
+    /**
+     * Return the package name this rule applies to.
+     *
+     * @return the package name, or null if this rule applies to any package signed with the given
+     *     certificate.
+     */
+    public @Nullable String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Returns the hex string of the certificate hash.
+     */
+    public String getCertificateHexString() {
+        return IccUtils.bytesToHexString(mCertificateHash);
+    }
+
+    /**
+     * Returns the carrier privilege status associated with the given package.
+     *
+     * @param packageInfo package info fetched from
+     *     {@link android.content.pm.PackageManager#getPackageInfo}.
+     *     {@link android.content.pm.PackageManager#GET_SIGNING_CERTIFICATES} must have been
+     *         passed in.
+     * @return either {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_HAS_ACCESS} or
+     *     {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}.
+     */
+    public int getCarrierPrivilegeStatus(PackageInfo packageInfo) {
+        List<Signature> signatures = getSignatures(packageInfo);
+        if (signatures.isEmpty()) {
+            throw new IllegalArgumentException(
+                    "Must use GET_SIGNING_CERTIFICATES when looking up package info");
+        }
+
+        for (Signature sig : signatures) {
+            int accessStatus = getCarrierPrivilegeStatus(sig, packageInfo.packageName);
+            if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
+                return accessStatus;
+            }
+        }
+
+        return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+    }
+
+    /**
+     * Returns the carrier privilege status for the given certificate and package name.
+     *
+     * @param signature The signature of the certificate.
+     * @param packageName name of the package.
+     * @return either {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_HAS_ACCESS} or
+     *     {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}.
+     */
+    public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
+        byte[] certHash256 = getCertHash(signature, "SHA-256");
+        // Check SHA-256 first as it's the new standard.
+        if (matches(certHash256, packageName)) {
+            return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+        }
+
+        // Then check SHA-1 for backward compatibility. This should be removed
+        // in the near future when GPD_SPE_068 fully replaces GPD_SPE_013.
+        if (this.mCertificateHash.length == 20) {
+            byte[] certHash = getCertHash(signature, "SHA-1");
+            if (matches(certHash, packageName)) {
+                return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+            }
+        }
+
+        return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+    }
+
+    /**
+     * Returns true if the given certificate and package name match this rule's values.
+     * @hide
+     */
+    public boolean matches(@Nullable String certHash, @Nullable String packageName) {
+        return matches(IccUtils.hexStringToBytes(certHash), packageName);
+    }
+
+    private boolean matches(byte[] certHash, String packageName) {
+        return certHash != null && Arrays.equals(this.mCertificateHash, certHash) &&
+                (TextUtils.isEmpty(this.mPackageName) || this.mPackageName.equals(packageName));
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        UiccAccessRule that = (UiccAccessRule) obj;
+        return Arrays.equals(mCertificateHash, that.mCertificateHash)
+                && Objects.equals(mPackageName, that.mPackageName)
+                && mAccessType == that.mAccessType;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 1;
+        result = 31 * result + Arrays.hashCode(mCertificateHash);
+        result = 31 * result + Objects.hashCode(mPackageName);
+        result = 31 * result + Objects.hashCode(mAccessType);
+        return result;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "cert: " + IccUtils.bytesToHexString(mCertificateHash) + " pkg: " +
+                mPackageName + " access: " + mAccessType;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Gets all of the Signatures from the given PackageInfo.
+     * @hide
+     */
+    @NonNull
+    public static List<Signature> getSignatures(PackageInfo packageInfo) {
+        Signature[] signatures = packageInfo.signatures;
+        SigningInfo signingInfo = packageInfo.signingInfo;
+
+        if (signingInfo != null) {
+            signatures = signingInfo.getSigningCertificateHistory();
+            if (signingInfo.hasMultipleSigners()) {
+                signatures = signingInfo.getApkContentsSigners();
+            }
+        }
+
+        return (signatures == null) ? Collections.EMPTY_LIST : Arrays.asList(signatures);
+    }
+
+    /**
+     * Converts a Signature into a Certificate hash usable for comparison.
+     * @hide
+     */
+    public static byte[] getCertHash(Signature signature, String algo) {
+        try {
+            MessageDigest md = MessageDigest.getInstance(algo);
+            return md.digest(signature.toByteArray());
+        } catch (NoSuchAlgorithmException ex) {
+            Rlog.e(TAG, "NoSuchAlgorithmException: " + ex);
+        }
+        return null;
+    }
+}
diff --git a/android-35/android/telephony/UiccCardInfo.java b/android-35/android/telephony/UiccCardInfo.java
new file mode 100644
index 0000000..6c069d4
--- /dev/null
+++ b/android-35/android/telephony/UiccCardInfo.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.telephony.Rlog;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The UiccCardInfo represents information about a currently inserted UICC or embedded eUICC.
+ */
+public final class UiccCardInfo implements Parcelable {
+    private final boolean mIsEuicc;
+    private final int mCardId;
+    private final String mEid;
+    private final String mIccId;
+    private final int mPhysicalSlotIndex;
+    private final boolean mIsRemovable;
+    private final boolean mIsMultipleEnabledProfilesSupported;
+    private final List<UiccPortInfo> mPortList;
+    private boolean mIccIdAccessRestricted = false;
+
+    public static final @NonNull Creator<UiccCardInfo> CREATOR = new Creator<UiccCardInfo>() {
+        @Override
+        public UiccCardInfo createFromParcel(Parcel in) {
+            return new UiccCardInfo(in);
+        }
+
+        @Override
+        public UiccCardInfo[] newArray(int size) {
+            return new UiccCardInfo[size];
+        }
+    };
+
+    private UiccCardInfo(Parcel in) {
+        mIsEuicc = in.readBoolean();
+        mCardId = in.readInt();
+        mEid = in.readString8();
+        mIccId = in.readString8();
+        mPhysicalSlotIndex = in.readInt();
+        mIsRemovable = in.readBoolean();
+        mIsMultipleEnabledProfilesSupported = in.readBoolean();
+        mPortList = new ArrayList<UiccPortInfo>();
+        in.readTypedList(mPortList, UiccPortInfo.CREATOR);
+        mIccIdAccessRestricted = in.readBoolean();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mIsEuicc);
+        dest.writeInt(mCardId);
+        dest.writeString8(mEid);
+        dest.writeString8(mIccId);
+        dest.writeInt(mPhysicalSlotIndex);
+        dest.writeBoolean(mIsRemovable);
+        dest.writeBoolean(mIsMultipleEnabledProfilesSupported);
+        dest.writeTypedList(mPortList, flags);
+        dest.writeBoolean(mIccIdAccessRestricted);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Construct a UiccCardInfo.
+     *
+     * @param isEuicc is a flag to check is eUICC or not
+     * @param cardId is unique ID used to identify a UiccCard.
+     * @param eid is unique eUICC Identifier
+     * @param physicalSlotIndex is unique index referring to a physical SIM slot.
+     * @param isRemovable is a flag to check is removable or embedded
+     * @param isMultipleEnabledProfilesSupported is a flag to check is MEP enabled or not
+     * @param portList has the information regarding port, ICCID and its active status
+     *
+     * @hide
+     */
+    public UiccCardInfo(boolean isEuicc, int cardId, String eid, int physicalSlotIndex,
+            boolean isRemovable, boolean isMultipleEnabledProfilesSupported,
+            @NonNull List<UiccPortInfo> portList) {
+        this.mIsEuicc = isEuicc;
+        this.mCardId = cardId;
+        this.mEid = eid;
+        this.mIccId = null;
+        this.mPhysicalSlotIndex = physicalSlotIndex;
+        this.mIsRemovable = isRemovable;
+        this.mIsMultipleEnabledProfilesSupported = isMultipleEnabledProfilesSupported;
+        this.mPortList = portList;
+    }
+
+    /**
+     * Return whether the UICC is an eUICC.
+     *
+     * @return true if the UICC is an eUICC.
+     */
+    public boolean isEuicc() {
+        return mIsEuicc;
+    }
+
+    /**
+     * Get the card ID of the UICC. See {@link TelephonyManager#getCardIdForDefaultEuicc()} for more
+     * details on card ID.
+     */
+    public int getCardId() {
+        return mCardId;
+    }
+
+    /**
+     * Get the embedded ID (EID) of the eUICC. If the UiccCardInfo is not an eUICC
+     * (see {@link #isEuicc()}), or the EID is not available, returns null.
+     * <p>
+     * Note that this field may be omitted if the caller does not have the correct permissions
+     * (see {@link TelephonyManager#getUiccCardsInfo()}).
+     */
+    @Nullable
+    public String getEid() {
+        if (!mIsEuicc) {
+            return null;
+        }
+        return mEid;
+    }
+
+    /**
+     * Get the ICCID of the UICC. If the ICCID is not availble, returns null.
+     * <p>
+     * Note that this field may be omitted if the caller does not have the correct permissions
+     * (see {@link TelephonyManager#getUiccCardsInfo()}).
+     *
+     * @deprecated with support for MEP(multiple enabled profile)
+     * {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP}, a SIM card can have more than one
+     * ICCID active at the same time. Instead use {@link UiccPortInfo#getIccId()} to retrieve ICCID.
+     * To find {@link UiccPortInfo} use {@link UiccCardInfo#getPorts()}.
+     *
+     * @throws UnsupportedOperationException if the calling app's target SDK is T and beyond.
+     */
+    @Nullable
+    @Deprecated
+    public String getIccId() {
+        if (mIccIdAccessRestricted) {
+            throw new UnsupportedOperationException("getIccId() is not supported by UiccCardInfo."
+                + " Please Use UiccPortInfo API instead");
+        }
+        //always return ICCID from first port.
+        return mPortList.isEmpty() ? null : mPortList.get(0).getIccId();
+    }
+
+    /**
+     * Gets the slot index for the slot that the UICC is currently inserted in.
+     *
+     * @deprecated use {@link #getPhysicalSlotIndex()}
+     */
+    @Deprecated
+    public int getSlotIndex() {
+        return mPhysicalSlotIndex;
+    }
+
+    /**
+     * Gets the physical slot index for the slot that the UICC is currently inserted in.
+     */
+    public int getPhysicalSlotIndex() {
+        return mPhysicalSlotIndex;
+    }
+
+    /**
+     * Return whether the UICC or eUICC is removable.
+     * <p>
+     * UICCs are generally removable, but eUICCs may be removable or built in to the device.
+     *
+     * @return true if the UICC or eUICC is removable
+     */
+    public boolean isRemovable() {
+        return mIsRemovable;
+    }
+
+    /*
+     * Whether the UICC card supports multiple enabled profile(MEP)
+     * UICCs are generally MEP disabled, there can be only one active profile on the physical
+     * sim card.
+     *
+     * @return {@code true} if the UICC is supporting multiple enabled profile(MEP).
+     */
+    public boolean isMultipleEnabledProfilesSupported() {
+        return mIsMultipleEnabledProfilesSupported;
+    }
+
+    /**
+     * Get information regarding port, ICCID and its active status.
+     *
+     * For device which support {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP}, it should return
+     * more than one {@link UiccPortInfo} object if the card is eUICC.
+     *
+     * @return Collection of {@link UiccPortInfo}
+     */
+    public @NonNull Collection<UiccPortInfo> getPorts() {
+        return Collections.unmodifiableList(mPortList);
+    }
+
+    /**
+     * if the flag is set to {@code true} the calling app is not allowed to access deprecated
+     * {@link #getIccId()}
+     * @param iccIdAccessRestricted is the flag to check if app is allowed to access ICCID
+     *
+     * @hide
+     */
+    public void setIccIdAccessRestricted(boolean iccIdAccessRestricted) {
+        this.mIccIdAccessRestricted = iccIdAccessRestricted;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        UiccCardInfo that = (UiccCardInfo) obj;
+        return ((mIsEuicc == that.mIsEuicc)
+                && (mCardId == that.mCardId)
+                && (Objects.equals(mEid, that.mEid))
+                && (Objects.equals(mIccId, that.mIccId))
+                && (mPhysicalSlotIndex == that.mPhysicalSlotIndex)
+                && (mIsRemovable == that.mIsRemovable)
+                && (mIsMultipleEnabledProfilesSupported == that.mIsMultipleEnabledProfilesSupported)
+                && (Objects.equals(mPortList, that.mPortList)));
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mPhysicalSlotIndex, mIsRemovable,
+                mIsMultipleEnabledProfilesSupported, mPortList);
+    }
+
+    @Override
+    public String toString() {
+        return "UiccCardInfo (mIsEuicc="
+                + mIsEuicc
+                + ", mCardId="
+                + mCardId
+                + ", mEid="
+                + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid)
+                + ", mPhysicalSlotIndex="
+                + mPhysicalSlotIndex
+                + ", mIsRemovable="
+                + mIsRemovable
+                + ", mIsMultipleEnabledProfilesSupported="
+                + mIsMultipleEnabledProfilesSupported
+                + ", mPortList="
+                + mPortList
+                + ", mIccIdAccessRestricted="
+                + mIccIdAccessRestricted
+                + ")";
+    }
+}
\ No newline at end of file
diff --git a/android-35/android/telephony/UiccPortInfo.java b/android-35/android/telephony/UiccPortInfo.java
new file mode 100644
index 0000000..41e743c
--- /dev/null
+++ b/android-35/android/telephony/UiccPortInfo.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2021 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;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * UiccPortInfo class represents information about a single port contained on {@link UiccCardInfo}.
+ * Per GSMA SGP.22 V3.0, a port is a logical entity to which an active UICC profile can be bound on
+ * a UICC card. If UICC supports 2 ports, then the port index is numbered 0,1.
+ * Each port index is unique within an UICC, but not necessarily unique across UICC’s.
+ * For UICC's does not support MEP(Multi-enabled profile)
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY_EUICC_MEP}, just return the default
+ * port index 0.
+ */
+public final class UiccPortInfo implements Parcelable{
+    private final String mIccId;
+    private final int mPortIndex;
+    private final int mLogicalSlotIndex;
+    private final boolean mIsActive;
+
+    /**
+     * A redacted String if caller does not have permission to read ICCID.
+     */
+    public static final String ICCID_REDACTED = "FFFFFFFFFFFFFFFFFFFF";
+
+    public static final @NonNull Creator<UiccPortInfo> CREATOR =
+            new Creator<UiccPortInfo>() {
+                @Override
+                public UiccPortInfo createFromParcel(Parcel in) {
+                    return new UiccPortInfo(in);
+                }
+                @Override
+                public UiccPortInfo[] newArray(int size) {
+                    return new UiccPortInfo[size];
+                }
+            };
+
+    private UiccPortInfo(Parcel in) {
+        mIccId = in.readString8();
+        mPortIndex = in.readInt();
+        mLogicalSlotIndex = in.readInt();
+        mIsActive = in.readBoolean();
+    }
+
+    @Override
+    public void writeToParcel(@Nullable Parcel dest, int flags) {
+        dest.writeString8(mIccId);
+        dest.writeInt(mPortIndex);
+        dest.writeInt(mLogicalSlotIndex);
+        dest.writeBoolean(mIsActive);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Construct a UiccPortInfo.
+     *
+     * @param iccId The ICCID of the profile.
+     * @param portIndex The port index is an enumeration of the ports available on the UICC.
+     * @param logicalSlotIndex is unique index referring to a logical SIM slot.
+     * @param isActive is flag to check if port was tied to a modem stack.
+     *
+     * @hide
+     */
+    public UiccPortInfo(String iccId, int portIndex, int logicalSlotIndex, boolean isActive) {
+        this.mIccId = iccId;
+        this.mPortIndex = portIndex;
+        this.mLogicalSlotIndex = logicalSlotIndex;
+        this.mIsActive = isActive;
+    }
+
+    /**
+     * Get the ICCID of the profile associated with this port.
+     * If this port is not {@link #isActive()}, returns {@code null}.
+     * If the caller does not have access to the ICCID for this port, it will be redacted and
+     * {@link #ICCID_REDACTED} will be returned.
+     */
+    public @Nullable String getIccId() {
+        return mIccId;
+    }
+
+    /**
+     * The port index is an enumeration of the ports available on the UICC.
+     * Example: if eUICC1 supports 2 ports, then the port index is numbered 0,1.
+     * Each port index is unique within an UICC, but not necessarily unique across UICC’s.
+     * For UICC's does not support MEP(Multi-enabled profile), just return the default port index 0.
+     */
+    @IntRange(from = 0)
+    public int getPortIndex() {
+        return mPortIndex;
+    }
+
+    /**
+     * @return {@code true} if port was tied to a modem stack.
+     */
+    public boolean isActive() {
+        return mIsActive;
+    }
+
+    /**
+     * Gets logical slot index for the slot that the UICC is currently attached.
+     * Logical slot index or ID: unique index referring to a logical SIM slot.
+     * Logical slot IDs start at 0 and go up depending on the number of supported active slots on
+     * a device.
+     * For example, a dual-SIM device typically has slot 0 and slot 1.
+     * If a device has multiple physical slots but only supports one active slot,
+     * it will have only the logical slot ID 0.
+     *
+     * @return the logical slot index for UICC port, if there is no logical slot index it returns
+     * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX}
+     */
+    @IntRange(from = 0)
+    public int getLogicalSlotIndex() {
+        return mLogicalSlotIndex;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        UiccPortInfo that = (UiccPortInfo) obj;
+        return (Objects.equals(mIccId, that.mIccId))
+                && (mPortIndex == that.mPortIndex)
+                && (mLogicalSlotIndex == that.mLogicalSlotIndex)
+                && (mIsActive == that.mIsActive);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIccId, mPortIndex, mLogicalSlotIndex, mIsActive);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "UiccPortInfo (isActive="
+                + mIsActive
+                + ", iccId="
+                + SubscriptionInfo.getPrintableId(mIccId)
+                + ", portIndex="
+                + mPortIndex
+                + ", mLogicalSlotIndex="
+                + mLogicalSlotIndex
+                + ")";
+    }
+}
diff --git a/android-35/android/telephony/UiccSlotInfo.java b/android-35/android/telephony/UiccSlotInfo.java
new file mode 100644
index 0000000..dda7349
--- /dev/null
+++ b/android-35/android/telephony/UiccSlotInfo.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.pm.PackageManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Class for the information of a UICC slot.
+ * @hide
+ */
+@SystemApi
+public class UiccSlotInfo implements Parcelable {
+    /**
+     * Card state.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "CARD_STATE_INFO_" }, value = {
+            CARD_STATE_INFO_ABSENT,
+            CARD_STATE_INFO_PRESENT,
+            CARD_STATE_INFO_ERROR,
+            CARD_STATE_INFO_RESTRICTED
+    })
+    public @interface CardStateInfo {}
+
+    /** Card state absent. */
+    public static final int CARD_STATE_INFO_ABSENT = 1;
+
+    /** Card state present. */
+    public static final int CARD_STATE_INFO_PRESENT = 2;
+
+    /** Card state error. */
+    public static final int CARD_STATE_INFO_ERROR = 3;
+
+    /** Card state restricted. */
+    public static final int CARD_STATE_INFO_RESTRICTED = 4;
+
+    private final boolean mIsActive;
+    private final boolean mIsEuicc;
+    private final String mCardId;
+    private final @CardStateInfo int mCardStateInfo;
+    private final int mLogicalSlotIdx;
+    private final boolean mIsExtendedApduSupported;
+    private final boolean mIsRemovable;
+    private final List<UiccPortInfo> mPortList;
+    private boolean mLogicalSlotAccessRestricted = false;
+
+    public static final @NonNull Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
+        @Override
+        public UiccSlotInfo createFromParcel(Parcel in) {
+            return new UiccSlotInfo(in);
+        }
+
+        @Override
+        public UiccSlotInfo[] newArray(int size) {
+            return new UiccSlotInfo[size];
+        }
+    };
+
+    private UiccSlotInfo(Parcel in) {
+        mIsActive = in.readBoolean();
+        mIsEuicc = in.readBoolean();
+        mCardId = in.readString8();
+        mCardStateInfo = in.readInt();
+        mLogicalSlotIdx = in.readInt();
+        mIsExtendedApduSupported = in.readBoolean();
+        mIsRemovable = in.readBoolean();
+        mPortList = new ArrayList<UiccPortInfo>();
+        in.readTypedList(mPortList, UiccPortInfo.CREATOR);
+        mLogicalSlotAccessRestricted = in.readBoolean();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mIsActive);
+        dest.writeBoolean(mIsEuicc);
+        dest.writeString8(mCardId);
+        dest.writeInt(mCardStateInfo);
+        dest.writeInt(mLogicalSlotIdx);
+        dest.writeBoolean(mIsExtendedApduSupported);
+        dest.writeBoolean(mIsRemovable);
+        dest.writeTypedList(mPortList, flags);
+        dest.writeBoolean(mLogicalSlotAccessRestricted);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Construct a UiccSlotInfo.
+     * @deprecated apps should not be constructing UiccSlotInfo objects
+     */
+    @Deprecated
+    public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
+            @CardStateInfo int cardStateInfo, int logicalSlotIdx, boolean isExtendedApduSupported) {
+        this.mIsActive = isActive;
+        this.mIsEuicc = isEuicc;
+        this.mCardId = cardId;
+        this.mCardStateInfo = cardStateInfo;
+        this.mLogicalSlotIdx = logicalSlotIdx;
+        this.mIsExtendedApduSupported = isExtendedApduSupported;
+        this.mIsRemovable = false;
+        this.mPortList = new ArrayList<UiccPortInfo>();
+    }
+
+    /**
+     * Construct a UiccSlotInfo.
+     * @hide
+     */
+    public UiccSlotInfo(boolean isEuicc, String cardId,
+            @CardStateInfo int cardStateInfo, boolean isExtendedApduSupported,
+            boolean isRemovable, @NonNull List<UiccPortInfo> portList) {
+        this.mIsEuicc = isEuicc;
+        this.mCardId = cardId;
+        this.mCardStateInfo = cardStateInfo;
+        this.mIsExtendedApduSupported = isExtendedApduSupported;
+        this.mIsRemovable = isRemovable;
+        this.mPortList = portList;
+        this.mIsActive = !portList.isEmpty() && portList.get(0).isActive();
+        this.mLogicalSlotIdx = portList.isEmpty()
+                ? SubscriptionManager.INVALID_PHONE_INDEX
+                : portList.get(0).getLogicalSlotIndex();
+    }
+
+    /**
+     * @deprecated There is no longer isActive state for each slot because ports belonging
+     * to the physical slot could have different states
+     * we instead use {@link UiccPortInfo#isActive()}
+     * To get UiccPortInfo use {@link UiccSlotInfo#getPorts()}
+     *
+     * @return {@code true} if status is active.
+     * @throws UnsupportedOperationException if the calling app's target SDK is T and beyond.
+     */
+    @Deprecated
+    public boolean getIsActive() {
+        if (mLogicalSlotAccessRestricted) {
+            throw new UnsupportedOperationException("getIsActive() is not supported by "
+            + "UiccSlotInfo. Please Use UiccPortInfo API instead");
+        }
+        return mIsActive;
+    }
+
+    public boolean getIsEuicc() {
+        return mIsEuicc;
+    }
+
+    /**
+     * Returns the ICCID of the card in the slot, or the EID of an active eUICC.
+     * <p>
+     * If the UICC slot is for an active eUICC, returns the EID.
+     * If the UICC slot is for an inactive eUICC, returns the ICCID of the enabled profile, or the
+     * root profile if all other profiles are disabled.
+     * If the UICC slot is not an eUICC, returns the ICCID.
+     */
+    public String getCardId() {
+        return mCardId;
+    }
+
+    @CardStateInfo
+    public int getCardStateInfo() {
+        return mCardStateInfo;
+    }
+
+    /**
+     * @deprecated There is no longer getLogicalSlotIndex
+     * There is no longer getLogicalSlotIdx as each port belonging to this physical slot could have
+     * different logical slot index. Use {@link UiccPortInfo#getLogicalSlotIndex()} instead
+     *
+     * @throws UnsupportedOperationException if the calling app's target SDK is T and beyond.
+     */
+    @Deprecated
+    public int getLogicalSlotIdx() {
+        if (mLogicalSlotAccessRestricted) {
+            throw new UnsupportedOperationException("getLogicalSlotIdx() is not supported by "
+                + "UiccSlotInfo. Please use UiccPortInfo API instead");
+        }
+        return mLogicalSlotIdx;
+    }
+
+    /**
+     * @return {@code true} if this slot supports extended APDU from ATR, {@code false} otherwise.
+     */
+    public boolean getIsExtendedApduSupported() {
+        return mIsExtendedApduSupported;
+    }
+
+    /**
+     * Return whether the UICC slot is for a removable UICC.
+     * <p>
+     * UICCs are generally removable, but eUICCs may be removable or built in to the device.
+     *
+     * @return true if the slot is for removable UICCs
+     */
+    public boolean isRemovable() {
+        return mIsRemovable;
+    }
+
+    /**
+     * Get Information regarding port, iccid and its active status.
+     *
+     * For device which support {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP}, it should return
+     * more than one {@link UiccPortInfo} object if the card is eUICC.
+     *
+     * @return Collection of {@link UiccPortInfo}
+     */
+    public @NonNull Collection<UiccPortInfo> getPorts() {
+        return Collections.unmodifiableList(mPortList);
+    }
+
+    /**
+     * Set the flag to check compatibility of the calling app's target SDK is T and beyond.
+     *
+     * @param logicalSlotAccessRestricted is the flag to check compatibility.
+     *
+     * @hide
+     */
+    public void setLogicalSlotAccessRestricted(boolean logicalSlotAccessRestricted) {
+        this.mLogicalSlotAccessRestricted = logicalSlotAccessRestricted;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        UiccSlotInfo that = (UiccSlotInfo) obj;
+        return (mIsActive == that.mIsActive)
+                && (mIsEuicc == that.mIsEuicc)
+                && (Objects.equals(mCardId, that.mCardId))
+                && (mCardStateInfo == that.mCardStateInfo)
+                && (mLogicalSlotIdx == that.mLogicalSlotIdx)
+                && (mIsExtendedApduSupported == that.mIsExtendedApduSupported)
+                && (mIsRemovable == that.mIsRemovable)
+                && (Objects.equals(mPortList, that.mPortList));
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIsActive, mIsEuicc, mCardId, mCardStateInfo, mLogicalSlotIdx,
+                mIsExtendedApduSupported, mIsRemovable, mPortList);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "UiccSlotInfo ("
+                + ", mIsEuicc="
+                + mIsEuicc
+                + ", mCardId="
+                + SubscriptionInfo.getPrintableId(mCardId)
+                + ", cardState="
+                + mCardStateInfo
+                + ", mIsExtendedApduSupported="
+                + mIsExtendedApduSupported
+                + ", mIsRemovable="
+                + mIsRemovable
+                + ", mPortList="
+                + mPortList
+                + ", mLogicalSlotAccessRestricted="
+                + mLogicalSlotAccessRestricted
+                + ")";
+    }
+}
diff --git a/android-35/android/telephony/UiccSlotMapping.java b/android-35/android/telephony/UiccSlotMapping.java
new file mode 100644
index 0000000..08de7fd
--- /dev/null
+++ b/android-35/android/telephony/UiccSlotMapping.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2021 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;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * <p>Provides information for a SIM slot mapping, which establishes a unique mapping between a
+ * logical SIM slot and a physical SIM slot and port index.  A logical SIM slot represents a
+ * potentially active SIM slot, where a physical SIM slot and port index represent a hardware SIM
+ * slot and port (capable of having an active profile) which can be mapped to a logical sim slot.
+ * <p>It contains the following parameters:
+ * <ul>
+ * <li>Port index: unique index referring to a port belonging to the physical SIM slot.
+ * If the SIM does not support multiple enabled profiles, the port index is default index 0.</li>
+ * <li>Physical slot index: unique index referring to a physical SIM slot. Physical slot IDs start
+ * at 0 and go up depending on the number of physical slots on the device.
+ * This differs from the number of logical slots a device has, which corresponds to the number of
+ * active slots a device is capable of using. For example, a device which switches between dual-SIM
+ * and single-SIM mode may always have two physical slots, but in single-SIM mode it will have only
+ * one logical slot.</li>
+ * <li>Logical slot index: unique index referring to a logical SIM slot, Logical slot IDs start at 0
+ * and go up depending on the number of supported active slots on a device.
+ * For example, a dual-SIM device typically has slot 0 and slot 1. If a device has multiple physical
+ * slots but only supports one active slot, it will have only the logical slot ID 0</li>
+ * </ul>
+ *
+ * <p> This configurations tells a specific logical slot is mapped to a port from an actual physical
+ * sim slot @see <a href="https://developer.android.com/guide/topics/connectivity/telecom/telephony-ids">the Android Developer Site</a>
+ * for more information.
+ * @hide
+ */
+@SystemApi
+public final class UiccSlotMapping implements Parcelable {
+    private final int mPortIndex;
+    private final int mPhysicalSlotIndex;
+    private final int mLogicalSlotIndex;
+
+    public static final @NonNull Creator<UiccSlotMapping> CREATOR =
+            new Creator<UiccSlotMapping>() {
+        @Override
+        public UiccSlotMapping createFromParcel(Parcel in) {
+            return new UiccSlotMapping(in);
+        }
+
+        @Override
+        public UiccSlotMapping[] newArray(int size) {
+            return new UiccSlotMapping[size];
+        }
+    };
+
+    private UiccSlotMapping(Parcel in) {
+        mPortIndex = in.readInt();
+        mPhysicalSlotIndex = in.readInt();
+        mLogicalSlotIndex = in.readInt();
+    }
+
+    @Override
+    public void writeToParcel(@Nullable Parcel dest, int flags) {
+        dest.writeInt(mPortIndex);
+        dest.writeInt(mPhysicalSlotIndex);
+        dest.writeInt(mLogicalSlotIndex);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     *
+     * @param portIndex The port index is an enumeration of the ports available on the UICC.
+     * @param physicalSlotIndex is unique index referring to a physical SIM slot.
+     * @param logicalSlotIndex is unique index referring to a logical SIM slot.
+     *
+     */
+    public UiccSlotMapping(int portIndex, int physicalSlotIndex, int logicalSlotIndex) {
+        this.mPortIndex = portIndex;
+        this.mPhysicalSlotIndex = physicalSlotIndex;
+        this.mLogicalSlotIndex = logicalSlotIndex;
+    }
+
+    /**
+     * Port index is the unique index referring to a port belonging to the physical SIM slot.
+     * If the SIM does not support multiple enabled profiles, the port index is default index 0.
+     *
+     * @return port index.
+     */
+    @IntRange(from = 0)
+    public int getPortIndex() {
+        return mPortIndex;
+    }
+
+    /**
+     * Gets the physical slot index for the slot that the UICC is currently inserted in.
+     *
+     * @return physical slot index which is the index of actual physical UICC slot.
+     */
+    @IntRange(from = 0)
+    public int getPhysicalSlotIndex() {
+        return mPhysicalSlotIndex;
+    }
+
+    /**
+     * Gets logical slot index for the slot that the UICC is currently attached.
+     * Logical slot index is the unique index referring to a logical slot(logical modem stack).
+     *
+     * @return logical slot index;
+     */
+    @IntRange(from = 0)
+    public int getLogicalSlotIndex() {
+        return mLogicalSlotIndex;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        UiccSlotMapping that = (UiccSlotMapping) obj;
+        return (mPortIndex == that.mPortIndex)
+                && (mPhysicalSlotIndex == that.mPhysicalSlotIndex)
+                && (mLogicalSlotIndex == that.mLogicalSlotIndex);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPortIndex, mPhysicalSlotIndex, mLogicalSlotIndex);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "UiccSlotMapping (mPortIndex="
+                + mPortIndex
+                + ", mPhysicalSlotIndex="
+                + mPhysicalSlotIndex
+                + ", mLogicalSlotIndex="
+                + mLogicalSlotIndex
+                + ")";
+    }
+}
diff --git a/android-35/android/telephony/UssdResponse.java b/android-35/android/telephony/UssdResponse.java
new file mode 100644
index 0000000..19756b5
--- /dev/null
+++ b/android-35/android/telephony/UssdResponse.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2006 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Represents the Ussd response, including
+ * the message and the return code.
+ * @hide
+ */
+public final class UssdResponse implements Parcelable {
+    private CharSequence mReturnMessage;
+    private String mUssdRequest;
+
+
+    /**
+     * Implement the Parcelable interface
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mUssdRequest);
+        TextUtils.writeToParcel(mReturnMessage, dest, 0);
+    }
+
+    public String getUssdRequest() {
+        return mUssdRequest;
+    }
+
+    public CharSequence getReturnMessage() {
+        return mReturnMessage;
+    }
+
+    /**
+     * Implement the Parcelable interface
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * * Initialize the object from the request and return message.
+     */
+    public UssdResponse(String ussdRequest, CharSequence returnMessage) {
+        mUssdRequest = ussdRequest;
+        mReturnMessage = returnMessage;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<UssdResponse> CREATOR = new Creator<UssdResponse>() {
+
+        @Override
+        public UssdResponse createFromParcel(Parcel in) {
+            String request = in.readString();
+            CharSequence message = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            return new UssdResponse(request, message);
+        }
+
+        @Override
+        public UssdResponse[] newArray(int size) {
+            return new UssdResponse[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/VisualVoicemailService.java b/android-35/android/telephony/VisualVoicemailService.java
new file mode 100644
index 0000000..a530917
--- /dev/null
+++ b/android-35/android/telephony/VisualVoicemailService.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2016 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;
+
+import android.annotation.MainThread;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.util.Log;
+
+/**
+ * This service is implemented by dialer apps that wishes to handle OMTP or similar visual
+ * voicemails. Telephony binds to this service when the cell service is first connected, a visual
+ * voicemail SMS has been received, or when a SIM has been removed. Telephony will only bind to the
+ * default dialer for such events (See {@link TelecomManager#getDefaultDialerPackage()}). The
+ * {@link android.service.carrier.CarrierMessagingService} precedes the VisualVoicemailService in
+ * the SMS filtering chain and may intercept the visual voicemail SMS before it reaches this
+ * service.
+ * <p>
+ * To extend this class, The service must be declared in the manifest file with
+ * the {@link android.Manifest.permission#BIND_VISUAL_VOICEMAIL_SERVICE} permission and include an
+ * intent filter with the {@link #SERVICE_INTERFACE} action.
+ * <p>
+ * Below is an example manifest registration for a {@code VisualVoicemailService}.
+ * <pre>
+ * {@code
+ * <service android:name="your.package.YourVisualVoicemailServiceImplementation"
+ *          android:permission="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE">
+ *      <intent-filter>
+ *          <action android:name="android.telephony.VisualVoicemailService"/>
+ *      </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ */
+public abstract class VisualVoicemailService extends Service {
+
+    private static final String TAG = "VvmService";
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE = "android.telephony.VisualVoicemailService";
+
+    /**
+     * @hide
+     */
+    public static final int MSG_ON_CELL_SERVICE_CONNECTED = 1;
+    /**
+     * @hide
+     */
+    public static final int MSG_ON_SMS_RECEIVED = 2;
+    /**
+     * @hide
+     */
+    public static final int MSG_ON_SIM_REMOVED = 3;
+    /**
+     * @hide
+     */
+    public static final int MSG_TASK_ENDED = 4;
+    /**
+     * @hide
+     */
+    public static final int MSG_TASK_STOPPED = 5;
+
+    /**
+     * @hide
+     */
+    public static final String DATA_PHONE_ACCOUNT_HANDLE = "data_phone_account_handle";
+    /**
+     * @hide
+     */
+    public static final String DATA_SMS = "data_sms";
+
+    /**
+     * Represents a visual voicemail event which needs to be handled. While the task is being
+     * processed telephony will hold a wakelock for the VisualVoicemailService. The service can
+     * unblock the main thread and pass the task to a worker thread. Once the task is finished,
+     * {@link VisualVoicemailTask#finish()} should be called to signal telephony to release the
+     * resources. Telephony will call {@link VisualVoicemailService#onStopped(VisualVoicemailTask)}
+     * when the task is going to be terminated before completion.
+     *
+     * @see #onCellServiceConnected(VisualVoicemailTask, PhoneAccountHandle)
+     * @see #onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)
+     * @see #onSimRemoved(VisualVoicemailTask, PhoneAccountHandle)
+     * @see #onStopped(VisualVoicemailTask)
+     */
+    public static class VisualVoicemailTask {
+
+        private final int mTaskId;
+        private final Messenger mReplyTo;
+
+        private VisualVoicemailTask(Messenger replyTo, int taskId) {
+            mTaskId = taskId;
+            mReplyTo = replyTo;
+        }
+
+        /**
+         * Call to signal telephony the task has completed. Must be called for every task.
+         */
+        public final void finish() {
+            Message message = Message.obtain();
+            try {
+                message.what = MSG_TASK_ENDED;
+                message.arg1 = mTaskId;
+                mReplyTo.send(message);
+            } catch (RemoteException e) {
+                Log.e(TAG,
+                        "Cannot send MSG_TASK_ENDED, remote handler no longer exist");
+            }
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof VisualVoicemailTask)) {
+                return false;
+            }
+            return mTaskId == ((VisualVoicemailTask) obj).mTaskId;
+        }
+
+        @Override
+        public int hashCode() {
+            return mTaskId;
+        }
+    }
+
+    /**
+     * Handles messages sent by telephony.
+     */
+    private final Messenger mMessenger = new Messenger(new Handler() {
+        @Override
+        public void handleMessage(final Message msg) {
+            final PhoneAccountHandle handle = msg.getData()
+                    .getParcelable(DATA_PHONE_ACCOUNT_HANDLE, android.telecom.PhoneAccountHandle.class);
+            VisualVoicemailTask task = new VisualVoicemailTask(msg.replyTo, msg.arg1);
+            switch (msg.what) {
+                case MSG_ON_CELL_SERVICE_CONNECTED:
+                    onCellServiceConnected(task, handle);
+                    break;
+                case MSG_ON_SMS_RECEIVED:
+                    VisualVoicemailSms sms = msg.getData().getParcelable(DATA_SMS, android.telephony.VisualVoicemailSms.class);
+                    onSmsReceived(task, sms);
+                    break;
+                case MSG_ON_SIM_REMOVED:
+                    onSimRemoved(task, handle);
+                    break;
+                case MSG_TASK_STOPPED:
+                    onStopped(task);
+                    break;
+                default:
+                    super.handleMessage(msg);
+                    break;
+            }
+        }
+    });
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+
+    /**
+     * Called when the cellular service is connected on a {@link PhoneAccountHandle} for the first
+     * time, or when the carrier config has changed. It will not be called when the signal is lost
+     * then restored.
+     *
+     * @param task The task representing this event. {@link VisualVoicemailTask#finish()} must be
+     * called when the task is completed.
+     * @param phoneAccountHandle The {@link PhoneAccountHandle} triggering this event.
+     */
+    @MainThread
+    public abstract void onCellServiceConnected(VisualVoicemailTask task,
+                                                PhoneAccountHandle phoneAccountHandle);
+
+    /**
+     * Called when a SMS matching the {@link VisualVoicemailSmsFilterSettings} set by
+     * {@link TelephonyManager#setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings)
+     * }
+     * is received.
+     *
+     * @param task The task representing this event. {@link VisualVoicemailTask#finish()} must be
+     * called when the task is completed.
+     * @param sms The content of the received SMS.
+     */
+    @MainThread
+    public abstract void onSmsReceived(VisualVoicemailTask task,
+                                       VisualVoicemailSms sms);
+
+    /**
+     * Called when a SIM is removed.
+     *
+     * @param task The task representing this event. {@link VisualVoicemailTask#finish()} must be
+     * called when the task is completed.
+     * @param phoneAccountHandle The {@link PhoneAccountHandle} triggering this event.
+     */
+    @MainThread
+    public abstract void onSimRemoved(VisualVoicemailTask task,
+                                      PhoneAccountHandle phoneAccountHandle);
+
+    /**
+     * Called before the system is about to terminate a task. The service should persist any
+     * necessary data and call finish on the task immediately.
+     */
+    @MainThread
+    public abstract void onStopped(VisualVoicemailTask task);
+
+    /**
+     * Set the visual voicemail SMS filter settings for the VisualVoicemailService.
+     * {@link #onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)} will be called when
+     * a SMS matching the settings is received. The caller should have
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} and implements a
+     * VisualVoicemailService.
+     * <p>
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *
+     * @param phoneAccountHandle The account to apply the settings to.
+     * @param settings The settings for the filter, or {@code null} to disable the filter.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final void setSmsFilterSettings(Context context,
+            PhoneAccountHandle phoneAccountHandle,
+            VisualVoicemailSmsFilterSettings settings) {
+        TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+        int subId = getSubId(context, phoneAccountHandle);
+        if (settings == null) {
+            telephonyManager.disableVisualVoicemailSmsFilter(subId);
+        } else {
+            telephonyManager.enableVisualVoicemailSmsFilter(subId, settings);
+        }
+    }
+
+    /**
+     * Send a visual voicemail SMS. The caller must be the current default dialer.
+     * <p>
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#SEND_SMS SEND_SMS}
+     *
+     * @param phoneAccountHandle The account to send the SMS with.
+     * @param number The destination number.
+     * @param port The destination port for data SMS, or 0 for text SMS.
+     * @param text The message content. For data sms, it will be encoded as a UTF-8 byte stream.
+     * @param sentIntent The sent intent passed to the {@link SmsManager}
+     *
+     * @throws SecurityException if the caller is not the current default dialer
+     *
+     * @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent)
+     * @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final void sendVisualVoicemailSms(Context context,
+            PhoneAccountHandle phoneAccountHandle, String number,
+            short port, String text, PendingIntent sentIntent) {
+        TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+        telephonyManager.sendVisualVoicemailSmsForSubscriber(getSubId(context, phoneAccountHandle),
+                number, port, text, sentIntent);
+    }
+
+    private static int getSubId(Context context, PhoneAccountHandle phoneAccountHandle) {
+        TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+        TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
+        return telephonyManager
+                .getSubIdForPhoneAccount(telecomManager.getPhoneAccount(phoneAccountHandle));
+    }
+
+}
diff --git a/android-35/android/telephony/VisualVoicemailSms.java b/android-35/android/telephony/VisualVoicemailSms.java
new file mode 100644
index 0000000..bec715e
--- /dev/null
+++ b/android-35/android/telephony/VisualVoicemailSms.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 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;
+
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.VisualVoicemailService.VisualVoicemailTask;
+
+/**
+ * Represents the content of a visual voicemail SMS. If a incoming SMS matches the {@link
+ * VisualVoicemailSmsFilterSettings} set by the default dialer, {@link
+ * VisualVoicemailService#onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)} will be called.
+ */
+public final class VisualVoicemailSms implements Parcelable {
+
+    private final PhoneAccountHandle mPhoneAccountHandle;
+    @Nullable
+    private final String mPrefix;
+
+    @Nullable
+    private final Bundle mFields;
+
+    private final String mMessageBody;
+
+    VisualVoicemailSms(Builder builder) {
+        mPhoneAccountHandle = builder.mPhoneAccountHandle;
+        mPrefix = builder.mPrefix;
+        mFields = builder.mFields;
+        mMessageBody = builder.mMessageBody;
+    }
+
+    /**
+     * The {@link PhoneAccountHandle} that received the SMS.
+     */
+    public PhoneAccountHandle getPhoneAccountHandle() {
+        return mPhoneAccountHandle;
+    }
+
+    /**
+     * The event type of the SMS or {@code null} if the framework cannot parse the SMS as voicemail
+     * but the carrier pattern indicates it is. Common values are "SYNC" or "STATUS".
+     */
+    public String getPrefix() {
+        return mPrefix;
+    }
+
+    /**
+     * The key-value pairs sent by the SMS, or {@code null} if the framework cannot parse the SMS as
+     * voicemail but the carrier pattern indicates it is. The interpretation of the fields is
+     * carrier dependent.
+     */
+    public Bundle getFields() {
+        return mFields;
+    }
+
+    /**
+     * Raw message body of the received SMS.
+     */
+    public String getMessageBody() {
+        return mMessageBody;
+    }
+
+    /**
+     * Builder for the {@link VisualVoicemailSms}. Internal use only.
+     *
+     * @hide
+     */
+    public static class Builder {
+
+        private PhoneAccountHandle mPhoneAccountHandle;
+        private String mPrefix;
+        private Bundle mFields;
+        private String mMessageBody;
+
+        public VisualVoicemailSms build() {
+            return new VisualVoicemailSms(this);
+        }
+
+        public Builder setPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+            this.mPhoneAccountHandle = phoneAccountHandle;
+            return this;
+        }
+
+        public Builder setPrefix(String prefix) {
+            this.mPrefix = prefix;
+            return this;
+        }
+
+        public Builder setFields(Bundle fields) {
+            this.mFields = fields;
+            return this;
+        }
+
+        public Builder setMessageBody(String messageBody) {
+            this.mMessageBody = messageBody;
+            return this;
+        }
+
+    }
+
+
+    public static final @android.annotation.NonNull Creator<VisualVoicemailSms> CREATOR =
+            new Creator<VisualVoicemailSms>() {
+                @Override
+                public VisualVoicemailSms createFromParcel(Parcel in) {
+                    return new Builder()
+                            .setPhoneAccountHandle((PhoneAccountHandle) in.readParcelable(null, android.telecom.PhoneAccountHandle.class))
+                            .setPrefix(in.readString())
+                            .setFields(in.readBundle())
+                            .setMessageBody(in.readString())
+                            .build();
+                }
+
+                @Override
+                public VisualVoicemailSms[] newArray(int size) {
+                    return new VisualVoicemailSms[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(getPhoneAccountHandle(), flags);
+        dest.writeString(getPrefix());
+        dest.writeBundle(getFields());
+        dest.writeString(getMessageBody());
+    }
+}
diff --git a/android-35/android/telephony/VisualVoicemailSmsFilterSettings.java b/android-35/android/telephony/VisualVoicemailSmsFilterSettings.java
new file mode 100644
index 0000000..eadb726
--- /dev/null
+++ b/android-35/android/telephony/VisualVoicemailSmsFilterSettings.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2016 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.VisualVoicemailService.VisualVoicemailTask;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Class to represent various settings for the visual voicemail SMS filter. When the filter is
+ * enabled, incoming SMS matching the generalized OMTP format:
+ *
+ * <p>[clientPrefix]:[prefix]:([key]=[value];)*
+ *
+ * <p>will be regarded as a visual voicemail SMS, and removed before reaching the SMS provider. The
+ * {@link VisualVoicemailService} in the current default dialer will be bound and
+ * {@link VisualVoicemailService#onSmsReceived(VisualVoicemailTask, VisualVoicemailSms)}
+ * will called with the information extracted from the SMS.
+ *
+ * <p>Use {@link android.telephony.VisualVoicemailSmsFilterSettings.Builder} to construct this
+ * class.
+ *
+ * @see TelephonyManager#setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings)
+ */
+public final class VisualVoicemailSmsFilterSettings implements Parcelable {
+
+
+    /**
+     * The visual voicemail SMS message does not have to be a data SMS, and can be directed to any
+     * port.
+     */
+    public static final int DESTINATION_PORT_ANY = -1;
+
+    /**
+     * The visual voicemail SMS message can be directed to any port, but must be a data SMS.
+     */
+    public static final int DESTINATION_PORT_DATA_SMS = -2;
+
+    /**
+     * @hide
+     */
+    public static final String DEFAULT_CLIENT_PREFIX = "//VVM";
+    /**
+     * @hide
+     */
+    public static final List<String> DEFAULT_ORIGINATING_NUMBERS = Collections.emptyList();
+    /**
+     * @hide
+     */
+    public static final int DEFAULT_DESTINATION_PORT = DESTINATION_PORT_ANY;
+
+    /**
+     * Builder class for {@link VisualVoicemailSmsFilterSettings} objects.
+     */
+    public static class Builder {
+
+        private String mClientPrefix = DEFAULT_CLIENT_PREFIX;
+        private List<String> mOriginatingNumbers = DEFAULT_ORIGINATING_NUMBERS;
+        private int mDestinationPort = DEFAULT_DESTINATION_PORT;
+        private String mPackageName;
+
+        public VisualVoicemailSmsFilterSettings build() {
+            return new VisualVoicemailSmsFilterSettings(this);
+        }
+
+        /**
+         * Sets the client prefix for the visual voicemail SMS filter. The client prefix will appear
+         * at the start of a visual voicemail SMS message, followed by a colon(:).
+         */
+        public Builder setClientPrefix(String clientPrefix) {
+            if (clientPrefix == null) {
+                throw new IllegalArgumentException("Client prefix cannot be null");
+            }
+            mClientPrefix = clientPrefix;
+            return this;
+        }
+
+        /**
+         * Sets the originating number allow list for the visual voicemail SMS filter. If the list
+         * is not null only the SMS messages from a number in the list can be considered as a visual
+         * voicemail SMS. Otherwise, messages from any address will be considered.
+         */
+        public Builder setOriginatingNumbers(List<String> originatingNumbers) {
+            if (originatingNumbers == null) {
+                throw new IllegalArgumentException("Originating numbers cannot be null");
+            }
+            mOriginatingNumbers = originatingNumbers;
+            return this;
+        }
+
+        /**
+         * Sets the destination port for the visual voicemail SMS filter.
+         *
+         * @param destinationPort The destination port, or {@link #DESTINATION_PORT_ANY}, or {@link
+         * #DESTINATION_PORT_DATA_SMS}
+         */
+        public Builder setDestinationPort(int destinationPort) {
+            mDestinationPort = destinationPort;
+            return this;
+        }
+
+        /**
+         * The package that registered this filter.
+         *
+         * @hide
+         */
+        public Builder setPackageName(String packageName) {
+            mPackageName = packageName;
+            return this;
+        }
+    }
+
+    /**
+     * The client prefix for the visual voicemail SMS filter. The client prefix will appear at the
+     * start of a visual voicemail SMS message, followed by a colon(:).
+     */
+    public final String clientPrefix;
+
+    /**
+     * The originating number allow list for the visual voicemail SMS filter of a phone account. If
+     * the list is not null only the SMS messages from a number in the list can be considered as a
+     * visual voicemail SMS. Otherwise, messages from any address will be considered.
+     */
+    public final List<String> originatingNumbers;
+
+    /**
+     * The destination port for the visual voicemail SMS filter, or {@link #DESTINATION_PORT_ANY},
+     * or {@link #DESTINATION_PORT_DATA_SMS}
+     */
+    public final int destinationPort;
+
+    /**
+     * The package that registered this filter.
+     *
+     * @hide
+     */
+    public final String packageName;
+
+    /**
+     * Use {@link Builder} to construct
+     */
+    private VisualVoicemailSmsFilterSettings(Builder builder) {
+        clientPrefix = builder.mClientPrefix;
+        originatingNumbers = builder.mOriginatingNumbers;
+        destinationPort = builder.mDestinationPort;
+        packageName = builder.mPackageName;
+    }
+
+    public static final @android.annotation.NonNull Creator<VisualVoicemailSmsFilterSettings> CREATOR =
+            new Creator<VisualVoicemailSmsFilterSettings>() {
+                @Override
+                public VisualVoicemailSmsFilterSettings createFromParcel(Parcel in) {
+                    Builder builder = new Builder();
+                    builder.setClientPrefix(in.readString());
+                    builder.setOriginatingNumbers(in.createStringArrayList());
+                    builder.setDestinationPort(in.readInt());
+                    builder.setPackageName(in.readString());
+                    return builder.build();
+                }
+
+                @Override
+                public VisualVoicemailSmsFilterSettings[] newArray(int size) {
+                    return new VisualVoicemailSmsFilterSettings[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(clientPrefix);
+        dest.writeStringList(originatingNumbers);
+        dest.writeInt(destinationPort);
+        dest.writeString(packageName);
+    }
+
+    @Override
+    public String toString() {
+        return "[VisualVoicemailSmsFilterSettings "
+                + "clientPrefix=" + clientPrefix
+                + ", originatingNumbers=" + originatingNumbers
+                + ", destinationPort=" + destinationPort
+                + "]";
+    }
+
+}
diff --git a/android-35/android/telephony/VoLteServiceState.java b/android-35/android/telephony/VoLteServiceState.java
new file mode 100644
index 0000000..27187e6
--- /dev/null
+++ b/android-35/android/telephony/VoLteServiceState.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.telephony.Rlog;
+
+/**
+ * Contains LTE network state related information.
+ * @deprecated Only contains SRVCC state, which isn't specific to LTE handovers. For SRVCC
+ * indications, use {@link PhoneStateListener#onSrvccStateChanged(int)}.
+ * @hide
+ */
+@Deprecated
+public final class VoLteServiceState implements Parcelable {
+
+    private static final String LOG_TAG = "VoLteServiceState";
+    private static final boolean DBG = false;
+
+    //Use int max, as -1 is a valid value in signal strength
+    public static final int INVALID = 0x7FFFFFFF;
+
+    public static final int NOT_SUPPORTED = 0;
+    public static final int SUPPORTED = 1;
+
+    // Single Radio Voice Call Continuity(SRVCC) progress state
+    public static final int HANDOVER_STARTED   = 0;
+    public static final int HANDOVER_COMPLETED = 1;
+    public static final int HANDOVER_FAILED    = 2;
+    public static final int HANDOVER_CANCELED  = 3;
+
+    private int mSrvccState;
+
+    /**
+     * Create a new VoLteServiceState from a intent notifier Bundle
+     *
+     * This method is maybe used by external applications.
+     *
+     * @param m Bundle from intent notifier
+     * @return newly created VoLteServiceState
+     *
+     * @hide
+     */
+    public static VoLteServiceState newFromBundle(Bundle m) {
+        VoLteServiceState ret;
+        ret = new VoLteServiceState();
+        ret.setFromNotifierBundle(m);
+        return ret;
+    }
+
+    /**
+     * Empty constructor
+     *
+     * @hide
+     */
+    public VoLteServiceState() {
+        initialize();
+    }
+
+    /**
+     * Constructor
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public VoLteServiceState(int srvccState) {
+        initialize();
+
+        mSrvccState = srvccState;
+    }
+
+    /**
+     * Copy constructors
+     *
+     * @param s Source VoLteServiceState
+     *
+     * @hide
+     */
+    public VoLteServiceState(VoLteServiceState s) {
+        copyFrom(s);
+    }
+
+    /**
+     * Initialize values to defaults.
+     *
+     * @hide
+     */
+    private void initialize() {
+        mSrvccState = INVALID;
+    }
+
+    /**
+     * @hide
+     */
+    protected void copyFrom(VoLteServiceState s) {
+        mSrvccState = s.mSrvccState;
+    }
+
+    /**
+     * Construct a VoLteServiceState object from the given parcel.
+     *
+     * @hide
+     */
+    public VoLteServiceState(Parcel in) {
+        if (DBG) log("Size of VoLteServiceState parcel:" + in.dataSize());
+
+        mSrvccState = in.readInt();
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mSrvccState);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable.Creator}
+     *
+     * @hide
+     */
+    public static final @android.annotation.NonNull Parcelable.Creator<VoLteServiceState> CREATOR = new Parcelable.Creator() {
+        public VoLteServiceState createFromParcel(Parcel in) {
+            return new VoLteServiceState(in);
+        }
+
+        public VoLteServiceState[] newArray(int size) {
+            return new VoLteServiceState[size];
+        }
+    };
+
+    /**
+     * Validate the individual fields as per the range
+     * specified in ril.h
+     * Set to invalid any field that is not in the valid range
+     *
+     * @return
+     *      Valid values for all fields
+     * @hide
+     */
+    public void validateInput() {
+    }
+
+    public int hashCode() {
+        int primeNum = 31;
+        return ((mSrvccState * primeNum));
+    }
+
+    /**
+     * @return true if the LTE network states are the same
+     */
+    @Override
+    public boolean equals (Object o) {
+        VoLteServiceState s;
+
+        try {
+            s = (VoLteServiceState) o;
+        } catch (ClassCastException ex) {
+            return false;
+        }
+
+        if (o == null) {
+            return false;
+        }
+
+        return (mSrvccState == s.mSrvccState);
+    }
+
+    /**
+     * @return string representation.
+     */
+    @Override
+    public String toString() {
+        return ("VoLteServiceState:"
+                + " " + mSrvccState);
+    }
+
+    /**
+     * Set VoLteServiceState based on intent notifier map
+     *
+     * @param m intent notifier map
+     * @hide
+     */
+    private void setFromNotifierBundle(Bundle m) {
+        mSrvccState = m.getInt("mSrvccState");
+    }
+
+    /**
+     * Set intent notifier Bundle based on VoLteServiceState
+     *
+     * @param m intent notifier Bundle
+     * @hide
+     */
+    public void fillInNotifierBundle(Bundle m) {
+        m.putInt("mSrvccState", mSrvccState);
+    }
+
+    public int getSrvccState() {
+        return mSrvccState;
+    }
+
+    /**
+     * log
+     */
+    private static void log(String s) {
+        Rlog.w(LOG_TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/VoiceSpecificRegistrationInfo.java b/android-35/android/telephony/VoiceSpecificRegistrationInfo.java
new file mode 100644
index 0000000..d43181e
--- /dev/null
+++ b/android-35/android/telephony/VoiceSpecificRegistrationInfo.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2018 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;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to voice network registration.
+ * @hide
+ */
+public class VoiceSpecificRegistrationInfo implements Parcelable{
+    /**
+     * oncurrent services support indicator. if
+     * registered on a CDMA system.
+     * false - Concurrent services not supported,
+     * true - Concurrent services supported
+     */
+    public final boolean cssSupported;
+
+    /**
+     * TSB-58 Roaming Indicator if registered
+     * on a CDMA or EVDO system or -1 if not.
+     * Valid values are 0-255.
+     */
+    public final int roamingIndicator;
+
+    /**
+     * indicates whether the current system is in the
+     * PRL if registered on a CDMA or EVDO system or -1 if
+     * not. 0=not in the PRL, 1=in the PRL
+     */
+    public final int systemIsInPrl;
+
+    /**
+     * default Roaming Indicator from the PRL,
+     * if registered on a CDMA or EVDO system or -1 if not.
+     * Valid values are 0-255.
+     */
+    public final int defaultRoamingIndicator;
+
+    VoiceSpecificRegistrationInfo(boolean cssSupported, int roamingIndicator, int systemIsInPrl,
+                                  int defaultRoamingIndicator) {
+        this.cssSupported = cssSupported;
+        this.roamingIndicator = roamingIndicator;
+        this.systemIsInPrl = systemIsInPrl;
+        this.defaultRoamingIndicator = defaultRoamingIndicator;
+    }
+
+    /**
+     * Constructor from another voice specific registration info
+     *
+     * @param vsri another voice specific registration info
+     * @hide
+     */
+    VoiceSpecificRegistrationInfo(VoiceSpecificRegistrationInfo vsri) {
+        cssSupported = vsri.cssSupported;
+        roamingIndicator = vsri.roamingIndicator;
+        systemIsInPrl = vsri.systemIsInPrl;
+        defaultRoamingIndicator = vsri.defaultRoamingIndicator;
+    }
+
+    private VoiceSpecificRegistrationInfo(Parcel source) {
+        this.cssSupported = source.readBoolean();
+        this.roamingIndicator = source.readInt();
+        this.systemIsInPrl = source.readInt();
+        this.defaultRoamingIndicator = source.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(cssSupported);
+        dest.writeInt(roamingIndicator);
+        dest.writeInt(systemIsInPrl);
+        dest.writeInt(defaultRoamingIndicator);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "VoiceSpecificRegistrationInfo {"
+                + " mCssSupported=" + cssSupported
+                + " mRoamingIndicator=" + roamingIndicator
+                + " mSystemIsInPrl=" + systemIsInPrl
+                + " mDefaultRoamingIndicator=" + defaultRoamingIndicator + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(cssSupported, roamingIndicator, systemIsInPrl,
+                defaultRoamingIndicator);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof VoiceSpecificRegistrationInfo)) {
+            return false;
+        }
+
+        VoiceSpecificRegistrationInfo other = (VoiceSpecificRegistrationInfo) o;
+        return this.cssSupported == other.cssSupported
+                && this.roamingIndicator == other.roamingIndicator
+                && this.systemIsInPrl == other.systemIsInPrl
+                && this.defaultRoamingIndicator == other.defaultRoamingIndicator;
+    }
+
+
+    public static final @NonNull Parcelable.Creator<VoiceSpecificRegistrationInfo> CREATOR =
+            new Parcelable.Creator<VoiceSpecificRegistrationInfo>() {
+                @Override
+                public VoiceSpecificRegistrationInfo createFromParcel(Parcel source) {
+                    return new VoiceSpecificRegistrationInfo(source);
+                }
+
+                @Override
+                public VoiceSpecificRegistrationInfo[] newArray(int size) {
+                    return new VoiceSpecificRegistrationInfo[size];
+                }
+            };
+}
diff --git a/android-35/android/telephony/VopsSupportInfo.java b/android-35/android/telephony/VopsSupportInfo.java
new file mode 100644
index 0000000..f89bfa9
--- /dev/null
+++ b/android-35/android/telephony/VopsSupportInfo.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2021 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;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+
+/**
+ * Abstract base class for the information related to network VoPS support.
+ * This is the base class for XxVopsSupportInfo which represent VoPS support
+ * information for specific network access techonology.
+ * @hide
+ */
+@SuppressLint("ParcelNotFinal")
+@SystemApi
+public abstract class VopsSupportInfo implements Parcelable {
+
+    /**
+     * @hide
+     */
+    public VopsSupportInfo() {}
+
+    /**
+     * Returns whether VoPS is supported by the network
+     */
+    public abstract boolean isVopsSupported();
+
+    /**
+     * Returns whether emergency service is supported by the network
+     */
+    public abstract boolean isEmergencyServiceSupported();
+
+    /**
+     * Returns whether emergency service fallback is supported by the network
+     */
+    public abstract boolean isEmergencyServiceFallbackSupported();
+
+    /**
+     * Implement the Parcelable interface
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public abstract void writeToParcel(@NonNull Parcel dest, int flags);
+
+    /**
+     * Used by child classes for parceling.
+     *
+     * @hide
+     */
+    protected void writeToParcel(@NonNull Parcel dest, int flags, int type) {
+        dest.writeInt(type);
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @android.annotation.NonNull Creator<VopsSupportInfo> CREATOR =
+            new Creator<VopsSupportInfo>() {
+        @Override
+        public VopsSupportInfo createFromParcel(Parcel in) {
+                int type = in.readInt();
+                switch (type) {
+                    case AccessNetworkType.EUTRAN:
+                        return LteVopsSupportInfo.createFromParcelBody(in);
+                    case AccessNetworkType.NGRAN:
+                        return NrVopsSupportInfo.createFromParcelBody(in);
+                    default: throw new RuntimeException("Bad VopsSupportInfo Parcel");
+                }
+        }
+
+        @Override
+        public VopsSupportInfo[] newArray(int size) {
+            return new VopsSupportInfo[size];
+        }
+    };
+
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    public abstract boolean equals(Object o);
+}
diff --git a/android-35/android/telephony/WwanSelectorCallback.java b/android-35/android/telephony/WwanSelectorCallback.java
new file mode 100644
index 0000000..b900af3
--- /dev/null
+++ b/android-35/android/telephony/WwanSelectorCallback.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.CancellationSignal;
+import android.telephony.DomainSelectionService.EmergencyScanType;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A callback class used to communicate with the framework to request network scans
+ * and notify the framework when a WWAN domain has been selected.
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
+public interface WwanSelectorCallback {
+    /**
+     * Notify the framework that the {@link DomainSelectionService} has requested an emergency
+     * network scan as part of selection.
+     *
+     * @param preferredNetworks The ordered list of preferred networks to scan.
+     * @param scanType Indicates the scan preference, such as full service or limited service.
+     * @param resetScan Indicates that the previous scan result shall be reset before scanning.
+     * @param signal Notifies when the operation is canceled.
+     * @param consumer The handler of the response, which will contain a one-shot result
+     *        of the network scan.
+     */
+    void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks,
+            @EmergencyScanType int scanType, boolean resetScan,
+            @NonNull CancellationSignal signal,
+            @NonNull Consumer<EmergencyRegistrationResult> consumer);
+
+    /**
+     * Notifies the FW that the domain has been selected. After this method is called,
+     * this interface can be discarded.
+     *
+     * @param domain The selected domain.
+     * @param useEmergencyPdn Indicates whether emergency services use emergency PDN or not.
+     */
+    void onDomainSelected(@NetworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn);
+}
diff --git a/android-35/android/telephony/cdma/CdmaCellLocation.java b/android-35/android/telephony/cdma/CdmaCellLocation.java
new file mode 100644
index 0000000..d4cb5ac
--- /dev/null
+++ b/android-35/android/telephony/cdma/CdmaCellLocation.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2006 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.cdma;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Bundle;
+import android.telephony.CellLocation;
+
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.telephony.Rlog;
+
+/**
+ * Represents the cell location on a CDMA phone.
+ *
+ * @deprecated use {@link android.telephony.CellIdentity CellIdentity}.
+ */
+@Deprecated
+public class CdmaCellLocation extends CellLocation {
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mBaseStationId = -1;
+
+    /**
+     * @hide
+     */
+    public final static int INVALID_LAT_LONG = Integer.MAX_VALUE;
+
+    /**
+     * Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+     * It is represented in units of 0.25 seconds and ranges from -1296000
+     * to 1296000, both values inclusive (corresponding to a range of -90
+     * to +90 degrees). Integer.MAX_VALUE is considered invalid value.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mBaseStationLatitude = INVALID_LAT_LONG;
+
+    /**
+     * Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+     * It is represented in units of 0.25 seconds and ranges from -2592000
+     * to 2592000, both values inclusive (corresponding to a range of -180
+     * to +180 degrees). Integer.MAX_VALUE is considered invalid value.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mBaseStationLongitude = INVALID_LAT_LONG;
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mSystemId = -1;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private int mNetworkId = -1;
+
+    /**
+     * Empty constructor.
+     * Initializes the BID, SID, NID and base station latitude and longitude
+     * to invalid values.
+     */
+    public CdmaCellLocation() {
+        this.mBaseStationId = -1;
+        this.mBaseStationLatitude = INVALID_LAT_LONG;
+        this.mBaseStationLongitude = INVALID_LAT_LONG;
+        this.mSystemId = -1;
+        this.mNetworkId = -1;
+    }
+
+    /**
+     * Initialize the object from a bundle.
+     */
+    public CdmaCellLocation(Bundle bundle) {
+        this.mBaseStationId = bundle.getInt("baseStationId", mBaseStationId);
+        this.mBaseStationLatitude = bundle.getInt("baseStationLatitude", mBaseStationLatitude);
+        this.mBaseStationLongitude = bundle.getInt("baseStationLongitude", mBaseStationLongitude);
+        this.mSystemId = bundle.getInt("systemId", mSystemId);
+        this.mNetworkId = bundle.getInt("networkId", mNetworkId);
+    }
+
+    /**
+     * @return cdma base station identification number, -1 if unknown
+     */
+    public int getBaseStationId() {
+        return this.mBaseStationId;
+    }
+
+    /**
+     * Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+     * (http://www.3gpp2.org/public_html/specs/C.S0005-A_v6.0.pdf)
+     * It is represented in units of 0.25 seconds and ranges from -1296000
+     * to 1296000, both values inclusive (corresponding to a range of -90
+     * to +90 degrees). Integer.MAX_VALUE is considered invalid value.
+     *
+     * @return cdma base station latitude in units of 0.25 seconds, Integer.MAX_VALUE if unknown
+     */
+    public int getBaseStationLatitude() {
+        return this.mBaseStationLatitude;
+    }
+
+    /**
+     * Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+     * (http://www.3gpp2.org/public_html/specs/C.S0005-A_v6.0.pdf)
+     * It is represented in units of 0.25 seconds and ranges from -2592000
+     * to 2592000, both values inclusive (corresponding to a range of -180
+     * to +180 degrees). Integer.MAX_VALUE is considered invalid value.
+     *
+     * @return cdma base station longitude in units of 0.25 seconds, Integer.MAX_VALUE if unknown
+     */
+    public int getBaseStationLongitude() {
+        return this.mBaseStationLongitude;
+    }
+
+    /**
+     * @return cdma system identification number, -1 if unknown
+     */
+    public int getSystemId() {
+        return this.mSystemId;
+    }
+
+    /**
+     * @return cdma network identification number, -1 if unknown
+     */
+    public int getNetworkId() {
+        return this.mNetworkId;
+    }
+
+    /**
+     * Invalidate this object.  The cell location data is set to invalid values.
+     */
+    @Override
+    public void setStateInvalid() {
+        this.mBaseStationId = -1;
+        this.mBaseStationLatitude = INVALID_LAT_LONG;
+        this.mBaseStationLongitude = INVALID_LAT_LONG;
+        this.mSystemId = -1;
+        this.mNetworkId = -1;
+    }
+
+    /**
+     * Set the cell location data.
+     */
+    public void setCellLocationData(int baseStationId, int baseStationLatitude,
+         int baseStationLongitude) {
+         // The following values have to be written in the correct sequence
+         this.mBaseStationId = baseStationId;
+         this.mBaseStationLatitude = baseStationLatitude;   //values[2];
+         this.mBaseStationLongitude = baseStationLongitude; //values[3];
+    }
+
+    /**
+     * Set the cell location data.
+     */
+     public void setCellLocationData(int baseStationId, int baseStationLatitude,
+         int baseStationLongitude, int systemId, int networkId) {
+         // The following values have to be written in the correct sequence
+         this.mBaseStationId = baseStationId;
+         this.mBaseStationLatitude = baseStationLatitude;   //values[2];
+         this.mBaseStationLongitude = baseStationLongitude; //values[3];
+         this.mSystemId = systemId;
+         this.mNetworkId = networkId;
+    }
+
+    @Override
+    public int hashCode() {
+        return this.mBaseStationId ^ this.mBaseStationLatitude ^ this.mBaseStationLongitude
+                ^ this.mSystemId ^ this.mNetworkId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        CdmaCellLocation s;
+
+        try {
+            s = (CdmaCellLocation)o;
+        } catch (ClassCastException ex) {
+            return false;
+        }
+
+        if (o == null) {
+            return false;
+        }
+
+        return (equalsHandlesNulls(this.mBaseStationId, s.mBaseStationId) &&
+                equalsHandlesNulls(this.mBaseStationLatitude, s.mBaseStationLatitude) &&
+                equalsHandlesNulls(this.mBaseStationLongitude, s.mBaseStationLongitude) &&
+                equalsHandlesNulls(this.mSystemId, s.mSystemId) &&
+                equalsHandlesNulls(this.mNetworkId, s.mNetworkId)
+        );
+    }
+
+    @Override
+    public String toString() {
+        return "[" + this.mBaseStationId + ","
+                   + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, this.mBaseStationLatitude) + ","
+                   + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, this.mBaseStationLongitude) + ","
+                   + this.mSystemId + ","
+                   + this.mNetworkId + "]";
+    }
+
+    /**
+     * Test whether two objects hold the same data values or both are null
+     *
+     * @param a first obj
+     * @param b second obj
+     * @return true if two objects equal or both are null
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    private static boolean equalsHandlesNulls(Object a, Object b) {
+        return (a == null) ? (b == null) : a.equals (b);
+    }
+
+    /**
+     * Fill the cell location data into the intent notifier Bundle based on service state
+     *
+     * @param bundleToFill intent notifier Bundle
+     */
+    public void fillInNotifierBundle(Bundle bundleToFill) {
+        bundleToFill.putInt("baseStationId", this.mBaseStationId);
+        bundleToFill.putInt("baseStationLatitude", this.mBaseStationLatitude);
+        bundleToFill.putInt("baseStationLongitude", this.mBaseStationLongitude);
+        bundleToFill.putInt("systemId", this.mSystemId);
+        bundleToFill.putInt("networkId", this.mNetworkId);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean isEmpty() {
+        return (this.mBaseStationId == -1 &&
+                this.mBaseStationLatitude == INVALID_LAT_LONG &&
+                this.mBaseStationLongitude == INVALID_LAT_LONG &&
+                this.mSystemId == -1 &&
+                this.mNetworkId == -1);
+    }
+
+    /**
+     * Converts latitude or longitude from 0.25 seconds (as defined in the
+     * 3GPP2 C.S0005-A v6.0 standard) to decimal degrees
+     *
+     * @param quartSec latitude or longitude in 0.25 seconds units
+     * @return latitude or longitude in decimal degrees units
+     * @throws IllegalArgumentException if value is less than -2592000,
+     *                                  greater than 2592000, or is not a number.
+     */
+    public static double convertQuartSecToDecDegrees(int quartSec) {
+        if(Double.isNaN(quartSec) || quartSec < -2592000 || quartSec > 2592000){
+            // Invalid value
+            throw new IllegalArgumentException("Invalid coordiante value:" + quartSec);
+        }
+        return ((double)quartSec) / (3600 * 4);
+    }
+
+}
+
+
diff --git a/android-35/android/telephony/cdma/CdmaSmsCbProgramData.java b/android-35/android/telephony/cdma/CdmaSmsCbProgramData.java
new file mode 100644
index 0000000..02429b5
--- /dev/null
+++ b/android-35/android/telephony/cdma/CdmaSmsCbProgramData.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2012 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.cdma;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * CDMA Service Category Program Data from SCPT (Service Category Programming Teleservice) SMS,
+ * as defined in 3GPP2 C.S0015-B section 4.5.19.
+ * <p>
+ * The CellBroadcastReceiver app receives an Intent with action
+ * {@link android.provider.Telephony.Sms.Intents#SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION}
+ * containing an array of these objects to update its list of cell broadcast service categories
+ * to display.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class CdmaSmsCbProgramData implements Parcelable {
+
+    /** Delete the specified service category from the list of enabled categories. */
+    public static final int OPERATION_DELETE_CATEGORY   = 0;
+
+    /** Add the specified service category to the list of enabled categories. */
+    public static final int OPERATION_ADD_CATEGORY      = 1;
+
+    /** Clear all service categories from the list of enabled categories. */
+    public static final int OPERATION_CLEAR_CATEGORIES  = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"OPERATION_"},
+            value = {
+                    OPERATION_DELETE_CATEGORY,
+                    OPERATION_ADD_CATEGORY,
+                    OPERATION_CLEAR_CATEGORIES,
+            })
+    public @interface Operation {}
+
+    // CMAS alert service category assignments, see 3GPP2 C.R1001 table 9.3.3-1
+    /** Indicates a presidential-level alert */
+    public static final int CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT  = 0x1000;
+
+    /** Indicates an extreme threat to life and property */
+    public static final int CATEGORY_CMAS_EXTREME_THREAT            = 0x1001;
+
+    /** Indicates an severe threat to life and property */
+    public static final int CATEGORY_CMAS_SEVERE_THREAT             = 0x1002;
+
+    /** Indicates an AMBER child abduction emergency */
+    public static final int CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 0x1003;
+
+    /** Indicates a CMAS test message */
+    public static final int CATEGORY_CMAS_TEST_MESSAGE              = 0x1004;
+
+    /** The last reserved value of a CMAS service category according to 3GPP C.R1001 table
+     * 9.3.3-1. */
+    public static final int CATEGORY_CMAS_LAST_RESERVED_VALUE       = 0x10ff;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CATEGORY_"},
+            value = {
+                    CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
+                    CATEGORY_CMAS_EXTREME_THREAT,
+                    CATEGORY_CMAS_SEVERE_THREAT,
+                    CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY,
+                    CATEGORY_CMAS_TEST_MESSAGE,
+                    CATEGORY_CMAS_LAST_RESERVED_VALUE,
+            })
+    public @interface Category {}
+
+    /** Alert option: no alert. @hide */
+    public static final int ALERT_OPTION_NO_ALERT               = 0;
+
+    /** Alert option: default alert. @hide */
+    public static final int ALERT_OPTION_DEFAULT_ALERT          = 1;
+
+    /** Alert option: vibrate alert once. @hide */
+    public static final int ALERT_OPTION_VIBRATE_ONCE           = 2;
+
+    /** Alert option: vibrate alert - repeat. @hide */
+    public static final int ALERT_OPTION_VIBRATE_REPEAT         = 3;
+
+    /** Alert option: visual alert once. @hide */
+    public static final int ALERT_OPTION_VISUAL_ONCE            = 4;
+
+    /** Alert option: visual alert - repeat. @hide */
+    public static final int ALERT_OPTION_VISUAL_REPEAT          = 5;
+
+    /** Alert option: low-priority alert once. @hide */
+    public static final int ALERT_OPTION_LOW_PRIORITY_ONCE      = 6;
+
+    /** Alert option: low-priority alert - repeat. @hide */
+    public static final int ALERT_OPTION_LOW_PRIORITY_REPEAT    = 7;
+
+    /** Alert option: medium-priority alert once. @hide */
+    public static final int ALERT_OPTION_MED_PRIORITY_ONCE      = 8;
+
+    /** Alert option: medium-priority alert - repeat. @hide */
+    public static final int ALERT_OPTION_MED_PRIORITY_REPEAT    = 9;
+
+    /** Alert option: high-priority alert once. @hide */
+    public static final int ALERT_OPTION_HIGH_PRIORITY_ONCE     = 10;
+
+    /** Alert option: high-priority alert - repeat. @hide */
+    public static final int ALERT_OPTION_HIGH_PRIORITY_REPEAT   = 11;
+
+    /** Service category operation (add/delete/clear). */
+    private final int mOperation;
+
+    /** Service category to modify. */
+    private final int mCategory;
+
+    /** Language used for service category name (defined in BearerData.LANGUAGE_*). */
+    private final int mLanguage;
+
+    /** Maximum number of messages to store for this service category. */
+    private final int mMaxMessages;
+
+    /** Service category alert option. */
+    private final int mAlertOption;
+
+    /** Name of service category. */
+    private final String mCategoryName;
+
+    /**
+     * Create a new CdmaSmsCbProgramData object with the specified values.
+     * @hide
+     */
+    public CdmaSmsCbProgramData(@Operation int operation, @Category int category, int language,
+            int maxMessages, int alertOption, @NonNull String categoryName) {
+        mOperation = operation;
+        mCategory = category;
+        mLanguage = language;
+        mMaxMessages = maxMessages;
+        mAlertOption = alertOption;
+        mCategoryName = categoryName;
+    }
+
+    /**
+     * Create a new CdmaSmsCbProgramData object from a Parcel.
+     * @hide
+     */
+    CdmaSmsCbProgramData(Parcel in) {
+        mOperation = in.readInt();
+        mCategory = in.readInt();
+        mLanguage = in.readInt();
+        mMaxMessages = in.readInt();
+        mAlertOption = in.readInt();
+        mCategoryName = in.readString();
+    }
+
+    /**
+     * Flatten this object into a Parcel.
+     *
+     * @param dest  The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written (ignored).
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mOperation);
+        dest.writeInt(mCategory);
+        dest.writeInt(mLanguage);
+        dest.writeInt(mMaxMessages);
+        dest.writeInt(mAlertOption);
+        dest.writeString(mCategoryName);
+    }
+
+    /**
+     * Returns the service category operation, e.g. {@link #OPERATION_ADD_CATEGORY}.
+     *
+     * @return the service category operation
+     */
+    public @Operation int getOperation() {
+        return mOperation;
+    }
+
+    /**
+     * Returns the CDMA service category to modify. See 3GPP2 C.S0015-B section 3.4.3.2 for more
+     * information on the service category. Currently only CMAS service categories 0x1000 through
+     * 0x10FF are supported.
+     *
+     * @return a 16-bit CDMA service category value
+     */
+    public @Category int getCategory() {
+        return mCategory;
+    }
+
+    /**
+     * Returns the CDMA language code for this service category.
+     * @return one of the language values defined in BearerData.LANGUAGE_*
+     * @hide
+     */
+    public int getLanguage() {
+        return mLanguage;
+    }
+
+    /**
+     * Returns the maximum number of messages to store for this service category.
+     * @return the maximum number of messages to store for this service category
+     * @hide
+     */
+    public int getMaxMessages() {
+        return mMaxMessages;
+    }
+
+    /**
+     * Returns the service category alert option, e.g. {@link #ALERT_OPTION_DEFAULT_ALERT}.
+     * @return one of the {@code ALERT_OPTION_*} values
+     * @hide
+     */
+    public int getAlertOption() {
+        return mAlertOption;
+    }
+
+    /**
+     * Returns the service category name, in the language specified by {@link #getLanguage()}.
+     * @return an optional service category name
+     * @hide
+     */
+    @NonNull
+    public String getCategoryName() {
+        return mCategoryName;
+    }
+
+    @Override
+    public String toString() {
+        return "CdmaSmsCbProgramData{operation=" + mOperation + ", category=" + mCategory
+                + ", language=" + mLanguage + ", max messages=" + mMaxMessages
+                + ", alert option=" + mAlertOption + ", category name=" + mCategoryName + '}';
+    }
+
+    /**
+     * Describe the kinds of special objects contained in the marshalled representation.
+     * @return a bitmask indicating this Parcelable contains no special objects
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Creator for unparcelling objects.
+     */
+    @NonNull
+    public static final Parcelable.Creator<CdmaSmsCbProgramData>
+            CREATOR = new Parcelable.Creator<CdmaSmsCbProgramData>() {
+        @Override
+        public CdmaSmsCbProgramData createFromParcel(Parcel in) {
+            return new CdmaSmsCbProgramData(in);
+        }
+
+        @Override
+        public CdmaSmsCbProgramData[] newArray(int size) {
+            return new CdmaSmsCbProgramData[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/cdma/CdmaSmsCbProgramResults.java b/android-35/android/telephony/cdma/CdmaSmsCbProgramResults.java
new file mode 100644
index 0000000..68bfa3c
--- /dev/null
+++ b/android-35/android/telephony/cdma/CdmaSmsCbProgramResults.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2012 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.cdma;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * CDMA Service Category Program Results from SCPT teleservice SMS.
+ *
+ * {@hide}
+ */
+public class CdmaSmsCbProgramResults implements Parcelable {
+
+    /** Program result: success. */
+    public static final int RESULT_SUCCESS                  = 0;
+
+    /** Program result: memory limit exceeded. */
+    public static final int RESULT_MEMORY_LIMIT_EXCEEDED    = 1;
+
+    /** Program result: limit exceeded. */
+    public static final int RESULT_CATEGORY_LIMIT_EXCEEDED  = 2;
+
+    /** Program result: category already opted in. */
+    public static final int RESULT_CATEGORY_ALREADY_ADDED   = 3;
+
+    /** Program result: category already opted in. */
+    public static final int RESULT_CATEGORY_ALREADY_DELETED = 4;
+
+    /** Program result: invalid MAX_MESSAGES. */
+    public static final int RESULT_INVALID_MAX_MESSAGES     = 5;
+
+    /** Program result: invalid ALERT_OPTION. */
+    public static final int RESULT_INVALID_ALERT_OPTION     = 6;
+
+    /** Program result: invalid service category name. */
+    public static final int RESULT_INVALID_CATEGORY_NAME    = 7;
+
+    /** Program result: unspecified programming failure. */
+    public static final int RESULT_UNSPECIFIED_FAILURE      = 8;
+
+    /** Service category to modify. */
+    private final int mCategory;
+
+    /** Language used for service category name (defined in BearerData.LANGUAGE_*). */
+    private final int mLanguage;
+
+    /** Result of service category programming for this category. */
+    private final int mCategoryResult;
+
+    /** Create a new CdmaSmsCbProgramResults object with the specified values. */
+    public CdmaSmsCbProgramResults(int category, int language, int categoryResult) {
+        mCategory = category;
+        mLanguage = language;
+        mCategoryResult = categoryResult;
+    }
+
+    /** Create a new CdmaSmsCbProgramResults object from a Parcel. */
+    CdmaSmsCbProgramResults(Parcel in) {
+        mCategory = in.readInt();
+        mLanguage = in.readInt();
+        mCategoryResult = in.readInt();
+    }
+
+    /**
+     * Flatten this object into a Parcel.
+     *
+     * @param dest  The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written (ignored).
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCategory);
+        dest.writeInt(mLanguage);
+        dest.writeInt(mCategoryResult);
+    }
+
+    /**
+     * Returns the CDMA service category to modify.
+     * @return a 16-bit CDMA service category value
+     */
+    public int getCategory() {
+        return mCategory;
+    }
+
+    /**
+     * Returns the CDMA language code for this service category.
+     * @return one of the language values defined in BearerData.LANGUAGE_*
+     */
+    public int getLanguage() {
+        return mLanguage;
+    }
+
+    /**
+     * Returns the result of service programming for this category
+     * @return the result of service programming for this category
+     */
+    public int getCategoryResult() {
+        return mCategoryResult;
+    }
+
+    @Override
+    public String toString() {
+        return "CdmaSmsCbProgramResults{category=" + mCategory
+                + ", language=" + mLanguage + ", result=" + mCategoryResult + '}';
+    }
+
+    /**
+     * Describe the kinds of special objects contained in the marshalled representation.
+     * @return a bitmask indicating this Parcelable contains no special objects
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Creator for unparcelling objects. */
+    public static final Parcelable.Creator<CdmaSmsCbProgramResults>
+            CREATOR = new Parcelable.Creator<CdmaSmsCbProgramResults>() {
+        @Override
+        public CdmaSmsCbProgramResults createFromParcel(Parcel in) {
+            return new CdmaSmsCbProgramResults(in);
+        }
+
+        @Override
+        public CdmaSmsCbProgramResults[] newArray(int size) {
+            return new CdmaSmsCbProgramResults[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/data/ApnSetting.java b/android-35/android/telephony/data/ApnSetting.java
new file mode 100644
index 0000000..44d3fca
--- /dev/null
+++ b/android-35/android/telephony/data/ApnSetting.java
@@ -0,0 +1,2410 @@
+/*
+ * Copyright (C) 2018 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.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.hardware.radio.data.ApnTypes;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.Telephony;
+import android.provider.Telephony.Carriers;
+import android.provider.Telephony.Carriers.EditStatus;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * An Access Point Name (APN) configuration for a carrier data connection.
+ *
+ * <p>The APN provides configuration to connect a cellular network device to an IP data network. A
+ * carrier uses the name, type and other configuration in an {@code APNSetting} to decide which IP
+ * address to assign, any security methods to apply, and how the device might be connected to
+ * private networks.
+ *
+ * <p>Use {@link ApnSetting.Builder} to create new instances.
+ */
+public class ApnSetting implements Parcelable {
+
+    private static final String LOG_TAG = "ApnSetting";
+    private static final boolean VDBG = false;
+
+    private static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*";
+    private static final String V3_FORMAT_REGEX = "^\\[ApnSettingV3\\]\\s*";
+    private static final String V4_FORMAT_REGEX = "^\\[ApnSettingV4\\]\\s*";
+    private static final String V5_FORMAT_REGEX = "^\\[ApnSettingV5\\]\\s*";
+    private static final String V6_FORMAT_REGEX = "^\\[ApnSettingV6\\]\\s*";
+    private static final String V7_FORMAT_REGEX = "^\\[ApnSettingV7\\]\\s*";
+
+    /**
+     * Default value for mtu if it's not set. Moved from PhoneConstants.
+     * @hide
+     */
+    public static final int UNSET_MTU = 0;
+    private static final int UNSPECIFIED_INT = -1;
+    private static final String UNSPECIFIED_STRING = "";
+
+    /**
+     * APN type for none. Should only be used for initialization.
+     * @hide
+     */
+    public static final int TYPE_NONE = ApnTypes.NONE;
+    /**
+     * APN type for all APNs (except wild-cardable types).
+     * @hide
+     */
+    public static final int TYPE_ALL = ApnTypes.DEFAULT | ApnTypes.HIPRI | ApnTypes.MMS
+            | ApnTypes.SUPL | ApnTypes.DUN | ApnTypes.FOTA | ApnTypes.IMS | ApnTypes.CBS;
+    /** APN type for default data traffic. */
+    public static final int TYPE_DEFAULT = ApnTypes.DEFAULT | ApnTypes.HIPRI;
+    /** APN type for MMS traffic. */
+    public static final int TYPE_MMS = ApnTypes.MMS;
+    /** APN type for SUPL assisted GPS. */
+    public static final int TYPE_SUPL = ApnTypes.SUPL;
+    /** APN type for DUN traffic. */
+    public static final int TYPE_DUN = ApnTypes.DUN;
+    /** APN type for HiPri traffic. */
+    public static final int TYPE_HIPRI = ApnTypes.HIPRI;
+    /** APN type for accessing the carrier's FOTA portal, used for over the air updates. */
+    public static final int TYPE_FOTA = ApnTypes.FOTA;
+    /** APN type for IMS. */
+    public static final int TYPE_IMS = ApnTypes.IMS;
+    /** APN type for CBS. */
+    public static final int TYPE_CBS = ApnTypes.CBS;
+    /** APN type for IA Initial Attach APN. */
+    public static final int TYPE_IA = ApnTypes.IA;
+    /**
+     * APN type for Emergency PDN. This is not an IA apn, but is used
+     * for access to carrier services in an emergency call situation.
+     */
+    public static final int TYPE_EMERGENCY = ApnTypes.EMERGENCY;
+    /** APN type for MCX (Mission Critical Service) where X can be PTT/Video/Data */
+    public static final int TYPE_MCX = ApnTypes.MCX;
+    /** APN type for XCAP. */
+    public static final int TYPE_XCAP = ApnTypes.XCAP;
+    /** APN type for VSIM. */
+    public static final int TYPE_VSIM = ApnTypes.VSIM;
+    /** APN type for BIP. */
+    public static final int TYPE_BIP = ApnTypes.BIP;
+    /** APN type for ENTERPRISE. */
+    public static final int TYPE_ENTERPRISE = ApnTypes.ENTERPRISE;
+    /** APN type for RCS (Rich Communication Services). */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final int TYPE_RCS = ApnTypes.RCS;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"TYPE_"}, value = {
+            TYPE_DEFAULT,
+            TYPE_MMS,
+            TYPE_SUPL,
+            TYPE_DUN,
+            TYPE_HIPRI,
+            TYPE_FOTA,
+            TYPE_IMS,
+            TYPE_CBS,
+            TYPE_IA,
+            TYPE_EMERGENCY,
+            TYPE_MCX,
+            TYPE_XCAP,
+            TYPE_BIP,
+            TYPE_VSIM,
+            TYPE_ENTERPRISE,
+            TYPE_RCS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApnType {
+    }
+
+    // Possible values for authentication types.
+    /**
+     * Authentication type is unknown.
+     * @hide
+     */
+    public static final int AUTH_TYPE_UNKNOWN = -1;
+    /** Authentication is not required. */
+    public static final int AUTH_TYPE_NONE = 0;
+    /** Authentication type for PAP. */
+    public static final int AUTH_TYPE_PAP = 1;
+    /** Authentication type for CHAP. */
+    public static final int AUTH_TYPE_CHAP = 2;
+    /** Authentication type for PAP or CHAP. */
+    public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
+
+    /** @hide */
+    @IntDef({
+            Telephony.Carriers.SKIP_464XLAT_DEFAULT,
+            Telephony.Carriers.SKIP_464XLAT_DISABLE,
+            Telephony.Carriers.SKIP_464XLAT_ENABLE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Skip464XlatStatus {}
+
+    /** @hide */
+    @StringDef(value = {
+            TYPE_ALL_STRING,
+            TYPE_CBS_STRING,
+            TYPE_DEFAULT_STRING,
+            TYPE_DUN_STRING,
+            TYPE_EMERGENCY_STRING,
+            TYPE_FOTA_STRING,
+            TYPE_HIPRI_STRING,
+            TYPE_IA_STRING,
+            TYPE_IMS_STRING,
+            TYPE_MCX_STRING,
+            TYPE_MMS_STRING,
+            TYPE_SUPL_STRING,
+            TYPE_XCAP_STRING,
+            TYPE_VSIM_STRING,
+            TYPE_BIP_STRING,
+            TYPE_ENTERPRISE_STRING,
+    }, prefix = "TYPE_", suffix = "_STRING")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApnTypeString {}
+
+    /**
+     * APN types for data connections.  These are usage categories for an APN
+     * entry.  One APN entry may support multiple APN types, eg, a single APN
+     * may service regular internet traffic ("default") as well as MMS-specific
+     * connections.<br/>
+     * APN_TYPE_ALL is a special type to indicate that this APN entry can
+     * service all data connections.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_ALL_STRING = "*";
+
+    /**
+     * APN type for default data traffic
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_DEFAULT_STRING = "default";
+
+
+    /**
+     * APN type for MMS (Multimedia Messaging Service) traffic.
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_MMS_STRING = "mms";
+
+
+    /**
+     * APN type for SUPL (Secure User Plane Location) assisted GPS.
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_SUPL_STRING = "supl";
+
+    /**
+     * APN type for DUN (Dial-up networking) traffic
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_DUN_STRING = "dun";
+
+    /**
+     * APN type for high-priority traffic
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_HIPRI_STRING = "hipri";
+
+    /**
+     * APN type for FOTA (Firmware over-the-air) traffic.
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_FOTA_STRING = "fota";
+
+    /**
+     * APN type for IMS (IP Multimedia Subsystem) traffic.
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_IMS_STRING = "ims";
+
+    /**
+     * APN type for CBS (Carrier Branded Services) traffic.
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_CBS_STRING = "cbs";
+
+    /**
+     * APN type for the IA (Initial Attach) APN
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_IA_STRING = "ia";
+
+    /**
+     * APN type for Emergency PDN. This is not an IA apn, but is used
+     * for access to carrier services in an emergency call situation.
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_EMERGENCY_STRING = "emergency";
+
+    /**
+     * APN type for Mission Critical Services.
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_MCX_STRING = "mcx";
+
+    /**
+     * APN type for XCAP (XML Configuration Access Protocol) traffic.
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_XCAP_STRING = "xcap";
+
+    /**
+     * APN type for Virtual SIM service.
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_VSIM_STRING = "vsim";
+
+    /**
+     * APN type for Bearer Independent Protocol.
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_BIP_STRING = "bip";
+
+    /**
+     * APN type for ENTERPRISE traffic.
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @SystemApi
+    public static final String TYPE_ENTERPRISE_STRING = "enterprise";
+
+    /**
+     * APN type for RCS (Rich Communication Services)
+     *
+     * Note: String representations of APN types are intended for system apps to communicate with
+     * modem components or carriers. Non-system apps should use the integer variants instead.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    @SystemApi
+    public static final String TYPE_RCS_STRING = "rcs";
+
+
+    /** @hide */
+    @IntDef(prefix = { "AUTH_TYPE_" }, value = {
+        AUTH_TYPE_UNKNOWN,
+        AUTH_TYPE_NONE,
+        AUTH_TYPE_PAP,
+        AUTH_TYPE_CHAP,
+        AUTH_TYPE_PAP_OR_CHAP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AuthType {}
+
+    // Possible values for protocol which is defined in TS 27.007 section 10.1.1.
+    /** Unknown protocol.
+     * @hide
+     */
+    public static final int PROTOCOL_UNKNOWN = -1;
+    /** Internet protocol. */
+    public static final int PROTOCOL_IP = 0;
+    /** Internet protocol, version 6. */
+    public static final int PROTOCOL_IPV6 = 1;
+    /** Virtual PDP type introduced to handle dual IP stack UE capability. */
+    public static final int PROTOCOL_IPV4V6 = 2;
+    /** Point to point protocol. */
+    public static final int PROTOCOL_PPP = 3;
+    /** Transfer of Non-IP data to external packet data network. */
+    public static final int PROTOCOL_NON_IP = 4;
+    /** Transfer of Unstructured data to the Data Network via N6. */
+    public static final int PROTOCOL_UNSTRUCTURED = 5;
+
+    /** @hide */
+    @IntDef(prefix = { "PROTOCOL_" }, value = {
+        PROTOCOL_IP,
+        PROTOCOL_IPV6,
+        PROTOCOL_IPV4V6,
+        PROTOCOL_PPP,
+        PROTOCOL_NON_IP,
+        PROTOCOL_UNSTRUCTURED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProtocolType {}
+
+    // Possible values for MVNO type.
+    /**
+     * Default value for MVNO type if it's not set.
+     * @hide
+     */
+    public static final int MVNO_TYPE_UNKNOWN = -1;
+    /** MVNO type for service provider name. */
+    public static final int MVNO_TYPE_SPN = 0;
+    /** MVNO type for IMSI. */
+    public static final int MVNO_TYPE_IMSI = 1;
+    /** MVNO type for group identifier level 1. */
+    public static final int MVNO_TYPE_GID = 2;
+    /** MVNO type for ICCID. */
+    public static final int MVNO_TYPE_ICCID = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "MVNO_TYPE_" }, value = {
+            MVNO_TYPE_UNKNOWN,
+            MVNO_TYPE_SPN,
+            MVNO_TYPE_IMSI,
+            MVNO_TYPE_GID,
+            MVNO_TYPE_ICCID,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MvnoType {}
+
+    /**
+     * Indicating this APN can be used when the device is using terrestrial cellular networks.
+     * @hide
+     */
+    public static final int INFRASTRUCTURE_CELLULAR = 1 << 0;
+
+    /**
+     * Indicating this APN can be used when the device is attached to satellites.
+     * @hide
+     */
+    public static final int INFRASTRUCTURE_SATELLITE = 1 << 1;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "INFRASTRUCTURE_" }, value = {
+            INFRASTRUCTURE_CELLULAR,
+            INFRASTRUCTURE_SATELLITE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InfrastructureBitmask {}
+
+    private static final Map<String, Integer> APN_TYPE_STRING_MAP;
+    private static final Map<Integer, String> APN_TYPE_INT_MAP;
+    private static final Map<String, Integer> PROTOCOL_STRING_MAP;
+    private static final Map<Integer, String> PROTOCOL_INT_MAP;
+    private static final Map<String, Integer> MVNO_TYPE_STRING_MAP;
+    private static final Map<Integer, String> MVNO_TYPE_INT_MAP;
+
+    static {
+        APN_TYPE_STRING_MAP = new ArrayMap<>();
+        APN_TYPE_STRING_MAP.put(TYPE_ALL_STRING, TYPE_ALL);
+        APN_TYPE_STRING_MAP.put(TYPE_DEFAULT_STRING, TYPE_DEFAULT);
+        APN_TYPE_STRING_MAP.put(TYPE_MMS_STRING, TYPE_MMS);
+        APN_TYPE_STRING_MAP.put(TYPE_SUPL_STRING, TYPE_SUPL);
+        APN_TYPE_STRING_MAP.put(TYPE_DUN_STRING, TYPE_DUN);
+        APN_TYPE_STRING_MAP.put(TYPE_HIPRI_STRING, TYPE_HIPRI);
+        APN_TYPE_STRING_MAP.put(TYPE_FOTA_STRING, TYPE_FOTA);
+        APN_TYPE_STRING_MAP.put(TYPE_IMS_STRING, TYPE_IMS);
+        APN_TYPE_STRING_MAP.put(TYPE_CBS_STRING, TYPE_CBS);
+        APN_TYPE_STRING_MAP.put(TYPE_IA_STRING, TYPE_IA);
+        APN_TYPE_STRING_MAP.put(TYPE_EMERGENCY_STRING, TYPE_EMERGENCY);
+        APN_TYPE_STRING_MAP.put(TYPE_MCX_STRING, TYPE_MCX);
+        APN_TYPE_STRING_MAP.put(TYPE_XCAP_STRING, TYPE_XCAP);
+        APN_TYPE_STRING_MAP.put(TYPE_ENTERPRISE_STRING, TYPE_ENTERPRISE);
+        APN_TYPE_STRING_MAP.put(TYPE_VSIM_STRING, TYPE_VSIM);
+        APN_TYPE_STRING_MAP.put(TYPE_BIP_STRING, TYPE_BIP);
+        APN_TYPE_STRING_MAP.put(TYPE_RCS_STRING, TYPE_RCS);
+
+        APN_TYPE_INT_MAP = new ArrayMap<>();
+        APN_TYPE_INT_MAP.put(TYPE_DEFAULT, TYPE_DEFAULT_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_MMS, TYPE_MMS_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_SUPL, TYPE_SUPL_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_DUN, TYPE_DUN_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_HIPRI, TYPE_HIPRI_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_FOTA, TYPE_FOTA_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_IMS, TYPE_IMS_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_CBS, TYPE_CBS_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_IA, TYPE_IA_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, TYPE_EMERGENCY_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_MCX, TYPE_MCX_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_XCAP, TYPE_XCAP_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_ENTERPRISE, TYPE_ENTERPRISE_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_VSIM, TYPE_VSIM_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_BIP, TYPE_BIP_STRING);
+        APN_TYPE_INT_MAP.put(TYPE_RCS, TYPE_RCS_STRING);
+
+        PROTOCOL_STRING_MAP = new ArrayMap<>();
+        PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
+        PROTOCOL_STRING_MAP.put("IPV6", PROTOCOL_IPV6);
+        PROTOCOL_STRING_MAP.put("IPV4V6", PROTOCOL_IPV4V6);
+        PROTOCOL_STRING_MAP.put("PPP", PROTOCOL_PPP);
+        PROTOCOL_STRING_MAP.put("NON-IP", PROTOCOL_NON_IP);
+        PROTOCOL_STRING_MAP.put("UNSTRUCTURED", PROTOCOL_UNSTRUCTURED);
+
+        PROTOCOL_INT_MAP = new ArrayMap<>();
+        PROTOCOL_INT_MAP.put(PROTOCOL_IP, "IP");
+        PROTOCOL_INT_MAP.put(PROTOCOL_IPV6, "IPV6");
+        PROTOCOL_INT_MAP.put(PROTOCOL_IPV4V6, "IPV4V6");
+        PROTOCOL_INT_MAP.put(PROTOCOL_PPP, "PPP");
+        PROTOCOL_INT_MAP.put(PROTOCOL_NON_IP, "NON-IP");
+        PROTOCOL_INT_MAP.put(PROTOCOL_UNSTRUCTURED, "UNSTRUCTURED");
+
+        MVNO_TYPE_STRING_MAP = new ArrayMap<>();
+        MVNO_TYPE_STRING_MAP.put("spn", MVNO_TYPE_SPN);
+        MVNO_TYPE_STRING_MAP.put("imsi", MVNO_TYPE_IMSI);
+        MVNO_TYPE_STRING_MAP.put("gid", MVNO_TYPE_GID);
+        MVNO_TYPE_STRING_MAP.put("iccid", MVNO_TYPE_ICCID);
+
+        MVNO_TYPE_INT_MAP = new ArrayMap<>();
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_SPN, "spn");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_IMSI, "imsi");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_GID, "gid");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_ICCID, "iccid");
+    }
+
+    private final String mEntryName;
+    private final String mApnName;
+    private final String mProxyAddress;
+    private final int mProxyPort;
+    private final Uri mMmsc;
+    private final String mMmsProxyAddress;
+    private final int mMmsProxyPort;
+    private final String mUser;
+    private final String mPassword;
+    private final int mAuthType;
+    private final int mApnTypeBitmask;
+    private final int mId;
+    private final String mOperatorNumeric;
+    private final int mProtocol;
+    private final int mRoamingProtocol;
+    private final int mMtuV4;
+    private final int mMtuV6;
+    private final boolean mCarrierEnabled;
+    private final @TelephonyManager.NetworkTypeBitMask int mNetworkTypeBitmask;
+    private final @TelephonyManager.NetworkTypeBitMask long mLingeringNetworkTypeBitmask;
+    private final int mProfileId;
+    private final boolean mPersistent;
+    private final int mMaxConns;
+    private final int mWaitTime;
+    private final int mMaxConnsTime;
+    private final int mMvnoType;
+    private final String mMvnoMatchData;
+    private final int mApnSetId;
+    private boolean mPermanentFailed = false;
+    private final int mCarrierId;
+    private final int mSkip464Xlat;
+    private final boolean mAlwaysOn;
+    private final @InfrastructureBitmask int mInfrastructureBitmask;
+    private final boolean mEsimBootstrapProvisioning;
+
+    /**
+     * The APN edited status.
+     *
+     * Note it is intended not using this field for {@link #equals(Object)} or {@link #hashCode()}.
+     */
+    private final @EditStatus int mEditedStatus;
+
+    /**
+     * Returns the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought
+     * up by this APN setting. Note this value will only be used when MTU size is not provided
+     * in {@code DataCallResponse#getMtuV4()} during network bring up.
+     *
+     * @return the MTU size in bytes of the route.
+     */
+    public int getMtuV4() {
+        return mMtuV4;
+    }
+
+    /**
+     * Returns the MTU size of the IPv6 mobile interface to which the APN connected. Note this value
+     * will only be used when MTU size is not provided in {@code DataCallResponse#getMtuV6()}
+     * during network bring up.
+     *
+     * @return the MTU size in bytes of the route.
+     */
+    public int getMtuV6() {
+        return mMtuV6;
+    }
+
+    /**
+     * Returns the profile id to which the APN saved in modem.
+     *
+     * @return the profile id of the APN
+     */
+    public int getProfileId() {
+        return mProfileId;
+    }
+
+    /**
+     * Returns if the APN setting is persistent on the modem.
+     *
+     * @return {@code true} if the APN setting is persistent on the modem.
+     */
+    public boolean isPersistent() {
+        return mPersistent;
+    }
+
+    /**
+     * Returns the max connections of this APN.
+     *
+     * @return the max connections of this APN
+     * @hide
+     */
+    public int getMaxConns() {
+        return mMaxConns;
+    }
+
+    /**
+     * Returns the wait time for retry of the APN.
+     *
+     * @return the wait time for retry of the APN
+     * @hide
+     */
+    public int getWaitTime() {
+        return mWaitTime;
+    }
+
+    /**
+     * Returns the time to limit max connection for the APN.
+     *
+     * @return the time to limit max connection for the APN
+     * @hide
+     */
+    public int getMaxConnsTime() {
+        return mMaxConnsTime;
+    }
+
+    /**
+     * Returns the MVNO data. Examples:
+     *   "spn": A MOBILE, BEN NL
+     *   "imsi": 302720x94, 2060188
+     *   "gid": 4E, 33
+     *   "iccid": 898603 etc..
+     *
+     * @return the mvno match data
+     * @hide
+     */
+    public String getMvnoMatchData() {
+        return mMvnoMatchData;
+    }
+
+    /**
+     * Returns the APN set id.
+     *
+     * APNs that are part of the same set should be preferred together, e.g. if the
+     * user selects a default APN with apnSetId=1, then we will prefer all APNs with apnSetId = 1.
+     *
+     * If the apnSetId = Carriers.NO_SET_SET(=0) then the APN is not part of a set.
+     *
+     * @return the APN set id
+     * @hide
+     */
+    public int getApnSetId() {
+        return mApnSetId;
+    }
+
+    /**
+     * Indicates this APN setting is permanently failed and cannot be
+     * retried by the retry manager anymore.
+     *
+     * @return if this APN setting is permanently failed
+     * @hide
+     */
+    public boolean getPermanentFailed() {
+        return mPermanentFailed;
+    }
+
+    /**
+     * Sets if this APN setting is permanently failed.
+     *
+     * @param permanentFailed if this APN setting is permanently failed
+     * @hide
+     */
+    public void setPermanentFailed(boolean permanentFailed) {
+        mPermanentFailed = permanentFailed;
+    }
+
+    /**
+     * Gets the human-readable name that describes the APN.
+     *
+     * @return the entry name for the APN
+     */
+    public String getEntryName() {
+        return mEntryName;
+    }
+
+    /**
+     * Returns the name of the APN.
+     *
+     * @return APN name
+     */
+    public String getApnName() {
+        return mApnName;
+    }
+
+    /**
+     * Gets the HTTP proxy address configured for the APN. The proxy address might be an IP address
+     * or hostname. This method returns {@code null} if system networking (typically DNS) isn’t
+     * available to resolve a hostname value—values set as IP addresses don’t have this restriction.
+     * This is a known problem and will be addressed in a future release.
+     *
+     * @return the HTTP proxy address or {@code null} if DNS isn’t available to resolve a hostname
+     * @deprecated use {@link #getProxyAddressAsString()} instead.
+     */
+    @Deprecated
+    public InetAddress getProxyAddress() {
+        return inetAddressFromString(mProxyAddress);
+    }
+
+    /**
+     * Returns the proxy address of the APN.
+     *
+     * @return proxy address.
+     */
+    public String getProxyAddressAsString() {
+        return mProxyAddress;
+    }
+
+    /**
+     * Returns the proxy address of the APN.
+     *
+     * @return proxy address.
+     */
+    public int getProxyPort() {
+        return mProxyPort;
+    }
+    /**
+     * Returns the MMSC Uri of the APN.
+     *
+     * @return MMSC Uri.
+     */
+    public Uri getMmsc() {
+        return mMmsc;
+    }
+
+    /**
+     * Gets the MMS proxy address configured for the APN. The MMS proxy address might be an IP
+     * address or hostname. This method returns {@code null} if system networking (typically DNS)
+     * isn’t available to resolve a hostname value—values set as IP addresses don’t have this
+     * restriction. This is a known problem and will be addressed in a future release.
+     *
+     * @return the MMS proxy address or {@code null} if DNS isn’t available to resolve a hostname
+     * @deprecated use {@link #getMmsProxyAddressAsString()} instead.
+     */
+    @Deprecated
+    public InetAddress getMmsProxyAddress() {
+        return inetAddressFromString(mMmsProxyAddress);
+    }
+
+    /**
+     * Returns the MMS proxy address of the APN.
+     *
+     * @return MMS proxy address.
+     */
+    public String getMmsProxyAddressAsString() {
+        return mMmsProxyAddress;
+    }
+
+    /**
+     * Returns the MMS proxy port of the APN.
+     *
+     * @return MMS proxy port
+     */
+    public int getMmsProxyPort() {
+        return mMmsProxyPort;
+    }
+
+    /**
+     * Returns the APN username of the APN.
+     *
+     * @return APN username
+     */
+    public String getUser() {
+        return mUser;
+    }
+
+    /**
+     * Returns the APN password of the APN.
+     *
+     * @return APN password
+     */
+    public String getPassword() {
+        return mPassword;
+    }
+
+    /**
+     * Returns the authentication type of the APN.
+     *
+     * @return authentication type
+     */
+    @AuthType
+    public int getAuthType() {
+        return mAuthType;
+    }
+
+    /**
+     * Returns the bitmask of APN types.
+     *
+     * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple
+     * APN types, eg, a single APN may service regular internet traffic ("default") as well as
+     * MMS-specific connections.
+     *
+     * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}.
+     *
+     * @see Builder#setApnTypeBitmask(int)
+     * @return a bitmask describing the types of the APN
+     */
+    public @ApnType int getApnTypeBitmask() {
+        return mApnTypeBitmask;
+    }
+
+    /**
+     * Returns the unique database id for this entry.
+     *
+     * @return the unique database id
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the numeric operator ID for the APN. Numeric operator ID is defined as
+     * {@link android.provider.Telephony.Carriers#MCC} +
+     * {@link android.provider.Telephony.Carriers#MNC}.
+     *
+     * @return the numeric operator ID
+     */
+    public String getOperatorNumeric() {
+        return mOperatorNumeric;
+    }
+
+    /**
+     * Returns the protocol to use to connect to this APN.
+     *
+     * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
+     *
+     * @see Builder#setProtocol(int)
+     * @return the protocol
+     */
+    @ProtocolType
+    public int getProtocol() {
+        return mProtocol;
+    }
+
+    /**
+     * Returns the protocol to use to connect to this APN while the device is roaming.
+     *
+     * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
+     *
+     * @see Builder#setRoamingProtocol(int)
+     * @return the roaming protocol
+     */
+    @ProtocolType
+    public int getRoamingProtocol() {
+        return mRoamingProtocol;
+    }
+
+    /**
+     * Returns the current status of APN.
+     *
+     * {@code true} : enabled APN.
+     * {@code false} : disabled APN.
+     *
+     * @return the current status
+     */
+    public boolean isEnabled() {
+        return mCarrierEnabled;
+    }
+
+    /**
+     * Returns a bitmask describing the Radio Technologies (Network Types) which this APN may use.
+     *
+     * NetworkType bitmask is calculated from NETWORK_TYPE defined in {@link TelephonyManager}.
+     *
+     * Examples of Network Types include {@link TelephonyManager#NETWORK_TYPE_UNKNOWN},
+     * {@link TelephonyManager#NETWORK_TYPE_GPRS}, {@link TelephonyManager#NETWORK_TYPE_EDGE}.
+     *
+     * @return a bitmask describing the Radio Technologies (Network Types) or 0 if it is undefined.
+     */
+    public int getNetworkTypeBitmask() {
+        return mNetworkTypeBitmask;
+    }
+
+    /**
+     * Returns a bitmask describing the Radio Technologies (Network Types) that should not be torn
+     * down if it exists or brought up if it already exists for this APN.
+     *
+     * NetworkType bitmask is calculated from NETWORK_TYPE defined in {@link TelephonyManager}.
+     *
+     * Examples of Network Types include {@link TelephonyManager#NETWORK_TYPE_UNKNOWN},
+     * {@link TelephonyManager#NETWORK_TYPE_GPRS}, {@link TelephonyManager#NETWORK_TYPE_EDGE}.
+     *
+     * @return a bitmask describing the Radio Technologies (Network Types) that should linger
+     *         or 0 if it is undefined.
+     * @hide
+     */
+    public @TelephonyManager.NetworkTypeBitMask long getLingeringNetworkTypeBitmask() {
+        return mLingeringNetworkTypeBitmask;
+    }
+
+    /**
+     * Returns the MVNO match type for this APN.
+     *
+     * @see Builder#setMvnoType(int)
+     * @return the MVNO match type
+     */
+    @MvnoType
+    public int getMvnoType() {
+        return mMvnoType;
+    }
+
+    /**
+     * Returns the carrier id for this APN.
+     *
+     * @see Builder#setCarrierId(int)
+     * @return the carrier id
+     */
+    public int getCarrierId() {
+        return mCarrierId;
+    }
+
+    /**
+     * Returns the skip464xlat flag for this APN.
+     *
+     * @return SKIP_464XLAT_DEFAULT, SKIP_464XLAT_DISABLE or SKIP_464XLAT_ENABLE
+     * @hide
+     */
+    @Skip464XlatStatus
+    public int getSkip464Xlat() {
+        return mSkip464Xlat;
+    }
+
+    /**
+     * Returns whether User Plane resources have to be activated during every transition from
+     * CM-IDLE mode to CM-CONNECTED state for this APN
+     * See 3GPP TS 23.501 section 5.6.13
+     *
+     * @return True if the PDU session for this APN should always be on and false otherwise
+     */
+    @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
+    public boolean isAlwaysOn() {
+        return mAlwaysOn;
+    }
+
+    /**
+     * Check if this APN can be used when the device is using certain infrastructure(s).
+     *
+     * @param infrastructures The infrastructure(s) the device is using.
+     *
+     * @return {@code true} if this APN can be used.
+     * @hide
+     */
+    public boolean isForInfrastructure(@InfrastructureBitmask int infrastructures) {
+        return (mInfrastructureBitmask & infrastructures) != 0;
+    }
+
+    /**
+     * @return The infrastructure bitmask of which the APN can be used on. For example, some APNs
+     * can only be used when the device is on cellular, on satellite, or both.
+     *
+     * @hide
+     */
+    @InfrastructureBitmask
+    public int getInfrastructureBitmask() {
+        return mInfrastructureBitmask;
+    }
+
+    /**
+     * Returns esim bootstrap provisioning flag for which the APN can be used on. For example,
+     * some APNs are only allowed to bring up network, when the device esim bootstrap provisioning
+     * is being activated.
+     *
+     * {@code true} if the APN is used for eSIM bootstrap provisioning, {@code false} otherwise.
+     * @hide
+     */
+    public boolean isEsimBootstrapProvisioning() {
+        return mEsimBootstrapProvisioning;
+    }
+
+    /**
+     * @return APN edited status. APN could be added/edited/deleted by a user or carrier.
+     *
+     * @see Carriers#UNEDITED
+     * @see Carriers#USER_EDITED
+     * @see Carriers#USER_DELETED
+     * @see Carriers#CARRIER_EDITED
+     * @see Carriers#CARRIER_DELETED
+     *
+     * @hide
+     */
+    @EditStatus
+    public int getEditedStatus() {
+        return mEditedStatus;
+    }
+
+    private ApnSetting(Builder builder) {
+        this.mEntryName = builder.mEntryName;
+        this.mApnName = builder.mApnName;
+        this.mProxyAddress = builder.mProxyAddress;
+        this.mProxyPort = builder.mProxyPort;
+        this.mMmsc = builder.mMmsc;
+        this.mMmsProxyAddress = builder.mMmsProxyAddress;
+        this.mMmsProxyPort = builder.mMmsProxyPort;
+        this.mUser = builder.mUser;
+        this.mPassword = builder.mPassword;
+        this.mAuthType = (builder.mAuthType != AUTH_TYPE_UNKNOWN)
+                ? builder.mAuthType
+                : TextUtils.isEmpty(builder.mUser)
+                        ? AUTH_TYPE_NONE
+                        : AUTH_TYPE_PAP_OR_CHAP;
+        this.mApnTypeBitmask = builder.mApnTypeBitmask;
+        this.mId = builder.mId;
+        this.mOperatorNumeric = builder.mOperatorNumeric;
+        this.mProtocol = builder.mProtocol;
+        this.mRoamingProtocol = builder.mRoamingProtocol;
+        this.mMtuV4 = builder.mMtuV4;
+        this.mMtuV6 = builder.mMtuV6;
+        this.mCarrierEnabled = builder.mCarrierEnabled;
+        this.mNetworkTypeBitmask = builder.mNetworkTypeBitmask;
+        this.mLingeringNetworkTypeBitmask = builder.mLingeringNetworkTypeBitmask;
+        this.mProfileId = builder.mProfileId;
+        this.mPersistent = builder.mModemCognitive;
+        this.mMaxConns = builder.mMaxConns;
+        this.mWaitTime = builder.mWaitTime;
+        this.mMaxConnsTime = builder.mMaxConnsTime;
+        this.mMvnoType = builder.mMvnoType;
+        this.mMvnoMatchData = builder.mMvnoMatchData;
+        this.mApnSetId = builder.mApnSetId;
+        this.mCarrierId = builder.mCarrierId;
+        this.mSkip464Xlat = builder.mSkip464Xlat;
+        this.mAlwaysOn = builder.mAlwaysOn;
+        this.mInfrastructureBitmask = builder.mInfrastructureBitmask;
+        this.mEsimBootstrapProvisioning = builder.mEsimBootstrapProvisioning;
+        this.mEditedStatus = builder.mEditedStatus;
+    }
+
+    /**
+     * @hide
+     */
+    public static ApnSetting makeApnSetting(Cursor cursor) {
+        final int apnTypesBitmask = getApnTypesBitmaskFromString(
+                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
+        int networkTypeBitmask = cursor.getInt(
+                cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
+        if (networkTypeBitmask == 0) {
+            final int bearerBitmask = cursor.getInt(cursor.getColumnIndexOrThrow(
+                    Telephony.Carriers.BEARER_BITMASK));
+            networkTypeBitmask =
+                ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask);
+        }
+        int mtuV4 = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU_V4));
+        if (mtuV4 == UNSET_MTU) {
+            mtuV4 = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU));
+        }
+
+        return new Builder()
+                .setId(cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)))
+                .setOperatorNumeric(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)))
+                .setEntryName(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)))
+                .setApnName(cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)))
+                .setProxyAddress(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)))
+                .setProxyPort(portFromString(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT))))
+                .setMmsc(UriFromString(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))))
+                .setMmsProxyAddress(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)))
+                .setMmsProxyPort(portFromString(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT))))
+                .setUser(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)))
+                .setPassword(cursor.getString(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)))
+                .setAuthType(cursor.getInt(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)))
+                .setApnTypeBitmask(apnTypesBitmask)
+                .setProtocol(getProtocolIntFromString(
+                        cursor.getString(
+                                cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL))))
+                .setRoamingProtocol(getProtocolIntFromString(
+                        cursor.getString(cursor.getColumnIndexOrThrow(
+                                Telephony.Carriers.ROAMING_PROTOCOL))))
+                .setCarrierEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.CARRIER_ENABLED)) == 1)
+                .setNetworkTypeBitmask(networkTypeBitmask)
+                .setLingeringNetworkTypeBitmask(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        Carriers.LINGERING_NETWORK_TYPE_BITMASK)))
+                .setProfileId(cursor.getInt(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)))
+                .setModemCognitive(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.MODEM_PERSIST)) == 1)
+                .setMaxConns(cursor.getInt(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNECTIONS)))
+                .setWaitTime(cursor.getInt(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.WAIT_TIME_RETRY)))
+                .setMaxConnsTime(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS)))
+                .setMtuV4(mtuV4)
+                .setMtuV6(cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU_V6)))
+                .setMvnoType(getMvnoTypeIntFromString(
+                        cursor.getString(cursor.getColumnIndexOrThrow(
+                                Telephony.Carriers.MVNO_TYPE))))
+                .setMvnoMatchData(cursor.getString(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.MVNO_MATCH_DATA)))
+                .setApnSetId(cursor.getInt(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.APN_SET_ID)))
+                .setCarrierId(cursor.getInt(
+                        cursor.getColumnIndexOrThrow(Telephony.Carriers.CARRIER_ID)))
+                .setSkip464Xlat(cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.SKIP_464XLAT)))
+                .setAlwaysOn(cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.ALWAYS_ON)) == 1)
+                .setInfrastructureBitmask(cursor.getInt(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.INFRASTRUCTURE_BITMASK)))
+                .setEsimBootstrapProvisioning(cursor.getInt(
+                        cursor.getColumnIndexOrThrow(Carriers.ESIM_BOOTSTRAP_PROVISIONING)) == 1)
+                .setEditedStatus(cursor.getInt(
+                        cursor.getColumnIndexOrThrow(Carriers.EDITED_STATUS)))
+                .buildWithoutCheck();
+    }
+
+    /**
+     * @hide
+     */
+    public static ApnSetting makeApnSetting(ApnSetting apn) {
+        return new Builder()
+                .setId(apn.mId)
+                .setOperatorNumeric(apn.mOperatorNumeric)
+                .setEntryName(apn.mEntryName)
+                .setApnName(apn.mApnName)
+                .setProxyAddress(apn.mProxyAddress)
+                .setProxyPort(apn.mProxyPort)
+                .setMmsc(apn.mMmsc)
+                .setMmsProxyAddress(apn.mMmsProxyAddress)
+                .setMmsProxyPort(apn.mMmsProxyPort)
+                .setUser(apn.mUser)
+                .setPassword(apn.mPassword)
+                .setAuthType(apn.mAuthType)
+                .setApnTypeBitmask(apn.mApnTypeBitmask)
+                .setProtocol(apn.mProtocol)
+                .setRoamingProtocol(apn.mRoamingProtocol)
+                .setCarrierEnabled(apn.mCarrierEnabled)
+                .setNetworkTypeBitmask(apn.mNetworkTypeBitmask)
+                .setLingeringNetworkTypeBitmask(apn.mLingeringNetworkTypeBitmask)
+                .setProfileId(apn.mProfileId)
+                .setModemCognitive(apn.mPersistent)
+                .setMaxConns(apn.mMaxConns)
+                .setWaitTime(apn.mWaitTime)
+                .setMaxConnsTime(apn.mMaxConnsTime)
+                .setMtuV4(apn.mMtuV4)
+                .setMtuV6(apn.mMtuV6)
+                .setMvnoType(apn.mMvnoType)
+                .setMvnoMatchData(apn.mMvnoMatchData)
+                .setApnSetId(apn.mApnSetId)
+                .setCarrierId(apn.mCarrierId)
+                .setSkip464Xlat(apn.mSkip464Xlat)
+                .setAlwaysOn(apn.mAlwaysOn)
+                .setInfrastructureBitmask(apn.mInfrastructureBitmask)
+                .setEsimBootstrapProvisioning(apn.mEsimBootstrapProvisioning)
+                .setEditedStatus(apn.mEditedStatus)
+                .buildWithoutCheck();
+    }
+
+    /**
+     * Returns the string representation of ApnSetting.
+     *
+     * This method prints null for unset elements. The output doesn't contain password or user.
+     * @hide
+     */
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[ApnSetting] ")
+                .append(mEntryName)
+                .append(", ").append(mId)
+                .append(", ").append(mOperatorNumeric)
+                .append(", ").append(mApnName)
+                .append(", ").append(mProxyAddress)
+                .append(", ").append(UriToString(mMmsc))
+                .append(", ").append(mMmsProxyAddress)
+                .append(", ").append(portToString(mMmsProxyPort))
+                .append(", ").append(portToString(mProxyPort))
+                .append(", ").append(mAuthType).append(", ");
+        final String[] types = getApnTypesStringFromBitmask(mApnTypeBitmask).split(",");
+        sb.append(TextUtils.join(" | ", types));
+        sb.append(", ").append(PROTOCOL_INT_MAP.get(mProtocol));
+        sb.append(", ").append(PROTOCOL_INT_MAP.get(mRoamingProtocol));
+        sb.append(", ").append(mCarrierEnabled);
+        sb.append(", ").append(mProfileId);
+        sb.append(", ").append(mPersistent);
+        sb.append(", ").append(mMaxConns);
+        sb.append(", ").append(mWaitTime);
+        sb.append(", ").append(mMaxConnsTime);
+        sb.append(", ").append(mMtuV4);
+        sb.append(", ").append(mMtuV6);
+        sb.append(", ").append(MVNO_TYPE_INT_MAP.get(mMvnoType));
+        sb.append(", ").append(mMvnoMatchData);
+        sb.append(", ").append(mPermanentFailed);
+        sb.append(", ").append(TelephonyManager.convertNetworkTypeBitmaskToString(
+                mNetworkTypeBitmask));
+        sb.append(", ").append(TelephonyManager.convertNetworkTypeBitmaskToString(
+                mLingeringNetworkTypeBitmask));
+        sb.append(", ").append(mApnSetId);
+        sb.append(", ").append(mCarrierId);
+        sb.append(", ").append(mSkip464Xlat);
+        sb.append(", ").append(mAlwaysOn);
+        sb.append(", ").append(mInfrastructureBitmask);
+        sb.append(", ").append(Objects.hash(mUser, mPassword));
+        sb.append(", ").append(mEsimBootstrapProvisioning);
+        sb.append(", ").append(TelephonyUtils.apnEditedStatusToString(mEditedStatus));
+        return sb.toString();
+    }
+
+    /**
+     * Returns true if there are MVNO params specified.
+     * @hide
+     */
+    public boolean hasMvnoParams() {
+        return !TextUtils.isEmpty(getMvnoTypeStringFromInt(mMvnoType))
+            && !TextUtils.isEmpty(mMvnoMatchData);
+    }
+
+    private boolean hasApnType(int type) {
+        return (mApnTypeBitmask & type) == type;
+    }
+
+    /** @hide */
+    public boolean isEmergencyApn() {
+        return hasApnType(TYPE_EMERGENCY);
+    }
+
+    /** @hide */
+    public boolean canHandleType(@ApnType int type) {
+        if (!mCarrierEnabled) {
+            return false;
+        }
+        // DEFAULT can handle HIPRI.
+        return hasApnType(type);
+    }
+
+    // Check whether the types of two APN same (even only one type of each APN is same).
+    private boolean typeSameAny(ApnSetting first, ApnSetting second) {
+        if (VDBG) {
+            StringBuilder apnType1 = new StringBuilder(first.mApnName + ": ");
+            apnType1.append(getApnTypesStringFromBitmask(first.mApnTypeBitmask));
+
+            StringBuilder apnType2 = new StringBuilder(second.mApnName + ": ");
+            apnType2.append(getApnTypesStringFromBitmask(second.mApnTypeBitmask));
+
+            Rlog.d(LOG_TAG, "APN1: is " + apnType1);
+            Rlog.d(LOG_TAG, "APN2: is " + apnType2);
+        }
+
+        if ((first.mApnTypeBitmask & second.mApnTypeBitmask) != 0) {
+            if (VDBG) {
+                Rlog.d(LOG_TAG, "typeSameAny: return true");
+            }
+            return true;
+        }
+
+        if (VDBG) {
+            Rlog.d(LOG_TAG, "typeSameAny: return false");
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mApnName, mProxyAddress, mProxyPort, mMmsc, mMmsProxyAddress,
+                mMmsProxyPort, mUser, mPassword, mAuthType, mApnTypeBitmask, mId, mOperatorNumeric,
+                mProtocol, mRoamingProtocol, mMtuV4, mMtuV6, mCarrierEnabled, mNetworkTypeBitmask,
+                mLingeringNetworkTypeBitmask, mProfileId, mPersistent, mMaxConns, mWaitTime,
+                mMaxConnsTime, mMvnoType, mMvnoMatchData, mApnSetId, mCarrierId, mSkip464Xlat,
+                mAlwaysOn, mInfrastructureBitmask, mEsimBootstrapProvisioning);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof ApnSetting == false) {
+            return false;
+        }
+
+        ApnSetting other = (ApnSetting) o;
+
+        return mEntryName.equals(other.mEntryName)
+                && mId == other.mId
+                && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
+                && Objects.equals(mApnName, other.mApnName)
+                && Objects.equals(mProxyAddress, other.mProxyAddress)
+                && Objects.equals(mMmsc, other.mMmsc)
+                && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
+                && mMmsProxyPort == other.mMmsProxyPort
+                && mProxyPort == other.mProxyPort
+                && Objects.equals(mUser, other.mUser)
+                && Objects.equals(mPassword, other.mPassword)
+                && mAuthType == other.mAuthType
+                && mApnTypeBitmask == other.mApnTypeBitmask
+                && mProtocol == other.mProtocol
+                && mRoamingProtocol == other.mRoamingProtocol
+                && mCarrierEnabled == other.mCarrierEnabled
+                && mProfileId == other.mProfileId
+                && mPersistent == other.mPersistent
+                && mMaxConns == other.mMaxConns
+                && mWaitTime == other.mWaitTime
+                && mMaxConnsTime == other.mMaxConnsTime
+                && mMtuV4 == other.mMtuV4
+                && mMtuV6 == other.mMtuV6
+                && mMvnoType == other.mMvnoType
+                && Objects.equals(mMvnoMatchData, other.mMvnoMatchData)
+                && mNetworkTypeBitmask == other.mNetworkTypeBitmask
+                && mLingeringNetworkTypeBitmask == other.mLingeringNetworkTypeBitmask
+                && mApnSetId == other.mApnSetId
+                && mCarrierId == other.mCarrierId
+                && mSkip464Xlat == other.mSkip464Xlat
+                && mAlwaysOn == other.mAlwaysOn
+                && mInfrastructureBitmask == other.mInfrastructureBitmask
+                && mEsimBootstrapProvisioning == other.mEsimBootstrapProvisioning;
+    }
+
+    /**
+     * Compare two APN settings
+     *
+     * Note: This method does not compare 'mId', 'mNetworkTypeBitmask'. We only use this for
+     * determining if tearing a data call is needed when conditions change. See
+     * cleanUpConnectionsOnUpdatedApns in DcTracker.
+     *
+     * @param o the other object to compare
+     * @param isDataRoaming True if the device is on data roaming
+     * @return True if the two APN settings are same
+     * @hide
+     */
+    public boolean equals(Object o, boolean isDataRoaming) {
+        if (!(o instanceof ApnSetting)) {
+            return false;
+        }
+
+        ApnSetting other = (ApnSetting) o;
+
+        return mEntryName.equals(other.mEntryName)
+                && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
+                && Objects.equals(mApnName, other.mApnName)
+                && Objects.equals(mProxyAddress, other.mProxyAddress)
+                && Objects.equals(mMmsc, other.mMmsc)
+                && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
+                && Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
+                && Objects.equals(mProxyPort, other.mProxyPort)
+                && Objects.equals(mUser, other.mUser)
+                && Objects.equals(mPassword, other.mPassword)
+                && Objects.equals(mAuthType, other.mAuthType)
+                && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
+                && Objects.equals(mLingeringNetworkTypeBitmask, other.mLingeringNetworkTypeBitmask)
+                && (isDataRoaming || Objects.equals(mProtocol, other.mProtocol))
+                && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol))
+                && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
+                && Objects.equals(mProfileId, other.mProfileId)
+                && Objects.equals(mPersistent, other.mPersistent)
+                && Objects.equals(mMaxConns, other.mMaxConns)
+                && Objects.equals(mWaitTime, other.mWaitTime)
+                && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
+                && Objects.equals(mMtuV4, other.mMtuV4)
+                && Objects.equals(mMtuV6, other.mMtuV6)
+                && Objects.equals(mMvnoType, other.mMvnoType)
+                && Objects.equals(mMvnoMatchData, other.mMvnoMatchData)
+                && Objects.equals(mApnSetId, other.mApnSetId)
+                && Objects.equals(mCarrierId, other.mCarrierId)
+                && Objects.equals(mSkip464Xlat, other.mSkip464Xlat)
+                && Objects.equals(mAlwaysOn, other.mAlwaysOn)
+                && Objects.equals(mInfrastructureBitmask, other.mInfrastructureBitmask)
+                && Objects.equals(mEsimBootstrapProvisioning, other.mEsimBootstrapProvisioning);
+    }
+
+    /**
+     * Check if neither mention DUN and are substantially similar
+     *
+     * @param other The other APN settings to compare
+     * @return True if two APN settings are similar
+     * @hide
+     */
+    public boolean similar(ApnSetting other) {
+        return (!this.canHandleType(TYPE_DUN)
+                && !other.canHandleType(TYPE_DUN)
+                && Objects.equals(this.mApnName, other.mApnName)
+                && xorEqualsString(this.mProxyAddress, other.mProxyAddress)
+                && xorEqualsInt(this.mProxyPort, other.mProxyPort)
+                && xorEquals(this.mMmsc, other.mMmsc)
+                && xorEqualsString(this.mMmsProxyAddress, other.mMmsProxyAddress)
+                && xorEqualsInt(this.mMmsProxyPort, other.mMmsProxyPort))
+                && xorEqualsString(this.mUser, other.mUser)
+                && xorEqualsString(this.mPassword, other.mPassword)
+                && Objects.equals(this.mAuthType, other.mAuthType)
+                && !typeSameAny(this, other)
+                && Objects.equals(this.mOperatorNumeric, other.mOperatorNumeric)
+                && Objects.equals(this.mProtocol, other.mProtocol)
+                && Objects.equals(this.mRoamingProtocol, other.mRoamingProtocol)
+                && mtuUnsetOrEquals(this.mMtuV4, other.mMtuV4)
+                && mtuUnsetOrEquals(this.mMtuV6, other.mMtuV6)
+                && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
+                && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask)
+                && Objects.equals(this.mLingeringNetworkTypeBitmask,
+                other.mLingeringNetworkTypeBitmask)
+                && Objects.equals(this.mProfileId, other.mProfileId)
+                && Objects.equals(this.mPersistent, other.mPersistent)
+                && Objects.equals(this.mApnSetId, other.mApnSetId)
+                && Objects.equals(this.mCarrierId, other.mCarrierId)
+                && Objects.equals(this.mSkip464Xlat, other.mSkip464Xlat)
+                && Objects.equals(this.mAlwaysOn, other.mAlwaysOn)
+                && Objects.equals(this.mInfrastructureBitmask, other.mInfrastructureBitmask)
+                && Objects.equals(this.mEsimBootstrapProvisioning,
+                other.mEsimBootstrapProvisioning);
+    }
+
+    // Equal or one is null.
+    private boolean xorEquals(Object first, Object second) {
+        return first == null || second == null || first.equals(second);
+    }
+
+    // Equal or one is null.
+    private boolean xorEqualsString(String first, String second) {
+        return TextUtils.isEmpty(first) || TextUtils.isEmpty(second) || first.equals(second);
+    }
+
+    // Equal or one is not specified.
+    private boolean xorEqualsInt(int first, int second) {
+        return first == UNSPECIFIED_INT || second == UNSPECIFIED_INT
+                || first == second;
+    }
+
+    // Equal or one is not specified. Specific to MTU where <= 0 indicates unset.
+    private boolean mtuUnsetOrEquals(int first, int second) {
+        return first <= 0 || second <= 0 || first == second;
+    }
+
+    private String nullToEmpty(String stringValue) {
+        return stringValue == null ? UNSPECIFIED_STRING : stringValue;
+    }
+
+    /**
+     * @hide
+     * Called by {@link android.app.admin.DevicePolicyManager} to convert this APN into
+     * ContentValue. If a field is not specified then we put "" instead of null.
+     */
+    public ContentValues toContentValues() {
+        ContentValues apnValue = new ContentValues();
+        apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric));
+        // If the APN is editable, the user may be able to set an invalid numeric. The numeric must
+        // always be 5 or 6 characters (depending on the length of the MNC), so skip if it is
+        // potentially invalid.
+        if (!TextUtils.isEmpty(mOperatorNumeric)
+                && (mOperatorNumeric.length() == 5 || mOperatorNumeric.length() == 6)) {
+            apnValue.put(Telephony.Carriers.MCC, mOperatorNumeric.substring(0, 3));
+            apnValue.put(Telephony.Carriers.MNC, mOperatorNumeric.substring(3));
+        }
+        apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName));
+        apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName));
+        apnValue.put(Telephony.Carriers.PROXY, nullToEmpty(mProxyAddress));
+        apnValue.put(Telephony.Carriers.PORT, nullToEmpty(portToString(mProxyPort)));
+        apnValue.put(Telephony.Carriers.MMSC, nullToEmpty(UriToString(mMmsc)));
+        apnValue.put(Telephony.Carriers.MMSPORT, nullToEmpty(portToString(mMmsProxyPort)));
+        apnValue.put(Telephony.Carriers.MMSPROXY, nullToEmpty(
+                mMmsProxyAddress));
+        apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser));
+        apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword));
+        apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
+        String apnType = getApnTypesStringFromBitmask(mApnTypeBitmask);
+        apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType));
+        apnValue.put(Telephony.Carriers.PROTOCOL,
+                getProtocolStringFromInt(mProtocol));
+        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL,
+                getProtocolStringFromInt(mRoamingProtocol));
+        apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
+        apnValue.put(Telephony.Carriers.MVNO_TYPE, getMvnoTypeStringFromInt(mMvnoType));
+        apnValue.put(Telephony.Carriers.MVNO_MATCH_DATA, nullToEmpty(mMvnoMatchData));
+        apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
+        apnValue.put(Telephony.Carriers.LINGERING_NETWORK_TYPE_BITMASK,
+                mLingeringNetworkTypeBitmask);
+        apnValue.put(Telephony.Carriers.MTU_V4, mMtuV4);
+        apnValue.put(Telephony.Carriers.MTU_V6, mMtuV6);
+        apnValue.put(Telephony.Carriers.CARRIER_ID, mCarrierId);
+        apnValue.put(Telephony.Carriers.SKIP_464XLAT, mSkip464Xlat);
+        apnValue.put(Telephony.Carriers.ALWAYS_ON, mAlwaysOn);
+        apnValue.put(Telephony.Carriers.INFRASTRUCTURE_BITMASK, mInfrastructureBitmask);
+        apnValue.put(Telephony.Carriers.ESIM_BOOTSTRAP_PROVISIONING, mEsimBootstrapProvisioning);
+        return apnValue;
+    }
+
+    /**
+     * Get supported APN types
+     *
+     * @return list of APN types
+     * @hide
+     */
+    @ApnType
+    public List<Integer> getApnTypes() {
+        List<Integer> types = new ArrayList<>();
+        for (Integer type : APN_TYPE_INT_MAP.keySet()) {
+            if ((mApnTypeBitmask & type) == type) {
+                types.add(type);
+            }
+        }
+        return types;
+    }
+
+    /**
+     * Converts the integer value of an APN type to the string version.
+     * @param apnTypeBitmask bitmask of APN types.
+     * @return comma delimited list of APN types.
+     * @hide
+     */
+    @NonNull
+    public static String getApnTypesStringFromBitmask(int apnTypeBitmask) {
+        List<String> types = new ArrayList<>();
+        for (Integer type : APN_TYPE_INT_MAP.keySet()) {
+            if ((apnTypeBitmask & type) == type) {
+                types.add(APN_TYPE_INT_MAP.get(type));
+            }
+        }
+        return TextUtils.join(",", types);
+    }
+
+    /**
+     * Converts the APN type bitmask to an array of all APN types
+     * @param apnTypeBitmask bitmask of APN types.
+     * @return int array of APN types
+     * @hide
+     */
+    @NonNull
+    public static int[] getApnTypesFromBitmask(int apnTypeBitmask) {
+        return APN_TYPE_INT_MAP.keySet().stream()
+                .filter(type -> ((apnTypeBitmask & type) == type))
+                .mapToInt(Integer::intValue)
+                .toArray();
+    }
+
+    /**
+     * Converts the integer representation of APN type to its string representation.
+     *
+     * @param apnType APN type as an integer
+     * @return String representation of the APN type, or an empty string if the provided integer is
+     * not a valid APN type.
+     * @hide
+     */
+    @SystemApi
+    public static @NonNull @ApnTypeString String getApnTypeString(@ApnType int apnType) {
+        if (apnType == TYPE_ALL) {
+            return "*";
+        }
+        String apnTypeString = APN_TYPE_INT_MAP.get(apnType);
+        return apnTypeString == null ? "" : apnTypeString;
+    }
+
+    /**
+     * Converts the string representation of an APN type to its integer representation.
+     *
+     * @param apnType APN type as a string
+     * @return Integer representation of the APN type, or 0 if the provided string is not a valid
+     * APN type.
+     * @hide
+     */
+    @SystemApi
+    public static @ApnType int getApnTypeInt(@NonNull @ApnTypeString String apnType) {
+        return APN_TYPE_STRING_MAP.getOrDefault(apnType.toLowerCase(Locale.ROOT), 0);
+    }
+
+    /**
+     * @param types comma delimited list of APN types.
+     * @return bitmask of APN types.
+     * @hide
+     */
+    public static int getApnTypesBitmaskFromString(String types) {
+        // If unset, set to ALL.
+        if (TextUtils.isEmpty(types)) {
+            return TYPE_ALL;
+        } else {
+            int result = 0;
+            for (String str : types.split(",")) {
+                Integer type = APN_TYPE_STRING_MAP.get(str.toLowerCase(Locale.ROOT));
+                if (type != null) {
+                    result |= type;
+                }
+            }
+            return result;
+        }
+    }
+
+    /** @hide */
+    public static int getMvnoTypeIntFromString(String mvnoType) {
+        String mvnoTypeString = TextUtils.isEmpty(mvnoType)
+                ? mvnoType : mvnoType.toLowerCase(Locale.ROOT);
+        Integer mvnoTypeInt = MVNO_TYPE_STRING_MAP.get(mvnoTypeString);
+        return  mvnoTypeInt == null ? MVNO_TYPE_UNKNOWN : mvnoTypeInt;
+    }
+
+    /** @hide */
+    public static String getMvnoTypeStringFromInt(int mvnoType) {
+        String mvnoTypeString = MVNO_TYPE_INT_MAP.get(mvnoType);
+        return  mvnoTypeString == null ? UNSPECIFIED_STRING : mvnoTypeString;
+    }
+
+    /** @hide */
+    public static int getProtocolIntFromString(String protocol) {
+        Integer protocolInt = PROTOCOL_STRING_MAP.get(protocol);
+        return  protocolInt == null ? UNSPECIFIED_INT : protocolInt;
+    }
+
+    /** @hide */
+    public static String getProtocolStringFromInt(int protocol) {
+        String protocolString = PROTOCOL_INT_MAP.get(protocol);
+        return  protocolString == null ? UNSPECIFIED_STRING : protocolString;
+    }
+
+    private static Uri UriFromString(String uri) {
+        return TextUtils.isEmpty(uri) ? null : Uri.parse(uri);
+    }
+
+    private static String UriToString(Uri uri) {
+        return uri == null ? null : uri.toString();
+    }
+
+    /** @hide */
+    public static InetAddress inetAddressFromString(String inetAddress) {
+        if (TextUtils.isEmpty(inetAddress)) {
+            return null;
+        }
+        try {
+            return InetAddress.getByName(inetAddress);
+        } catch (UnknownHostException e) {
+            Log.e(LOG_TAG, "Can't parse InetAddress from string: unknown host.");
+            return null;
+        }
+    }
+
+    /** @hide */
+    public static String inetAddressToString(InetAddress inetAddress) {
+        if (inetAddress == null) {
+            return null;
+        }
+        final String inetAddressString = inetAddress.toString();
+        if (TextUtils.isEmpty(inetAddressString)) {
+            return null;
+        }
+        final String hostName = inetAddressString.substring(0, inetAddressString.indexOf("/"));
+        final String address = inetAddressString.substring(inetAddressString.indexOf("/") + 1);
+        if (TextUtils.isEmpty(hostName) && TextUtils.isEmpty(address)) {
+            return null;
+        }
+        return TextUtils.isEmpty(hostName) ? address : hostName;
+    }
+
+    private static int portFromString(String strPort) {
+        int port = UNSPECIFIED_INT;
+        if (!TextUtils.isEmpty(strPort)) {
+            try {
+                port = Integer.parseInt(strPort);
+            } catch (NumberFormatException e) {
+                Log.e(LOG_TAG, "Can't parse port from String");
+            }
+        }
+        return port;
+    }
+
+    private static String portToString(int port) {
+        return port == UNSPECIFIED_INT ? null : Integer.toString(port);
+    }
+
+    /**
+     * Check if this APN setting can support the given network
+     *
+     * @param networkType The network type
+     * @return {@code true} if this APN setting can support the given network.
+     *
+     * @hide
+     */
+    public boolean canSupportNetworkType(@NetworkType int networkType) {
+        // Do a special checking for GSM. In reality, GSM is a voice only network type and can never
+        // be used for data. We allow it here because in some DSDS corner cases, on the non-DDS
+        // sub, modem reports data rat unknown. In that case if voice is GSM and this APN supports
+        // GPRS or EDGE, this APN setting should be selected.
+        if (networkType == TelephonyManager.NETWORK_TYPE_GSM
+                && (mNetworkTypeBitmask & (TelephonyManager.NETWORK_TYPE_BITMASK_GPRS
+                | TelephonyManager.NETWORK_TYPE_BITMASK_EDGE)) != 0) {
+            return true;
+        }
+
+        return ServiceState.bitmaskHasTech(mNetworkTypeBitmask, networkType);
+    }
+
+    /**
+     * Check if this APN setting can support the given lingering network
+     *
+     * @param networkType The lingering network type
+     * @return {@code true} if this APN setting can support the given lingering network.
+     *
+     * @hide
+     */
+    public boolean canSupportLingeringNetworkType(@NetworkType int networkType) {
+        // For backwards compatibility, if this field is not set, we just use the existing
+        // network type bitmask.
+        if (mLingeringNetworkTypeBitmask == 0) {
+            return canSupportNetworkType(networkType);
+        }
+        // Do a special checking for GSM. In reality, GSM is a voice only network type and can never
+        // be used for data. We allow it here because in some DSDS corner cases, on the non-DDS
+        // sub, modem reports data rat unknown. In that case if voice is GSM and this APN supports
+        // GPRS or EDGE, this APN setting should be selected.
+        if (networkType == TelephonyManager.NETWORK_TYPE_GSM
+                && (mLingeringNetworkTypeBitmask & (TelephonyManager.NETWORK_TYPE_BITMASK_GPRS
+                | TelephonyManager.NETWORK_TYPE_BITMASK_EDGE)) != 0) {
+            return true;
+        }
+
+        return ServiceState.bitmaskHasTech((int) mLingeringNetworkTypeBitmask, networkType);
+    }
+
+    // Implement Parcelable.
+    @Override
+    /** @hide */
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    /** @hide */
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mId);
+        dest.writeString(mOperatorNumeric);
+        dest.writeString(mEntryName);
+        dest.writeString(mApnName);
+        dest.writeString(mProxyAddress);
+        dest.writeInt(mProxyPort);
+        dest.writeParcelable(mMmsc, flags);
+        dest.writeString(mMmsProxyAddress);
+        dest.writeInt(mMmsProxyPort);
+        dest.writeString(mUser);
+        dest.writeString(mPassword);
+        dest.writeInt(mAuthType);
+        dest.writeInt(mApnTypeBitmask);
+        dest.writeInt(mProtocol);
+        dest.writeInt(mRoamingProtocol);
+        dest.writeBoolean(mCarrierEnabled);
+        dest.writeInt(mNetworkTypeBitmask);
+        dest.writeLong(mLingeringNetworkTypeBitmask);
+        dest.writeInt(mProfileId);
+        dest.writeBoolean(mPersistent);
+        dest.writeInt(mMaxConns);
+        dest.writeInt(mWaitTime);
+        dest.writeInt(mMaxConnsTime);
+        dest.writeInt(mMtuV4);
+        dest.writeInt(mMtuV6);
+        dest.writeInt(mMvnoType);
+        dest.writeString(mMvnoMatchData);
+        dest.writeInt(mApnSetId);
+        dest.writeInt(mCarrierId);
+        dest.writeInt(mSkip464Xlat);
+        dest.writeBoolean(mAlwaysOn);
+        dest.writeInt(mInfrastructureBitmask);
+        dest.writeBoolean(mEsimBootstrapProvisioning);
+        dest.writeInt(mEditedStatus);
+    }
+
+    private static ApnSetting readFromParcel(Parcel in) {
+        return new Builder()
+                .setId(in.readInt())
+                .setOperatorNumeric(in.readString())
+                .setEntryName(in.readString())
+                .setApnName(in.readString())
+                .setProxyAddress(in.readString())
+                .setProxyPort(in.readInt())
+                .setMmsc(in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class))
+                .setMmsProxyAddress(in.readString())
+                .setMmsProxyPort(in.readInt())
+                .setUser(in.readString())
+                .setPassword(in.readString())
+                .setAuthType(in.readInt())
+                .setApnTypeBitmask(in.readInt())
+                .setProtocol(in.readInt())
+                .setRoamingProtocol(in.readInt())
+                .setCarrierEnabled(in.readBoolean())
+                .setNetworkTypeBitmask(in.readInt())
+                .setLingeringNetworkTypeBitmask(in.readLong())
+                .setProfileId(in.readInt())
+                .setModemCognitive(in.readBoolean())
+                .setMaxConns(in.readInt())
+                .setWaitTime(in.readInt())
+                .setMaxConnsTime(in.readInt())
+                .setMtuV4(in.readInt())
+                .setMtuV6(in.readInt())
+                .setMvnoType(in.readInt())
+                .setMvnoMatchData(in.readString())
+                .setApnSetId(in.readInt())
+                .setCarrierId(in.readInt())
+                .setSkip464Xlat(in.readInt())
+                .setAlwaysOn(in.readBoolean())
+                .setInfrastructureBitmask(in.readInt())
+                .setEsimBootstrapProvisioning(in.readBoolean())
+                .setEditedStatus(in.readInt())
+                .buildWithoutCheck();
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ApnSetting> CREATOR =
+            new Parcelable.Creator<ApnSetting>() {
+                @Override
+                public ApnSetting createFromParcel(Parcel in) {
+                    return readFromParcel(in);
+                }
+
+                @Override
+                public ApnSetting[] newArray(int size) {
+                    return new ApnSetting[size];
+                }
+            };
+
+    /**
+     * Provides a convenient way to set the fields of a {@link ApnSetting} when creating a new
+     * instance. The following settings are required to build an {@code ApnSetting}:
+     *
+     * <ul><li>apnTypeBitmask</li>
+     * <li>apnName</li>
+     * <li>entryName</li></ul>
+     *
+     * <p>The example below shows how you might create a new {@code ApnSetting}:
+     *
+     * <pre><code>
+     * // Create an MMS proxy address with a hostname. A network might not be
+     * // available, so supply a placeholder (0.0.0.0) IPv4 address to avoid DNS lookup.
+     * String host = "mms.example.com";
+     * byte[] ipAddress = new byte[4];
+     * InetAddress mmsProxy;
+     * try {
+     *   mmsProxy = InetAddress.getByAddress(host, ipAddress);
+     * } catch (UnknownHostException e) {
+     *   e.printStackTrace();
+     *   return;
+     * }
+     *
+     * ApnSetting apn = new ApnSetting.Builder()
+     *     .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS)
+     *     .setApnName("apn.example.com")
+     *     .setEntryName("Example Carrier APN")
+     *     .setMmsc(Uri.parse("http://mms.example.com:8002"))
+     *     .setMmsProxyAddress(mmsProxy)
+     *     .setMmsProxyPort(8799)
+     *     .build();
+     * </code></pre>
+     */
+    public static class Builder{
+        private String mEntryName;
+        private String mApnName;
+        private String mProxyAddress;
+        private int mProxyPort = UNSPECIFIED_INT;
+        private Uri mMmsc;
+        private String mMmsProxyAddress;
+        private int mMmsProxyPort = UNSPECIFIED_INT;
+        private String mUser;
+        private String mPassword;
+        private int mAuthType = AUTH_TYPE_UNKNOWN;
+        private int mApnTypeBitmask;
+        private int mId;
+        private String mOperatorNumeric;
+        private int mProtocol = UNSPECIFIED_INT;
+        private int mRoamingProtocol = UNSPECIFIED_INT;
+        private int mMtuV4;
+        private int mMtuV6;
+        private @TelephonyManager.NetworkTypeBitMask int mNetworkTypeBitmask;
+        private @TelephonyManager.NetworkTypeBitMask long mLingeringNetworkTypeBitmask;
+        private boolean mCarrierEnabled;
+        private int mProfileId;
+        private boolean mModemCognitive;
+        private int mMaxConns;
+        private int mWaitTime;
+        private int mMaxConnsTime;
+        private int mMvnoType = MVNO_TYPE_UNKNOWN;
+        private String mMvnoMatchData;
+        private int mApnSetId;
+        private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+        private int mSkip464Xlat = Carriers.SKIP_464XLAT_DEFAULT;
+        private boolean mAlwaysOn;
+        private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR | INFRASTRUCTURE_SATELLITE;
+        private boolean mEsimBootstrapProvisioning;
+        private @EditStatus int mEditedStatus = Carriers.UNEDITED;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {}
+
+        /**
+         * Sets the unique database id for this entry.
+         *
+         * @param id the unique database id to set for this entry
+         * @hide
+         */
+        public Builder setId(int id) {
+            this.mId = id;
+            return this;
+        }
+
+        /**
+         * Set the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought
+         * up by this APN setting. Note this value will only be used when MTU size is not provided
+         * in {@code DataCallResponse#getMtuV4()} during network bring up.
+         *
+         * @param mtuV4 the MTU size in bytes of the route.
+         */
+        public @NonNull Builder setMtuV4(int mtuV4) {
+            this.mMtuV4 = mtuV4;
+            return this;
+        }
+
+        /**
+         * Set the default MTU (Maximum Transmission Unit) size in bytes of the IPv6 routes brought
+         * up by this APN setting. Note this value will only be used when MTU size is not provided
+         * in {@code DataCallResponse#getMtuV6()} during network bring up.
+         *
+         * @param mtuV6 the MTU size in bytes of the route.
+         */
+        public @NonNull Builder setMtuV6(int mtuV6) {
+            this.mMtuV6 = mtuV6;
+            return this;
+        }
+
+        /**
+         * Sets the profile id to which the APN saved in modem.
+         *
+         * @param profileId the profile id to set for the APN.
+         */
+        public @NonNull Builder setProfileId(int profileId) {
+            this.mProfileId = profileId;
+            return this;
+        }
+
+        /**
+         * Set if the APN setting should be persistent/non-persistent in modem.
+         *
+         * @param isPersistent {@code true} if this APN setting should be persistent/non-persistent
+         * in modem.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setPersistent(boolean isPersistent) {
+            return setModemCognitive(isPersistent);
+        }
+
+        /**
+         * Sets if the APN setting is to be set in modem.
+         *
+         * @param modemCognitive if the APN setting is to be set in modem
+         * @hide
+         */
+        public Builder setModemCognitive(boolean modemCognitive) {
+            this.mModemCognitive = modemCognitive;
+            return this;
+        }
+
+        /**
+         * Sets the max connections of this APN.
+         *
+         * @param maxConns the max connections of this APN
+         * @hide
+         */
+        public Builder setMaxConns(int maxConns) {
+            this.mMaxConns = maxConns;
+            return this;
+        }
+
+        /**
+         * Sets the wait time for retry of the APN.
+         *
+         * @param waitTime the wait time for retry of the APN
+         * @hide
+         */
+        public Builder setWaitTime(int waitTime) {
+            this.mWaitTime = waitTime;
+            return this;
+        }
+
+        /**
+         * Sets the time to limit max connection for the APN.
+         *
+         * @param maxConnsTime the time to limit max connection for the APN
+         * @hide
+         */
+        public Builder setMaxConnsTime(int maxConnsTime) {
+            this.mMaxConnsTime = maxConnsTime;
+            return this;
+        }
+
+        /**
+         * Sets the MVNO match data for the APN.
+         *
+         * @param mvnoMatchData the MVNO match data for the APN
+         * @hide
+         */
+        public Builder setMvnoMatchData(@Nullable String mvnoMatchData) {
+            this.mMvnoMatchData = mvnoMatchData;
+            return this;
+        }
+
+        /**
+         * Sets the APN set id for the APN.
+         *
+         * @param apnSetId the set id for the APN
+         * @hide
+         */
+        public Builder setApnSetId(int apnSetId) {
+            this.mApnSetId = apnSetId;
+            return this;
+        }
+
+        /**
+         * Sets a human-readable name that describes the APN.
+         *
+         * @param entryName the entry name to set for the APN
+         */
+        @NonNull
+        public Builder setEntryName(@Nullable String entryName) {
+            this.mEntryName = entryName;
+            return this;
+        }
+
+        /**
+         * Sets the name of the APN.
+         *
+         * @param apnName the name to set for the APN
+         */
+        @NonNull
+        public Builder setApnName(@Nullable String apnName) {
+            this.mApnName = apnName;
+            return this;
+        }
+
+        /**
+         * Sets the address of an HTTP proxy for the APN. The proxy address can be an IP address or
+         * hostname. If {@code proxy} contains both an IP address and hostname, this method ignores
+         * the IP address.
+         *
+         * <p>The {@link java.net.InetAddress} methods
+         * {@link java.net.InetAddress#getAllByName getAllByName()} require DNS for hostname
+         * resolution. To avoid this requirement when setting a hostname, call
+         * {@link java.net.InetAddress#getByAddress(java.lang.String, byte[])} with both the
+         * hostname and a placeholder IP address. See {@link ApnSetting.Builder above} for an
+         * example.
+         *
+         * @param proxy the proxy address to set for the APN
+         * @deprecated use {@link #setProxyAddress(String)} instead.
+         */
+        @Deprecated
+        public Builder setProxyAddress(InetAddress proxy) {
+            this.mProxyAddress = inetAddressToString(proxy);
+            return this;
+        }
+
+        /**
+         * Sets the proxy address of the APN.
+         *
+         * @param proxy the proxy address to set for the APN
+         */
+        @NonNull
+        public Builder setProxyAddress(@Nullable String proxy) {
+            this.mProxyAddress = proxy;
+            return this;
+        }
+
+        /**
+         * Sets the proxy port of the APN.
+         *
+         * @param port the proxy port to set for the APN
+         */
+        @NonNull
+        public Builder setProxyPort(int port) {
+            this.mProxyPort = port;
+            return this;
+        }
+
+        /**
+         * Sets the MMSC Uri of the APN.
+         *
+         * @param mmsc the MMSC Uri to set for the APN
+         */
+        @NonNull
+        public Builder setMmsc(@Nullable Uri mmsc) {
+            this.mMmsc = mmsc;
+            return this;
+        }
+
+        /**
+         * Sets the address of an MMS proxy for the APN. The MMS proxy address can be an IP address
+         * or hostname. If {@code mmsProxy} contains both an IP address and hostname, this method
+         * ignores the IP address.
+         *
+         * <p>The {@link java.net.InetAddress} methods
+         * {@link java.net.InetAddress#getByName getByName()} and
+         * {@link java.net.InetAddress#getAllByName getAllByName()} require DNS for hostname
+         * resolution. To avoid this requirement when setting a hostname, call
+         * {@link java.net.InetAddress#getByAddress(java.lang.String, byte[])} with both the
+         * hostname and a placeholder IP address. See {@link ApnSetting.Builder above} for an
+         * example.
+         *
+         * @param mmsProxy the MMS proxy address to set for the APN
+         * @deprecated use {@link #setMmsProxyAddress(String)} instead.
+         */
+        @Deprecated
+        public Builder setMmsProxyAddress(InetAddress mmsProxy) {
+            this.mMmsProxyAddress = inetAddressToString(mmsProxy);
+            return this;
+        }
+
+        /**
+         * Sets the MMS proxy address of the APN.
+         *
+         * @param mmsProxy the MMS proxy address to set for the APN
+         */
+        @NonNull
+        public Builder setMmsProxyAddress(@Nullable String mmsProxy) {
+            this.mMmsProxyAddress = mmsProxy;
+            return this;
+        }
+
+        /**
+         * Sets the MMS proxy port of the APN.
+         *
+         * @param mmsPort the MMS proxy port to set for the APN
+         */
+        @NonNull
+        public Builder setMmsProxyPort(int mmsPort) {
+            this.mMmsProxyPort = mmsPort;
+            return this;
+        }
+
+        /**
+         * Sets the APN username of the APN.
+         *
+         * @param user the APN username to set for the APN
+         */
+        @NonNull
+        public Builder setUser(@Nullable String user) {
+            this.mUser = user;
+            return this;
+        }
+
+        /**
+         * Sets the APN password of the APN.
+         *
+         * @see android.provider.Telephony.Carriers#PASSWORD
+         * @param password the APN password to set for the APN
+         */
+        @NonNull
+        public Builder setPassword(@Nullable String password) {
+            this.mPassword = password;
+            return this;
+        }
+
+        /**
+         * Sets the authentication type of the APN.
+         *
+         * @param authType the authentication type to set for the APN
+         */
+        @NonNull
+        public Builder setAuthType(@AuthType int authType) {
+            this.mAuthType = authType;
+            return this;
+        }
+
+        /**
+         * Sets the bitmask of APN types.
+         *
+         * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple
+         * APN types, eg, a single APN may service regular internet traffic ("default") as well as
+         * MMS-specific connections.
+         *
+         * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}.
+         *
+         * @param apnTypeBitmask a bitmask describing the types of the APN
+         */
+        @NonNull
+        public Builder setApnTypeBitmask(@ApnType int apnTypeBitmask) {
+            this.mApnTypeBitmask = apnTypeBitmask;
+            return this;
+        }
+
+        /**
+         * Sets the numeric operator ID for the APN. Numeric operator ID is defined as
+         * {@link android.provider.Telephony.Carriers#MCC} +
+         * {@link android.provider.Telephony.Carriers#MNC}.
+         *
+         * @param operatorNumeric the numeric operator ID to set for this entry
+         */
+        @NonNull
+        public Builder setOperatorNumeric(@Nullable String operatorNumeric) {
+            this.mOperatorNumeric = operatorNumeric;
+            return this;
+        }
+
+        /**
+         * Sets the protocol to use to connect to this APN.
+         *
+         * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
+         *
+         * @param protocol the protocol to set to use to connect to this APN
+         */
+        @NonNull
+        public Builder setProtocol(@ProtocolType int protocol) {
+            this.mProtocol = protocol;
+            return this;
+        }
+
+        /**
+         * Sets the protocol to use to connect to this APN when the device is roaming.
+         *
+         * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
+         *
+         * @param roamingProtocol the protocol to set to use to connect to this APN when roaming
+         */
+        @NonNull
+        public Builder setRoamingProtocol(@ProtocolType  int roamingProtocol) {
+            this.mRoamingProtocol = roamingProtocol;
+            return this;
+        }
+
+        /**
+         * Sets the current status for this APN.
+         *
+         * @param carrierEnabled the current status to set for this APN
+         */
+        @NonNull
+        public Builder setCarrierEnabled(boolean carrierEnabled) {
+            this.mCarrierEnabled = carrierEnabled;
+            return this;
+        }
+
+        /**
+         * Sets Radio Technology (Network Type) info for this APN.
+         *
+         * @param networkTypeBitmask the Radio Technology (Network Type) info
+         */
+        @NonNull
+        public Builder setNetworkTypeBitmask(int networkTypeBitmask) {
+            this.mNetworkTypeBitmask = networkTypeBitmask;
+            return this;
+        }
+
+        /**
+         * Sets lingering Radio Technology (Network Type) for this APN.
+         *
+         * @param lingeringNetworkTypeBitmask the Radio Technology (Network Type) that should linger
+         * @hide
+         */
+        @NonNull
+        public Builder setLingeringNetworkTypeBitmask(@TelephonyManager.NetworkTypeBitMask
+                long lingeringNetworkTypeBitmask) {
+            this.mLingeringNetworkTypeBitmask = lingeringNetworkTypeBitmask;
+            return this;
+        }
+
+        /**
+         * Sets the MVNO match type for this APN.
+         *
+         * @param mvnoType the MVNO match type to set for this APN
+         */
+        @NonNull
+        public Builder setMvnoType(@MvnoType int mvnoType) {
+            this.mMvnoType = mvnoType;
+            return this;
+        }
+
+        /**
+         * Sets the carrier id for this APN.
+         *
+         * See {@link TelephonyManager#getSimCarrierId()} which provides more background for what a
+         * carrier ID is.
+         *
+         * @param carrierId the carrier id to set for this APN
+         */
+        @NonNull
+        public Builder setCarrierId(int carrierId) {
+            this.mCarrierId = carrierId;
+            return this;
+        }
+
+        /**
+         * Sets skip464xlat flag for this APN.
+         *
+         * @param skip464xlat skip464xlat for this APN.
+         * @hide
+         */
+        public Builder setSkip464Xlat(@Skip464XlatStatus int skip464xlat) {
+            this.mSkip464Xlat = skip464xlat;
+            return this;
+        }
+
+        /**
+         * Sets whether the PDU session brought up by this APN should always be on.
+         * See 3GPP TS 23.501 section 5.6.13
+         *
+         * @param alwaysOn the always on status to set for this APN
+         */
+        @FlaggedApi(Flags.FLAG_APN_SETTING_FIELD_SUPPORT_FLAG)
+        public @NonNull Builder setAlwaysOn(boolean alwaysOn) {
+            this.mAlwaysOn = alwaysOn;
+            return this;
+        }
+
+        /**
+         * Set the infrastructure bitmask.
+         *
+         * @param infrastructureBitmask The infrastructure bitmask of which the APN can be used on.
+         * For example, some APNs can only be used when the device is on cellular, on satellite, or
+         * both.
+         *
+         * @return The builder.
+         * @hide
+         */
+        @NonNull
+        public Builder setInfrastructureBitmask(@InfrastructureBitmask int infrastructureBitmask) {
+            this.mInfrastructureBitmask = infrastructureBitmask;
+            return this;
+        }
+
+        /**
+         * Sets esim bootstrap provisioning flag
+         *
+         * @param esimBootstrapProvisioning {@code true} if the APN is used for eSIM bootstrap
+         * provisioning, {@code false} otherwise.
+         *
+         * @return The builder.
+         * @hide
+         */
+        @NonNull
+        public Builder setEsimBootstrapProvisioning(boolean esimBootstrapProvisioning) {
+            this.mEsimBootstrapProvisioning = esimBootstrapProvisioning;
+            return this;
+        }
+
+        /**
+         * Set the edited status. APN could be added/edited/deleted by a user or carrier.
+         *
+         * @param editedStatus The APN edited status
+         * @return The builder.
+         *
+         * @see Carriers#UNEDITED
+         * @see Carriers#USER_EDITED
+         * @see Carriers#USER_DELETED
+         * @see Carriers#CARRIER_EDITED
+         * @see Carriers#CARRIER_DELETED
+         *
+         * @hide
+         */
+        @NonNull
+        public Builder setEditedStatus(@EditStatus int editedStatus) {
+            this.mEditedStatus = editedStatus;
+            return this;
+        }
+
+        /**
+         * Builds {@link ApnSetting} from this builder.
+         *
+         * @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)}
+         * is empty, or {@link #setApnTypeBitmask(int)} doesn't contain a valid bit,
+         * {@link ApnSetting} built from this builder otherwise.
+         */
+        public ApnSetting build() {
+            if ((mApnTypeBitmask & (TYPE_DEFAULT | TYPE_MMS | TYPE_SUPL | TYPE_DUN | TYPE_HIPRI
+                    | TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX
+                    | TYPE_XCAP | TYPE_VSIM | TYPE_BIP | TYPE_ENTERPRISE | TYPE_RCS)) == 0
+                || TextUtils.isEmpty(mApnName) || TextUtils.isEmpty(mEntryName)) {
+                return null;
+            }
+            if ((mApnTypeBitmask & TYPE_MMS) != 0 && !TextUtils.isEmpty(mMmsProxyAddress)
+                    && mMmsProxyAddress.startsWith("http")) {
+                Log.wtf(LOG_TAG,"mms proxy(" + mMmsProxyAddress
+                        + ") should be a hostname, not a url");
+                Uri mMmsProxyAddressUri = Uri.parse(mMmsProxyAddress);
+                mMmsProxyAddress = mMmsProxyAddressUri.getHost();
+            }
+            return new ApnSetting(this);
+        }
+
+        /**
+         * Builds {@link ApnSetting} from this builder. This function doesn't check if
+         * {@link #setApnName(String)} or {@link #setEntryName(String)}, or
+         * {@link #setApnTypeBitmask(int)} is empty.
+         * @hide
+         */
+        public ApnSetting buildWithoutCheck() {
+            return new ApnSetting(this);
+        }
+    }
+}
diff --git a/android-35/android/telephony/data/DataCallResponse.java b/android-35/android/telephony/data/DataCallResponse.java
new file mode 100644
index 0000000..b6f9e1f
--- /dev/null
+++ b/android-35/android/telephony/data/DataCallResponse.java
@@ -0,0 +1,959 @@
+/*
+ * Copyright (C) 2009 Qualcomm Innovation Center, Inc.  All Rights Reserved.
+ * Copyright (C) 2009 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.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.LinkAddress;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.DataFailureCause;
+import android.telephony.DataFailCause;
+import android.telephony.PreciseDataConnectionState;
+import android.telephony.data.ApnSetting.ProtocolType;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Description of the response of a setup data call connection request.
+ *
+ * @hide
+ */
+@SystemApi
+public final class DataCallResponse implements Parcelable {
+
+    /** {@hide} */
+    @IntDef(prefix = "LINK_STATUS_", value = {
+            LINK_STATUS_UNKNOWN,
+            LINK_STATUS_INACTIVE,
+            LINK_STATUS_DORMANT,
+            LINK_STATUS_ACTIVE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LinkStatus {}
+
+    /** Unknown status */
+    public static final int LINK_STATUS_UNKNOWN = -1;
+
+    /** Indicates the data connection is inactive. */
+    public static final int LINK_STATUS_INACTIVE = 0;
+
+    /** Indicates the data connection is active with physical link dormant. */
+    public static final int LINK_STATUS_DORMANT = 1;
+
+    /** Indicates the data connection is active with physical link up. */
+    public static final int LINK_STATUS_ACTIVE = 2;
+
+    /** {@hide} */
+    @IntDef(prefix = "HANDOVER_FAILURE_MODE_", value = {
+            HANDOVER_FAILURE_MODE_UNKNOWN,
+            HANDOVER_FAILURE_MODE_LEGACY,
+            HANDOVER_FAILURE_MODE_DO_FALLBACK,
+            HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER,
+            HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HandoverFailureMode {}
+
+    /**
+     * Data handover failure mode is unknown.
+     */
+    public static final int HANDOVER_FAILURE_MODE_UNKNOWN = -1;
+
+    /**
+     * Perform fallback to the source data transport on data handover failure using
+     * the legacy logic, which is fallback if the fail cause is
+     * {@link DataFailCause#HANDOFF_PREFERENCE_CHANGED}.
+     */
+    public static final int HANDOVER_FAILURE_MODE_LEGACY = 0;
+
+    /**
+     * Perform fallback to the source data transport on data handover failure.
+     */
+    public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1;
+
+    /**
+     * Do not perform fallback to the source data transport on data handover failure.
+     * Framework will retry setting up a new data connection by sending
+     * {@link DataService#REQUEST_REASON_HANDOVER} request to the underlying {@link DataService}.
+     */
+    public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER = 2;
+
+    /**
+     * Do not perform fallback to the source transport on data handover failure.
+     * Framework will retry setting up a new data connection by sending
+     * {@link DataService#REQUEST_REASON_NORMAL} request to the underlying {@link DataService}.
+     */
+    public static final int HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL = 3;
+
+    /**
+     * Indicates that data retry duration is not specified. Platform can determine when to
+     * perform data setup appropriately.
+     */
+    public static final int RETRY_DURATION_UNDEFINED = -1;
+
+    /**
+     * Indicates that the pdu session id is not set.
+     */
+    public static final int PDU_SESSION_ID_NOT_SET = 0;
+    private final @DataFailureCause int mCause;
+    private final long mSuggestedRetryTime;
+    private final int mId;
+    private final @LinkStatus int mLinkStatus;
+    private final @ProtocolType int mProtocolType;
+    private final String mInterfaceName;
+    private final List<LinkAddress> mAddresses;
+    private final List<InetAddress> mDnsAddresses;
+    private final List<InetAddress> mGatewayAddresses;
+    private final List<InetAddress> mPcscfAddresses;
+    private final int mMtu;
+    private final int mMtuV4;
+    private final int mMtuV6;
+    private final @HandoverFailureMode int mHandoverFailureMode;
+    private final int mPduSessionId;
+    private final Qos mDefaultQos;
+    private final List<QosBearerSession> mQosBearerSessions;
+    private final NetworkSliceInfo mSliceInfo;
+    private final List<TrafficDescriptor> mTrafficDescriptors;
+    private final @PreciseDataConnectionState.NetworkValidationStatus int mNetworkValidationStatus;
+
+    /**
+     * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
+     * @param suggestedRetryTime The suggested data retry time in milliseconds.
+     * @param id The unique id of the data connection.
+     * @param linkStatus Data connection link status.
+     * @param protocolType The connection protocol, should be one of the PDP_type values in 3GPP
+     * TS 27.007 section 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP".
+     * @param interfaceName The network interface name.
+     * @param addresses A list of addresses with optional "/" prefix length, e.g.,
+     * "192.0.1.3" or "192.0.1.11/16 2001:db8::1/64". Typically 1 IPv4 or 1 IPv6 or
+     * one of each. If the prefix length is absent the addresses are assumed to be
+     * point to point with IPv4 having a prefix length of 32 and IPv6 128.
+     * @param dnsAddresses A list of DNS server addresses, e.g., "192.0.1.3" or
+     * "192.0.1.11 2001:db8::1". Null if no dns server addresses returned.
+     * @param gatewayAddresses A list of default gateway addresses, e.g., "192.0.1.3" or
+     * "192.0.1.11 2001:db8::1". When null, the addresses represent point to point connections.
+     * @param pcscfAddresses A list of Proxy Call State Control Function address via PCO (Protocol
+     * Configuration Option) for IMS client.
+     * @param mtu MTU (maximum transmission unit) in bytes received from network.
+     * Zero or negative values means network has either not sent a value or sent an invalid value.
+     *
+     * @removed Use the {@link Builder()} instead.
+     */
+    public DataCallResponse(@DataFailureCause int cause, int suggestedRetryTime, int id,
+                            @LinkStatus int linkStatus,
+                            @ProtocolType int protocolType, @Nullable String interfaceName,
+                            @Nullable List<LinkAddress> addresses,
+                            @Nullable List<InetAddress> dnsAddresses,
+                            @Nullable List<InetAddress> gatewayAddresses,
+                            @Nullable List<InetAddress> pcscfAddresses, int mtu) {
+        this(cause, suggestedRetryTime, id,
+                linkStatus, protocolType,
+                interfaceName == null ? "" : interfaceName,
+                addresses == null ? Collections.emptyList() : addresses,
+                dnsAddresses == null ? Collections.emptyList() : dnsAddresses,
+                gatewayAddresses == null ? Collections.emptyList() : gatewayAddresses,
+                pcscfAddresses == null ? Collections.emptyList() : pcscfAddresses,
+                mtu, mtu /* mtuV4 */, mtu /* mtuV6 */,
+                HANDOVER_FAILURE_MODE_LEGACY, PDU_SESSION_ID_NOT_SET,
+                null /* defaultQos */, Collections.emptyList() /* qosBearerSessions */,
+                null /* sliceInfo */,
+                Collections.emptyList(), /* trafficDescriptors */
+                PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED);
+    }
+
+    private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
+            @LinkStatus int linkStatus, @ProtocolType int protocolType,
+            @NonNull String interfaceName, @NonNull List<LinkAddress> addresses,
+            @NonNull List<InetAddress> dnsAddresses, @NonNull List<InetAddress> gatewayAddresses,
+            @NonNull List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
+            @HandoverFailureMode int handoverFailureMode, int pduSessionId,
+            @Nullable Qos defaultQos, @NonNull List<QosBearerSession> qosBearerSessions,
+            @Nullable NetworkSliceInfo sliceInfo,
+            @NonNull List<TrafficDescriptor> trafficDescriptors,
+            @PreciseDataConnectionState.NetworkValidationStatus int networkValidationStatus) {
+        mCause = cause;
+        mSuggestedRetryTime = suggestedRetryTime;
+        mId = id;
+        mLinkStatus = linkStatus;
+        mProtocolType = protocolType;
+        mInterfaceName = interfaceName;
+        mAddresses = new ArrayList<>(addresses);
+        mDnsAddresses = new ArrayList<>(dnsAddresses);
+        mGatewayAddresses = new ArrayList<>(gatewayAddresses);
+        mPcscfAddresses = new ArrayList<>(pcscfAddresses);
+        mMtu = mtu;
+        mMtuV4 = mtuV4;
+        mMtuV6 = mtuV6;
+        mHandoverFailureMode = handoverFailureMode;
+        mPduSessionId = pduSessionId;
+        mDefaultQos = defaultQos;
+        mQosBearerSessions = new ArrayList<>(qosBearerSessions);
+        mSliceInfo = sliceInfo;
+        mTrafficDescriptors = new ArrayList<>(trafficDescriptors);
+        mNetworkValidationStatus = networkValidationStatus;
+
+        if (mLinkStatus == LINK_STATUS_ACTIVE
+                || mLinkStatus == LINK_STATUS_DORMANT) {
+            Objects.requireNonNull(
+                    mInterfaceName, "Active data calls must be on a valid interface!");
+            if (mCause != DataFailCause.NONE) {
+                throw new IllegalStateException("Active data call must not have a failure!");
+            }
+        }
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public DataCallResponse(Parcel source) {
+        mCause = source.readInt();
+        mSuggestedRetryTime = source.readLong();
+        mId = source.readInt();
+        mLinkStatus = source.readInt();
+        mProtocolType = source.readInt();
+        mInterfaceName = source.readString();
+        mAddresses = new ArrayList<>();
+        source.readList(mAddresses,
+                LinkAddress.class.getClassLoader(),
+                android.net.LinkAddress.class);
+        mDnsAddresses = new ArrayList<>();
+        source.readList(mDnsAddresses,
+                InetAddress.class.getClassLoader(),
+                java.net.InetAddress.class);
+        mGatewayAddresses = new ArrayList<>();
+        source.readList(mGatewayAddresses,
+                InetAddress.class.getClassLoader(),
+                java.net.InetAddress.class);
+        mPcscfAddresses = new ArrayList<>();
+        source.readList(mPcscfAddresses,
+                InetAddress.class.getClassLoader(),
+                java.net.InetAddress.class);
+        mMtu = source.readInt();
+        mMtuV4 = source.readInt();
+        mMtuV6 = source.readInt();
+        mHandoverFailureMode = source.readInt();
+        mPduSessionId = source.readInt();
+        mDefaultQos = source.readParcelable(Qos.class.getClassLoader(),
+                android.telephony.data.Qos.class);
+        mQosBearerSessions = new ArrayList<>();
+        source.readList(mQosBearerSessions,
+                QosBearerSession.class.getClassLoader(),
+                android.telephony.data.QosBearerSession.class);
+        mSliceInfo = source.readParcelable(
+                NetworkSliceInfo.class.getClassLoader(),
+                android.telephony.data.NetworkSliceInfo.class);
+        mTrafficDescriptors = new ArrayList<>();
+        source.readList(mTrafficDescriptors,
+                TrafficDescriptor.class.getClassLoader(),
+                android.telephony.data.TrafficDescriptor.class);
+        mNetworkValidationStatus = source.readInt();
+    }
+
+    /**
+     * @return Data call fail cause. {@link DataFailCause#NONE} indicates no error.
+     */
+    @DataFailureCause
+    public int getCause() { return mCause; }
+
+    /**
+     * @return The suggested data retry time in milliseconds. 0 when network does not
+     * suggest a retry time (Note this is different from the replacement
+     * {@link #getRetryDurationMillis()}).
+     *
+     * @deprecated Use {@link #getRetryDurationMillis()} instead.
+     */
+    @Deprecated
+    public int getSuggestedRetryTime() {
+        // To match the pre-deprecated getSuggestedRetryTime() behavior.
+        if (mSuggestedRetryTime == RETRY_DURATION_UNDEFINED) {
+            return 0;
+        } else if (mSuggestedRetryTime > Integer.MAX_VALUE) {
+            return Integer.MAX_VALUE;
+        }
+        return (int) mSuggestedRetryTime;
+    }
+
+    /**
+     * @return The network suggested data retry duration in milliseconds as specified in
+     * 3GPP TS 24.302 section 8.2.9.1.  The APN associated to this data call will be throttled for
+     * the specified duration unless {@link DataServiceCallback#onApnUnthrottled} is called.
+     * {@code Long.MAX_VALUE} indicates data retry should not occur.
+     * {@link #RETRY_DURATION_UNDEFINED} indicates network did not suggest any retry duration.
+     */
+    public long getRetryDurationMillis() {
+        return mSuggestedRetryTime;
+    }
+
+    /**
+     * @return The unique id of the data connection.
+     */
+    public int getId() { return mId; }
+
+    /**
+     * @return The link status
+     */
+    @LinkStatus public int getLinkStatus() { return mLinkStatus; }
+
+    /**
+     * @return The connection protocol type.
+     */
+    @ProtocolType
+    public int getProtocolType() { return mProtocolType; }
+
+    /**
+     * @return The network interface name (e.g. "rmnet_data1").
+     */
+    @NonNull
+    public String getInterfaceName() { return mInterfaceName; }
+
+    /**
+     * @return A list of addresses of this data connection.
+     */
+    @NonNull
+    public List<LinkAddress> getAddresses() {
+        return Collections.unmodifiableList(mAddresses);
+    }
+
+    /**
+     * @return A list of DNS server addresses, e.g., "192.0.1.3" or
+     * "192.0.1.11 2001:db8::1". Empty list if no dns server addresses returned.
+     */
+    @NonNull
+    public List<InetAddress> getDnsAddresses() {
+        return Collections.unmodifiableList(mDnsAddresses);
+    }
+
+    /**
+     * @return A list of default gateway addresses, e.g., "192.0.1.3" or
+     * "192.0.1.11 2001:db8::1". Empty list if the addresses represent point to point connections.
+     */
+    @NonNull
+    public List<InetAddress> getGatewayAddresses() {
+        return Collections.unmodifiableList(mGatewayAddresses);
+    }
+
+    /**
+     * @return A list of Proxy Call State Control Function address via PCO (Protocol Configuration
+     * Option) for IMS client.
+     */
+    @NonNull
+    public List<InetAddress> getPcscfAddresses() {
+        return Collections.unmodifiableList(mPcscfAddresses);
+    }
+
+    /**
+     * @return MTU (maximum transmission unit) in bytes received from network. Zero or negative
+     * values means network has either not sent a value or sent an invalid value.
+     * @deprecated For IRadio 1.5 and up, use {@link #getMtuV4} or {@link #getMtuV6} instead.
+     */
+    @Deprecated
+    public int getMtu() {
+        return mMtu;
+    }
+
+    /**
+     * This replaces the deprecated method getMtu.
+     * @return MTU (maximum transmission unit) in bytes received from network, for IPv4.
+     * Zero or negative values means network has either not sent a value or sent an invalid value.
+     */
+    public int getMtuV4() {
+        return mMtuV4;
+    }
+
+    /**
+     * @return MTU (maximum transmission unit) in bytes received from network, for IPv6.
+     * Zero or negative values means network has either not sent a value or sent an invalid value.
+     */
+    public int getMtuV6() {
+        return mMtuV6;
+    }
+
+    /**
+     * @return The data handover failure mode.
+     */
+    public @HandoverFailureMode int getHandoverFailureMode() {
+        return mHandoverFailureMode;
+    }
+
+    /**
+     * @return The pdu session id
+     */
+    public int getPduSessionId() {
+        return mPduSessionId;
+    }
+
+    /**
+     * @return default QOS of the data connection received from the network
+     *
+     * @hide
+     */
+    @Nullable
+    public Qos getDefaultQos() {
+        return mDefaultQos;
+    }
+
+    /**
+     * @return All the dedicated bearer QOS sessions of the data connection received from the
+     * network.
+     *
+     * @hide
+     */
+    @NonNull
+    public List<QosBearerSession> getQosBearerSessions() {
+        return Collections.unmodifiableList(mQosBearerSessions);
+    }
+
+    /**
+     * @return The slice info related to this data connection.
+     */
+    @Nullable
+    public NetworkSliceInfo getSliceInfo() {
+        return mSliceInfo;
+    }
+
+    /**
+     * @return The traffic descriptors related to this data connection.
+     */
+    @NonNull
+    public List<TrafficDescriptor> getTrafficDescriptors() {
+        return Collections.unmodifiableList(mTrafficDescriptors);
+    }
+
+    /**
+     * Return the network validation status that was initiated by {@link
+     * DataService.DataServiceProvider#requestNetworkValidation}
+     *
+     * @return The network validation status of data connection.
+     */
+    @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+    public @PreciseDataConnectionState.NetworkValidationStatus int getNetworkValidationStatus() {
+        return mNetworkValidationStatus;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("DataCallResponse: {")
+           .append(" cause=").append(DataFailCause.toString(mCause))
+           .append(" retry=").append(mSuggestedRetryTime)
+           .append(" cid=").append(mId)
+           .append(" linkStatus=").append(mLinkStatus)
+           .append(" protocolType=").append(mProtocolType)
+           .append(" ifname=").append(mInterfaceName)
+           .append(" addresses=").append(mAddresses)
+           .append(" dnses=").append(mDnsAddresses)
+           .append(" gateways=").append(mGatewayAddresses)
+           .append(" pcscf=").append(mPcscfAddresses)
+           .append(" mtu=").append(getMtu())
+           .append(" mtuV4=").append(getMtuV4())
+           .append(" mtuV6=").append(getMtuV6())
+           .append(" handoverFailureMode=").append(failureModeToString(mHandoverFailureMode))
+           .append(" pduSessionId=").append(getPduSessionId())
+           .append(" defaultQos=").append(mDefaultQos)
+           .append(" qosBearerSessions=").append(mQosBearerSessions)
+           .append(" sliceInfo=").append(mSliceInfo)
+           .append(" trafficDescriptors=").append(mTrafficDescriptors)
+           .append(" networkValidationStatus=").append(PreciseDataConnectionState
+                        .networkValidationStatusToString(mNetworkValidationStatus))
+           .append("}");
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+
+        if (!(o instanceof DataCallResponse)) {
+            return false;
+        }
+
+        DataCallResponse other = (DataCallResponse) o;
+
+        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()
+                && mDnsAddresses.containsAll(other.mDnsAddresses)
+                && mGatewayAddresses.size() == other.mGatewayAddresses.size()
+                && mGatewayAddresses.containsAll(other.mGatewayAddresses)
+                && mPcscfAddresses.size() == other.mPcscfAddresses.size()
+                && mPcscfAddresses.containsAll(other.mPcscfAddresses)
+                && mMtu == other.mMtu
+                && mMtuV4 == other.mMtuV4
+                && mMtuV6 == other.mMtuV6
+                && mHandoverFailureMode == other.mHandoverFailureMode
+                && mPduSessionId == other.mPduSessionId
+                && Objects.equals(mDefaultQos, other.mDefaultQos)
+                && mQosBearerSessions.size() == other.mQosBearerSessions.size() // non-null
+                && mQosBearerSessions.containsAll(other.mQosBearerSessions) // non-null
+                && Objects.equals(mSliceInfo, other.mSliceInfo)
+                && mTrafficDescriptors.size() == other.mTrafficDescriptors.size() // non-null
+                && mTrafficDescriptors.containsAll(other.mTrafficDescriptors) // non-null
+                && mNetworkValidationStatus == other.mNetworkValidationStatus;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
+                mInterfaceName, Set.copyOf(mAddresses), Set.copyOf(mDnsAddresses),
+                Set.copyOf(mGatewayAddresses), Set.copyOf(mPcscfAddresses), mMtu, mMtuV4, mMtuV6,
+                mHandoverFailureMode, mPduSessionId, mDefaultQos, Set.copyOf(mQosBearerSessions),
+                mSliceInfo, Set.copyOf(mTrafficDescriptors), mNetworkValidationStatus);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCause);
+        dest.writeLong(mSuggestedRetryTime);
+        dest.writeInt(mId);
+        dest.writeInt(mLinkStatus);
+        dest.writeInt(mProtocolType);
+        dest.writeString(mInterfaceName);
+        dest.writeList(mAddresses);
+        dest.writeList(mDnsAddresses);
+        dest.writeList(mGatewayAddresses);
+        dest.writeList(mPcscfAddresses);
+        dest.writeInt(mMtu);
+        dest.writeInt(mMtuV4);
+        dest.writeInt(mMtuV6);
+        dest.writeInt(mHandoverFailureMode);
+        dest.writeInt(mPduSessionId);
+        dest.writeParcelable(mDefaultQos, flags);
+        dest.writeList(mQosBearerSessions);
+        dest.writeParcelable(mSliceInfo, flags);
+        dest.writeList(mTrafficDescriptors);
+        dest.writeInt(mNetworkValidationStatus);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
+            new Parcelable.Creator<DataCallResponse>() {
+                @Override
+                public DataCallResponse createFromParcel(Parcel source) {
+                    return new DataCallResponse(source);
+                }
+
+                @Override
+                public DataCallResponse[] newArray(int size) {
+                    return new DataCallResponse[size];
+                }
+            };
+
+    /**
+     * Convert handover failure mode to string.
+     *
+     * @param handoverFailureMode Handover failure mode
+     * @return Handover failure mode in string
+     *
+     * @hide
+     */
+    public static String failureModeToString(@HandoverFailureMode int handoverFailureMode) {
+        switch (handoverFailureMode) {
+            case HANDOVER_FAILURE_MODE_UNKNOWN: return "unknown";
+            case HANDOVER_FAILURE_MODE_LEGACY: return "legacy";
+            case HANDOVER_FAILURE_MODE_DO_FALLBACK: return "fallback";
+            case HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER: return "retry handover";
+            case HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL: return "retry setup new one";
+            default: return Integer.toString(handoverFailureMode);
+        }
+    }
+
+    /**
+     * Provides a convenient way to set the fields of a {@link DataCallResponse} when creating a new
+     * instance.
+     *
+     * <p>The example below shows how you might create a new {@code DataCallResponse}:
+     *
+     * <pre><code>
+     *
+     * DataCallResponse response = new DataCallResponse.Builder()
+     *     .setAddresses(Arrays.asList("192.168.1.2"))
+     *     .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
+     *     .build();
+     * </code></pre>
+     */
+    public static final class Builder {
+        private @DataFailureCause int mCause;
+
+        private long mSuggestedRetryTime = RETRY_DURATION_UNDEFINED;
+
+        private int mId;
+
+        private @LinkStatus int mLinkStatus;
+
+        private @ProtocolType int mProtocolType;
+
+        private String mInterfaceName = "";
+
+        private List<LinkAddress> mAddresses = Collections.emptyList();
+
+        private List<InetAddress> mDnsAddresses = Collections.emptyList();
+
+        private List<InetAddress> mGatewayAddresses = Collections.emptyList();
+
+        private List<InetAddress> mPcscfAddresses = Collections.emptyList();
+
+        private int mMtu;
+
+        private int mMtuV4;
+
+        private int mMtuV6;
+
+        private @HandoverFailureMode int mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY;
+
+        private int mPduSessionId = PDU_SESSION_ID_NOT_SET;
+
+        private @Nullable Qos mDefaultQos;
+
+        private List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
+
+        private @Nullable NetworkSliceInfo mSliceInfo;
+
+        private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
+
+        private @PreciseDataConnectionState.NetworkValidationStatus int mNetworkValidationStatus =
+                PreciseDataConnectionState.NETWORK_VALIDATION_UNSUPPORTED;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Set data call fail cause.
+         *
+         * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error, which
+         * is the only valid value for data calls that are {@link LINK_STATUS_ACTIVE} or
+         * {@link LINK_STATUS_DORMANT}.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setCause(@DataFailureCause int cause) {
+            mCause = cause;
+            return this;
+        }
+
+        /**
+         * Set the suggested data retry time.
+         *
+         * @param suggestedRetryTime The suggested data retry time in milliseconds.
+         * @return The same instance of the builder.
+         *
+         * @deprecated Use {@link #setRetryDurationMillis(long)} instead.
+         */
+        @Deprecated
+        public @NonNull Builder setSuggestedRetryTime(int suggestedRetryTime) {
+            mSuggestedRetryTime = (long) suggestedRetryTime;
+            return this;
+        }
+
+        /**
+         * Set the network suggested data retry duration.
+         *
+         * @param retryDurationMillis The suggested data retry duration in milliseconds.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setRetryDurationMillis(long retryDurationMillis) {
+            mSuggestedRetryTime = retryDurationMillis;
+            return this;
+        }
+
+        /**
+         * Set the unique id of the data connection.
+         *
+         * @param id The unique id of the data connection.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setId(int id) {
+            mId = id;
+            return this;
+        }
+
+        /**
+         * Set the link status
+         *
+         * @param linkStatus The link status
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setLinkStatus(@LinkStatus int linkStatus) {
+            mLinkStatus = linkStatus;
+            return this;
+        }
+
+        /**
+         * Set the connection protocol type.
+         *
+         * @param protocolType The connection protocol type.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setProtocolType(@ProtocolType int protocolType) {
+            mProtocolType = protocolType;
+            return this;
+        }
+
+        /**
+         * Set the network interface name.
+         *
+         * @param interfaceName The network interface name (e.g. "rmnet_data1"). This value may not
+         * be null for valid data calls (those that are {@link LINK_STATUS_ACTIVE} or
+         * {@link LINK_STATUS_DORMANT}).
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setInterfaceName(@Nullable String interfaceName) {
+            if (interfaceName == null) interfaceName = "";
+            mInterfaceName = interfaceName;
+            return this;
+        }
+
+        /**
+         * Set the addresses of this data connection.
+         *
+         * @param addresses The list of address of the data connection.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setAddresses(@NonNull List<LinkAddress> addresses) {
+            Objects.requireNonNull(addresses);
+            mAddresses = addresses;
+            return this;
+        }
+
+        /**
+         * Set the DNS addresses of this data connection
+         *
+         * @param dnsAddresses The list of DNS address of the data connection.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setDnsAddresses(@NonNull List<InetAddress> dnsAddresses) {
+            Objects.requireNonNull(dnsAddresses);
+            mDnsAddresses = dnsAddresses;
+            return this;
+        }
+
+        /**
+         * Set the gateway addresses of this data connection
+         *
+         * @param gatewayAddresses The list of gateway address of the data connection.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setGatewayAddresses(@NonNull List<InetAddress> gatewayAddresses) {
+            Objects.requireNonNull(gatewayAddresses);
+            mGatewayAddresses = gatewayAddresses;
+            return this;
+        }
+
+        /**
+         * Set the Proxy Call State Control Function address via PCO(Protocol Configuration
+         * Option) for IMS client.
+         *
+         * @param pcscfAddresses The list of pcscf address of the data connection.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setPcscfAddresses(@NonNull List<InetAddress> pcscfAddresses) {
+            Objects.requireNonNull(pcscfAddresses);
+            mPcscfAddresses = pcscfAddresses;
+            return this;
+        }
+
+        /**
+         * Set maximum transmission unit of the data connection.
+         *
+         * @param mtu MTU (maximum transmission unit) in bytes received from network. Zero or
+         * negative values means network has either not sent a value or sent an invalid value.
+         *
+         * @return The same instance of the builder.
+         * @deprecated For IRadio 1.5 and up, use {@link #setMtuV4} or {@link #setMtuV6} instead.
+         */
+        public @NonNull Builder setMtu(int mtu) {
+            mMtu = mtu;
+            return this;
+        }
+
+        /**
+         * Set maximum transmission unit of the data connection, for IPv4.
+         *
+         * @param mtu MTU (maximum transmission unit) in bytes received from network. Zero or
+         * negative values means network has either not sent a value or sent an invalid value.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setMtuV4(int mtu) {
+            mMtuV4 = mtu;
+            return this;
+        }
+
+        /**
+         * Set maximum transmission unit of the data connection, for IPv6.
+         *
+         * @param mtu MTU (maximum transmission unit) in bytes received from network. Zero or
+         * negative values means network has either not sent a value or sent an invalid value.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setMtuV6(int mtu) {
+            mMtuV6 = mtu;
+            return this;
+        }
+
+        /**
+         * Set data handover failure mode for the data call response.
+         *
+         * @param failureMode Handover failure mode.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setHandoverFailureMode(@HandoverFailureMode int failureMode) {
+            mHandoverFailureMode = failureMode;
+            return this;
+        }
+
+        /**
+         * Set pdu session id.
+         * <p/>
+         * The id must be between 1 and 15 when linked to a pdu session. If no pdu session
+         * exists for the current data call, the id must be set to {@link #PDU_SESSION_ID_NOT_SET}.
+         *
+         * @param pduSessionId Pdu Session Id of the data call.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setPduSessionId(
+                @IntRange(from = PDU_SESSION_ID_NOT_SET, to = 15) int pduSessionId) {
+            Preconditions.checkArgument(pduSessionId >= PDU_SESSION_ID_NOT_SET,
+                    "pduSessionId must be greater than or equal to" + PDU_SESSION_ID_NOT_SET);
+            Preconditions.checkArgument(pduSessionId <= 15,
+                    "pduSessionId must be less than or equal to 15.");
+            mPduSessionId = pduSessionId;
+            return this;
+        }
+
+        /**
+         * 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 qosBearerSessions Dedicated bearer QOS (Quality Of Service) sessions received
+         * from network.
+         *
+         * @return The same instance of the builder.
+         *
+         * @hide
+         */
+        public @NonNull Builder setQosBearerSessions(
+                @NonNull List<QosBearerSession> qosBearerSessions) {
+            Objects.requireNonNull(qosBearerSessions);
+            mQosBearerSessions = qosBearerSessions;
+            return this;
+        }
+
+        /**
+         * The Slice used for this data connection.
+         * <p/>
+         * If a handover occurs from EPDG to 5G,
+         * this is the {@link NetworkSliceInfo} used in {@link DataService#setupDataCall}.
+         *
+         * @param sliceInfo the slice info for the data call
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setSliceInfo(@Nullable NetworkSliceInfo sliceInfo) {
+            mSliceInfo = sliceInfo;
+            return this;
+        }
+
+        /**
+         * The traffic descriptors for this data connection, as defined in 3GPP TS 24.526
+         * Section 5.2. They are used for URSP traffic matching as described in 3GPP TS 24.526
+         * Section 4.2.2. They includes an optional DNN, which, if present, must be used for traffic
+         * matching; it does not specify the end point to be used for the data call. The end point
+         * is specified by {@link DataProfile}, which must be used as the end point if one is not
+         * specified through URSP rules.
+         *
+         * @param trafficDescriptors the traffic descriptors for the data call.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setTrafficDescriptors(
+                @NonNull List<TrafficDescriptor> trafficDescriptors) {
+            Objects.requireNonNull(trafficDescriptors);
+            mTrafficDescriptors = trafficDescriptors;
+            return this;
+        }
+
+        /**
+         * Set the network validation status that corresponds to the state of the network validation
+         * request started by {@link DataService.DataServiceProvider#requestNetworkValidation}
+         *
+         * @param status The network validation status.
+         * @return The same instance of the builder.
+         */
+        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+        public @NonNull Builder setNetworkValidationStatus(
+                @PreciseDataConnectionState.NetworkValidationStatus int status) {
+            mNetworkValidationStatus = status;
+            return this;
+        }
+
+        /**
+         * Build the DataCallResponse.
+         *
+         * @return the DataCallResponse object.
+         */
+        public @NonNull DataCallResponse build() {
+            return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
+                    mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
+                    mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
+                    mDefaultQos, mQosBearerSessions, mSliceInfo, mTrafficDescriptors,
+                    mNetworkValidationStatus);
+        }
+    }
+}
diff --git a/android-35/android/telephony/data/DataProfile.java b/android-35/android/telephony/data/DataProfile.java
new file mode 100644
index 0000000..88a32d1
--- /dev/null
+++ b/android-35/android/telephony/data/DataProfile.java
@@ -0,0 +1,853 @@
+/*
+ * Copyright 2017 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 static android.telephony.data.ApnSetting.ProtocolType;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.NetworkCapabilities;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.NetCapability;
+import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.NetworkTypeBitMask;
+import android.telephony.data.ApnSetting.ApnType;
+import android.telephony.data.ApnSetting.AuthType;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Description of a mobile data profile used for establishing data networks. The data profile
+ * consist an {@link ApnSetting} which is needed for 2G/3G/4G networks bring up, and a
+ * {@link TrafficDescriptor} contains additional information that can be used for 5G standalone
+ * network bring up.
+ *
+ * @hide
+ */
+@SystemApi
+public final class DataProfile implements Parcelable {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"TYPE_"},
+            value = {
+                    TYPE_COMMON,
+                    TYPE_3GPP,
+                    TYPE_3GPP2})
+    public @interface Type {}
+
+    /** Common data profile */
+    public static final int TYPE_COMMON = 0;
+
+    /** 3GPP type data profile */
+    public static final int TYPE_3GPP = 1;
+
+    /** 3GPP2 type data profile */
+    public static final int TYPE_3GPP2 = 2;
+
+    private final @Type int mType;
+
+    private final @Nullable ApnSetting mApnSetting;
+
+    private final @Nullable TrafficDescriptor mTrafficDescriptor;
+
+    private boolean mPreferred;
+
+    /**
+     * The last timestamp of this data profile being used for data network setup. Never add this
+     * to {@link #equals(Object)} and {@link #hashCode()}.
+     */
+    private @ElapsedRealtimeLong long mSetupTimestamp;
+
+    private DataProfile(@NonNull Builder builder) {
+        mApnSetting = builder.mApnSetting;
+        mTrafficDescriptor = builder.mTrafficDescriptor;
+        mPreferred = builder.mPreferred;
+
+        if (builder.mType != -1) {
+            mType = builder.mType;
+        } else if (mApnSetting != null) {
+            int networkTypes = mApnSetting.getNetworkTypeBitmask();
+
+            if (networkTypes == 0) {
+                mType = DataProfile.TYPE_COMMON;
+            } else if ((networkTypes & TelephonyManager.NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2)
+                    == networkTypes) {
+                mType = DataProfile.TYPE_3GPP2;
+            } else if ((networkTypes & TelephonyManager.NETWORK_STANDARDS_FAMILY_BITMASK_3GPP)
+                    == networkTypes) {
+                mType = DataProfile.TYPE_3GPP;
+            } else {
+                mType = DataProfile.TYPE_COMMON;
+            }
+        } else {
+            mType = DataProfile.TYPE_COMMON;
+        }
+    }
+
+    private DataProfile(Parcel source) {
+        mType = source.readInt();
+        mApnSetting = source.readParcelable(ApnSetting.class.getClassLoader(), android.telephony.data.ApnSetting.class);
+        mTrafficDescriptor = source.readParcelable(TrafficDescriptor.class.getClassLoader(), android.telephony.data.TrafficDescriptor.class);
+        mPreferred = source.readBoolean();
+        mSetupTimestamp = source.readLong();
+    }
+
+    /**
+     * @return Id of the data profile.
+     * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getProfileId()} instead.
+     */
+    @Deprecated
+    public int getProfileId() {
+        if (mApnSetting != null) {
+            return mApnSetting.getProfileId();
+        }
+        return 0;
+    }
+
+    /**
+     * @return The APN (Access Point Name) to establish data connection. This is a string
+     * specifically defined by the carrier.
+     * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getApnName()} instead.
+     */
+    @Deprecated
+    public @NonNull String getApn() {
+        if (mApnSetting != null) {
+            return TextUtils.emptyIfNull(mApnSetting.getApnName());
+        }
+        return "";
+    }
+
+    /**
+     * @return The connection protocol defined in 3GPP TS 27.007 section 10.1.1.
+     * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getProtocol()} instead.
+     */
+    @Deprecated
+    public @ProtocolType int getProtocolType() {
+        if (mApnSetting != null) {
+            return mApnSetting.getProtocol();
+        }
+        return ApnSetting.PROTOCOL_IPV4V6;
+    }
+
+    /**
+     * @return The authentication protocol used for this PDP context.
+     * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getAuthType()} instead.
+     */
+    @Deprecated
+    public @AuthType int getAuthType() {
+        if (mApnSetting != null) {
+            return mApnSetting.getAuthType();
+        }
+        return ApnSetting.AUTH_TYPE_NONE;
+    }
+
+    /**
+     * @return The username for APN.
+     * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getUser()} instead.
+     */
+    @Deprecated
+    public @Nullable String getUserName() {
+        if (mApnSetting != null) {
+            return mApnSetting.getUser();
+        }
+        return null;
+    }
+
+    /**
+     * @return The password for APN.
+     * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getPassword()} instead.
+     */
+    @Deprecated
+    public @Nullable String getPassword() {
+        if (mApnSetting != null) {
+            return mApnSetting.getPassword();
+        }
+        return null;
+    }
+
+    /**
+     * @return The profile type.
+     */
+    public @Type int getType() {
+        return mType;
+    }
+
+    /**
+     * @return The period in seconds to limit the maximum connections.
+     *
+     * @hide
+     */
+    public int getMaxConnectionsTime() {
+        if (mApnSetting != null) {
+            return mApnSetting.getMaxConnsTime();
+        }
+        return 0;
+    }
+
+    /**
+     * @return The maximum connections allowed.
+     *
+     * @hide
+     */
+    public int getMaxConnections() {
+        if (mApnSetting != null) {
+            return mApnSetting.getMaxConns();
+        }
+        return 0;
+    }
+
+    /**
+     * @return The required wait time in seconds after a successful UE initiated disconnect of a
+     * given PDN connection before the device can send a new PDN connection request for that given
+     * PDN.
+     *
+     * @hide
+     */
+    public int getWaitTime() {
+        if (mApnSetting != null) {
+            return mApnSetting.getWaitTime();
+        }
+        return 0;
+    }
+
+    /**
+     * @return {@code true} if the profile is enabled. If the profile only has a
+     * {@link TrafficDescriptor}, but no {@link ApnSetting}, then this profile is always enabled.
+     */
+    public boolean isEnabled() {
+        if (mApnSetting != null) {
+            return mApnSetting.isEnabled();
+        }
+        return true;
+    }
+
+    /**
+     * @return The supported APN types bitmask.
+     * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getApnTypeBitmask()} instead.
+     */
+    @Deprecated public @ApnType int getSupportedApnTypesBitmask() {
+        if (mApnSetting != null) {
+            return mApnSetting.getApnTypeBitmask();
+        }
+        return ApnSetting.TYPE_NONE;
+    }
+
+    /**
+     * @return The connection protocol on roaming network defined in 3GPP TS 27.007 section 10.1.1.
+     * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getRoamingProtocol()} instead.
+     */
+    @Deprecated
+    public @ProtocolType int getRoamingProtocolType() {
+        if (mApnSetting != null) {
+            return mApnSetting.getRoamingProtocol();
+        }
+        return ApnSetting.PROTOCOL_IP;
+    }
+
+    /**
+     * @return The bearer bitmask indicating the applicable networks for this data profile.
+     * @deprecated use {@link #getApnSetting()} and {@link ApnSetting#getNetworkTypeBitmask()}
+     * instead.
+     */
+    @Deprecated
+    public @NetworkTypeBitMask int getBearerBitmask() {
+        if (mApnSetting != null) {
+            return mApnSetting.getNetworkTypeBitmask();
+        }
+        return (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN;
+    }
+
+    /**
+     * @return The maximum transmission unit (MTU) size in bytes.
+     * @deprecated use {@link #getApnSetting()} and {@link ApnSetting#getMtuV4()}/
+     * {@link ApnSetting#getMtuV6()} instead.
+     */
+    @Deprecated
+    public int getMtu() {
+        return getMtuV4();
+    }
+
+    /**
+     * This replaces the deprecated method getMtu.
+     * @return The maximum transmission unit (MTU) size in bytes, for IPv4.
+     * @deprecated use {@link #getApnSetting()} and {@link ApnSetting#getMtuV4()} instead.
+     */
+    @Deprecated
+    public int getMtuV4() {
+        if (mApnSetting != null) {
+            return mApnSetting.getMtuV4();
+        }
+        return 0;
+    }
+
+    /**
+     * @return The maximum transmission unit (MTU) size in bytes, for IPv6.
+     * @deprecated use {@link #getApnSetting()} and {@link ApnSetting#getMtuV6()} instead.
+     */
+    @Deprecated
+    public int getMtuV6() {
+        if (mApnSetting != null) {
+            return mApnSetting.getMtuV6();
+        }
+        return 0;
+    }
+
+    /**
+     * @return {@code true} if modem must persist this data profile.
+     * @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#isPersistent()} instead.
+     */
+    @Deprecated
+    public boolean isPersistent() {
+        if (mApnSetting != null) {
+            return mApnSetting.isPersistent();
+        }
+        return false;
+    }
+
+    /**
+     * Set the preferred flag for the data profile.
+     *
+     * @param preferred {@code true} if this data profile is preferred for internet.
+     * @hide
+     */
+    public void setPreferred(boolean preferred) {
+        mPreferred = preferred;
+    }
+
+    /**
+     * @return {@code true} if this data profile was used to bring up the last default
+     * (i.e internet) data connection successfully, or the one chosen by the user in Settings'
+     * APN editor. For one carrier there can be only one profiled preferred.
+     */
+    public boolean isPreferred() {
+        return mPreferred;
+    }
+
+    /**
+     * @return The APN setting {@link ApnSetting}, which is used to establish data network on
+     * 2G/3G/4G.
+     */
+    public @Nullable ApnSetting getApnSetting() {
+        return mApnSetting;
+    }
+
+    /**
+     * @return The traffic descriptor {@link TrafficDescriptor}, which can be used to establish
+     * data network on 5G.
+     */
+    public @Nullable TrafficDescriptor getTrafficDescriptor() {
+        return mTrafficDescriptor;
+    }
+
+    /**
+     * Check if this data profile can satisfy certain network capabilities
+     *
+     * @param networkCapabilities The network capabilities. Note that the non-APN-type capabilities
+     * will be ignored.
+     *
+     * @return {@code true} if this data profile can satisfy the given network capabilities.
+     * @hide
+     */
+    public boolean canSatisfy(@NonNull @NetCapability int[] networkCapabilities) {
+        if (mApnSetting != null) {
+            for (int netCap : networkCapabilities) {
+                if (!canSatisfy(netCap)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Check if this data profile can satisfy a certain network capability.
+     *
+     * @param networkCapability The network capability. Note that the non-APN-type capability
+     * will always be satisfied.
+     * @return {@code true} if this data profile can satisfy the given network capability.
+     * @hide
+     */
+    public boolean canSatisfy(@NetCapability int networkCapability) {
+        return mApnSetting != null && mApnSetting.canHandleType(
+                networkCapabilityToApnType(networkCapability));
+    }
+
+    /**
+     * Convert network capability into APN type.
+     *
+     * @param networkCapability Network capability.
+     * @return APN type.
+     * @hide
+     */
+    private static @ApnType int networkCapabilityToApnType(@NetCapability int networkCapability) {
+        switch (networkCapability) {
+            case NetworkCapabilities.NET_CAPABILITY_MMS:
+                return ApnSetting.TYPE_MMS;
+            case NetworkCapabilities.NET_CAPABILITY_SUPL:
+                return ApnSetting.TYPE_SUPL;
+            case NetworkCapabilities.NET_CAPABILITY_DUN:
+                return ApnSetting.TYPE_DUN;
+            case NetworkCapabilities.NET_CAPABILITY_FOTA:
+                return ApnSetting.TYPE_FOTA;
+            case NetworkCapabilities.NET_CAPABILITY_IMS:
+                return ApnSetting.TYPE_IMS;
+            case NetworkCapabilities.NET_CAPABILITY_CBS:
+                return ApnSetting.TYPE_CBS;
+            case NetworkCapabilities.NET_CAPABILITY_XCAP:
+                return ApnSetting.TYPE_XCAP;
+            case NetworkCapabilities.NET_CAPABILITY_EIMS:
+                return ApnSetting.TYPE_EMERGENCY;
+            case NetworkCapabilities.NET_CAPABILITY_INTERNET:
+                return ApnSetting.TYPE_DEFAULT;
+            case NetworkCapabilities.NET_CAPABILITY_MCX:
+                return ApnSetting.TYPE_MCX;
+            case NetworkCapabilities.NET_CAPABILITY_IA:
+                return ApnSetting.TYPE_IA;
+            case NetworkCapabilities.NET_CAPABILITY_BIP:
+                return ApnSetting.TYPE_BIP;
+            case NetworkCapabilities.NET_CAPABILITY_VSIM:
+                return ApnSetting.TYPE_VSIM;
+            case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE:
+                return ApnSetting.TYPE_ENTERPRISE;
+            case NetworkCapabilities.NET_CAPABILITY_RCS:
+                return ApnSetting.TYPE_RCS;
+            default:
+                return ApnSetting.TYPE_NONE;
+        }
+    }
+
+    /**
+     * Set the timestamp of this data profile being used for data network setup.
+     *
+     * @hide
+     */
+    public void setLastSetupTimestamp(@ElapsedRealtimeLong long timestamp) {
+        mSetupTimestamp = timestamp;
+    }
+
+    /**
+     * @return the timestamp of this data profile being used for data network setup.
+     *
+     * @hide
+     */
+    public @ElapsedRealtimeLong long getLastSetupTimestamp() {
+        return mSetupTimestamp;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "[DataProfile=" + mApnSetting + ", " + mTrafficDescriptor + ", preferred="
+                + mPreferred + "]";
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeParcelable(mApnSetting, flags);
+        dest.writeParcelable(mTrafficDescriptor, flags);
+        dest.writeBoolean(mPreferred);
+        dest.writeLong(mSetupTimestamp);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<DataProfile> CREATOR =
+            new Parcelable.Creator<DataProfile>() {
+        @Override
+        public DataProfile createFromParcel(Parcel source) {
+            return new DataProfile(source);
+        }
+
+        @Override
+        public DataProfile[] newArray(int size) {
+            return new DataProfile[size];
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DataProfile that = (DataProfile) o;
+        return mType == that.mType
+                && Objects.equals(mApnSetting, that.mApnSetting)
+                && Objects.equals(mTrafficDescriptor, that.mTrafficDescriptor);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mType, mApnSetting, mTrafficDescriptor);
+    }
+
+    /**
+     * Provides a convenient way to set the fields of a {@link DataProfile} when creating a new
+     * instance.
+     *
+     * <p>The example below shows how you might create a new {@code DataProfile}:
+     *
+     * <pre><code>
+     *
+     * DataProfile dp = new DataProfile.Builder()
+     *     .setApn("apn.xyz.com")
+     *     .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
+     *     .build();
+     * </code></pre>
+     */
+    public static final class Builder {
+        private int mProfileId;
+
+        private String mApn;
+
+        @ProtocolType
+        private int mProtocolType;
+
+        @AuthType
+        private int mAuthType;
+
+        private String mUserName;
+
+        private String mPassword;
+
+        @Type
+        private int mType = -1;
+
+        private boolean mEnabled = true;
+
+        @ApnType
+        private int mSupportedApnTypesBitmask;
+
+        @ProtocolType
+        private int mRoamingProtocolType;
+
+        @NetworkTypeBitMask
+        private int mBearerBitmask;
+
+        private int mMtuV4;
+
+        private int mMtuV6;
+
+        private boolean mPersistent;
+
+        private boolean mPreferred;
+
+        private ApnSetting mApnSetting;
+
+        private TrafficDescriptor mTrafficDescriptor;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Set profile id. Note that this is not a global unique id of the data profile. This id
+         * is only used by certain CDMA carriers to identify the type of data profile.
+         *
+         * @param profileId Network domain.
+         * @return The same instance of the builder.
+         * @deprecated use {@link #setApnSetting(ApnSetting)} and
+         * {@link ApnSetting.Builder#setProfileId(int)} instead.
+         */
+        @Deprecated
+        public @NonNull Builder setProfileId(int profileId) {
+            mProfileId = profileId;
+            return this;
+        }
+
+        /**
+         * Set the APN (Access Point Name) to establish data connection. This is a string
+         * specifically defined by the carrier.
+         *
+         * @param apn Access point name
+         * @return The same instance of the builder.
+         * @deprecated use {@link #setApnSetting(ApnSetting)} and
+         * {@link ApnSetting.Builder#setApnName(String)} instead.
+         */
+        @Deprecated
+        public @NonNull Builder setApn(@NonNull String apn) {
+            mApn = apn;
+            return this;
+        }
+
+        /**
+         * Set the connection protocol type.
+         *
+         * @param protocolType The connection protocol defined in 3GPP TS 27.007 section 10.1.1.
+         * @return The same instance of the builder.
+         * @deprecated use {@link #setApnSetting(ApnSetting)} and
+         * {@link ApnSetting.Builder#setProtocol(int)} instead.
+         */
+        @Deprecated
+        public @NonNull Builder setProtocolType(@ProtocolType int protocolType) {
+            mProtocolType = protocolType;
+            return this;
+        }
+
+        /**
+         * Set the authentication type.
+         *
+         * @param authType The authentication type
+         * @return The same instance of the builder.
+         * @deprecated use {@link #setApnSetting(ApnSetting)} and
+         * {@link ApnSetting.Builder#setAuthType(int)} instead.
+         */
+        @Deprecated
+        public @NonNull Builder setAuthType(@AuthType int authType) {
+            mAuthType = authType;
+            return this;
+        }
+
+        /**
+         * Set the user name
+         *
+         * @param userName The user name
+         * @return The same instance of the builder.
+         * @deprecated use {@link #setApnSetting(ApnSetting)} and
+         * {@link ApnSetting.Builder#setUser(String)} instead.
+         */
+        @Deprecated
+        public @NonNull Builder setUserName(@NonNull String userName) {
+            mUserName = userName;
+            return this;
+        }
+
+        /**
+         * Set the password
+         *
+         * @param password The password
+         * @return The same instance of the builder.
+         * @deprecated use {@link #setApnSetting(ApnSetting)} and
+         * {@link ApnSetting.Builder#setPassword(String)} (int)} instead.
+         */
+        @Deprecated
+        public @NonNull Builder setPassword(@NonNull String password) {
+            mPassword = password;
+            return this;
+        }
+
+        /**
+         * Set the type
+         *
+         * @param type The profile type
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setType(@Type int type) {
+            mType = type;
+            return this;
+        }
+
+        /**
+         * Enable the data profile
+         *
+         * @param isEnabled {@code true} to enable the data profile, otherwise disable.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder enable(boolean isEnabled) {
+            mEnabled = isEnabled;
+            return this;
+        }
+
+        /**
+         * Set the supported APN types bitmask.
+         *
+         * @param supportedApnTypesBitmask The supported APN types bitmask.
+         * @return The same instance of the builder.
+         * @deprecated use {@link #setApnSetting(ApnSetting)} and
+         * {@link ApnSetting.Builder#setApnTypeBitmask(int)} instead.
+         */
+        @Deprecated
+        public @NonNull Builder setSupportedApnTypesBitmask(@ApnType int supportedApnTypesBitmask) {
+            mSupportedApnTypesBitmask = supportedApnTypesBitmask;
+            return this;
+        }
+
+        /**
+         * Set the connection protocol type for roaming.
+         *
+         * @param protocolType The connection protocol defined in 3GPP TS 27.007 section 10.1.1.
+         * @return The same instance of the builder.
+         * @deprecated use {@link #setApnSetting(ApnSetting)} and
+         * {@link ApnSetting.Builder#setRoamingProtocol(int)} instead.
+         */
+        @Deprecated
+        public @NonNull Builder setRoamingProtocolType(@ProtocolType int protocolType) {
+            mRoamingProtocolType = protocolType;
+            return this;
+        }
+
+        /**
+         * Set the bearer bitmask indicating the applicable networks for this data profile.
+         *
+         * @param bearerBitmask The bearer bitmask indicating the applicable networks for this data
+         * profile.
+         * @return The same instance of the builder.
+         * @deprecated use {@link #setApnSetting(ApnSetting)} and
+         * {@link ApnSetting.Builder#setNetworkTypeBitmask(int)} instead.
+         */
+        @Deprecated
+        public @NonNull Builder setBearerBitmask(@NetworkTypeBitMask int bearerBitmask) {
+            mBearerBitmask = bearerBitmask;
+            return this;
+        }
+
+        /**
+         * Set the maximum transmission unit (MTU) size in bytes.
+         *
+         * @param mtu The maximum transmission unit (MTU) size in bytes.
+         * @return The same instance of the builder.
+         * @deprecated use {@link #setApnSetting(ApnSetting)} and
+         * {@link ApnSetting.Builder#setMtuV4(int)}/{@link ApnSetting.Builder#setMtuV6(int)}
+         * instead.
+         */
+        @Deprecated
+        public @NonNull Builder setMtu(int mtu) {
+            mMtuV4 = mMtuV6 = mtu;
+            return this;
+        }
+
+        /**
+         * Set the maximum transmission unit (MTU) size in bytes, for IPv4.
+         *
+         * @param mtu The maximum transmission unit (MTU) size in bytes.
+         * @return The same instance of the builder.
+         * @deprecated Use {{@link #setApnSetting(ApnSetting)}} and
+         * {@link ApnSetting.Builder#setMtuV4(int)} instead.
+         */
+        @Deprecated
+        public @NonNull Builder setMtuV4(int mtu) {
+            mMtuV4 = mtu;
+            return this;
+        }
+
+        /**
+         * Set the maximum transmission unit (MTU) size in bytes, for IPv6.
+         *
+         * @param mtu The maximum transmission unit (MTU) size in bytes.
+         * @return The same instance of the builder.
+         * @deprecated Use {{@link #setApnSetting(ApnSetting)}} and
+         * {@link ApnSetting.Builder#setMtuV6(int)} instead.
+         */
+        @Deprecated
+        public @NonNull Builder setMtuV6(int mtu) {
+            mMtuV6 = mtu;
+            return this;
+        }
+
+        /**
+         * Set data profile as preferred/non-preferred.
+         *
+         * @param isPreferred {@code true} if this data profile was used to bring up the last
+         * default (i.e internet) data connection successfully, or the one chosen by the user in
+         * Settings' APN editor. For one carrier there can be only one profiled preferred.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setPreferred(boolean isPreferred) {
+            mPreferred = isPreferred;
+            return this;
+        }
+
+        /**
+         * Set data profile as persistent/non-persistent.
+         *
+         * @param isPersistent {@code true} if this data profile was used to bring up the last
+         * default (i.e internet) data connection successfully.
+         * @return The same instance of the builder.
+         * @deprecated Use {{@link #setApnSetting(ApnSetting)}} and
+         * {@link ApnSetting.Builder#setPersistent(boolean)} instead.
+         */
+        @Deprecated
+        public @NonNull Builder setPersistent(boolean isPersistent) {
+            mPersistent = isPersistent;
+            return this;
+        }
+
+        /**
+         * Set the APN setting. Note that if an APN setting is not set here, then either
+         * {@link #setApn(String)} or {@link #setTrafficDescriptor(TrafficDescriptor)} must be
+         * called. Otherwise {@link IllegalArgumentException} will be thrown when {@link #build()}
+         * the data profile.
+         *
+         * @param apnSetting The APN setting.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setApnSetting(@NonNull ApnSetting apnSetting) {
+            mApnSetting = apnSetting;
+            return this;
+        }
+
+        /**
+         * Set the traffic descriptor. Note that if a traffic descriptor is not set here, then
+         * either {@link #setApnSetting(ApnSetting)} or {@link #setApn(String)} must be called.
+         * Otherwise {@link IllegalArgumentException} will be thrown when {@link #build()} the data
+         * profile.
+         *
+         * @param trafficDescriptor The traffic descriptor.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setTrafficDescriptor(@NonNull TrafficDescriptor trafficDescriptor) {
+            mTrafficDescriptor = trafficDescriptor;
+            return this;
+        }
+
+        /**
+         * Build the DataProfile object.
+         *
+         * @return The data profile object.
+         */
+        public @NonNull DataProfile build() {
+            if (mApnSetting == null && mApn != null) {
+                // This is for backwards compatibility.
+                mApnSetting = new ApnSetting.Builder()
+                        .setEntryName(mApn)
+                        .setApnName(mApn)
+                        .setApnTypeBitmask(mSupportedApnTypesBitmask)
+                        .setAuthType(mAuthType)
+                        .setCarrierEnabled(mEnabled)
+                        .setModemCognitive(mPersistent)
+                        .setMtuV4(mMtuV4)
+                        .setMtuV6(mMtuV6)
+                        .setNetworkTypeBitmask(mBearerBitmask)
+                        .setProfileId(mProfileId)
+                        .setPassword(mPassword)
+                        .setProtocol(mProtocolType)
+                        .setRoamingProtocol(mRoamingProtocolType)
+                        .setUser(mUserName)
+                        .build();
+            }
+
+            if (mApnSetting == null && mTrafficDescriptor == null) {
+                throw new IllegalArgumentException("APN setting and traffic descriptor can't be "
+                        + "both null.");
+            }
+
+            return new DataProfile(this);
+        }
+    }
+}
diff --git a/android-35/android/telephony/data/DataService.java b/android-35/android/telephony/data/DataService.java
new file mode 100644
index 0000000..f04e1c9
--- /dev/null
+++ b/android-35/android/telephony/data/DataService.java
@@ -0,0 +1,947 @@
+/*
+ * Copyright 2017 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.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.net.LinkProperties;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.util.FunctionalUtils;
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Base class of data service. Services that extend DataService must register the service in
+ * their AndroidManifest to be detected by the framework. They must be protected by the permission
+ * "android.permission.BIND_TELEPHONY_DATA_SERVICE". The data service definition in the manifest
+ * must follow the following format:
+ * ...
+ * <service android:name=".xxxDataService"
+ *     android:permission="android.permission.BIND_TELEPHONY_DATA_SERVICE" >
+ *     <intent-filter>
+ *         <action android:name="android.telephony.data.DataService" />
+ *     </intent-filter>
+ * </service>
+ * @hide
+ */
+@SystemApi
+public abstract class DataService extends Service {
+    private static final String TAG = DataService.class.getSimpleName();
+
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE = "android.telephony.data.DataService";
+
+    /** {@hide} */
+    @IntDef(prefix = "REQUEST_REASON_", value = {
+            REQUEST_REASON_UNKNOWN,
+            REQUEST_REASON_NORMAL,
+            REQUEST_REASON_HANDOVER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SetupDataReason {}
+
+    /** {@hide} */
+    @IntDef(prefix = "REQUEST_REASON_", value = {
+            REQUEST_REASON_UNKNOWN,
+            REQUEST_REASON_NORMAL,
+            REQUEST_REASON_SHUTDOWN,
+            REQUEST_REASON_HANDOVER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DeactivateDataReason {}
+
+    /** The reason of the data request is unknown */
+    public static final int REQUEST_REASON_UNKNOWN = 0;
+
+    /** The reason of the data request is normal */
+    public static final int REQUEST_REASON_NORMAL = 1;
+
+    /** The reason of the data request is device shutdown */
+    public static final int REQUEST_REASON_SHUTDOWN = 2;
+
+    /** The reason of the data request is IWLAN handover */
+    public static final int REQUEST_REASON_HANDOVER = 3;
+
+    private static final int DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER                 = 1;
+    private static final int DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER                 = 2;
+    private static final int DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS            = 3;
+    private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL                      = 4;
+    private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL                 = 5;
+    private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN               = 6;
+    private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE                     = 7;
+    private static final int DATA_SERVICE_REQUEST_REQUEST_DATA_CALL_LIST               = 8;
+    private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED      = 9;
+    private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED    = 10;
+    private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED            = 11;
+    private static final int DATA_SERVICE_REQUEST_START_HANDOVER                       = 12;
+    private static final int DATA_SERVICE_REQUEST_CANCEL_HANDOVER                      = 13;
+    private static final int DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED             = 14;
+    private static final int DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED           = 15;
+    private static final int DATA_SERVICE_INDICATION_APN_UNTHROTTLED                   = 16;
+    private static final int DATA_SERVICE_REQUEST_VALIDATION                           = 17;
+
+    private final HandlerThread mHandlerThread;
+
+    private final DataServiceHandler mHandler;
+
+    private final Executor mHandlerExecutor;
+
+    private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>();
+
+    /** @hide */
+    @VisibleForTesting
+    public final IDataServiceWrapper mBinder = new IDataServiceWrapper();
+
+    /**
+     * The abstract class of the actual data service implementation. The data service provider
+     * must extend this class to support data connection. Note that each instance of data service
+     * provider is associated with one physical SIM slot.
+     */
+    public abstract class DataServiceProvider implements AutoCloseable {
+
+        private final int mSlotIndex;
+
+        private final List<IDataServiceCallback> mDataCallListChangedCallbacks = new ArrayList<>();
+
+        private final List<IDataServiceCallback> mApnUnthrottledCallbacks = new ArrayList<>();
+
+        /**
+         * Constructor
+         * @param slotIndex SIM slot index the data service provider associated with.
+         */
+        public DataServiceProvider(int slotIndex) {
+            mSlotIndex = slotIndex;
+        }
+
+        /**
+         * @return SIM slot index the data service provider associated with.
+         */
+        public final int getSlotIndex() {
+            return mSlotIndex;
+        }
+
+        /**
+         * Setup a data connection. The data service provider must implement this method to support
+         * establishing a packet data connection. When completed or error, the service must invoke
+         * the provided callback to notify the platform.
+         *
+         * @param accessNetworkType Access network type that the data call will be established on.
+         *        Must be one of {@link android.telephony.AccessNetworkConstants.AccessNetworkType}.
+         * @param dataProfile Data profile used for data call setup. See {@link DataProfile}
+         * @param isRoaming True if the device is data roaming.
+         * @param allowRoaming True if data roaming is allowed by the user.
+         * @param reason The reason for data setup. Must be {@link #REQUEST_REASON_NORMAL} or
+         *        {@link #REQUEST_REASON_HANDOVER}.
+         * @param linkProperties If {@code reason} is {@link #REQUEST_REASON_HANDOVER}, this is the
+         *        link properties of the existing data connection, otherwise null.
+         * @param callback The result callback for this request.
+         */
+        public void setupDataCall(
+                @RadioAccessNetworkType int accessNetworkType, @NonNull DataProfile dataProfile,
+                boolean isRoaming, boolean allowRoaming,
+                @SetupDataReason int reason, @Nullable LinkProperties linkProperties,
+                @NonNull DataServiceCallback callback) {
+            // The default implementation is to return unsupported.
+            if (callback != null) {
+                callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED,
+                        null);
+            }
+        }
+
+        /**
+         * Setup a data connection. The data service provider must implement this method to support
+         * establishing a packet data connection. When completed or error, the service must invoke
+         * the provided callback to notify the platform.
+         *
+         * @param accessNetworkType Access network type that the data call will be established on.
+         *        Must be one of {@link android.telephony.AccessNetworkConstants.AccessNetworkType}.
+         * @param dataProfile Data profile used for data call setup. See {@link DataProfile}
+         * @param isRoaming True if the device is data roaming.
+         * @param allowRoaming True if data roaming is allowed by the user.
+         * @param reason The reason for data setup. Must be {@link #REQUEST_REASON_NORMAL} or
+         *        {@link #REQUEST_REASON_HANDOVER}.
+         * @param linkProperties If {@code reason} is {@link #REQUEST_REASON_HANDOVER}, this is the
+         *        link properties of the existing data connection, otherwise null.
+         * @param pduSessionId The pdu session id to be used for this data call.
+         *                     The standard range of values are 1-15 while 0 means no pdu session id
+         *                     was attached to this call.  Reference: 3GPP TS 24.007 section
+         *                     11.2.3.1b.
+         * @param sliceInfo used within the data connection when a handover occurs from EPDG to 5G.
+         *        The value is null unless the access network is
+         *        {@link android.telephony.AccessNetworkConstants.AccessNetworkType#NGRAN} and a
+         *        handover is occurring from EPDG to 5G.  If the slice passed is rejected, then
+         *        {@link DataCallResponse#getCause()} is
+         *        {@link android.telephony.DataFailCause#SLICE_REJECTED}.
+         * @param trafficDescriptor {@link TrafficDescriptor} for which data connection needs to be
+         *        established. It is used for URSP traffic matching as described in 3GPP TS 24.526
+         *        Section 4.2.2. It includes an optional DNN which, if present, must be used for
+         *        traffic matching; it does not specify the end point to be used for the data call.
+         * @param matchAllRuleAllowed Indicates if using default match-all URSP rule for this
+         *        request is allowed. If false, this request must not use the match-all URSP rule
+         *        and if a non-match-all rule is not found (or if URSP rules are not available) then
+         *        {@link DataCallResponse#getCause()} is
+         *        {@link android.telephony.DataFailCause#MATCH_ALL_RULE_NOT_ALLOWED}. This is needed
+         *        as some requests need to have a hard failure if the intention cannot be met,
+         *        for example, a zero-rating slice.
+         * @param callback The result callback for this request.
+         */
+        public void setupDataCall(
+                @RadioAccessNetworkType int accessNetworkType, @NonNull DataProfile dataProfile,
+                boolean isRoaming, boolean allowRoaming,
+                @SetupDataReason int reason,
+                @Nullable LinkProperties linkProperties,
+                @IntRange(from = 0, to = 15) int pduSessionId, @Nullable NetworkSliceInfo sliceInfo,
+                @Nullable TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
+                @NonNull DataServiceCallback callback) {
+            /* Call the old version since the new version isn't supported */
+            setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming, reason,
+                    linkProperties, callback);
+        }
+
+        /**
+         * Deactivate a data connection. The data service provider must implement this method to
+         * support data connection tear down. When completed or error, the service must invoke the
+         * provided callback to notify the platform.
+         *
+         * @param cid Call id returned in the callback of {@link DataServiceProvider#setupDataCall(
+         *        int, DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)}.
+         * @param reason The reason for data deactivation. Must be {@link #REQUEST_REASON_NORMAL},
+         *        {@link #REQUEST_REASON_SHUTDOWN} or {@link #REQUEST_REASON_HANDOVER}.
+         * @param callback The result callback for this request. Null if the client does not care
+         *        about the result.
+         *
+         */
+        public void deactivateDataCall(int cid, @DeactivateDataReason int reason,
+                                       @Nullable DataServiceCallback callback) {
+            // The default implementation is to return unsupported.
+            if (callback != null) {
+                callback.onDeactivateDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+            }
+        }
+
+        /**
+         * Set an APN to initial attach network.
+         *
+         * @param dataProfile Data profile used for data call setup. See {@link DataProfile}.
+         * @param isRoaming True if the device is data roaming.
+         * @param callback The result callback for this request.
+         */
+        public void setInitialAttachApn(@NonNull DataProfile dataProfile, boolean isRoaming,
+                                        @NonNull DataServiceCallback callback) {
+            // The default implementation is to return unsupported.
+            if (callback != null) {
+                callback.onSetInitialAttachApnComplete(
+                        DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+            }
+        }
+
+        /**
+         * Send current carrier's data profiles to the data service for data call setup. This is
+         * only for CDMA carrier that can change the profile through OTA. The data service should
+         * always uses the latest data profile sent by the framework.
+         *
+         * @param dps A list of data profiles.
+         * @param isRoaming True if the device is data roaming.
+         * @param callback The result callback for this request.
+         */
+        public void setDataProfile(@NonNull List<DataProfile> dps, boolean isRoaming,
+                                   @NonNull DataServiceCallback callback) {
+            // The default implementation is to return unsupported.
+            if (callback != null) {
+                callback.onSetDataProfileComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+            }
+        }
+
+        /**
+         * Indicates that a handover has begun.  This is called on the source transport.
+         *
+         * Any resources being transferred cannot be released while a
+         * handover is underway.
+         * <p/>
+         * If a handover was unsuccessful, then the framework calls
+         * {@link DataService#cancelHandover}.  The target transport retains ownership over any of
+         * the resources being transferred.
+         * <p/>
+         * If a handover was successful, the framework calls {@link DataService#deactivateDataCall}
+         * with reason {@link DataService.REQUEST_REASON_HANDOVER}. The target transport now owns
+         * the transferred resources and is responsible for releasing them.
+         *
+         * <p/>
+         * Note that the callback will be executed on binder thread.
+         *
+         * @param cid The identifier of the data call which is provided in {@link DataCallResponse}
+         * @param callback The result callback for this request.
+         *
+         * @hide
+         */
+        public void startHandover(int cid, @NonNull DataServiceCallback callback) {
+            Objects.requireNonNull(callback, "callback cannot be null");
+            // The default implementation is to return unsupported.
+            Log.d(TAG, "startHandover: " + cid);
+            callback.onHandoverStarted(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+        }
+
+        /**
+         * Indicates that a handover was cancelled after a call to
+         * {@link DataService#startHandover}. This is called on the source transport.
+         * <p/>
+         * Since the handover was unsuccessful, the source transport retains ownership over any of
+         * the resources being transferred and is still responsible for releasing them.
+         * <p/>
+         * The handover can be cancelled up until either:
+         * <ul><li>
+         *     The handover was successful after receiving a successful response from
+         *     {@link DataService#setupDataCall} on the target transport.
+         * </li><li>
+         *     The data call on the source transport was lost.
+         * </li>
+         * </ul>
+         *
+         * <p/>
+         * Note that the callback will be executed on binder thread.
+         *
+         * @param cid The identifier of the data call which is provided in {@link DataCallResponse}
+         * @param callback The result callback for this request.
+         *
+         * @hide
+         */
+        public void cancelHandover(int cid, @NonNull DataServiceCallback callback) {
+            Objects.requireNonNull(callback, "callback cannot be null");
+            // The default implementation is to return unsupported.
+            Log.d(TAG, "cancelHandover: " + cid);
+            callback.onHandoverCancelled(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+        }
+
+        /**
+         * Get the active data call list.
+         *
+         * @param callback The result callback for this request.
+         */
+        public void requestDataCallList(@NonNull DataServiceCallback callback) {
+            // The default implementation is to return unsupported.
+            callback.onRequestDataCallListComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED,
+                    Collections.EMPTY_LIST);
+        }
+
+        private void registerForDataCallListChanged(IDataServiceCallback callback) {
+            synchronized (mDataCallListChangedCallbacks) {
+                mDataCallListChangedCallbacks.add(callback);
+            }
+        }
+
+        private void unregisterForDataCallListChanged(IDataServiceCallback callback) {
+            synchronized (mDataCallListChangedCallbacks) {
+                mDataCallListChangedCallbacks.remove(callback);
+            }
+        }
+
+        private void registerForApnUnthrottled(IDataServiceCallback callback) {
+            synchronized (mApnUnthrottledCallbacks) {
+                mApnUnthrottledCallbacks.add(callback);
+            }
+        }
+
+        private void unregisterForApnUnthrottled(IDataServiceCallback callback) {
+            synchronized (mApnUnthrottledCallbacks) {
+                mApnUnthrottledCallbacks.remove(callback);
+            }
+        }
+
+        /**
+         * Request validation check to see if the network is working properly for a given data call.
+         *
+         * <p>This request is completed immediately after submitting the request to the data service
+         * provider and receiving {@link DataServiceCallback.ResultCode}, and progress status or
+         * validation results are notified through {@link
+         * DataCallResponse#getNetworkValidationStatus}.
+         *
+         * <p> If the network validation request is submitted successfully, {@link
+         * DataServiceCallback#RESULT_SUCCESS} is passed to {@code resultCodeCallback}. If the
+         * network validation feature is not supported by the data service provider itself, {@link
+         * DataServiceCallback#RESULT_ERROR_UNSUPPORTED} is passed to {@code resultCodeCallback}.
+         * See {@link DataServiceCallback.ResultCode} for the type of response that indicates
+         * whether the request was successfully submitted or had an error.
+         *
+         * <p>In response to this network validation request, providers can validate the data call
+         * in their own way. For example, in IWLAN, the DPD (Dead Peer Detection) can be used as a
+         * tool to check whether a data call is alive.
+         *
+         * @param cid The identifier of the data call which is provided in {@link DataCallResponse}
+         * @param executor The callback executor for the response.
+         * @param resultCodeCallback Listener for the {@link DataServiceCallback.ResultCode} that
+         *     request validation to the DataService and checks if the request has been submitted.
+         */
+        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+        public void requestNetworkValidation(int cid,
+                @NonNull @CallbackExecutor Executor executor,
+                @NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) {
+            Objects.requireNonNull(executor, "executor cannot be null");
+            Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null");
+
+            Log.d(TAG, "requestNetworkValidation: " + cid);
+
+            // The default implementation is to return unsupported.
+            executor.execute(() -> resultCodeCallback
+                    .accept(DataServiceCallback.RESULT_ERROR_UNSUPPORTED));
+        }
+
+        /**
+         * Notify the system that current data call list changed. Data service must invoke this
+         * method whenever there is any data call status changed.
+         *
+         * @param dataCallList List of the current active data call.
+         */
+        public final void notifyDataCallListChanged(List<DataCallResponse> dataCallList) {
+            synchronized (mDataCallListChangedCallbacks) {
+                for (IDataServiceCallback callback : mDataCallListChangedCallbacks) {
+                    mHandler.obtainMessage(DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED,
+                            mSlotIndex, 0, new DataCallListChangedIndication(dataCallList,
+                                    callback)).sendToTarget();
+                }
+            }
+        }
+
+        /**
+         * Notify the system that a given APN was unthrottled.
+         *
+         * @param apn Access Point Name defined by the carrier.
+         */
+        public final void notifyApnUnthrottled(@NonNull String apn) {
+            synchronized (mApnUnthrottledCallbacks) {
+                for (IDataServiceCallback callback : mApnUnthrottledCallbacks) {
+                    mHandler.obtainMessage(DATA_SERVICE_INDICATION_APN_UNTHROTTLED,
+                            mSlotIndex, 0, new ApnUnthrottledIndication(apn,
+                                    callback)).sendToTarget();
+                }
+            }
+        }
+
+        /**
+         * Notify the system that a given DataProfile was unthrottled.
+         *
+         * @param dataProfile DataProfile associated with an APN returned from the modem
+         */
+        public final void notifyDataProfileUnthrottled(@NonNull DataProfile dataProfile) {
+            synchronized (mApnUnthrottledCallbacks) {
+                for (IDataServiceCallback callback : mApnUnthrottledCallbacks) {
+                    mHandler.obtainMessage(DATA_SERVICE_INDICATION_APN_UNTHROTTLED,
+                            mSlotIndex, 0, new ApnUnthrottledIndication(dataProfile,
+                                    callback)).sendToTarget();
+                }
+            }
+        }
+
+        /**
+         * Called when the instance of data service is destroyed (e.g. got unbind or binder died)
+         * or when the data service provider is removed. The extended class should implement this
+         * method to perform cleanup works.
+         */
+        @Override
+        public abstract void close();
+    }
+
+    private static final class SetupDataCallRequest {
+        public final int accessNetworkType;
+        public final DataProfile dataProfile;
+        public final boolean isRoaming;
+        public final boolean allowRoaming;
+        public final int reason;
+        public final LinkProperties linkProperties;
+        public final int pduSessionId;
+        public final NetworkSliceInfo sliceInfo;
+        public final TrafficDescriptor trafficDescriptor;
+        public final boolean matchAllRuleAllowed;
+        public final IDataServiceCallback callback;
+        SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+                boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
+                NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+                boolean matchAllRuleAllowed, IDataServiceCallback callback) {
+            this.accessNetworkType = accessNetworkType;
+            this.dataProfile = dataProfile;
+            this.isRoaming = isRoaming;
+            this.allowRoaming = allowRoaming;
+            this.linkProperties = linkProperties;
+            this.reason = reason;
+            this.pduSessionId = pduSessionId;
+            this.sliceInfo = sliceInfo;
+            this.trafficDescriptor = trafficDescriptor;
+            this.matchAllRuleAllowed = matchAllRuleAllowed;
+            this.callback = callback;
+        }
+    }
+
+    private static final class DeactivateDataCallRequest {
+        public final int cid;
+        public final int reason;
+        public final IDataServiceCallback callback;
+        DeactivateDataCallRequest(int cid, int reason, IDataServiceCallback callback) {
+            this.cid = cid;
+            this.reason = reason;
+            this.callback = callback;
+        }
+    }
+
+    private static final class SetInitialAttachApnRequest {
+        public final DataProfile dataProfile;
+        public final boolean isRoaming;
+        public final IDataServiceCallback callback;
+        SetInitialAttachApnRequest(DataProfile dataProfile, boolean isRoaming,
+                                   IDataServiceCallback callback) {
+            this.dataProfile = dataProfile;
+            this.isRoaming = isRoaming;
+            this.callback = callback;
+        }
+    }
+
+    private static final class SetDataProfileRequest {
+        public final List<DataProfile> dps;
+        public final boolean isRoaming;
+        public final IDataServiceCallback callback;
+        SetDataProfileRequest(List<DataProfile> dps, boolean isRoaming,
+                              IDataServiceCallback callback) {
+            this.dps = dps;
+            this.isRoaming = isRoaming;
+            this.callback = callback;
+        }
+    }
+
+    private static final class BeginCancelHandoverRequest {
+        public final int cid;
+        public final IDataServiceCallback callback;
+        BeginCancelHandoverRequest(int cid,
+                IDataServiceCallback callback) {
+            this.cid = cid;
+            this.callback = callback;
+        }
+    }
+
+    private static final class DataCallListChangedIndication {
+        public final List<DataCallResponse> dataCallList;
+        public final IDataServiceCallback callback;
+        DataCallListChangedIndication(List<DataCallResponse> dataCallList,
+                                      IDataServiceCallback callback) {
+            this.dataCallList = dataCallList;
+            this.callback = callback;
+        }
+    }
+
+    private static final class ApnUnthrottledIndication {
+        public final DataProfile dataProfile;
+        public final String apn;
+        public final IDataServiceCallback callback;
+        ApnUnthrottledIndication(String apn,
+                IDataServiceCallback callback) {
+            this.dataProfile = null;
+            this.apn = apn;
+            this.callback = callback;
+        }
+        ApnUnthrottledIndication(DataProfile dataProfile, IDataServiceCallback callback) {
+            this.dataProfile = dataProfile;
+            this.apn = null;
+            this.callback = callback;
+        }
+    }
+
+    private static final class ValidationRequest {
+        public final int cid;
+        public final Executor executor;
+        public final IIntegerConsumer callback;
+        ValidationRequest(int cid, Executor executor, IIntegerConsumer callback) {
+            this.cid = cid;
+            this.executor = executor;
+            this.callback = callback;
+        }
+    }
+
+    private class DataServiceHandler extends Handler {
+
+        DataServiceHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            IDataServiceCallback callback;
+            final int slotIndex = message.arg1;
+            DataServiceProvider serviceProvider = mServiceMap.get(slotIndex);
+
+            switch (message.what) {
+                case DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER:
+                    serviceProvider = onCreateDataServiceProvider(message.arg1);
+                    if (serviceProvider != null) {
+                        mServiceMap.put(slotIndex, serviceProvider);
+                    }
+                    break;
+                case DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER:
+                    if (serviceProvider != null) {
+                        serviceProvider.close();
+                        mServiceMap.remove(slotIndex);
+                    }
+                    break;
+                case DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS:
+                    for (int i = 0; i < mServiceMap.size(); i++) {
+                        serviceProvider = mServiceMap.get(i);
+                        if (serviceProvider != null) {
+                            serviceProvider.close();
+                        }
+                    }
+                    mServiceMap.clear();
+                    break;
+                case DATA_SERVICE_REQUEST_SETUP_DATA_CALL:
+                    if (serviceProvider == null) break;
+                    SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj;
+                    serviceProvider.setupDataCall(setupDataCallRequest.accessNetworkType,
+                            setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
+                            setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
+                            setupDataCallRequest.linkProperties, setupDataCallRequest.pduSessionId,
+                            setupDataCallRequest.sliceInfo, setupDataCallRequest.trafficDescriptor,
+                            setupDataCallRequest.matchAllRuleAllowed,
+                            (setupDataCallRequest.callback != null)
+                                    ? new DataServiceCallback(setupDataCallRequest.callback)
+                                    : null);
+
+                    break;
+                case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL:
+                    if (serviceProvider == null) break;
+                    DeactivateDataCallRequest deactivateDataCallRequest =
+                            (DeactivateDataCallRequest) message.obj;
+                    serviceProvider.deactivateDataCall(deactivateDataCallRequest.cid,
+                            deactivateDataCallRequest.reason,
+                            (deactivateDataCallRequest.callback != null)
+                                    ? new DataServiceCallback(deactivateDataCallRequest.callback)
+                                    : null);
+                    break;
+                case DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN:
+                    if (serviceProvider == null) break;
+                    SetInitialAttachApnRequest setInitialAttachApnRequest =
+                            (SetInitialAttachApnRequest) message.obj;
+                    serviceProvider.setInitialAttachApn(setInitialAttachApnRequest.dataProfile,
+                            setInitialAttachApnRequest.isRoaming,
+                            (setInitialAttachApnRequest.callback != null)
+                                    ? new DataServiceCallback(setInitialAttachApnRequest.callback)
+                                    : null);
+                    break;
+                case DATA_SERVICE_REQUEST_SET_DATA_PROFILE:
+                    if (serviceProvider == null) break;
+                    SetDataProfileRequest setDataProfileRequest =
+                            (SetDataProfileRequest) message.obj;
+                    serviceProvider.setDataProfile(setDataProfileRequest.dps,
+                            setDataProfileRequest.isRoaming,
+                            (setDataProfileRequest.callback != null)
+                                    ? new DataServiceCallback(setDataProfileRequest.callback)
+                                    : null);
+                    break;
+                case DATA_SERVICE_REQUEST_REQUEST_DATA_CALL_LIST:
+                    if (serviceProvider == null) break;
+
+                    serviceProvider.requestDataCallList(new DataServiceCallback(
+                            (IDataServiceCallback) message.obj));
+                    break;
+                case DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED:
+                    if (serviceProvider == null) break;
+                    serviceProvider.registerForDataCallListChanged((IDataServiceCallback) message.obj);
+                    break;
+                case DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED:
+                    if (serviceProvider == null) break;
+                    callback = (IDataServiceCallback) message.obj;
+                    serviceProvider.unregisterForDataCallListChanged(callback);
+                    break;
+                case DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED:
+                    if (serviceProvider == null) break;
+                    DataCallListChangedIndication indication =
+                            (DataCallListChangedIndication) message.obj;
+                    try {
+                        indication.callback.onDataCallListChanged(indication.dataCallList);
+                    } catch (RemoteException e) {
+                        loge("Failed to call onDataCallListChanged. " + e);
+                    }
+                    break;
+                case DATA_SERVICE_REQUEST_START_HANDOVER:
+                    if (serviceProvider == null) break;
+                    BeginCancelHandoverRequest bReq = (BeginCancelHandoverRequest) message.obj;
+                    serviceProvider.startHandover(bReq.cid,
+                            (bReq.callback != null)
+                                    ? new DataServiceCallback(bReq.callback) : null);
+                    break;
+                case DATA_SERVICE_REQUEST_CANCEL_HANDOVER:
+                    if (serviceProvider == null) break;
+                    BeginCancelHandoverRequest cReq = (BeginCancelHandoverRequest) message.obj;
+                    serviceProvider.cancelHandover(cReq.cid,
+                            (cReq.callback != null)
+                                    ? new DataServiceCallback(cReq.callback) : null);
+                    break;
+                case DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED:
+                    if (serviceProvider == null) break;
+                    serviceProvider.registerForApnUnthrottled((IDataServiceCallback) message.obj);
+                    break;
+                case DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED:
+                    if (serviceProvider == null) break;
+                    callback = (IDataServiceCallback) message.obj;
+                    serviceProvider.unregisterForApnUnthrottled(callback);
+                    break;
+                case DATA_SERVICE_INDICATION_APN_UNTHROTTLED:
+                    if (serviceProvider == null) break;
+                    ApnUnthrottledIndication apnUnthrottledIndication =
+                            (ApnUnthrottledIndication) message.obj;
+                    try {
+                        if (apnUnthrottledIndication.dataProfile != null) {
+                            apnUnthrottledIndication.callback
+                                    .onDataProfileUnthrottled(apnUnthrottledIndication.dataProfile);
+                        } else {
+                            apnUnthrottledIndication.callback
+                                    .onApnUnthrottled(apnUnthrottledIndication.apn);
+                        }
+                    } catch (RemoteException e) {
+                        loge("Failed to call onApnUnthrottled. " + e);
+                    }
+                    break;
+                case DATA_SERVICE_REQUEST_VALIDATION:
+                    if (serviceProvider == null) break;
+                    ValidationRequest validationRequest = (ValidationRequest) message.obj;
+                    serviceProvider.requestNetworkValidation(
+                            validationRequest.cid,
+                            validationRequest.executor,
+                            FunctionalUtils
+                                    .ignoreRemoteException(validationRequest.callback::accept));
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Default constructor.
+     */
+    public DataService() {
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+
+        mHandler = new DataServiceHandler(mHandlerThread.getLooper());
+        mHandlerExecutor = new HandlerExecutor(mHandler);
+        log("Data service created");
+    }
+
+    /**
+     * Create the instance of {@link DataServiceProvider}. Data service provider must override
+     * this method to facilitate the creation of {@link DataServiceProvider} instances. The system
+     * will call this method after binding the data service for each active SIM slot id.
+     *
+     * This methead is guaranteed to be invoked in {@link DataService}'s internal handler thread
+     * whose looper can be retrieved with {@link Looper.myLooper()} when override this method.
+     *
+     * @param slotIndex SIM slot id the data service associated with.
+     * @return Data service object. Null if failed to create the provider (e.g. invalid slot index)
+     */
+    @Nullable
+    public abstract DataServiceProvider onCreateDataServiceProvider(int slotIndex);
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (intent == null || !SERVICE_INTERFACE.equals(intent.getAction())) {
+            loge("Unexpected intent " + intent);
+            return null;
+        }
+        return mBinder;
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        mHandler.obtainMessage(DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS).sendToTarget();
+        return false;
+    }
+
+    @Override
+    public void onDestroy() {
+        mHandlerThread.quitSafely();
+        super.onDestroy();
+    }
+
+    /**
+     * A wrapper around IDataService that forwards calls to implementations of {@link DataService}.
+     */
+    private class IDataServiceWrapper extends IDataService.Stub {
+        @Override
+        public void createDataServiceProvider(int slotIndex) {
+            mHandler.obtainMessage(DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER, slotIndex, 0)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void removeDataServiceProvider(int slotIndex) {
+            mHandler.obtainMessage(DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER, slotIndex, 0)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile,
+                boolean isRoaming, boolean allowRoaming, int reason,
+                LinkProperties linkProperties, int pduSessionId, NetworkSliceInfo sliceInfo,
+                TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
+                IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotIndex, 0,
+                    new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
+                            allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
+                            trafficDescriptor, matchAllRuleAllowed, callback))
+                    .sendToTarget();
+        }
+
+        @Override
+        public void deactivateDataCall(int slotIndex, int cid, int reason,
+                                       IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, slotIndex, 0,
+                    new DeactivateDataCallRequest(cid, reason, callback))
+                    .sendToTarget();
+        }
+
+        @Override
+        public void setInitialAttachApn(int slotIndex, DataProfile dataProfile, boolean isRoaming,
+                                        IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, slotIndex, 0,
+                    new SetInitialAttachApnRequest(dataProfile, isRoaming, callback))
+                    .sendToTarget();
+        }
+
+        @Override
+        public void setDataProfile(int slotIndex, List<DataProfile> dps, boolean isRoaming,
+                                   IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, slotIndex, 0,
+                    new SetDataProfileRequest(dps, isRoaming, callback)).sendToTarget();
+        }
+
+        @Override
+        public void requestDataCallList(int slotIndex, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("requestDataCallList: callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_REQUEST_DATA_CALL_LIST, slotIndex, 0,
+                    callback).sendToTarget();
+        }
+
+        @Override
+        public void registerForDataCallListChanged(int slotIndex, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("registerForDataCallListChanged: callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, slotIndex,
+                    0, callback).sendToTarget();
+        }
+
+        @Override
+        public void unregisterForDataCallListChanged(int slotIndex, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("unregisterForDataCallListChanged: callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED,
+                    slotIndex, 0, callback).sendToTarget();
+        }
+
+        @Override
+        public void startHandover(int slotIndex, int cid, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("startHandover: callback is null");
+                return;
+            }
+            BeginCancelHandoverRequest req = new BeginCancelHandoverRequest(cid, callback);
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_START_HANDOVER,
+                    slotIndex, 0, req)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void cancelHandover(int slotIndex, int cid, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("cancelHandover: callback is null");
+                return;
+            }
+            BeginCancelHandoverRequest req = new BeginCancelHandoverRequest(cid, callback);
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_CANCEL_HANDOVER,
+                    slotIndex, 0, req).sendToTarget();
+        }
+
+        @Override
+        public void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("registerForUnthrottleApn: callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED, slotIndex,
+                    0, callback).sendToTarget();
+        }
+
+        @Override
+        public void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("uregisterForUnthrottleApn: callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED,
+                    slotIndex, 0, callback).sendToTarget();
+        }
+
+        @Override
+        public void requestNetworkValidation(int slotIndex, int cid,
+                IIntegerConsumer resultCodeCallback) {
+            if (resultCodeCallback == null) {
+                loge("requestNetworkValidation: resultCodeCallback is null");
+                return;
+            }
+            ValidationRequest validationRequest =
+                    new ValidationRequest(cid, mHandlerExecutor, resultCodeCallback);
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_VALIDATION,
+                    slotIndex, 0, validationRequest).sendToTarget();
+        }
+    }
+
+    private void log(String s) {
+        Rlog.d(TAG, s);
+    }
+
+    private void loge(String s) {
+        Rlog.e(TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/data/DataServiceCallback.java b/android-35/android/telephony/data/DataServiceCallback.java
new file mode 100644
index 0000000..8287a03
--- /dev/null
+++ b/android-35/android/telephony/data/DataServiceCallback.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2017 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.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.LinkProperties;
+import android.os.RemoteException;
+import android.telephony.data.DataService.DataServiceProvider;
+
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Data service callback, which is for bound data service to invoke for solicited and unsolicited
+ * response. The caller is responsible to create a callback object for each single asynchronous
+ * request.
+ *
+ * @hide
+ */
+@SystemApi
+public class DataServiceCallback {
+
+    private static final String TAG = DataServiceCallback.class.getSimpleName();
+
+    private static final boolean DBG = true;
+
+    /**
+     * Result of data requests
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
+            RESULT_ERROR_ILLEGAL_STATE, RESULT_ERROR_TEMPORARILY_UNAVAILABLE})
+    public @interface ResultCode {}
+
+    /** Request is completed successfully */
+    public static final int RESULT_SUCCESS              = 0;
+    /** Request is not supported */
+    public static final int RESULT_ERROR_UNSUPPORTED    = 1;
+    /** Request contains invalid arguments */
+    public static final int RESULT_ERROR_INVALID_ARG    = 2;
+    /** Service is busy */
+    public static final int RESULT_ERROR_BUSY           = 3;
+    /** Request sent in illegal state */
+    public static final int RESULT_ERROR_ILLEGAL_STATE  = 4;
+    /**
+     * Service is temporarily unavailable. Frameworks should retry the request again.
+     * @hide
+     */
+    public static final int RESULT_ERROR_TEMPORARILY_UNAVAILABLE = 5;
+
+    private final IDataServiceCallback mCallback;
+
+    /** @hide */
+    public DataServiceCallback(IDataServiceCallback callback) {
+        mCallback = callback;
+    }
+
+    /**
+     * Called to indicate result for the request {@link DataServiceProvider#setupDataCall(int,
+     * DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)} .
+     *
+     * @param result The result code. Must be one of the {@link ResultCode}.
+     * @param response Setup data call response.
+     */
+    public void onSetupDataCallComplete(@ResultCode int result,
+            @Nullable DataCallResponse response) {
+        if (mCallback != null) {
+            try {
+                if (DBG) Rlog.d(TAG, "onSetupDataCallComplete");
+                mCallback.onSetupDataCallComplete(result, response);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onSetupDataCallComplete on the remote");
+            }
+        } else {
+            Rlog.e(TAG, "onSetupDataCallComplete: callback is null!");
+        }
+    }
+
+    /**
+     * Called to indicate result for the request {@link DataServiceProvider#deactivateDataCall(int,
+     * int, DataServiceCallback)}
+     *
+     * @param result The result code. Must be one of the {@link ResultCode}.
+     */
+    public void onDeactivateDataCallComplete(@ResultCode int result) {
+        if (mCallback != null) {
+            try {
+                if (DBG) Rlog.d(TAG, "onDeactivateDataCallComplete");
+                mCallback.onDeactivateDataCallComplete(result);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onDeactivateDataCallComplete on the remote");
+            }
+        } else {
+            Rlog.e(TAG, "onDeactivateDataCallComplete: callback is null!");
+        }
+    }
+
+    /**
+     * Called to indicate result for the request {@link DataServiceProvider#setInitialAttachApn(
+     * DataProfile, boolean, DataServiceCallback)}.
+     *
+     * @param result The result code. Must be one of the {@link ResultCode}.
+     */
+    public void onSetInitialAttachApnComplete(@ResultCode int result) {
+        if (mCallback != null) {
+            try {
+                mCallback.onSetInitialAttachApnComplete(result);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onSetInitialAttachApnComplete on the remote");
+            }
+        } else {
+            Rlog.e(TAG, "onSetInitialAttachApnComplete: callback is null!");
+        }
+    }
+
+    /**
+     * Called to indicate result for the request {@link DataServiceProvider#setDataProfile(List,
+     * boolean, DataServiceCallback)}.
+     *
+     * @param result The result code. Must be one of the {@link ResultCode}.
+     */
+    public void onSetDataProfileComplete(@ResultCode int result) {
+        if (mCallback != null) {
+            try {
+                mCallback.onSetDataProfileComplete(result);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onSetDataProfileComplete on the remote");
+            }
+        } else {
+            Rlog.e(TAG, "onSetDataProfileComplete: callback is null!");
+        }
+    }
+
+    /**
+     * Called to indicate result for the request {@link DataServiceProvider#requestDataCallList(
+     * DataServiceCallback)}.
+     *
+     * @param result The result code. Must be one of the {@link ResultCode}.
+     * @param dataCallList List of the current active data connection. If no data call is presented,
+     * set it to an empty list.
+     */
+    public void onRequestDataCallListComplete(@ResultCode int result,
+            @NonNull List<DataCallResponse> dataCallList) {
+        if (mCallback != null) {
+            try {
+                mCallback.onRequestDataCallListComplete(result, dataCallList);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onRequestDataCallListComplete on the remote");
+            }
+        } else {
+            Rlog.e(TAG, "onRequestDataCallListComplete: callback is null!");
+        }
+    }
+
+    /**
+     * Called to indicate that data connection list changed. If no data call is presented, set it to
+     * an empty list.
+     *
+     * @param dataCallList List of the current active data connection.
+     */
+    public void onDataCallListChanged(@NonNull List<DataCallResponse> dataCallList) {
+        if (mCallback != null) {
+            try {
+                if (DBG) Rlog.d(TAG, "onDataCallListChanged");
+                mCallback.onDataCallListChanged(dataCallList);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onDataCallListChanged on the remote");
+            }
+        } else {
+            Rlog.e(TAG, "onDataCallListChanged: callback is null!");
+        }
+    }
+
+    /**
+     * Called to indicate result for the request {@link DataService#startHandover}.
+     *
+     * @param result The result code. Must be one of the {@link ResultCode}
+     *
+     * @hide
+     */
+    public void onHandoverStarted(@ResultCode int result) {
+        if (mCallback != null) {
+            try {
+                if (DBG) Rlog.d(TAG, "onHandoverStarted");
+                mCallback.onHandoverStarted(result);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onHandoverStarted on the remote");
+            }
+        } else {
+            Rlog.e(TAG, "onHandoverStarted: callback is null!");
+        }
+    }
+
+    /**
+     * Called to indicate result for the request {@link DataService#cancelHandover}.
+     *
+     * @param result The result code. Must be one of the {@link ResultCode}
+     *
+     * @hide
+     */
+    public void onHandoverCancelled(@ResultCode int result) {
+        if (mCallback != null) {
+            try {
+                if (DBG) Rlog.d(TAG, "onHandoverCancelled");
+                mCallback.onHandoverCancelled(result);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "Failed to onHandoverCancelled on the remote");
+            }
+        } else {
+            Rlog.e(TAG, "onHandoverCancelled: callback is null!");
+        }
+    }
+
+    /**
+     * Get the result code as a string
+     *
+     * @param resultCode The result code. Must be one of the {@link ResultCode}
+     * @return the string representation
+     *
+     * @hide
+     */
+    @NonNull
+    public static String resultCodeToString(@DataServiceCallback.ResultCode int resultCode) {
+        switch (resultCode) {
+            case RESULT_SUCCESS:
+                return "RESULT_SUCCESS";
+            case RESULT_ERROR_UNSUPPORTED:
+                return "RESULT_ERROR_UNSUPPORTED";
+            case RESULT_ERROR_INVALID_ARG:
+                return "RESULT_ERROR_INVALID_ARG";
+            case RESULT_ERROR_BUSY:
+                return "RESULT_ERROR_BUSY";
+            case RESULT_ERROR_ILLEGAL_STATE:
+                return "RESULT_ERROR_ILLEGAL_STATE";
+            case RESULT_ERROR_TEMPORARILY_UNAVAILABLE:
+                return "RESULT_ERROR_TEMPORARILY_UNAVAILABLE";
+            default:
+                return "Unknown(" + resultCode + ")";
+        }
+    }
+
+    /**
+     * Unthrottles the APN on the current transport.
+     * The APN is throttled when {@link IDataService#setupDataCall} fails within
+     * the time specified by {@link DataCallResponse#getRetryDurationMillis} and will remain
+     * throttled until this method is called.
+     * <p/>
+     * see: {@link DataCallResponse#getRetryDurationMillis}
+     *
+     * @param apn Access Point Name defined by the carrier.
+     */
+    public void onApnUnthrottled(final @NonNull String apn) {
+        if (mCallback != null) {
+            try {
+                if (DBG) Rlog.d(TAG, "onApnUnthrottled");
+                mCallback.onApnUnthrottled(apn);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "onApnUnthrottled: remote exception", e);
+            }
+        } else {
+            Rlog.e(TAG, "onApnUnthrottled: callback is null!");
+        }
+    }
+
+    /**
+     * Unthrottles the DataProfile on the current transport.
+     * The DataProfile is throttled when {@link IDataService#setupDataCall} fails within
+     * the time specified by {@link DataCallResponse#getRetryDurationMillis} and will remain
+     * throttled until this method is called.
+     * <p/>
+     * see: {@link DataCallResponse#getRetryDurationMillis}
+     *
+     * @param dataProfile DataProfile containing the APN to be throttled
+     */
+    public void onDataProfileUnthrottled(final @NonNull DataProfile dataProfile) {
+        if (mCallback != null) {
+            try {
+                if (DBG) Rlog.d(TAG, "onDataProfileUnthrottled");
+                mCallback.onDataProfileUnthrottled(dataProfile);
+            } catch (RemoteException e) {
+                Rlog.e(TAG, "onDataProfileUnthrottled: remote exception", e);
+            }
+        } else {
+            Rlog.e(TAG, "onDataProfileUnthrottled: callback is null!");
+        }
+    }
+}
diff --git a/android-35/android/telephony/data/EpsBearerQosSessionAttributes.java b/android-35/android/telephony/data/EpsBearerQosSessionAttributes.java
new file mode 100644
index 0000000..5ffee56
--- /dev/null
+++ b/android-35/android/telephony/data/EpsBearerQosSessionAttributes.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.QosSessionAttributes;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides QOS attributes of an EPS bearer.
+ *
+ * <p> The dedicated EPS bearer along with QOS is allocated by the LTE network and notified to the
+ * device. The Telephony framework creates the {@link EpsBearerQosSessionAttributes} object which
+ * represents the QOS of the dedicated bearer and notifies the same to applications via
+ * {@link QosCallback}.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class EpsBearerQosSessionAttributes implements Parcelable, QosSessionAttributes {
+    private static final String TAG = EpsBearerQosSessionAttributes.class.getSimpleName();
+    private final int mQci;
+    private final long mMaxUplinkBitRate;
+    private final long mMaxDownlinkBitRate;
+    private final long mGuaranteedUplinkBitRate;
+    private final long mGuaranteedDownlinkBitRate;
+    @NonNull private final List<InetSocketAddress> mRemoteAddresses;
+
+    /**
+     * Quality of Service Class Identifier (QCI), see 3GPP TS 23.203 and 29.212.
+     * The allowed values are standard values(1-9, 65-68, 69-70, 75, 79-80, 82-85)
+     * defined in the spec and operator specific values in the range 128-254.
+     *
+     * @return the qci of the session
+     */
+    public int getQosIdentifier() {
+        return mQci;
+    }
+
+    /**
+     * Minimum bit rate in kbps that is guaranteed to be provided by the network on the uplink.
+     *
+     * see 3GPP TS 23.107 section 6.4.3.1
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the guaranteed bit rate in kbps
+     */
+    public long getGuaranteedUplinkBitRateKbps() {
+        return mGuaranteedUplinkBitRate;
+    }
+
+    /**
+     * Minimum bit rate in kbps that is guaranteed to be provided by the network on the downlink.
+     *
+     * see 3GPP TS 23.107 section 6.4.3.1
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the guaranteed bit rate in kbps
+     */
+    public long getGuaranteedDownlinkBitRateKbps() {
+        return mGuaranteedDownlinkBitRate;
+    }
+
+    /**
+     * The maximum kbps that the network will accept.
+     *
+     * see 3GPP TS 23.107 section 6.4.3.1
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the max uplink bit rate in kbps
+     */
+    public long getMaxUplinkBitRateKbps() {
+        return mMaxUplinkBitRate;
+    }
+
+    /**
+     * The maximum kbps that the network can provide.
+     *
+     * see 3GPP TS 23.107 section 6.4.3.1
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the max downlink bit rate in kbps
+     */
+    public long getMaxDownlinkBitRateKbps() {
+        return mMaxDownlinkBitRate;
+    }
+
+    /**
+     * List of remote addresses associated with the Qos Session.  The given uplink bit rates apply
+     * to this given list of remote addresses.
+     *
+     * Note: In the event that the list is empty, it is assumed that the uplink bit rates apply to
+     * all remote addresses that are not contained in a different set of attributes.
+     *
+     * @return list of remote socket addresses that the attributes apply to
+     */
+    @NonNull
+    public List<InetSocketAddress> getRemoteAddresses() {
+        return mRemoteAddresses;
+    }
+
+    /**
+     * ..ctor for attributes
+     *
+     * @param qci quality class indicator
+     * @param maxDownlinkBitRate the max downlink bit rate in kbps
+     * @param maxUplinkBitRate the max uplink bit rate in kbps
+     * @param guaranteedDownlinkBitRate the guaranteed downlink bit rate in kbps
+     * @param guaranteedUplinkBitRate the guaranteed uplink bit rate in kbps
+     * @param remoteAddresses the remote addresses that the uplink bit rates apply to
+     *
+     * @hide
+     */
+    public EpsBearerQosSessionAttributes(final int qci,
+            final long maxDownlinkBitRate, final long maxUplinkBitRate,
+            final long guaranteedDownlinkBitRate, final long guaranteedUplinkBitRate,
+            @NonNull final List<InetSocketAddress> remoteAddresses) {
+        Objects.requireNonNull(remoteAddresses, "remoteAddress must be non-null");
+        mQci = qci;
+        mMaxDownlinkBitRate = maxDownlinkBitRate;
+        mMaxUplinkBitRate = maxUplinkBitRate;
+        mGuaranteedDownlinkBitRate = guaranteedDownlinkBitRate;
+        mGuaranteedUplinkBitRate = guaranteedUplinkBitRate;
+
+        final List<InetSocketAddress> remoteAddressesTemp = copySocketAddresses(remoteAddresses);
+        mRemoteAddresses = Collections.unmodifiableList(remoteAddressesTemp);
+    }
+
+    private static List<InetSocketAddress> copySocketAddresses(
+            @NonNull final List<InetSocketAddress> remoteAddresses) {
+        final List<InetSocketAddress> remoteAddressesTemp = new ArrayList<>();
+        for (final InetSocketAddress socketAddress : remoteAddresses) {
+            if (socketAddress != null && socketAddress.getAddress() != null) {
+                remoteAddressesTemp.add(socketAddress);
+            }
+        }
+        return remoteAddressesTemp;
+    }
+
+    private EpsBearerQosSessionAttributes(@NonNull final Parcel in) {
+        mQci = in.readInt();
+        mMaxDownlinkBitRate = in.readLong();
+        mMaxUplinkBitRate = in.readLong();
+        mGuaranteedDownlinkBitRate = in.readLong();
+        mGuaranteedUplinkBitRate = in.readLong();
+
+        final int size = in.readInt();
+        final List<InetSocketAddress> remoteAddresses = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            final byte[] addressBytes = in.createByteArray();
+            final int port = in.readInt();
+            try {
+                remoteAddresses.add(
+                        new InetSocketAddress(InetAddress.getByAddress(addressBytes), port));
+            } catch (final UnknownHostException e) {
+                // Impossible case since we filter out null values in the ..ctor
+                Log.e(TAG, "unable to unparcel remote address at index: " + i, e);
+            }
+        }
+        mRemoteAddresses = Collections.unmodifiableList(remoteAddresses);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+        dest.writeInt(mQci);
+        dest.writeLong(mMaxDownlinkBitRate);
+        dest.writeLong(mMaxUplinkBitRate);
+        dest.writeLong(mGuaranteedDownlinkBitRate);
+        dest.writeLong(mGuaranteedUplinkBitRate);
+
+        final int size = mRemoteAddresses.size();
+        dest.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            final InetSocketAddress address = mRemoteAddresses.get(i);
+            dest.writeByteArray(address.getAddress().getAddress());
+            dest.writeInt(address.getPort());
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        EpsBearerQosSessionAttributes epsBearerAttr = (EpsBearerQosSessionAttributes) o;
+        return mQci == epsBearerAttr.mQci
+                && mMaxUplinkBitRate == epsBearerAttr.mMaxUplinkBitRate
+                && mMaxDownlinkBitRate == epsBearerAttr.mMaxDownlinkBitRate
+                && mGuaranteedUplinkBitRate == epsBearerAttr.mGuaranteedUplinkBitRate
+                && mGuaranteedDownlinkBitRate == epsBearerAttr.mGuaranteedDownlinkBitRate
+                && mRemoteAddresses.size() == epsBearerAttr.mRemoteAddresses.size()
+                && mRemoteAddresses.containsAll(epsBearerAttr.mRemoteAddresses);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mQci, mMaxUplinkBitRate, mMaxDownlinkBitRate,
+                mGuaranteedUplinkBitRate, mGuaranteedDownlinkBitRate, mRemoteAddresses);
+    }
+
+    @NonNull
+    public static final Creator<EpsBearerQosSessionAttributes> CREATOR =
+            new Creator<EpsBearerQosSessionAttributes>() {
+        @NonNull
+        @Override
+        public EpsBearerQosSessionAttributes createFromParcel(@NonNull final Parcel in) {
+            return new EpsBearerQosSessionAttributes(in);
+        }
+
+        @NonNull
+        @Override
+        public EpsBearerQosSessionAttributes[] newArray(final int size) {
+            return new EpsBearerQosSessionAttributes[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/data/EpsQos.java b/android-35/android/telephony/data/EpsQos.java
new file mode 100644
index 0000000..64c956f
--- /dev/null
+++ b/android-35/android/telephony/data/EpsQos.java
@@ -0,0 +1,103 @@
+/**
+ * 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(QosBandwidth downlink, QosBandwidth uplink, int qosClassId) {
+        super(Qos.QOS_TYPE_EPS, downlink, uplink);
+        this.qosClassId = qosClassId;
+    }
+
+    private EpsQos(Parcel source) {
+        super(source);
+        qosClassId = source.readInt();
+    }
+
+    public int getQci() {
+        return qosClassId;
+    }
+
+    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/android-35/android/telephony/data/NetworkSliceInfo.java b/android-35/android/telephony/data/NetworkSliceInfo.java
new file mode 100644
index 0000000..232a930
--- /dev/null
+++ b/android-35/android/telephony/data/NetworkSliceInfo.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+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;
+
+/**
+ * Represents a S-NSSAI as defined in 3GPP TS 24.501, which represents a network slice.
+ *
+ * There are 2 main fields that define a slice, SliceServiceType and SliceDifferentiator.
+ * SliceServiceType defines the type of service provided by the slice, and SliceDifferentiator is
+ * used to differentiate between multiple slices of the same type. If the devices is not on HPLMN,
+ * the mappedHplmn versions of these 2 fields indicate the corresponding values in HPLMN.
+ */
+public final class NetworkSliceInfo implements Parcelable {
+    /**
+     * When set on a Slice Differentiator, this value indicates that there is no corresponding
+     * Slice.
+     */
+    public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1;
+
+    /**
+     *  Indicates that the service type is not present.
+     */
+    public static final int SLICE_SERVICE_TYPE_NONE = 0;
+
+    /**
+     *  Slice suitable for the handling of 5G enhanced Mobile Broadband.
+     */
+    public static final int SLICE_SERVICE_TYPE_EMBB = 1;
+
+    /**
+     * Slice suitable for the handling of ultra-reliable low latency communications.
+     */
+    public static final int SLICE_SERVICE_TYPE_URLLC = 2;
+
+    /**
+     * Slice suitable for the handling of massive IoT.
+     */
+    public static final int SLICE_SERVICE_TYPE_MIOT = 3;
+
+    /**
+     * The min acceptable value for a Slice Differentiator
+     * @hide
+     */
+    public static final int MIN_SLICE_DIFFERENTIATOR = -1;
+
+    /**
+     * The max acceptable value for a Slice Differentiator
+     * @hide
+     */
+    public static final int MAX_SLICE_DIFFERENTIATOR = 0xFFFFFE;
+
+    /** @hide */
+    @IntDef(prefix = { "SLICE_SERVICE_TYPE_" }, value = {
+            SLICE_SERVICE_TYPE_NONE,
+            SLICE_SERVICE_TYPE_EMBB,
+            SLICE_SERVICE_TYPE_URLLC,
+            SLICE_SERVICE_TYPE_MIOT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SliceServiceType {}
+
+    /**
+     * The slice status is unknown. This can happen during IWLAN->cellular handover when the
+     * NetworkSliceInfo is received over IWLAN.
+     */
+    public static final int SLICE_STATUS_UNKNOWN = 0;
+
+    /**
+     * The slice is configured but not allowed or rejected yet.
+     */
+    public static final int SLICE_STATUS_CONFIGURED = 1;
+
+    /**
+     * The slice is allowed to be used.
+     */
+    public static final int SLICE_STATUS_ALLOWED = 2;
+
+    /**
+     * The slice is rejected because not available in PLMN.
+     */
+    public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN = 3;
+
+    /**
+     * The slice is rejected because not available in registered area.
+     */
+    public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA = 4;
+
+    /**
+     * The slice is configured by home operator(HPLMN) in default and is used if configured/allowed
+     * slices are not available for the serving PLMN.
+     */
+    public static final int SLICE_STATUS_DEFAULT_CONFIGURED = 5;
+
+    /**
+     * The min acceptable value for a slice status.
+     * @hide
+     */
+    public static final int MIN_SLICE_STATUS = SLICE_STATUS_UNKNOWN;
+
+    /**
+     * The max acceptable value for a slice status.
+     * @hide
+     */
+    public static final int MAX_SLICE_STATUS = SLICE_STATUS_DEFAULT_CONFIGURED;
+
+    /** @hide */
+    @IntDef(prefix = { "SLICE_STATUS_" }, value = {
+            SLICE_STATUS_UNKNOWN,
+            SLICE_STATUS_CONFIGURED,
+            SLICE_STATUS_ALLOWED,
+            SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN,
+            SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA,
+            SLICE_STATUS_DEFAULT_CONFIGURED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SliceStatus {}
+
+
+    @SliceServiceType
+    private final int mSliceServiceType;
+    @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+    private final int mSliceDifferentiator;
+    @SliceServiceType
+    private final int mMappedHplmnSliceServiceType;
+    @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+    private final int mMappedHplmnSliceDifferentiator;
+    @SliceStatus
+    @IntRange(from = MIN_SLICE_STATUS, to = MAX_SLICE_STATUS)
+    private final int mStatus;
+
+    private NetworkSliceInfo(@SliceServiceType int sliceServiceType,
+            int sliceDifferentiator, int mappedHplmnSliceServiceType,
+            int mappedHplmnSliceDifferentiator, int status) {
+        mSliceServiceType = sliceServiceType;
+        mSliceDifferentiator = sliceDifferentiator;
+        mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator;
+        mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType;
+        mStatus = status;
+    }
+
+    /**
+     * The type of service provided by the slice.
+     * <p/>
+     * see: 3GPP TS 24.501 Section 9.11.2.8.
+     */
+    @SliceServiceType
+    public int getSliceServiceType() {
+        return mSliceServiceType;
+    }
+
+    /**
+     * Identifies the slice from others with the same Slice Service Type.
+     * <p/>
+     * Returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE} if {@link #getSliceServiceType} returns
+     * {@link #SLICE_SERVICE_TYPE_NONE}.
+     * <p/>
+     * see: 3GPP TS 24.501 Section 9.11.2.8.
+     */
+    @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+    public int getSliceDifferentiator() {
+        return mSliceDifferentiator;
+    }
+
+    /**
+     * Corresponds to a Slice Info (S-NSSAI) of the HPLMN.
+     * <p/>
+     * see: 3GPP TS 24.501 Section 9.11.2.8.
+     */
+    @SliceServiceType
+    public int getMappedHplmnSliceServiceType() {
+        return mMappedHplmnSliceServiceType;
+    }
+
+    /**
+     * This Slice Differentiator corresponds to a {@link NetworkSliceInfo} (S-NSSAI) of the HPLMN;
+     * {@link #getSliceDifferentiator()} is mapped to this value.
+     * <p/>
+     * Returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE} if either of the following are true:
+     * <ul>
+     * <li>{@link #getSliceDifferentiator()} returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE}</li>
+     * <li>{@link #getMappedHplmnSliceServiceType()} returns {@link #SLICE_SERVICE_TYPE_NONE}</li>
+     * </ul>
+     * <p/>
+     * see: 3GPP TS 24.501 Section 9.11.2.8.
+     */
+    @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+    public int getMappedHplmnSliceDifferentiator() {
+        return mMappedHplmnSliceDifferentiator;
+    }
+
+    /**
+     * Field to indicate the current status of the slice.
+     * @return the current status for this slice info.
+     */
+    @SliceStatus
+    public int getStatus() {
+        return mStatus;
+    }
+
+    private NetworkSliceInfo(@NonNull Parcel in) {
+        mSliceServiceType = in.readInt();
+        mSliceDifferentiator = in.readInt();
+        mMappedHplmnSliceServiceType = in.readInt();
+        mMappedHplmnSliceDifferentiator = in.readInt();
+        mStatus = in.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mSliceServiceType);
+        dest.writeInt(mSliceDifferentiator);
+        dest.writeInt(mMappedHplmnSliceServiceType);
+        dest.writeInt(mMappedHplmnSliceDifferentiator);
+        dest.writeInt(mStatus);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<NetworkSliceInfo> CREATOR =
+            new Parcelable.Creator<NetworkSliceInfo>() {
+                @Override
+                @NonNull
+                public NetworkSliceInfo createFromParcel(@NonNull Parcel source) {
+                    return new NetworkSliceInfo(source);
+                }
+
+                @Override
+                @NonNull
+                public NetworkSliceInfo[] newArray(int size) {
+                    return new NetworkSliceInfo[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return "SliceInfo{"
+                + "mSliceServiceType=" + sliceServiceTypeToString(mSliceServiceType)
+                + ", mSliceDifferentiator=" + mSliceDifferentiator
+                + ", mMappedHplmnSliceServiceType="
+                + sliceServiceTypeToString(mMappedHplmnSliceServiceType)
+                + ", mMappedHplmnSliceDifferentiator=" + mMappedHplmnSliceDifferentiator
+                + ", mStatus=" + sliceStatusToString(mStatus)
+                + '}';
+    }
+
+    private static String sliceServiceTypeToString(@SliceServiceType int sliceServiceType) {
+        switch(sliceServiceType) {
+            case SLICE_SERVICE_TYPE_NONE:
+                return "NONE";
+            case SLICE_SERVICE_TYPE_EMBB:
+                return "EMBB";
+            case SLICE_SERVICE_TYPE_URLLC:
+                return "URLLC";
+            case SLICE_SERVICE_TYPE_MIOT:
+                return "MIOT";
+            default:
+                return Integer.toString(sliceServiceType);
+        }
+    }
+
+    private static String sliceStatusToString(@SliceStatus int sliceStatus) {
+        switch(sliceStatus) {
+            case SLICE_STATUS_UNKNOWN:
+                return "UNKNOWN";
+            case SLICE_STATUS_CONFIGURED:
+                return "CONFIGURED";
+            case SLICE_STATUS_ALLOWED:
+                return "ALLOWED";
+            case SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN:
+                return "REJECTED_NOT_AVAILABLE_IN_PLMN";
+            case SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA:
+                return "REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA";
+            case SLICE_STATUS_DEFAULT_CONFIGURED:
+                return "DEFAULT_CONFIGURED";
+            default:
+                return Integer.toString(sliceStatus);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        NetworkSliceInfo sliceInfo = (NetworkSliceInfo) o;
+        return mSliceServiceType == sliceInfo.mSliceServiceType
+                && mSliceDifferentiator == sliceInfo.mSliceDifferentiator
+                && mMappedHplmnSliceServiceType == sliceInfo.mMappedHplmnSliceServiceType
+                && mMappedHplmnSliceDifferentiator == sliceInfo.mMappedHplmnSliceDifferentiator
+                && mStatus == sliceInfo.mStatus;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSliceServiceType, mSliceDifferentiator, mMappedHplmnSliceServiceType,
+                mMappedHplmnSliceDifferentiator, mStatus);
+    }
+
+    /**
+     * Provides a convenient way to set the fields of a {@link NetworkSliceInfo} when creating a
+     * new instance.
+     *
+     * <p>The example below shows how you might create a new {@code SliceInfo}:
+     *
+     * <pre><code>
+     *
+     * SliceInfo response = new SliceInfo.Builder()
+     *     .setSliceServiceType(SLICE_SERVICE_TYPE_URLLC)
+     *     .build();
+     * </code></pre>
+     */
+    public static final class Builder {
+        @SliceServiceType
+        private int mSliceServiceType = SLICE_SERVICE_TYPE_NONE;
+        @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+        private int mSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE;
+        @SliceServiceType
+        private int mMappedHplmnSliceServiceType = SLICE_SERVICE_TYPE_NONE;
+        @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+        private int mMappedHplmnSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE;
+        @SliceStatus
+        @IntRange(from = MIN_SLICE_STATUS, to = MAX_SLICE_STATUS)
+        private int mStatus = SLICE_STATUS_UNKNOWN;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Set the Slice Service Type.
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setSliceServiceType(@SliceServiceType int mSliceServiceType) {
+            this.mSliceServiceType = mSliceServiceType;
+            return this;
+        }
+
+        /**
+         * Set the Slice Differentiator.
+         * <p/>
+         * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no
+         * corresponding Slice.
+         *
+         * @throws IllegalArgumentException if the parameter is not in the expected range.
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setSliceDifferentiator(
+                @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+                        int sliceDifferentiator) {
+            if (sliceDifferentiator < MIN_SLICE_DIFFERENTIATOR
+                    || sliceDifferentiator > MAX_SLICE_DIFFERENTIATOR) {
+                throw new IllegalArgumentException("The slice diffentiator value is out of range");
+            }
+            this.mSliceDifferentiator = sliceDifferentiator;
+            return this;
+        }
+
+        /**
+         * Set the HPLMN Slice Service Type.
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setMappedHplmnSliceServiceType(
+                @SliceServiceType int mappedHplmnSliceServiceType) {
+            this.mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType;
+            return this;
+        }
+
+        /**
+         * Set the HPLMN Slice Differentiator.
+         * <p/>
+         * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no
+         * corresponding Slice of the HPLMN.
+         *
+         * @throws IllegalArgumentException if the parameter is not in the expected range.
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setMappedHplmnSliceDifferentiator(
+                @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+                        int mappedHplmnSliceDifferentiator) {
+            if (mappedHplmnSliceDifferentiator < MIN_SLICE_DIFFERENTIATOR
+                    || mappedHplmnSliceDifferentiator > MAX_SLICE_DIFFERENTIATOR) {
+                throw new IllegalArgumentException("The slice diffentiator value is out of range");
+            }
+            this.mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator;
+            return this;
+        }
+
+        /**
+         * Set the slice status.
+         *
+         * @throws IllegalArgumentException if the status is invalid.
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setStatus(@SliceStatus int status) {
+            if (status < MIN_SLICE_STATUS || status > MAX_SLICE_STATUS) {
+                throw new IllegalArgumentException("The slice status is not valid");
+            }
+            this.mStatus = status;
+            return this;
+        }
+
+        /**
+         * Build the {@link NetworkSliceInfo}.
+         *
+         * @return the {@link NetworkSliceInfo} object.
+         */
+        @NonNull
+        public NetworkSliceInfo build() {
+            return new NetworkSliceInfo(this.mSliceServiceType, this.mSliceDifferentiator,
+                    this.mMappedHplmnSliceServiceType, this.mMappedHplmnSliceDifferentiator,
+                    this.mStatus);
+        }
+    }
+}
diff --git a/android-35/android/telephony/data/NetworkSlicingConfig.java b/android-35/android/telephony/data/NetworkSlicingConfig.java
new file mode 100644
index 0000000..024a3f5
--- /dev/null
+++ b/android-35/android/telephony/data/NetworkSlicingConfig.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright 2021 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.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a slicing configuration
+ */
+public final class NetworkSlicingConfig implements Parcelable {
+    private final List<UrspRule> mUrspRules;
+    private final List<NetworkSliceInfo> mSliceInfo;
+
+    public NetworkSlicingConfig() {
+        mUrspRules = new ArrayList<>();
+        mSliceInfo = new ArrayList<>();
+    }
+
+    /** @hide */
+    public NetworkSlicingConfig(List<UrspRule> urspRules, List<NetworkSliceInfo> sliceInfo) {
+        this();
+        mUrspRules.addAll(urspRules);
+        mSliceInfo.addAll(sliceInfo);
+    }
+
+    /** @hide */
+    public NetworkSlicingConfig(Parcel p) {
+        mUrspRules = p.createTypedArrayList(UrspRule.CREATOR);
+        mSliceInfo = p.createTypedArrayList(NetworkSliceInfo.CREATOR);
+    }
+
+    /**
+     * This list contains the current URSP rules. Empty list represents that no rules are
+     * configured.
+     * @return the current URSP rules for this slicing configuration.
+     */
+    public @NonNull List<UrspRule> getUrspRules() {
+        return mUrspRules;
+    }
+
+    /**
+     * @return the list of all slices for this slicing configuration.
+     */
+    public @NonNull List<NetworkSliceInfo> getSliceInfo() {
+        return mSliceInfo;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedList(mUrspRules, flags);
+        dest.writeTypedList(mSliceInfo, flags);
+    }
+
+    public static final @NonNull Parcelable.Creator<NetworkSlicingConfig> CREATOR =
+            new Parcelable.Creator<NetworkSlicingConfig>() {
+                @Override
+                public NetworkSlicingConfig createFromParcel(Parcel source) {
+                    return new NetworkSlicingConfig(source);
+                }
+
+                @Override
+                public NetworkSlicingConfig[] newArray(int size) {
+                    return new NetworkSlicingConfig[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        NetworkSlicingConfig that = (NetworkSlicingConfig) o;
+        return mUrspRules.size() == that.mUrspRules.size()
+                && mUrspRules.containsAll(that.mUrspRules)
+                && mSliceInfo.size() == that.mSliceInfo.size()
+                && mSliceInfo.containsAll(that.mSliceInfo);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUrspRules, mSliceInfo);
+    }
+
+    @Override
+    public String toString() {
+        return "{.urspRules = " + mUrspRules + ", .sliceInfo = " + mSliceInfo + "}";
+    }
+}
diff --git a/android-35/android/telephony/data/NrQos.java b/android-35/android/telephony/data/NrQos.java
new file mode 100644
index 0000000..a24d5fa
--- /dev/null
+++ b/android-35/android/telephony/data/NrQos.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.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(QosBandwidth downlink, QosBandwidth uplink, int qosFlowId, int fiveQi,
+            int averagingWindowMs) {
+        super(Qos.QOS_TYPE_NR, downlink, uplink);
+        this.qosFlowId = qosFlowId;
+        this.fiveQi = fiveQi;
+        this.averagingWindowMs = 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);
+    }
+
+    public int get5Qi() {
+        return fiveQi;
+    }
+
+    public int getQfi() {
+        return qosFlowId;
+    }
+
+    public int getAveragingWindow() {
+        return averagingWindowMs;
+    }
+
+    @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/android-35/android/telephony/data/NrQosSessionAttributes.java b/android-35/android/telephony/data/NrQosSessionAttributes.java
new file mode 100644
index 0000000..c3a0eed
--- /dev/null
+++ b/android-35/android/telephony/data/NrQosSessionAttributes.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2021 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.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.QosSessionAttributes;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides Qos attributes of an NR bearer.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class NrQosSessionAttributes implements Parcelable, QosSessionAttributes {
+    private static final String TAG = NrQosSessionAttributes.class.getSimpleName();
+    private final int m5Qi;
+    private final @IntRange(from=1, to=63) int mQfi;
+    private final long mMaxUplinkBitRate;
+    private final long mMaxDownlinkBitRate;
+    private final long mGuaranteedUplinkBitRate;
+    private final long mGuaranteedDownlinkBitRate;
+    private final long mAveragingWindow;
+    @NonNull private final List<InetSocketAddress> mRemoteAddresses;
+
+    /**
+     * 5G QOS Identifier (5QI), see 3GPP TS 24.501 and 23.501.
+     * The allowed values are standard values(1-9, 65-68, 69-70, 75, 79-80, 82-85)
+     * defined in the spec and operator specific values in the range 128-254.
+     *
+     * @return the 5QI of the QOS flow
+     */
+    public int getQosIdentifier() {
+        return m5Qi;
+    }
+
+    /**
+     * QOS flow identifier of the QOS flow description in the
+     * range of 1 to 63. see 3GPP TS 24.501 and 23.501.
+     *
+     * @return the QOS flow identifier of the session
+     */
+    public @IntRange(from=1, to=63) int getQosFlowIdentifier() {
+        return mQfi;
+    }
+
+    /**
+     * Minimum bit rate in kbps that is guaranteed to be provided by the network on the uplink.
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the guaranteed bit rate in kbps
+     */
+    public long getGuaranteedUplinkBitRateKbps() {
+        return mGuaranteedUplinkBitRate;
+    }
+
+    /**
+     * Minimum bit rate in kbps that is guaranteed to be provided by the network on the downlink.
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the guaranteed bit rate in kbps
+     */
+    public long getGuaranteedDownlinkBitRateKbps() {
+        return mGuaranteedDownlinkBitRate;
+    }
+
+    /**
+     * The maximum uplink kbps that the network will accept.
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the max uplink bit rate in kbps
+     */
+    public long getMaxUplinkBitRateKbps() {
+        return mMaxUplinkBitRate;
+    }
+
+    /**
+     * The maximum downlink kbps that the network can provide.
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * Note: The Qos Session may be shared with OTHER applications besides yours.
+     *
+     * @return the max downlink bit rate in kbps
+     */
+    public long getMaxDownlinkBitRateKbps() {
+        return mMaxDownlinkBitRate;
+    }
+
+    /**
+     * The duration in milliseconds over which the maximum bit rates and guaranteed bit rates
+     * are calculated
+     *
+     * see 3GPP TS 24.501 section 6.2.5
+     *
+     * @return the averaging window duration in milliseconds
+     */
+    @NonNull
+    public Duration getBitRateWindowDuration() {
+        return Duration.ofMillis(mAveragingWindow);
+    }
+
+    /**
+     * List of remote addresses associated with the Qos Session.  The given uplink bit rates apply
+     * to this given list of remote addresses.
+     *
+     * Note: In the event that the list is empty, it is assumed that the uplink bit rates apply to
+     * all remote addresses that are not contained in a different set of attributes.
+     *
+     * @return list of remote socket addresses that the attributes apply to
+     */
+    @NonNull
+    public List<InetSocketAddress> getRemoteAddresses() {
+        return mRemoteAddresses;
+    }
+
+    /**
+     * ..ctor for attributes
+     *
+     * @param fiveQi 5G quality class indicator
+     * @param qfi QOS flow identifier
+     * @param maxDownlinkBitRate the max downlink bit rate in kbps
+     * @param maxUplinkBitRate the max uplink bit rate in kbps
+     * @param guaranteedDownlinkBitRate the guaranteed downlink bit rate in kbps
+     * @param guaranteedUplinkBitRate the guaranteed uplink bit rate in kbps
+     * @param averagingWindow the averaging window duration in milliseconds
+     * @param remoteAddresses the remote addresses that the uplink bit rates apply to
+     *
+     * @hide
+     */
+    public NrQosSessionAttributes(final int fiveQi, final int qfi,
+            final long maxDownlinkBitRate, final long maxUplinkBitRate,
+            final long guaranteedDownlinkBitRate, final long guaranteedUplinkBitRate,
+            final long averagingWindow, @NonNull final List<InetSocketAddress> remoteAddresses) {
+        Objects.requireNonNull(remoteAddresses, "remoteAddress must be non-null");
+        m5Qi = fiveQi;
+        mQfi = qfi;
+        mMaxDownlinkBitRate = maxDownlinkBitRate;
+        mMaxUplinkBitRate = maxUplinkBitRate;
+        mGuaranteedDownlinkBitRate = guaranteedDownlinkBitRate;
+        mGuaranteedUplinkBitRate = guaranteedUplinkBitRate;
+        mAveragingWindow = averagingWindow;
+
+        final List<InetSocketAddress> remoteAddressesTemp = copySocketAddresses(remoteAddresses);
+        mRemoteAddresses = Collections.unmodifiableList(remoteAddressesTemp);
+    }
+
+    private static List<InetSocketAddress> copySocketAddresses(
+            @NonNull final List<InetSocketAddress> remoteAddresses) {
+        final List<InetSocketAddress> remoteAddressesTemp = new ArrayList<>();
+        for (final InetSocketAddress socketAddress : remoteAddresses) {
+            if (socketAddress != null && socketAddress.getAddress() != null) {
+                remoteAddressesTemp.add(socketAddress);
+            }
+        }
+        return remoteAddressesTemp;
+    }
+
+    private NrQosSessionAttributes(@NonNull final Parcel in) {
+        m5Qi = in.readInt();
+        mQfi = in.readInt();
+        mMaxDownlinkBitRate = in.readLong();
+        mMaxUplinkBitRate = in.readLong();
+        mGuaranteedDownlinkBitRate = in.readLong();
+        mGuaranteedUplinkBitRate = in.readLong();
+        mAveragingWindow = in.readLong();
+
+        final int size = in.readInt();
+        final List<InetSocketAddress> remoteAddresses = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            final byte[] addressBytes = in.createByteArray();
+            final int port = in.readInt();
+            try {
+                remoteAddresses.add(
+                        new InetSocketAddress(InetAddress.getByAddress(addressBytes), port));
+            } catch (final UnknownHostException e) {
+                // Impossible case since its filtered out the null values in the ..ctor
+                Log.e(TAG, "unable to unparcel remote address at index: " + i, e);
+            }
+        }
+        mRemoteAddresses = Collections.unmodifiableList(remoteAddresses);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+        dest.writeInt(m5Qi);
+        dest.writeInt(mQfi);
+        dest.writeLong(mMaxDownlinkBitRate);
+        dest.writeLong(mMaxUplinkBitRate);
+        dest.writeLong(mGuaranteedDownlinkBitRate);
+        dest.writeLong(mGuaranteedUplinkBitRate);
+        dest.writeLong(mAveragingWindow);
+
+        final int size = mRemoteAddresses.size();
+        dest.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            final InetSocketAddress address = mRemoteAddresses.get(i);
+            dest.writeByteArray(address.getAddress().getAddress());
+            dest.writeInt(address.getPort());
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        NrQosSessionAttributes nrQosAttr = (NrQosSessionAttributes) o;
+        return m5Qi == nrQosAttr.m5Qi
+                && mQfi == nrQosAttr.mQfi
+                && mMaxUplinkBitRate == nrQosAttr.mMaxUplinkBitRate
+                && mMaxDownlinkBitRate == nrQosAttr.mMaxDownlinkBitRate
+                && mGuaranteedUplinkBitRate == nrQosAttr.mGuaranteedUplinkBitRate
+                && mGuaranteedDownlinkBitRate == nrQosAttr.mGuaranteedDownlinkBitRate
+                && mAveragingWindow == nrQosAttr.mAveragingWindow
+                && mRemoteAddresses.size() == nrQosAttr.mRemoteAddresses.size()
+                && mRemoteAddresses.containsAll(nrQosAttr.mRemoteAddresses);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(m5Qi, mQfi, mMaxUplinkBitRate,
+                mMaxDownlinkBitRate, mGuaranteedUplinkBitRate,
+                mGuaranteedDownlinkBitRate, mAveragingWindow, mRemoteAddresses);
+    }
+
+
+    @NonNull
+    public static final Creator<NrQosSessionAttributes> CREATOR =
+            new Creator<NrQosSessionAttributes>() {
+        @NonNull
+        @Override
+        public NrQosSessionAttributes createFromParcel(@NonNull final Parcel in) {
+            return new NrQosSessionAttributes(in);
+        }
+
+        @NonNull
+        @Override
+        public NrQosSessionAttributes[] newArray(final int size) {
+            return new NrQosSessionAttributes[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/data/Qos.java b/android-35/android/telephony/data/Qos.java
new file mode 100644
index 0000000..d7273b9
--- /dev/null
+++ b/android-35/android/telephony/data/Qos.java
@@ -0,0 +1,176 @@
+/**
+ * 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 implements Parcelable {
+
+    /** @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, QosBandwidth downlink, QosBandwidth uplink) {
+        this.type = type;
+        this.downlink = downlink;
+        this.uplink = uplink;
+    }
+
+    public QosBandwidth getDownlinkBandwidth() {
+        return downlink;
+    }
+
+    public QosBandwidth getUplinkBandwidth() {
+        return uplink;
+    }
+
+    public static class QosBandwidth implements Parcelable {
+        int maxBitrateKbps;
+        int guaranteedBitrateKbps;
+
+        public QosBandwidth(int maxBitrateKbps, int guaranteedBitrateKbps) {
+            this.maxBitrateKbps = maxBitrateKbps;
+            this.guaranteedBitrateKbps = guaranteedBitrateKbps;
+        }
+
+        private QosBandwidth(Parcel source) {
+            maxBitrateKbps = source.readInt();
+            guaranteedBitrateKbps = source.readInt();
+        }
+
+        public int getMaxBitrateKbps() {
+            return maxBitrateKbps;
+        }
+
+        public int getGuaranteedBitrateKbps() {
+            return guaranteedBitrateKbps;
+        }
+
+        @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(), android.telephony.data.Qos.QosBandwidth.class);
+        uplink = source.readParcelable(
+                QosBandwidth.class.getClassLoader(), android.telephony.data.Qos.QosBandwidth.class);
+    }
+
+    /**
+     * 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 @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/android-35/android/telephony/data/QosBearerFilter.java b/android-35/android/telephony/data/QosBearerFilter.java
new file mode 100644
index 0000000..baa160e
--- /dev/null
+++ b/android-35/android/telephony/data/QosBearerFilter.java
@@ -0,0 +1,303 @@
+/**
+ * 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.annotation.Nullable;
+import android.net.LinkAddress;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+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 @NonNull List<LinkAddress> localAddresses;
+    private @NonNull List<LinkAddress> remoteAddresses;
+    private @Nullable PortRange localPort;
+    private @Nullable 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.data.QosFilter.PROTOCOL_UNSPECIFIED;
+    public static final int QOS_PROTOCOL_TCP = android.hardware.radio.data.QosFilter.PROTOCOL_TCP;
+    public static final int QOS_PROTOCOL_UDP = android.hardware.radio.data.QosFilter.PROTOCOL_UDP;
+    public static final int QOS_PROTOCOL_ESP = android.hardware.radio.data.QosFilter.PROTOCOL_ESP;
+    public static final int QOS_PROTOCOL_AH = android.hardware.radio.data.QosFilter.PROTOCOL_AH;
+    public static final int QOS_MIN_PORT = android.hardware.radio.data.PortRange.PORT_RANGE_MIN;
+    public static final int QOS_MAX_PORT = android.hardware.radio.data.PortRange.PORT_RANGE_MAX;
+
+    private @QosProtocol 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.data.QosFilter.DIRECTION_DOWNLINK;
+    public static final int QOS_FILTER_DIRECTION_UPLINK =
+            android.hardware.radio.data.QosFilter.DIRECTION_UPLINK;
+    public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL =
+            android.hardware.radio.data.QosFilter.DIRECTION_BIDIRECTIONAL;
+
+    private @QosBearerFilterDirection int filterDirection;
+
+    /**
+     * Specified the order in which the filter needs to be matched.
+     * A Lower numerical value has a higher precedence.
+     */
+    private int precedence;
+
+    public QosBearerFilter(@NonNull List<LinkAddress> localAddresses,
+            @NonNull List<LinkAddress> remoteAddresses, @Nullable PortRange localPort,
+            @Nullable PortRange remotePort, @QosProtocol int protocol, int tos, long flowLabel,
+            long spi, @QosBearerFilterDirection int direction, int precedence) {
+        this.localAddresses = new ArrayList<>();
+        this.localAddresses.addAll(localAddresses);
+        this.remoteAddresses = new ArrayList<>();
+        this.remoteAddresses.addAll(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 @NonNull List<LinkAddress> getLocalAddresses() {
+        return localAddresses;
+    }
+
+    public @NonNull List<LinkAddress> getRemoteAddresses() {
+        return remoteAddresses;
+    }
+
+    public @Nullable PortRange getLocalPortRange() {
+        return localPort;
+    }
+
+    public @Nullable PortRange getRemotePortRange() {
+        return remotePort;
+    }
+
+    public int getPrecedence() {
+        return precedence;
+    }
+
+    public int getProtocol() {
+        return protocol;
+    }
+
+    public static class PortRange implements Parcelable {
+        int start;
+        int end;
+
+        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)
+                && Objects.equals(localPort, other.localPort)
+                && Objects.equals(remotePort, other.remotePort)
+                && protocol == other.protocol
+                && typeOfServiceMask == other.typeOfServiceMask
+                && flowLabel == other.flowLabel
+                && securityParameterIndex == other.securityParameterIndex
+                && filterDirection == other.filterDirection
+                && precedence == other.precedence;
+    }
+
+    private QosBearerFilter(Parcel source) {
+        localAddresses = new ArrayList<>();
+        source.readList(localAddresses, LinkAddress.class.getClassLoader(), android.net.LinkAddress.class);
+        remoteAddresses = new ArrayList<>();
+        source.readList(remoteAddresses, LinkAddress.class.getClassLoader(), android.net.LinkAddress.class);
+        localPort = source.readParcelable(PortRange.class.getClassLoader(), android.telephony.data.QosBearerFilter.PortRange.class);
+        remotePort = source.readParcelable(PortRange.class.getClassLoader(), android.telephony.data.QosBearerFilter.PortRange.class);
+        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];
+                }
+            };
+}
diff --git a/android-35/android/telephony/data/QosBearerSession.java b/android-35/android/telephony/data/QosBearerSession.java
new file mode 100644
index 0000000..1668193
--- /dev/null
+++ b/android-35/android/telephony/data/QosBearerSession.java
@@ -0,0 +1,122 @@
+/**
+ * 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 QosBearerSession implements Parcelable{
+
+    final int qosBearerSessionId;
+    final Qos qos;
+    final List<QosBearerFilter> qosBearerFilterList;
+
+    public QosBearerSession(int qosBearerSessionId, @NonNull Qos qos,
+            @NonNull List<QosBearerFilter> qosBearerFilterList) {
+        this.qosBearerSessionId = qosBearerSessionId;
+        this.qos = qos;
+        this.qosBearerFilterList = new ArrayList<>();
+        this.qosBearerFilterList.addAll(qosBearerFilterList);
+    }
+
+    private QosBearerSession(Parcel source) {
+        qosBearerSessionId = source.readInt();
+        qos = source.readParcelable(Qos.class.getClassLoader(), android.telephony.data.Qos.class);
+        qosBearerFilterList = new ArrayList<>();
+        source.readList(qosBearerFilterList, QosBearerFilter.class.getClassLoader(), android.telephony.data.QosBearerFilter.class);
+    }
+
+    public int getQosBearerSessionId() {
+        return qosBearerSessionId;
+    }
+
+    public Qos getQos() {
+        return qos;
+    }
+
+    public List<QosBearerFilter> getQosBearerFilterList() {
+        return qosBearerFilterList;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(qosBearerSessionId);
+        if (qos.getType() == Qos.QOS_TYPE_EPS) {
+            dest.writeParcelable((EpsQos)qos, flags);
+        } else {
+            dest.writeParcelable((NrQos)qos, flags);
+        }
+        dest.writeList(qosBearerFilterList);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "QosBearerSession {"
+                + " qosBearerSessionId=" + qosBearerSessionId
+                + " qos=" + qos
+                + " qosBearerFilterList=" + qosBearerFilterList + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(qosBearerSessionId, qos, qosBearerFilterList);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof QosBearerSession)) {
+            return false;
+        }
+
+        QosBearerSession other = (QosBearerSession) o;
+        return this.qosBearerSessionId == other.qosBearerSessionId
+                && Objects.equals(this.qos, other.qos)
+                && this.qosBearerFilterList.size() == other.qosBearerFilterList.size()
+                && this.qosBearerFilterList.containsAll(other.qosBearerFilterList);
+    }
+
+    public static final @NonNull Parcelable.Creator<QosBearerSession> CREATOR =
+            new Parcelable.Creator<QosBearerSession>() {
+                @Override
+                public QosBearerSession createFromParcel(Parcel source) {
+                    return new QosBearerSession(source);
+                }
+
+                @Override
+                public QosBearerSession[] newArray(int size) {
+                    return new QosBearerSession[size];
+                }
+            };
+}
diff --git a/android-35/android/telephony/data/QualifiedNetworksService.java b/android-35/android/telephony/data/QualifiedNetworksService.java
new file mode 100644
index 0000000..f775de6
--- /dev/null
+++ b/android-35/android/telephony/data/QualifiedNetworksService.java
@@ -0,0 +1,524 @@
+/*
+ * Copyright 2018 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.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.NetCapability;
+import android.telephony.PreciseDataConnectionState;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.FeatureFlagsImpl;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.util.FunctionalUtils;
+import com.android.telephony.Rlog;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Base class of the qualified networks service, which is a vendor service providing up-to-date
+ * qualified network information to the frameworks for data handover control. A qualified network
+ * is defined as an access network that is ready for bringing up data connection for given APN
+ * types.
+ *
+ * Services that extend QualifiedNetworksService must register the service in their AndroidManifest
+ * to be detected by the framework. They must be protected by the permission
+ * "android.permission.BIND_TELEPHONY_DATA_SERVICE". The qualified networks service definition in
+ * the manifest must follow the following format:
+ * ...
+ * <service android:name=".xxxQualifiedNetworksService"
+ *     android:permission="android.permission.BIND_TELEPHONY_DATA_SERVICE" >
+ *     <intent-filter>
+ *         <action android:name="android.telephony.data.QualifiedNetworksService" />
+ *     </intent-filter>
+ * </service>
+ * @hide
+ */
+@SystemApi
+public abstract class QualifiedNetworksService extends Service {
+    private static final String TAG = QualifiedNetworksService.class.getSimpleName();
+
+    public static final String QUALIFIED_NETWORKS_SERVICE_INTERFACE =
+            "android.telephony.data.QualifiedNetworksService";
+
+    private static final int QNS_CREATE_NETWORK_AVAILABILITY_PROVIDER               = 1;
+    private static final int QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER               = 2;
+    private static final int QNS_REMOVE_ALL_NETWORK_AVAILABILITY_PROVIDERS          = 3;
+    private static final int QNS_UPDATE_QUALIFIED_NETWORKS                          = 4;
+    private static final int QNS_APN_THROTTLE_STATUS_CHANGED                        = 5;
+    private static final int QNS_EMERGENCY_DATA_NETWORK_PREFERRED_TRANSPORT_CHANGED = 6;
+    private static final int QNS_REQUEST_NETWORK_VALIDATION                         = 7;
+    private static final int QNS_RECONNECT_QUALIFIED_NETWORK                        = 8;
+
+    /** Feature flags */
+    private static final FeatureFlags sFeatureFlag = new FeatureFlagsImpl();
+
+    private final HandlerThread mHandlerThread;
+
+    private final QualifiedNetworksServiceHandler mHandler;
+
+    private final SparseArray<NetworkAvailabilityProvider> mProviders = new SparseArray<>();
+
+    /** @hide */
+    @VisibleForTesting
+    public final IQualifiedNetworksServiceWrapper mBinder = new IQualifiedNetworksServiceWrapper();
+
+    /**
+     * The abstract class of the network availability provider implementation. The vendor qualified
+     * network service must extend this class to report the available networks for data
+     * connection setup. Note that each instance of network availability provider is associated with
+     * one physical SIM slot.
+     */
+    public abstract class NetworkAvailabilityProvider implements AutoCloseable {
+        private final int mSlotIndex;
+
+        private IQualifiedNetworksServiceCallback mCallback;
+
+        /**
+         * Qualified networks for each APN type. Key is the {@link ApnType}, value is the array
+         * of available networks.
+         */
+        private SparseArray<int[]> mQualifiedNetworkTypesList = new SparseArray<>();
+
+        /**
+         * Constructor
+         * @param slotIndex SIM slot index the network availability provider associated with.
+         */
+        public NetworkAvailabilityProvider(int slotIndex) {
+            mSlotIndex = slotIndex;
+        }
+
+        /**
+         * @return SIM slot index the network availability provider associated with.
+         */
+        public final int getSlotIndex() {
+            return mSlotIndex;
+        }
+
+        private void registerForQualifiedNetworkTypesChanged(
+                IQualifiedNetworksServiceCallback callback) {
+            mCallback = callback;
+
+            // Force sending the qualified networks upon registered.
+            if (mCallback != null) {
+                for (int i = 0; i < mQualifiedNetworkTypesList.size(); i++) {
+                    try {
+                        mCallback.onQualifiedNetworkTypesChanged(
+                                mQualifiedNetworkTypesList.keyAt(i),
+                                mQualifiedNetworkTypesList.valueAt(i));
+                    } catch (RemoteException e) {
+                        loge("Failed to call onQualifiedNetworksChanged. " + e);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Update the suggested qualified networks list. Network availability provider must invoke
+         * this method whenever the suggested qualified networks changes. If this method is never
+         * invoked for certain APN types, then frameworks uses its own logic to determine the
+         * transport to setup the data network.
+         *
+         * For example, QNS can suggest frameworks setting up IMS data network on IWLAN by
+         * specifying {@link ApnSetting#TYPE_IMS} with a list containing
+         * {@link AccessNetworkType#IWLAN}.
+         *
+         * If QNS considers multiple access networks qualified for certain APN type, it can
+         * suggest frameworks by specifying the APN type with multiple access networks in the list,
+         * for example {{@link AccessNetworkType#EUTRAN}, {@link AccessNetworkType#IWLAN}}.
+         * Frameworks will then first attempt to setup data on LTE network, and If the device moves
+         * from LTE to UMTS, then frameworks will perform handover the data network to the second
+         * preferred access network if available.
+         *
+         * If the {@code qualifiedNetworkTypes} list is empty, it means QNS has no suggestion to the
+         * frameworks, and for that APN type frameworks will route the corresponding network
+         * requests to {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN}.
+         *
+         * @param apnTypes APN type(s) of the qualified networks. This must be a bitmask combination
+         * of {@link ApnType}. The same qualified networks will be applicable to all APN types
+         * specified here.
+         * @param qualifiedNetworkTypes List of access network types which are qualified for data
+         * connection setup for {@code apnTypes} in the preferred order. Empty list means QNS has no
+         * suggestion to the frameworks, and for that APN type frameworks will route the
+         * corresponding network requests to {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN}.
+         *
+         * If one of the element is invalid, for example, {@link AccessNetworkType#UNKNOWN}, then
+         * this operation becomes a no-op.
+         */
+        public final void updateQualifiedNetworkTypes(
+                @ApnType int apnTypes, @NonNull List<Integer> qualifiedNetworkTypes) {
+            int[] qualifiedNetworkTypesArray =
+                    qualifiedNetworkTypes.stream().mapToInt(i->i).toArray();
+            mHandler.obtainMessage(QNS_UPDATE_QUALIFIED_NETWORKS, mSlotIndex, apnTypes,
+                    qualifiedNetworkTypesArray).sendToTarget();
+        }
+
+        /**
+         * Request to make a clean initial connection instead of handover to a transport type mapped
+         * to the {@code qualifiedNetworkType} for the {@code apnTypes}. This will update the
+         * preferred network type like {@link #updateQualifiedNetworkTypes(int, List)}, however if
+         * the data network for the {@code apnTypes} is not in the state {@link TelephonyManager
+         * #DATA_CONNECTED} or it's already connected on the transport type mapped to the
+         * qualified network type, forced reconnection will be ignored.
+         *
+         * <p>This will tear down current data network even though target transport type mapped to
+         * the {@code qualifiedNetworkType} is not available, and the data network will be connected
+         * to the transport type when it becomes available.
+         *
+         * <p>This is one shot request and does not mean further handover is not allowed to the
+         * qualified network type for this APN type.
+         *
+         * @param apnTypes APN type(s) of the qualified networks. This must be a bitmask combination
+         * of {@link ApnType}. The same qualified networks will be applicable to all APN types
+         * specified here.
+         * @param qualifiedNetworkType Access network types which are qualified for data connection
+         * setup for {@link ApnType}. Empty list means QNS has no suggestion to the frameworks, and
+         * for that APN type frameworks will route the corresponding network requests to
+         * {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN}.
+         *
+         * <p> If one of the element is invalid, for example, {@link AccessNetworkType#UNKNOWN},
+         * then this operation becomes a no-op.
+         *
+         * @hide
+         */
+        public final void reconnectQualifiedNetworkType(@ApnType int apnTypes,
+                @AccessNetworkConstants.RadioAccessNetworkType int qualifiedNetworkType) {
+            mHandler.obtainMessage(QNS_RECONNECT_QUALIFIED_NETWORK, mSlotIndex, apnTypes,
+                    new Integer(qualifiedNetworkType)).sendToTarget();
+        }
+
+        private void onUpdateQualifiedNetworkTypes(
+                @ApnType int apnTypes, int[] qualifiedNetworkTypes) {
+            mQualifiedNetworkTypesList.put(apnTypes, qualifiedNetworkTypes);
+            if (mCallback != null) {
+                try {
+                    mCallback.onQualifiedNetworkTypesChanged(apnTypes, qualifiedNetworkTypes);
+                } catch (RemoteException e) {
+                    loge("Failed to call onQualifiedNetworksChanged. " + e);
+                }
+            }
+        }
+
+        private void onReconnectQualifiedNetworkType(@ApnType int apnTypes,
+                @AccessNetworkConstants.RadioAccessNetworkType int qualifiedNetworkType) {
+            if (mCallback != null) {
+                try {
+                    mCallback.onReconnectQualifiedNetworkType(apnTypes, qualifiedNetworkType);
+                } catch (RemoteException e) {
+                    loge("Failed to call onReconnectQualifiedNetworkType. " + e);
+                }
+            }
+        }
+
+        /**
+         * The framework calls this method when the throttle status of an APN changes.
+         *
+         * This method is meant to be overridden.
+         *
+         * @param statuses the statuses that have changed
+         */
+        public void reportThrottleStatusChanged(@NonNull List<ThrottleStatus> statuses) {
+            Log.d(TAG, "reportThrottleStatusChanged: statuses size=" + statuses.size());
+        }
+
+        /**
+         * The framework calls this method when the preferred transport type used to set up
+         * emergency data network is changed.
+         *
+         * This method is meant to be overridden.
+         *
+         * @param transportType transport type changed to be preferred
+         */
+        public void reportEmergencyDataNetworkPreferredTransportChanged(
+                @AccessNetworkConstants.TransportType int transportType) {
+            Log.d(TAG, "reportEmergencyDataNetworkPreferredTransportChanged: "
+                    + AccessNetworkConstants.transportTypeToString(transportType));
+        }
+
+        /**
+         * Request network validation to the connected data network for given a network capability.
+         *
+         * <p>This network validation can only be performed when a data network is in connected
+         * state, and will not be triggered if the data network does not support network validation
+         * feature or network validation is not in connected state.
+         *
+         * <p>See {@link DataServiceCallback.ResultCode} for the type of response that indicates
+         * whether the request was successfully submitted or had an error.
+         *
+         * <p>If network validation is requested, monitor network validation status in {@link
+         * PreciseDataConnectionState#getNetworkValidationStatus()}.
+         *
+         * @param networkCapability A network capability. (Note that only APN-type capabilities are
+         *     supported.
+         * @param executor executor The callback executor that responds whether the request has been
+         *     successfully submitted or not.
+         * @param resultCodeCallback A callback to determine whether the request was successfully
+         *     submitted or not.
+         */
+        @FlaggedApi(Flags.FLAG_NETWORK_VALIDATION)
+        public void requestNetworkValidation(
+                @NetCapability int networkCapability,
+                @NonNull @CallbackExecutor Executor executor,
+                @NonNull @DataServiceCallback.ResultCode Consumer<Integer> resultCodeCallback) {
+            Objects.requireNonNull(executor, "executor cannot be null");
+            Objects.requireNonNull(resultCodeCallback, "resultCodeCallback cannot be null");
+
+            if (!sFeatureFlag.networkValidation()) {
+                loge("networkValidation feature is disabled");
+                executor.execute(
+                        () ->
+                                resultCodeCallback.accept(
+                                        DataServiceCallback.RESULT_ERROR_UNSUPPORTED));
+                return;
+            }
+
+            IIntegerConsumer callback = new IIntegerConsumer.Stub() {
+                @Override
+                public void accept(int result) {
+                    executor.execute(() -> resultCodeCallback.accept(result));
+                }
+            };
+
+            // Move to the internal handler and process it.
+            mHandler.obtainMessage(
+                            QNS_REQUEST_NETWORK_VALIDATION,
+                            mSlotIndex,
+                            0,
+                            new NetworkValidationRequestData(networkCapability, callback))
+                    .sendToTarget();
+        }
+
+        /** Process a network validation request on the internal handler. */
+        private void onRequestNetworkValidation(NetworkValidationRequestData data) {
+            try {
+                log("onRequestNetworkValidation");
+                // Callback to request a network validation.
+                mCallback.onNetworkValidationRequested(data.mNetworkCapability, data.mCallback);
+            } catch (RemoteException | NullPointerException e) {
+                loge("Failed to call onRequestNetworkValidation. " + e);
+                FunctionalUtils.ignoreRemoteException(data.mCallback::accept)
+                        .accept(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
+            }
+        }
+
+        /**
+         * Called when the qualified networks provider is removed. The extended class should
+         * implement this method to perform cleanup works.
+         */
+        @Override
+        public abstract void close();
+    }
+
+    private class QualifiedNetworksServiceHandler extends Handler {
+        QualifiedNetworksServiceHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            IQualifiedNetworksServiceCallback callback;
+            final int slotIndex = message.arg1;
+            NetworkAvailabilityProvider provider = mProviders.get(slotIndex);
+
+            switch (message.what) {
+                case QNS_CREATE_NETWORK_AVAILABILITY_PROVIDER:
+                    if (mProviders.get(slotIndex) != null) {
+                        loge("Network availability provider for slot " + slotIndex
+                                + " already existed.");
+                        return;
+                    }
+
+                    provider = onCreateNetworkAvailabilityProvider(slotIndex);
+                    if (provider != null) {
+                        mProviders.put(slotIndex, provider);
+
+                        callback = (IQualifiedNetworksServiceCallback) message.obj;
+                        provider.registerForQualifiedNetworkTypesChanged(callback);
+                    } else {
+                        loge("Failed to create network availability provider. slot index = "
+                                + slotIndex);
+                    }
+                    break;
+                case QNS_APN_THROTTLE_STATUS_CHANGED:
+                    if (provider != null) {
+                        List<ThrottleStatus> statuses = (List<ThrottleStatus>) message.obj;
+                        provider.reportThrottleStatusChanged(statuses);
+                    }
+                    break;
+
+                case QNS_EMERGENCY_DATA_NETWORK_PREFERRED_TRANSPORT_CHANGED:
+                    if (provider != null) {
+                        int transportType = (int) message.arg2;
+                        provider.reportEmergencyDataNetworkPreferredTransportChanged(transportType);
+                    }
+                    break;
+
+                case QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER:
+                    if (provider != null) {
+                        provider.close();
+                        mProviders.remove(slotIndex);
+                    }
+                    break;
+
+                case QNS_REMOVE_ALL_NETWORK_AVAILABILITY_PROVIDERS:
+                    for (int i = 0; i < mProviders.size(); i++) {
+                        provider = mProviders.get(i);
+                        if (provider != null) {
+                            provider.close();
+                        }
+                    }
+                    mProviders.clear();
+                    break;
+
+                case QNS_UPDATE_QUALIFIED_NETWORKS:
+                    if (provider == null) break;
+                    provider.onUpdateQualifiedNetworkTypes(message.arg2, (int[]) message.obj);
+                    break;
+
+                case QNS_REQUEST_NETWORK_VALIDATION:
+                    if (provider == null) break;
+                    provider.onRequestNetworkValidation((NetworkValidationRequestData) message.obj);
+                    break;
+
+                case QNS_RECONNECT_QUALIFIED_NETWORK:
+                    if (provider == null) break;
+                    provider.onReconnectQualifiedNetworkType(message.arg2, (Integer) message.obj);
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Default constructor.
+     */
+    public QualifiedNetworksService() {
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+
+        mHandler = new QualifiedNetworksServiceHandler(mHandlerThread.getLooper());
+        log("Qualified networks service created");
+    }
+
+    /**
+     * Create the instance of {@link NetworkAvailabilityProvider}. Vendor qualified network service
+     * must override this method to facilitate the creation of {@link NetworkAvailabilityProvider}
+     * instances. The system will call this method after binding the qualified networks service for
+     * each active SIM slot index.
+     *
+     * @param slotIndex SIM slot index the qualified networks service associated with.
+     * @return Qualified networks service instance
+     */
+    @NonNull
+    public abstract NetworkAvailabilityProvider onCreateNetworkAvailabilityProvider(int slotIndex);
+
+    /** @hide */
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (intent == null || !QUALIFIED_NETWORKS_SERVICE_INTERFACE.equals(intent.getAction())) {
+            loge("Unexpected intent " + intent);
+            return null;
+        }
+        return mBinder;
+    }
+
+    /** @hide */
+    @Override
+    public boolean onUnbind(Intent intent) {
+        mHandler.obtainMessage(QNS_REMOVE_ALL_NETWORK_AVAILABILITY_PROVIDERS).sendToTarget();
+        return false;
+    }
+
+    /** @hide */
+    @Override
+    public void onDestroy() {
+        mHandlerThread.quit();
+    }
+
+    /**
+     * A wrapper around IQualifiedNetworksService that forwards calls to implementations of
+     * {@link QualifiedNetworksService}.
+     */
+    private class IQualifiedNetworksServiceWrapper extends IQualifiedNetworksService.Stub {
+        @Override
+        public void createNetworkAvailabilityProvider(int slotIndex,
+                                                      IQualifiedNetworksServiceCallback callback) {
+            mHandler.obtainMessage(QNS_CREATE_NETWORK_AVAILABILITY_PROVIDER, slotIndex, 0,
+                    callback).sendToTarget();
+        }
+
+        @Override
+        public void removeNetworkAvailabilityProvider(int slotIndex) {
+            mHandler.obtainMessage(QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER, slotIndex, 0)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void reportThrottleStatusChanged(int slotIndex,
+                List<ThrottleStatus> statuses) {
+            mHandler.obtainMessage(QNS_APN_THROTTLE_STATUS_CHANGED, slotIndex, 0, statuses)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void reportEmergencyDataNetworkPreferredTransportChanged(int slotIndex,
+                @AccessNetworkConstants.TransportType int transportType) {
+            mHandler.obtainMessage(
+                    QNS_EMERGENCY_DATA_NETWORK_PREFERRED_TRANSPORT_CHANGED,
+                            slotIndex, transportType).sendToTarget();
+        }
+    }
+
+    private static final class NetworkValidationRequestData {
+        final @NetCapability int mNetworkCapability;
+        final IIntegerConsumer mCallback;
+
+        private NetworkValidationRequestData(@NetCapability int networkCapability,
+                @NonNull IIntegerConsumer callback) {
+            mNetworkCapability = networkCapability;
+            mCallback = callback;
+        }
+    }
+
+    private void log(String s) {
+        Rlog.d(TAG, s);
+    }
+
+    private void loge(String s) {
+        Rlog.e(TAG, s);
+    }
+}
diff --git a/android-35/android/telephony/data/RouteSelectionDescriptor.java b/android-35/android/telephony/data/RouteSelectionDescriptor.java
new file mode 100644
index 0000000..b3b8464
--- /dev/null
+++ b/android-35/android/telephony/data/RouteSelectionDescriptor.java
@@ -0,0 +1,243 @@
+/**
+ * Copyright 2021 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.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a single route selection descriptor as defined in
+ * 3GPP TS 24.526.
+ */
+public final class RouteSelectionDescriptor implements Parcelable {
+    /**
+     * The min acceptable value for the precedence of a route selection descriptor.
+     * @hide
+     */
+    public static final int MIN_ROUTE_PRECEDENCE = 0;
+
+    /**
+     * The max acceptable value for the precedence of a route selection descriptor.
+     * @hide
+     */
+    public static final int MAX_ROUTE_PRECEDENCE = 255;
+
+    /**
+     * The route selection descriptor is for the session with IPV4 type.
+     */
+    public static final int SESSION_TYPE_IPV4 = 0;
+
+    /**
+     * The route selection descriptor is for the session with IPV6 type.
+     */
+    public static final int SESSION_TYPE_IPV6 = 1;
+
+    /**
+     * The route selection descriptor is for the session with both IP and IPV6 types.
+     */
+    public static final int SESSION_TYPE_IPV4V6 = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "SESSION_TYPE_" }, value = {
+            SESSION_TYPE_IPV4,
+            SESSION_TYPE_IPV6,
+            SESSION_TYPE_IPV4V6,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RouteSessionType {}
+
+    /**
+     * The route selection descriptor is using SSC mode 1. The session will provide continual
+     * support when UE's location is updated.
+     */
+    public static final int ROUTE_SSC_MODE_1 = 1;
+
+    /**
+     * The route selection descriptor is using SSC mode 2. The new session for the same network
+     * will be established after releasing the old session when UE's location is updated.
+     */
+    public static final int ROUTE_SSC_MODE_2 = 2;
+
+    /**
+     * The route selection descriptor is using SSC mode 3. The new session for the same network is
+     * allowed to be established before releasing the old session when UE's location is updated.
+     */
+    public static final int ROUTE_SSC_MODE_3 = 3;
+
+    /**
+     * The min acceptable value for the SSC mode of a route selection descriptor.
+     * @hide
+     */
+    public static final int MIN_ROUTE_SSC_MODE = ROUTE_SSC_MODE_1;
+
+    /**
+     * The max acceptable value for the SSC mode of a route selection descriptor.
+     * @hide
+     */
+    public static final int MAX_ROUTE_SSC_MODE = ROUTE_SSC_MODE_3;
+
+    /** @hide */
+    @IntDef(prefix = { "ROUTE_SSC_MODE_" }, value = {
+            ROUTE_SSC_MODE_1,
+            ROUTE_SSC_MODE_2,
+            ROUTE_SSC_MODE_3,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RouteSscMode {}
+
+    @IntRange(from = MIN_ROUTE_PRECEDENCE, to = MAX_ROUTE_PRECEDENCE)
+    private final int mPrecedence;
+    @RouteSessionType
+    private final int mSessionType;
+    @RouteSscMode
+    @IntRange(from = MIN_ROUTE_SSC_MODE, to = MAX_ROUTE_SSC_MODE)
+    private final int mSscMode;
+    private final List<NetworkSliceInfo> mSliceInfo;
+    private final List<String> mDnn;
+
+    /** @hide */
+    public RouteSelectionDescriptor(int precedence, int sessionType, int sscMode,
+            List<NetworkSliceInfo> sliceInfo, List<String> dnn) {
+        mPrecedence = precedence;
+        mSessionType = sessionType;
+        mSscMode = sscMode;
+        mSliceInfo = new ArrayList<>();
+        mSliceInfo.addAll(sliceInfo);
+        mDnn = new ArrayList<>();
+        mDnn.addAll(dnn);
+    }
+
+    private RouteSelectionDescriptor(Parcel p) {
+        mPrecedence = p.readInt();
+        mSessionType = p.readInt();
+        mSscMode = p.readInt();
+        mSliceInfo = p.createTypedArrayList(NetworkSliceInfo.CREATOR);
+        mDnn = new ArrayList<>();
+        p.readStringList(mDnn);
+    }
+
+    /**
+     * Precedence value in the range of 0 to 255. Higher value has lower precedence.
+     * @return the precedence value for this route selection descriptor.
+     */
+    @IntRange(from = MIN_ROUTE_PRECEDENCE, to = MAX_ROUTE_PRECEDENCE)
+    public int getPrecedence() {
+        return mPrecedence;
+    }
+
+    /**
+     * This is used for checking which session type defined in 3GPP TS 23.501 is allowed for the
+     * route in a route selection descriptor.
+     * @return the session type for this route selection descriptor.
+     */
+    @RouteSessionType
+    public int getSessionType() {
+        return mSessionType;
+    }
+
+    /**
+     * SSC mode stands for Session and Service Continuity mode (which specifies the IP continuity
+     * mode) as defined in 3GPP TS 23.501.
+     * @return the SSC mode for this route selection descriptor.
+     */
+    @RouteSscMode
+    public int getSscMode() {
+        return mSscMode;
+    }
+
+    /**
+     * This is the list of all the slices available in the route selection descriptor as indicated
+     * by the network. These are the slices that can be used by the device if this route selection
+     * descriptor is used based the traffic (see 3GPP TS 23.501 for details).
+     * @return the list of all the slices available in the route selection descriptor.
+     */
+    public @NonNull List<NetworkSliceInfo> getSliceInfo() {
+        return mSliceInfo;
+    }
+
+    /**
+     * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003. There
+     * can be 0 or more DNNs specified in a route selection descriptor.
+     * @return the list of DNN for this route selection descriptor.
+     */
+    public @NonNull List<String> getDataNetworkName() {
+        return mDnn;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mPrecedence);
+        dest.writeInt(mSessionType);
+        dest.writeInt(mSscMode);
+        dest.writeTypedList(mSliceInfo, flags);
+        dest.writeStringList(mDnn);
+    }
+
+    public static final @NonNull Parcelable.Creator<RouteSelectionDescriptor> CREATOR =
+            new Parcelable.Creator<RouteSelectionDescriptor>() {
+                @Override
+                public RouteSelectionDescriptor createFromParcel(Parcel source) {
+                    return new RouteSelectionDescriptor(source);
+                }
+
+                @Override
+                public RouteSelectionDescriptor[] newArray(int size) {
+                    return new RouteSelectionDescriptor[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        RouteSelectionDescriptor that = (RouteSelectionDescriptor) o;
+        return mPrecedence == that.mPrecedence
+                && mSessionType == that.mSessionType
+                && mSscMode == that.mSscMode
+                && mSliceInfo.size() == that.mSliceInfo.size()
+                && mSliceInfo.containsAll(that.mSliceInfo)
+                && mDnn.size() == that.mDnn.size()
+                && mDnn.containsAll(that.mDnn);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPrecedence, mSessionType, mSscMode, mSliceInfo, mDnn);
+    }
+
+    @Override
+    public String toString() {
+        return "{.precedence = " + mPrecedence + ", .sessionType = " + mSessionType
+                + ", .sscMode = " + mSscMode + ", .sliceInfo = " + mSliceInfo
+                + ", .dnn = " + mDnn + "}";
+    }
+}
diff --git a/android-35/android/telephony/data/ThrottleStatus.java b/android-35/android/telephony/data/ThrottleStatus.java
new file mode 100644
index 0000000..0dff6ff
--- /dev/null
+++ b/android-35/android/telephony/data/ThrottleStatus.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Status information regarding the throttle status of an APN type.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ThrottleStatus implements Parcelable {
+    /**
+     * The APN type is not throttled.
+     */
+    public static final int THROTTLE_TYPE_NONE = 1;
+
+    /**
+     * The APN type is throttled until {@link android.os.SystemClock#elapsedRealtime()}
+     * has reached {@link ThrottleStatus#getThrottleExpiryTimeMillis}
+     */
+    public static final int THROTTLE_TYPE_ELAPSED_TIME = 2;
+
+    /** {@hide} */
+    @IntDef(flag = true, prefix = {"THROTTLE_TYPE_"}, value = {
+            ThrottleStatus.THROTTLE_TYPE_NONE,
+            ThrottleStatus.THROTTLE_TYPE_ELAPSED_TIME,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ThrottleType {
+    }
+
+    /**
+     * The framework will not retry the APN type.
+     */
+    public static final int RETRY_TYPE_NONE = 1;
+
+    /**
+     * The next time the framework retries, it will attempt to establish a new connection.
+     */
+    public static final int RETRY_TYPE_NEW_CONNECTION = 2;
+
+    /**
+     * The next time the framework retires, it will retry to handover.
+     */
+    public static final int RETRY_TYPE_HANDOVER = 3;
+
+    /** {@hide} */
+    @IntDef(flag = true, prefix = {"RETRY_TYPE_"}, value = {
+            ThrottleStatus.RETRY_TYPE_NONE,
+            ThrottleStatus.RETRY_TYPE_NEW_CONNECTION,
+            ThrottleStatus.RETRY_TYPE_HANDOVER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RetryType {
+    }
+
+    private final int mSlotIndex;
+    private final @AccessNetworkConstants.TransportType int mTransportType;
+    private final @Annotation.ApnType int mApnType;
+    private final long mThrottleExpiryTimeMillis;
+    private final @RetryType int mRetryType;
+    private final @ThrottleType int mThrottleType;
+
+    /**
+     * The slot index that the status applies to.
+     *
+     * @return the slot index
+     */
+    public int getSlotIndex() {
+        return mSlotIndex;
+    }
+
+    /**
+     * The type of transport that the status applies to.
+     *
+     * @return the transport type
+     */
+    @AccessNetworkConstants.TransportType
+    public int getTransportType() {
+        return mTransportType;
+    }
+
+    /**
+     * The APN type that the status applies to.
+     *
+     * @return the apn type
+     */
+    @Annotation.ApnType
+    public int getApnType() {
+        return mApnType;
+    }
+
+    /**
+     * The type of throttle applied to the APN type.
+     *
+     * @return the throttle type
+     */
+    @ThrottleType
+    public int getThrottleType() {
+        return mThrottleType;
+    }
+
+    /**
+     * Indicates the type of request that the framework will make the next time it retries
+     * to call {@link IDataService#setupDataCall}.
+     *
+     * @return the retry type
+     */
+    @RetryType
+    public int getRetryType() {
+        return mRetryType;
+    }
+
+    /**
+     * Gets the time at which the throttle expires.  The value is based off of
+     * {@link SystemClock#elapsedRealtime}.
+     *
+     * This value only applies when the throttle type is set to
+     * {@link ThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}.
+     *
+     * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely.
+     *
+     * @return the time at which the throttle expires
+     */
+    @ElapsedRealtimeLong
+    public long getThrottleExpiryTimeMillis() {
+        return mThrottleExpiryTimeMillis;
+    }
+
+    private ThrottleStatus(int slotIndex,
+            @AccessNetworkConstants.TransportType int transportType,
+            @Annotation.ApnType int apnTypes,
+            @ThrottleType int throttleType,
+            long throttleExpiryTimeMillis,
+            @RetryType int retryType) {
+        mSlotIndex = slotIndex;
+        mTransportType = transportType;
+        mApnType = apnTypes;
+        mThrottleType = throttleType;
+        mThrottleExpiryTimeMillis = throttleExpiryTimeMillis;
+        mRetryType = retryType;
+    }
+
+    private ThrottleStatus(@NonNull Parcel source) {
+        mSlotIndex = source.readInt();
+        mTransportType = source.readInt();
+        mApnType = source.readInt();
+        mThrottleExpiryTimeMillis = source.readLong();
+        mRetryType = source.readInt();
+        mThrottleType = source.readInt();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mSlotIndex);
+        dest.writeInt(mTransportType);
+        dest.writeInt(mApnType);
+        dest.writeLong(mThrottleExpiryTimeMillis);
+        dest.writeInt(mRetryType);
+        dest.writeInt(mThrottleType);
+    }
+
+    public static final @NonNull Parcelable.Creator<ThrottleStatus> CREATOR =
+            new Parcelable.Creator<ThrottleStatus>() {
+                @Override
+                public ThrottleStatus createFromParcel(@NonNull Parcel source) {
+                    return new ThrottleStatus(source);
+                }
+
+                @Override
+                public ThrottleStatus[] newArray(int size) {
+                    return new ThrottleStatus[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSlotIndex, mApnType, mRetryType, mThrottleType,
+                mThrottleExpiryTimeMillis, mTransportType);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        } else if (obj instanceof ThrottleStatus) {
+            ThrottleStatus other = (ThrottleStatus) obj;
+            return this.mSlotIndex == other.mSlotIndex
+                    && this.mApnType == other.mApnType
+                    && this.mRetryType == other.mRetryType
+                    && this.mThrottleType == other.mThrottleType
+                    && this.mThrottleExpiryTimeMillis == other.mThrottleExpiryTimeMillis
+                    && this.mTransportType == other.mTransportType;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "ThrottleStatus{"
+                + "mSlotIndex=" + mSlotIndex
+                + ", mTransportType=" + mTransportType
+                + ", mApnType=" + ApnSetting.getApnTypeString(mApnType)
+                + ", mThrottleExpiryTimeMillis=" + mThrottleExpiryTimeMillis
+                + ", mRetryType=" + mRetryType
+                + ", mThrottleType=" + mThrottleType
+                + '}';
+    }
+
+    /**
+     * Provides a convenient way to set the fields of an {@link ThrottleStatus} when creating a
+     * new instance.
+     *
+     * <p>The example below shows how you might create a new {@code ThrottleStatus}:
+     *
+     * <pre><code>
+     *
+     * ThrottleStatus = new ThrottleStatus.Builder()
+     *     .setSlotIndex(1)
+     *     .setApnType({@link ApnSetting#TYPE_EMERGENCY})
+     *     .setNoThrottle()
+     *     .setRetryType({@link ThrottleStatus#RETRY_TYPE_NEW_CONNECTION})
+     *     .build();
+     * </code></pre>
+     */
+    public static final class Builder {
+        private int mSlotIndex;
+        private @AccessNetworkConstants.TransportType int mTransportType;
+        private @Annotation.ApnType int mApnType;
+        private long mThrottleExpiryTimeMillis;
+        private @RetryType int mRetryType;
+        private @ThrottleType int mThrottleType;
+
+        /**
+         * @hide
+         */
+        public static final long NO_THROTTLE_EXPIRY_TIME =
+                DataCallResponse.RETRY_DURATION_UNDEFINED;
+
+        /**
+         * Default constructor for the Builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Set the slot index.
+         *
+         * @param slotIndex the slot index.
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setSlotIndex(int slotIndex) {
+            this.mSlotIndex = slotIndex;
+            return this;
+        }
+
+        /**
+         * Set the transport type.
+         *
+         * @param transportType the transport type.
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setTransportType(@AccessNetworkConstants.TransportType
+                int transportType) {
+            this.mTransportType = transportType;
+            return this;
+        }
+
+        /**
+         * Set the APN type.
+         *
+         * @param apnType  the APN type.
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setApnType(@Annotation.ApnType int apnType) {
+            this.mApnType = apnType;
+            return this;
+        }
+
+        /**
+         * Sets the time at which the throttle will expire.  The value is based off of
+         * {@link SystemClock#elapsedRealtime}.
+         *
+         * When setting this value, the throttle type is set to
+         * {@link ThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}.
+         *
+         * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely.
+         *
+         * @param throttleExpiryTimeMillis The elapsed time at which the throttle expires.
+         *                                 Throws {@link IllegalArgumentException} for values less
+         *                                 than 0.
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setThrottleExpiryTimeMillis(
+                @ElapsedRealtimeLong long throttleExpiryTimeMillis) {
+            if (throttleExpiryTimeMillis >= 0) {
+                this.mThrottleExpiryTimeMillis = throttleExpiryTimeMillis;
+                this.mThrottleType = THROTTLE_TYPE_ELAPSED_TIME;
+            } else {
+                throw new IllegalArgumentException("throttleExpiryTimeMillis must be greater than "
+                        + "or equal to 0");
+            }
+            return this;
+        }
+
+        /**
+         * Sets the status of the APN type as not being throttled.
+         *
+         * When setting this value, the throttle type is set to
+         * {@link ThrottleStatus#THROTTLE_TYPE_NONE} and the expiry time is set to
+         * {@link Builder#NO_THROTTLE_EXPIRY_TIME}.
+         *
+         * @return The same instance of the builder.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder setNoThrottle() {
+            mThrottleType = THROTTLE_TYPE_NONE;
+            mThrottleExpiryTimeMillis = NO_THROTTLE_EXPIRY_TIME;
+            return this;
+        }
+
+        /**
+         * Set the type of request that the framework will make the next time it retries
+         * to call {@link IDataService#setupDataCall}.
+         *
+         * @param retryType the type of request
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setRetryType(@RetryType int retryType) {
+            this.mRetryType = retryType;
+            return this;
+        }
+
+        /**
+         * Build the {@link ThrottleStatus}
+         *
+         * @return the {@link ThrottleStatus} object
+         */
+        @NonNull
+        public ThrottleStatus build() {
+            return new ThrottleStatus(
+                    mSlotIndex,
+                    mTransportType,
+                    mApnType,
+                    mThrottleType,
+                    mThrottleExpiryTimeMillis,
+                    mRetryType);
+        }
+    }
+}
diff --git a/android-35/android/telephony/data/TrafficDescriptor.java b/android-35/android/telephony/data/TrafficDescriptor.java
new file mode 100644
index 0000000..66dcf8f
--- /dev/null
+++ b/android-35/android/telephony/data/TrafficDescriptor.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2021 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.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for UE Route Selection
+ * Policy(URSP) traffic matching as described in 3GPP TS 24.526 Section 4.2.2. It includes an
+ * optional Data Network Name(DNN), which, if present, must be used for traffic matching; it does
+ * not specify the end point to be used for the data call.
+ */
+public final class TrafficDescriptor implements Parcelable {
+    /**
+     * The OS/App id
+     *
+     * @hide
+     */
+    public static final class OsAppId {
+        /**
+         * OSId for "Android", using UUID version 5 with namespace ISO OSI.
+         * Prepended to the OsAppId in TrafficDescriptor to use for URSP matching.
+         */
+        public static final UUID ANDROID_OS_ID =
+                UUID.fromString("97a498e3-fc92-5c94-8986-0333d06e4e47");
+
+        /**
+         * Allowed app ids.
+         */
+        // The following app ids are the only apps id Android supports. OEMs or vendors are
+        // prohibited to modify/extend the allowed list, especially passing the real package name to
+        // the network.
+        private static final Set<String> ALLOWED_APP_IDS = Set.of(
+                "ENTERPRISE", "PRIORITIZE_LATENCY", "PRIORITIZE_BANDWIDTH", "CBS"
+        );
+
+        /** OS id in UUID format. */
+        private final @NonNull UUID mOsId;
+
+        /**
+         * App id in string format. Note that Android will not allow use specific app id. This must
+         * be a category/capability identifier.
+         */
+        private final @NonNull String mAppId;
+
+        /**
+         * The differentiator when multiple traffic descriptor has the same OS and app id. Must be
+         * greater than 1.
+         */
+        private final int mDifferentiator;
+
+        /**
+         * Constructor
+         *
+         * @param osId OS id in UUID format.
+         * @param appId App id in string format. Note that Android will not allow use specific app
+         * id. This must be a category/capability identifier.
+         */
+        public OsAppId(@NonNull UUID osId, @NonNull String appId) {
+            this(osId, appId, 1);
+        }
+
+        /**
+         * Constructor
+         *
+         * @param osId OS id in UUID format.
+         * @param appId App id in string format. Note that Android will not allow use specific app
+         * id. This must be a category/capability identifier.
+         * @param differentiator The differentiator when multiple traffic descriptor has the same
+         * OS and app id. Must be greater than 0.
+         */
+        public OsAppId(@NonNull UUID osId, @NonNull String appId, int differentiator) {
+            Objects.requireNonNull(osId);
+            Objects.requireNonNull(appId);
+            if (differentiator < 1) {
+                throw new IllegalArgumentException("Invalid differentiator " + differentiator);
+            }
+
+            mOsId = osId;
+            mAppId = appId;
+            mDifferentiator = differentiator;
+        }
+
+        /**
+         * Constructor from raw byte array.
+         *
+         * @param rawOsAppId The raw OS/App id.
+         */
+        public OsAppId(@NonNull byte[] rawOsAppId) {
+            try {
+                ByteBuffer bb = ByteBuffer.wrap(rawOsAppId);
+                // OS id is the first 16 bytes.
+                mOsId = new UUID(bb.getLong(), bb.getLong());
+                // App id length is 1 byte.
+                int appIdLen = bb.get();
+                // The remaining is the app id + differentiator.
+                byte[] appIdAndDifferentiator = new byte[appIdLen];
+                bb.get(appIdAndDifferentiator, 0, appIdLen);
+                // Extract trailing numbers, for example, "ENTERPRISE", "ENTERPRISE3".
+                String appIdAndDifferentiatorStr = new String(appIdAndDifferentiator);
+                Pattern pattern = Pattern.compile("[^0-9]+([0-9]+)$");
+                Matcher matcher = pattern.matcher(new String(appIdAndDifferentiator));
+                if (matcher.find()) {
+                    mDifferentiator = Integer.parseInt(matcher.group(1));
+                    mAppId = appIdAndDifferentiatorStr.replace(matcher.group(1), "");
+                } else {
+                    mDifferentiator = 1;
+                    mAppId = appIdAndDifferentiatorStr;
+                }
+            } catch (Exception e) {
+                throw new IllegalArgumentException("Failed to decode " + (rawOsAppId != null
+                        ? new BigInteger(1, rawOsAppId).toString(16) : null));
+            }
+        }
+
+        /**
+         * @return The OS id in UUID format.
+         */
+        public @NonNull UUID getOsId() {
+            return mOsId;
+        }
+
+        /**
+         * @return App id in string format. Note that Android will not allow use specific app id.
+         * This must be a category/capability identifier.
+         */
+        public @NonNull String getAppId() {
+            return mAppId;
+        }
+
+        /**
+         * @return The differentiator when multiple traffic descriptor has the same OS and app id.
+         * Must be greater than 1.
+         */
+        public int getDifferentiator() {
+            return mDifferentiator;
+        }
+
+        /**
+         * @return OS/App id in raw byte format.
+         */
+        public @NonNull byte[] getBytes() {
+            byte[] osAppId = (mAppId + (mDifferentiator > 1 ? mDifferentiator : "")).getBytes();
+            // 16 bytes for UUID, 1 byte for length of osAppId, and up to 255 bytes for osAppId
+            ByteBuffer bb = ByteBuffer.allocate(16 + 1 + osAppId.length);
+            bb.putLong(mOsId.getMostSignificantBits());
+            bb.putLong(mOsId.getLeastSignificantBits());
+            bb.put((byte) osAppId.length);
+            bb.put(osAppId);
+            return bb.array();
+        }
+
+        @Override
+        public String toString() {
+            return "[OsAppId: OS=" + mOsId + ", App=" + mAppId + ", differentiator="
+                    + mDifferentiator + ", raw="
+                    + new BigInteger(1, getBytes()).toString(16) + "]";
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            OsAppId osAppId = (OsAppId) o;
+            return mDifferentiator == osAppId.mDifferentiator && mOsId.equals(osAppId.mOsId)
+                    && mAppId.equals(osAppId.mAppId);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mOsId, mAppId, mDifferentiator);
+        }
+    }
+
+    private final String mDnn;
+    private final OsAppId mOsAppId;
+
+    private TrafficDescriptor(@NonNull Parcel in) {
+        mDnn = in.readString();
+        byte[] osAppIdBytes = in.createByteArray();
+        OsAppId osAppId = null;
+        if (osAppIdBytes != null) {
+            osAppId = new OsAppId(osAppIdBytes);
+        }
+        mOsAppId = osAppId;
+
+        enforceAllowedIds();
+    }
+
+    /**
+     * Create a traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2
+     * @param dnn optional DNN, which must be used for traffic matching, if present
+     * @param osAppIdRawBytes Raw bytes of OsId + osAppId of the traffic descriptor
+     *
+     * @hide
+     */
+    public TrafficDescriptor(String dnn, @Nullable byte[] osAppIdRawBytes) {
+        mDnn = dnn;
+        OsAppId osAppId = null;
+        if (osAppIdRawBytes != null) {
+            osAppId = new OsAppId(osAppIdRawBytes);
+        }
+        mOsAppId = osAppId;
+
+        enforceAllowedIds();
+    }
+
+    /**
+     * Enforce the OS id and app id are in the allowed list.
+     *
+     * @throws IllegalArgumentException if ids are not allowed.
+     */
+    private void enforceAllowedIds() {
+        if (mOsAppId != null && !mOsAppId.getOsId().equals(OsAppId.ANDROID_OS_ID)) {
+            throw new IllegalArgumentException("OS id " + mOsAppId.getOsId() + " does not match "
+                    + OsAppId.ANDROID_OS_ID);
+        }
+
+        if (mOsAppId != null && !OsAppId.ALLOWED_APP_IDS.contains(mOsAppId.getAppId())) {
+            throw new IllegalArgumentException("Illegal app id " + mOsAppId.getAppId()
+                    + ". Only allowing one of the following " + OsAppId.ALLOWED_APP_IDS);
+        }
+    }
+
+    /**
+     * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003.
+     * @return the DNN of this traffic descriptor if one is included by the network, null
+     * otherwise.
+     */
+    public @Nullable String getDataNetworkName() {
+        return mDnn;
+    }
+
+    /**
+     * OsAppId identifies a broader traffic category. Although it names Os/App id, it only includes
+     * OS version with a general/broader category id used as app id.
+     *
+     * @return The id in byte format. {@code null} if not available.
+     */
+    public @Nullable byte[] getOsAppId() {
+        return mOsAppId != null ? mOsAppId.getBytes() : null;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull @Override
+    public String toString() {
+        return "TrafficDescriptor={mDnn=" + mDnn + ", " + mOsAppId + "}";
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mDnn);
+        dest.writeByteArray(mOsAppId != null ? mOsAppId.getBytes() : null);
+    }
+
+    public static final @NonNull Parcelable.Creator<TrafficDescriptor> CREATOR =
+            new Parcelable.Creator<TrafficDescriptor>() {
+                @Override
+                public @NonNull TrafficDescriptor createFromParcel(@NonNull Parcel source) {
+                    return new TrafficDescriptor(source);
+                }
+
+                @Override
+                public @NonNull TrafficDescriptor[] newArray(int size) {
+                    return new TrafficDescriptor[size];
+                }
+            };
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        TrafficDescriptor that = (TrafficDescriptor) o;
+        return Objects.equals(mDnn, that.mDnn) && Objects.equals(mOsAppId, that.mOsAppId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mDnn, mOsAppId);
+    }
+
+    /**
+     * Provides a convenient way to set the fields of a {@link TrafficDescriptor} when creating a
+     * new instance.
+     *
+     * <p>The example below shows how you might create a new {@code TrafficDescriptor}:
+     *
+     * <pre><code>
+     *
+     * TrafficDescriptor response = new TrafficDescriptor.Builder()
+     *     .setDnn("")
+     *     .build();
+     * </code></pre>
+     *
+     */
+    public static final class Builder {
+        private String mDnn = null;
+        private byte[] mOsAppId = null;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Set the Data Network Name(DNN).
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setDataNetworkName(@NonNull String dnn) {
+            this.mDnn = dnn;
+            return this;
+        }
+
+        /**
+         * Set the OS App ID (including OS Id as defined in the specs).
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setOsAppId(@NonNull byte[] osAppId) {
+            this.mOsAppId = osAppId;
+            return this;
+        }
+
+        /**
+         * Build the {@link TrafficDescriptor}.
+         *
+         * @throws IllegalArgumentException if DNN and OS App ID are null.
+         *
+         * @return the {@link TrafficDescriptor} object.
+         */
+        @NonNull
+        public TrafficDescriptor build() {
+            if (this.mDnn == null && this.mOsAppId == null) {
+                throw new IllegalArgumentException("DNN and OS App ID are null");
+            }
+            return new TrafficDescriptor(this.mDnn, this.mOsAppId);
+        }
+    }
+}
diff --git a/android-35/android/telephony/data/UrspRule.java b/android-35/android/telephony/data/UrspRule.java
new file mode 100644
index 0000000..afbd429
--- /dev/null
+++ b/android-35/android/telephony/data/UrspRule.java
@@ -0,0 +1,141 @@
+/**
+ * Copyright 2021 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.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a single URSP rule as defined in 3GPP TS 24.526. URSP stands for UE Route Selection
+ * Policy. In 5G, network can provide URSP information to devices which provides information on
+ * what connection parameters should be used for what traffic.
+ */
+public final class UrspRule implements Parcelable {
+    /**
+     * The min acceptable value for the precedence of a URSP rule.
+     * @hide
+     */
+    public static final int MIN_URSP_PRECEDENCE = 0;
+
+    /**
+     * The max acceptable value for the precedence of a URSP rule.
+     * @hide
+     */
+    public static final int MAX_URSP_PRECEDENCE = 255;
+
+    @IntRange(from = MIN_URSP_PRECEDENCE, to = MAX_URSP_PRECEDENCE)
+    private final int mPrecedence;
+    private final List<TrafficDescriptor> mTrafficDescriptors;
+    private final List<RouteSelectionDescriptor> mRouteSelectionDescriptor;
+
+    /** @hide */
+    public UrspRule(int precedence, List<TrafficDescriptor> trafficDescriptors,
+            List<RouteSelectionDescriptor> routeSelectionDescriptor) {
+        mPrecedence = precedence;
+        mTrafficDescriptors = new ArrayList<>();
+        mTrafficDescriptors.addAll(trafficDescriptors);
+        mRouteSelectionDescriptor = new ArrayList<>();
+        mRouteSelectionDescriptor.addAll(routeSelectionDescriptor);
+    }
+
+    private UrspRule(Parcel p) {
+        mPrecedence = p.readInt();
+        mTrafficDescriptors = p.createTypedArrayList(TrafficDescriptor.CREATOR);
+        mRouteSelectionDescriptor = p.createTypedArrayList(RouteSelectionDescriptor.CREATOR);
+    }
+
+    /**
+     * Precedence value in the range of 0 to 255. Higher value has lower precedence.
+     * @return the precedence value for this URSP rule.
+     */
+    @IntRange(from = MIN_URSP_PRECEDENCE, to = MAX_URSP_PRECEDENCE)
+    public int getPrecedence() {
+        return mPrecedence;
+    }
+
+    /**
+     * These traffic descriptors are used as a matcher for network requests.
+     * @return the traffic descriptors which are associated to this URSP rule.
+     */
+    public @NonNull List<TrafficDescriptor> getTrafficDescriptors() {
+        return mTrafficDescriptors;
+    }
+
+    /**
+     * List of routes (connection parameters) that must be used by the device for requests matching
+     * a traffic descriptor.
+     * @return the route selection descriptors which are associated to this URSP rule.
+     */
+    public @NonNull List<RouteSelectionDescriptor> getRouteSelectionDescriptor() {
+        return mRouteSelectionDescriptor;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mPrecedence);
+        dest.writeTypedList(mTrafficDescriptors, flags);
+        dest.writeTypedList(mRouteSelectionDescriptor, flags);
+    }
+
+    public static final @NonNull Parcelable.Creator<UrspRule> CREATOR =
+            new Parcelable.Creator<UrspRule>() {
+                @Override
+                public UrspRule createFromParcel(Parcel source) {
+                    return new UrspRule(source);
+                }
+
+                @Override
+                public UrspRule[] newArray(int size) {
+                    return new UrspRule[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        UrspRule that = (UrspRule) o;
+        return mPrecedence == that.mPrecedence
+                && mTrafficDescriptors.size() == that.mTrafficDescriptors.size()
+                && mTrafficDescriptors.containsAll(that.mTrafficDescriptors)
+                && mRouteSelectionDescriptor.size() == that.mRouteSelectionDescriptor.size()
+                && mRouteSelectionDescriptor.containsAll(that.mRouteSelectionDescriptor);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPrecedence, mTrafficDescriptors, mRouteSelectionDescriptor);
+    }
+
+    @Override
+    public String toString() {
+        return "{.precedence = " + mPrecedence + ", .trafficDescriptors = " + mTrafficDescriptors
+                + ", .routeSelectionDescriptor = " + mRouteSelectionDescriptor + "}";
+    }
+}
diff --git a/android-35/android/telephony/emergency/EmergencyNumber.java b/android-35/android/telephony/emergency/EmergencyNumber.java
new file mode 100644
index 0000000..d44a43e
--- /dev/null
+++ b/android-35/android/telephony/emergency/EmergencyNumber.java
@@ -0,0 +1,957 @@
+/*
+ * Copyright (C) 2018 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.emergency;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.hardware.radio.voice.EmergencyServiceCategory;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.CarrierConfigManager;
+import android.telephony.PhoneNumberUtils;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * A parcelable class that wraps and retrieves the information of number, service category(s) and
+ * country code for a specific emergency number.
+ */
+public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNumber> {
+
+    private static final String LOG_TAG = "EmergencyNumber";
+
+    /**
+     * Defining Emergency Service Category as follows:
+     *  - General emergency call, all categories;
+     *  - Police;
+     *  - Ambulance;
+     *  - Fire Brigade;
+     *  - Marine Guard;
+     *  - Mountain Rescue;
+     *  - Manually Initiated eCall (MIeC);
+     *  - Automatically Initiated eCall (AIeC);
+     *
+     * Category UNSPECIFIED (General emergency call, all categories) indicates that no specific
+     * services are associated with this emergency number; if the emergency number is specified,
+     * it has one or more defined emergency service categories.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     *
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "EMERGENCY_SERVICE_CATEGORY_" }, value = {
+            EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+            EMERGENCY_SERVICE_CATEGORY_POLICE,
+            EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
+            EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+            EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD,
+            EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE,
+            EMERGENCY_SERVICE_CATEGORY_MIEC,
+            EMERGENCY_SERVICE_CATEGORY_AIEC
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EmergencyServiceCategories {}
+
+    /**
+     * Emergency Service Category UNSPECIFIED (General emergency call, all categories) bit-field
+     * indicates that no specific services are associated with this emergency number; if the
+     * emergency number is specified, it has one or more defined emergency service categories.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED =
+            EmergencyServiceCategory.UNSPECIFIED;
+    /**
+     * Bit-field that indicates Emergency Service Category for Police.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_POLICE = EmergencyServiceCategory.POLICE;
+    /**
+     * Bit-field that indicates Emergency Service Category for Ambulance.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_AMBULANCE =
+            EmergencyServiceCategory.AMBULANCE;
+    /**
+     * Bit-field that indicates Emergency Service Category for Fire Brigade.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE =
+            EmergencyServiceCategory.FIRE_BRIGADE;
+    /**
+     * Bit-field that indicates Emergency Service Category for Marine Guard.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD =
+            EmergencyServiceCategory.MARINE_GUARD;
+    /**
+     * Bit-field that indicates Emergency Service Category for Mountain Rescue.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE =
+            EmergencyServiceCategory.MOUNTAIN_RESCUE;
+    /**
+     * Bit-field that indicates Emergency Service Category for Manually Initiated eCall (MIeC)
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_MIEC = EmergencyServiceCategory.MIEC;
+    /**
+     * Bit-field that indicates Emergency Service Category for Automatically Initiated eCall (AIeC)
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_AIEC = EmergencyServiceCategory.AIEC;
+
+    private static final Set<Integer> EMERGENCY_SERVICE_CATEGORY_SET;
+    static {
+        EMERGENCY_SERVICE_CATEGORY_SET = new HashSet<Integer>();
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_POLICE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MIEC);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_AIEC);
+    }
+
+    /**
+     * The source to tell where the corresponding @1.4::EmergencyNumber comes from.
+     *
+     * The emergency number has one or more defined emergency number sources.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     *
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "EMERGENCY_NUMBER_SOURCE_" }, value = {
+            EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+            EMERGENCY_NUMBER_SOURCE_SIM,
+            EMERGENCY_NUMBER_SOURCE_DATABASE,
+            EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
+            EMERGENCY_NUMBER_SOURCE_DEFAULT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EmergencyNumberSources {}
+
+    /**
+     * Bit-field which indicates the number is from the network signaling.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING =
+            android.hardware.radio.voice.EmergencyNumber.SOURCE_NETWORK_SIGNALING;
+    /**
+     * Bit-field which indicates the number is from the sim.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_NUMBER_SOURCE_SIM =
+            android.hardware.radio.voice.EmergencyNumber.SOURCE_SIM;
+    /**
+     * Bit-field which indicates the number is from the platform-maintained database.
+     */
+    public static final int EMERGENCY_NUMBER_SOURCE_DATABASE =  1 << 4;
+    /**
+     * Bit-field which indicates the number is from test mode.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int EMERGENCY_NUMBER_SOURCE_TEST =  1 << 5;
+    /** Bit-field which indicates the number is from the modem config. */
+    public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG =
+            android.hardware.radio.voice.EmergencyNumber.SOURCE_MODEM_CONFIG;
+    /**
+     * Bit-field which indicates the number is available as default.
+     *
+     * 112, 911 must always be available; additionally, 000, 08, 110, 999, 118 and 119 must be
+     * available when sim is not present.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT =
+            android.hardware.radio.voice.EmergencyNumber.SOURCE_DEFAULT;
+
+    private static final Set<Integer> EMERGENCY_NUMBER_SOURCE_SET;
+    static {
+        EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_SIM);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DATABASE);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT);
+    }
+
+    /**
+     * Indicated the framework does not know whether an emergency call should be placed using
+     * emergency or normal call routing. This means the underlying radio or IMS implementation is
+     * free to determine for itself how to route the call.
+     */
+    public static final int EMERGENCY_CALL_ROUTING_UNKNOWN = 0;
+    /**
+     * Indicates the radio or IMS implementation must handle the call through emergency routing.
+     */
+    public static final int EMERGENCY_CALL_ROUTING_EMERGENCY = 1;
+    /**
+     * Indicates the radio or IMS implementation must handle the call through normal call routing.
+     */
+    public static final int EMERGENCY_CALL_ROUTING_NORMAL = 2;
+
+    /**
+     * The routing to tell how to handle the call for the corresponding emergency number.
+     *
+     * @hide
+     */
+    @IntDef(flag = false, prefix = { "EMERGENCY_CALL_ROUTING_" }, value = {
+            EMERGENCY_CALL_ROUTING_UNKNOWN,
+            EMERGENCY_CALL_ROUTING_EMERGENCY,
+            EMERGENCY_CALL_ROUTING_NORMAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EmergencyCallRouting {}
+
+
+    private final String mNumber;
+    private final String mCountryIso;
+    private final String mMnc;
+    private final int mEmergencyServiceCategoryBitmask;
+    private final List<String> mEmergencyUrns;
+    private final int mEmergencyNumberSourceBitmask;
+    private final int mEmergencyCallRouting;
+    /**
+     * The source of the EmergencyNumber in the order of precedence.
+     */
+    private static final int[] EMERGENCY_NUMBER_SOURCE_PRECEDENCE;
+    static {
+        EMERGENCY_NUMBER_SOURCE_PRECEDENCE = new int[4];
+        EMERGENCY_NUMBER_SOURCE_PRECEDENCE[0] = EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING;
+        EMERGENCY_NUMBER_SOURCE_PRECEDENCE[1] = EMERGENCY_NUMBER_SOURCE_SIM;
+        EMERGENCY_NUMBER_SOURCE_PRECEDENCE[2] = EMERGENCY_NUMBER_SOURCE_DATABASE;
+        EMERGENCY_NUMBER_SOURCE_PRECEDENCE[3] = EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG;
+    }
+
+    /** @hide */
+    public EmergencyNumber(@NonNull String number, @NonNull String countryIso, @NonNull String mnc,
+                           @EmergencyServiceCategories int emergencyServiceCategories,
+                           @NonNull List<String> emergencyUrns,
+                           @EmergencyNumberSources int emergencyNumberSources,
+                           @EmergencyCallRouting int emergencyCallRouting) {
+        this.mNumber = number;
+        this.mCountryIso = countryIso;
+        this.mMnc = mnc;
+        this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories;
+        this.mEmergencyUrns = emergencyUrns;
+        this.mEmergencyNumberSourceBitmask = emergencyNumberSources;
+        this.mEmergencyCallRouting = emergencyCallRouting;
+    }
+
+    /** @hide */
+    public EmergencyNumber(Parcel source) {
+        mNumber = source.readString();
+        mCountryIso = source.readString();
+        mMnc = source.readString();
+        mEmergencyServiceCategoryBitmask = source.readInt();
+        mEmergencyUrns = source.createStringArrayList();
+        mEmergencyNumberSourceBitmask = source.readInt();
+        mEmergencyCallRouting = source.readInt();
+    }
+
+    @Override
+    /** @hide */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mNumber);
+        dest.writeString(mCountryIso);
+        dest.writeString(mMnc);
+        dest.writeInt(mEmergencyServiceCategoryBitmask);
+        dest.writeStringList(mEmergencyUrns);
+        dest.writeInt(mEmergencyNumberSourceBitmask);
+        dest.writeInt(mEmergencyCallRouting);
+    }
+
+    public static final @NonNull Creator<EmergencyNumber> CREATOR =
+            new Creator<EmergencyNumber>() {
+                @Override
+                public EmergencyNumber createFromParcel(Parcel in) {
+                    return new EmergencyNumber(in);
+                }
+
+                @Override
+                public EmergencyNumber[] newArray(int size) {
+                    return new EmergencyNumber[size];
+                }
+            };
+
+    /**
+     * Get the dialing number of the emergency number.
+     *
+     * The character in the number string is only the dial pad
+     * character('0'-'9', '*', '+', or '#'). For example: 911.
+     *
+     * If the number starts with carrier prefix, the carrier prefix is configured in
+     * {@link CarrierConfigManager#KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY}.
+     *
+     * @return the dialing number.
+     */
+    public @NonNull String getNumber() {
+        return mNumber;
+    }
+
+    /**
+     * Get the country code string (lowercase character) in ISO 3166 format of the emergency number.
+     *
+     * @return the country code string (lowercase character) in ISO 3166 format.
+     */
+    public @NonNull String getCountryIso() {
+        return mCountryIso;
+    }
+
+    /**
+     * Get the Mobile Network Code of the emergency number.
+     *
+     * @return the Mobile Network Code of the emergency number.
+     */
+    public @NonNull String getMnc() {
+        return mMnc;
+    }
+
+    /**
+     * Returns the bitmask of emergency service categories of the emergency number.
+     *
+     * @return bitmask of the emergency service categories
+     *
+     * @hide
+     */
+    public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmask() {
+        return mEmergencyServiceCategoryBitmask;
+    }
+
+    /**
+     * Returns the bitmask of emergency service categories of the emergency number for
+     * internal dialing.
+     *
+     * @return bitmask of the emergency service categories
+     *
+     * @hide
+     */
+    public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmaskInternalDial() {
+        if (mEmergencyNumberSourceBitmask == EMERGENCY_NUMBER_SOURCE_DATABASE) {
+            return EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+        }
+        return mEmergencyServiceCategoryBitmask;
+    }
+
+    /**
+     * Returns the emergency service categories of the emergency number.
+     *
+     * Note: if the emergency number is in {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}, only
+     * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} is returned and it means the number is in
+     * all categories.
+     *
+     * @return a list of the emergency service categories
+     */
+    public @NonNull List<Integer> getEmergencyServiceCategories() {
+        List<Integer> categories = new ArrayList<>();
+        if (serviceUnspecified()) {
+            categories.add(EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
+            return categories;
+        }
+        for (Integer category : EMERGENCY_SERVICE_CATEGORY_SET) {
+            if (isInEmergencyServiceCategories(category)) {
+                categories.add(category);
+            }
+        }
+        return categories;
+    }
+
+    /**
+     * Returns the list of emergency Uniform Resources Names (URN) of the emergency number.
+     *
+     * For example, {@code urn:service:sos} is the generic URN for contacting emergency services
+     * of all type.
+     *
+     * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
+     *            RFC 5031
+     *
+     * @return list of emergency Uniform Resources Names (URN) or an empty list if the emergency
+     *         number does not have a specified emergency Uniform Resource Name.
+     */
+    public @NonNull List<String> getEmergencyUrns() {
+        return Collections.unmodifiableList(mEmergencyUrns);
+    }
+
+    /**
+     * Checks if the emergency service category is unspecified for the emergency number
+     * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}.
+     *
+     * @return {@code true} if the emergency service category is unspecified for the emergency
+     * number {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise.
+     */
+    private boolean serviceUnspecified() {
+        return mEmergencyServiceCategoryBitmask == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+    }
+
+    /**
+     * Checks if the emergency number is in the supplied emergency service category(s).
+     *
+     * @param categories - the supplied emergency service categories
+     *
+     * @return {@code true} if the emergency number is in the specified emergency service
+     * category(s) or if its emergency service category is
+     * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise.
+     */
+    public boolean isInEmergencyServiceCategories(@EmergencyServiceCategories int categories) {
+        if (categories == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED) {
+            return serviceUnspecified();
+        }
+        if (serviceUnspecified()) {
+            return true;
+        }
+        return (mEmergencyServiceCategoryBitmask & categories) == categories;
+    }
+
+    /**
+     * Returns the bitmask of the sources of the emergency number.
+     *
+     * @return bitmask of the emergency number sources
+     *
+     * @hide
+     */
+    public @EmergencyNumberSources int getEmergencyNumberSourceBitmask() {
+        return mEmergencyNumberSourceBitmask;
+    }
+
+    /**
+     * Returns a list of sources of the emergency number.
+     *
+     * @return a list of emergency number sources
+     */
+    public @NonNull List<Integer> getEmergencyNumberSources() {
+        List<Integer> sources = new ArrayList<>();
+        for (Integer source : EMERGENCY_NUMBER_SOURCE_SET) {
+            if ((mEmergencyNumberSourceBitmask & source) == source) {
+                sources.add(source);
+            }
+        }
+        return sources;
+    }
+
+    /**
+     * Checks if the emergency number is from the specified emergency number source(s).
+     *
+     * @return {@code true} if the emergency number is from the specified emergency number
+     * source(s); {@code false} otherwise.
+     *
+     * @param sources - the supplied emergency number sources
+     */
+    public boolean isFromSources(@EmergencyNumberSources int sources) {
+        return (mEmergencyNumberSourceBitmask & sources) == sources;
+    }
+
+    /**
+     * Returns the emergency call routing information.
+     *
+     * <p>Some regions require some emergency numbers which are not routed using typical emergency
+     * call processing, but are instead placed as regular phone calls. The emergency call routing
+     * field provides information about how an emergency call will be routed when it is placed.
+     *
+     * @return the emergency call routing requirement
+     */
+    public @EmergencyCallRouting int getEmergencyCallRouting() {
+        return mEmergencyCallRouting;
+    }
+
+    @Override
+    /** @hide */
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("[EmergencyNumber: %s, countryIso=%s, mnc=%s, src=%s, routing=%s, "
+                        + "categories=%s, urns=%s]",
+                mNumber,
+                mCountryIso,
+                mMnc,
+                sourceBitmaskToString(mEmergencyNumberSourceBitmask),
+                routingToString(mEmergencyCallRouting),
+                categoriesToString(mEmergencyServiceCategoryBitmask),
+                (mEmergencyUrns == null ? "" :
+                        mEmergencyUrns.stream().collect(Collectors.joining(","))));
+    }
+
+    /**
+     * @param categories emergency service category bitmask
+     * @return loggable string describing the category bitmask
+     */
+    private String categoriesToString(@EmergencyServiceCategories int categories) {
+        StringBuilder sb = new StringBuilder();
+        if ((categories & EMERGENCY_SERVICE_CATEGORY_AIEC) == EMERGENCY_SERVICE_CATEGORY_AIEC) {
+            sb.append("auto ");
+        }
+        if ((categories & EMERGENCY_SERVICE_CATEGORY_AMBULANCE)
+                == EMERGENCY_SERVICE_CATEGORY_AMBULANCE) {
+            sb.append("ambulance ");
+        }
+        if ((categories & EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE)
+                == EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE) {
+            sb.append("fire ");
+        }
+        if ((categories & EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD)
+                == EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD) {
+            sb.append("marine ");
+        }
+        if ((categories & EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE)
+                == EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE) {
+            sb.append("mountain ");
+        }
+        if ((categories & EMERGENCY_SERVICE_CATEGORY_POLICE) == EMERGENCY_SERVICE_CATEGORY_POLICE) {
+            sb.append("police ");
+        }
+        if ((categories & EMERGENCY_SERVICE_CATEGORY_MIEC) == EMERGENCY_SERVICE_CATEGORY_MIEC) {
+            sb.append("manual ");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * @param routing emergency call routing type
+     * @return loggable string describing the routing type.
+     */
+    private String routingToString(@EmergencyCallRouting int routing) {
+        return switch(routing) {
+            case EMERGENCY_CALL_ROUTING_EMERGENCY -> "emergency";
+            case EMERGENCY_CALL_ROUTING_NORMAL -> "normal";
+            case EMERGENCY_CALL_ROUTING_UNKNOWN -> "unknown";
+            default -> "🤷";
+        };
+    }
+
+    /**
+     * Builds a string describing the sources for an emergency number.
+     * @param sourceBitmask the source bitmask
+     * @return loggable string describing the sources.
+     */
+    private String sourceBitmaskToString(@EmergencyNumberSources int sourceBitmask) {
+        StringBuilder sb = new StringBuilder();
+        if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING)
+                == EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING) {
+            sb.append("net ");
+        }
+        if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_SIM) == EMERGENCY_NUMBER_SOURCE_SIM) {
+            sb.append("sim ");
+        }
+        if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_DATABASE)
+                == EMERGENCY_NUMBER_SOURCE_DATABASE) {
+            sb.append("db ");
+        }
+        if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG)
+                == EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG) {
+            sb.append("mdm ");
+        }
+        if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_DEFAULT) == EMERGENCY_NUMBER_SOURCE_DEFAULT) {
+            sb.append("def ");
+        }
+        if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_TEST) == EMERGENCY_NUMBER_SOURCE_TEST) {
+            sb.append("tst ");
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!EmergencyNumber.class.isInstance(o)) {
+            return false;
+        }
+        EmergencyNumber other = (EmergencyNumber) o;
+        return mNumber.equals(other.mNumber)
+                && mCountryIso.equals(other.mCountryIso)
+                && mMnc.equals(other.mMnc)
+                && mEmergencyServiceCategoryBitmask == other.mEmergencyServiceCategoryBitmask
+                && mEmergencyUrns.equals(other.mEmergencyUrns)
+                && mEmergencyNumberSourceBitmask == other.mEmergencyNumberSourceBitmask
+                && mEmergencyCallRouting == other.mEmergencyCallRouting;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mNumber, mCountryIso, mMnc, mEmergencyServiceCategoryBitmask,
+                mEmergencyUrns, mEmergencyNumberSourceBitmask, mEmergencyCallRouting);
+    }
+
+    /**
+     * Calculate the score for display priority.
+     *
+     * A higher display priority score means the emergency number has a higher display priority.
+     * The score is higher if the source is defined for a higher display priority.
+     *
+     * The priority of sources are defined as follows:
+     *     EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING >
+     *     EMERGENCY_NUMBER_SOURCE_SIM >
+     *     EMERGENCY_NUMBER_SOURCE_DATABASE >
+     *     EMERGENCY_NUMBER_SOURCE_DEFAULT >
+     *     EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
+     *
+     */
+    private int getDisplayPriorityScore() {
+        int score = 0;
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING)) {
+            score += 1 << 4;
+        }
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_SIM)) {
+            score += 1 << 3;
+        }
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
+            score += 1 << 2;
+        }
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DEFAULT)) {
+            score += 1 << 1;
+        }
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG)) {
+            score += 1 << 0;
+        }
+        return score;
+    }
+
+    /**
+     * Compare the display priority for this emergency number and the supplied emergency number.
+     *
+     * @param emergencyNumber the supplied emergency number
+     * @return a negative value if the supplied emergency number has a lower display priority;
+     *         a positive value if the supplied emergency number has a higher display priority;
+     *         0 if both have equal display priority.
+     */
+    @Override
+    public int compareTo(@NonNull EmergencyNumber emergencyNumber) {
+        if (this.getDisplayPriorityScore()
+                > emergencyNumber.getDisplayPriorityScore()) {
+            return -1;
+        } else if (this.getDisplayPriorityScore()
+                < emergencyNumber.getDisplayPriorityScore()) {
+            return 1;
+        } else if (this.getNumber().compareTo(emergencyNumber.getNumber()) != 0) {
+            return this.getNumber().compareTo(emergencyNumber.getNumber());
+        } else if (this.getCountryIso().compareTo(emergencyNumber.getCountryIso()) != 0) {
+            return this.getCountryIso().compareTo(emergencyNumber.getCountryIso());
+        } else if (this.getMnc().compareTo(emergencyNumber.getMnc()) != 0) {
+            return this.getMnc().compareTo(emergencyNumber.getMnc());
+        } else if (this.getEmergencyServiceCategoryBitmask()
+                != emergencyNumber.getEmergencyServiceCategoryBitmask()) {
+            return this.getEmergencyServiceCategoryBitmask()
+                    > emergencyNumber.getEmergencyServiceCategoryBitmask() ? -1 : 1;
+        } else if (this.getEmergencyUrns().toString().compareTo(
+                emergencyNumber.getEmergencyUrns().toString()) != 0) {
+            return this.getEmergencyUrns().toString().compareTo(
+                    emergencyNumber.getEmergencyUrns().toString());
+        } else if (this.getEmergencyCallRouting()
+                != emergencyNumber.getEmergencyCallRouting()) {
+            return this.getEmergencyCallRouting()
+                    > emergencyNumber.getEmergencyCallRouting() ? -1 : 1;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * In-place merge same emergency numbers in the emergency number list.
+     *
+     * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
+     * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
+     * for the same EmergencyNumber.
+     *
+     * @param emergencyNumberList the emergency number list to process
+     *
+     * @hide
+     */
+    public static void mergeSameNumbersInEmergencyNumberList(
+            List<EmergencyNumber> emergencyNumberList) {
+        mergeSameNumbersInEmergencyNumberList(emergencyNumberList, false);
+    }
+
+    /**
+     * In-place merge same emergency numbers in the emergency number list.
+     *
+     * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’ and 'mnc' fields.
+     * If mergeServiceCategoriesAndUrns is true ignore comparing of 'urns' and
+     * 'categories' fields and determine these fields from most precedent number. Else compare
+     * to get unique combination of EmergencyNumber.
+     * Multiple Emergency Number Sources should be merged into one bitfield for the
+     * same EmergencyNumber.
+     *
+     * @param emergencyNumberList the emergency number list to process
+     * @param mergeServiceCategoriesAndUrns {@code true} determine service category and urns
+     * from most precedent number. {@code false} compare those fields for determing duplicate.
+     *
+     * @hide
+     */
+    public static void mergeSameNumbersInEmergencyNumberList(
+            @NonNull List<EmergencyNumber> emergencyNumberList,
+            boolean mergeServiceCategoriesAndUrns) {
+        if (emergencyNumberList == null) {
+            return;
+        }
+
+        Set<Integer> duplicatedEmergencyNumberPosition = new HashSet<>();
+        for (int i = 0; i < emergencyNumberList.size(); i++) {
+            for (int j = 0; j < i; j++) {
+                if (areSameEmergencyNumbers(emergencyNumberList.get(i),
+                        emergencyNumberList.get(j), mergeServiceCategoriesAndUrns)) {
+                    Rlog.e(LOG_TAG, "Found unexpected duplicate numbers "
+                            + emergencyNumberList.get(i)
+                            + " vs " + emergencyNumberList.get(j));
+                    // Set the merged emergency number in the current position
+                    emergencyNumberList.set(i,
+                            mergeSameEmergencyNumbers(emergencyNumberList.get(i),
+                            emergencyNumberList.get(j), mergeServiceCategoriesAndUrns));
+                    // Mark the emergency number has been merged
+                    duplicatedEmergencyNumberPosition.add(j);
+                }
+            }
+        }
+
+        // Remove the marked emergency number in the original list
+        for (int i = emergencyNumberList.size() - 1; i >= 0; i--) {
+            if (duplicatedEmergencyNumberPosition.contains(i)) {
+                emergencyNumberList.remove(i);
+            }
+        }
+        Collections.sort(emergencyNumberList);
+    }
+
+    /**
+     * Check if two emergency numbers are the same.
+     *
+     * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' fields.
+     * If mergeServiceCategoriesAndUrns is true ignore comparing of 'urns' and
+     * 'categories' fields and determine these fields from most precedent number. Else compare
+     * to get unique combination of EmergencyNumber.
+     * Multiple Emergency Number Sources should be
+     * merged into one bitfield for the same EmergencyNumber.
+     *
+     * @param first first EmergencyNumber to compare
+     * @param second second EmergencyNumber to compare
+     * @param ignoreServiceCategoryAndUrns {@code true} Ignore comparing of service category
+     * and Urns so that they can be determined from most precedent number. {@code false} compare
+     * those fields for determing duplicate.
+     * @return true if they are the same EmergencyNumbers; false otherwise.
+     *
+     * @hide
+     */
+    public static boolean areSameEmergencyNumbers(@NonNull EmergencyNumber first,
+            @NonNull EmergencyNumber second, boolean ignoreServiceCategoryAndUrns) {
+        if (!first.getNumber().equals(second.getNumber())) {
+            return false;
+        }
+        if (!first.getCountryIso().equals(second.getCountryIso())) {
+            return false;
+        }
+        if (!first.getMnc().equals(second.getMnc())) {
+            return false;
+        }
+        if (!ignoreServiceCategoryAndUrns) {
+            if (first.getEmergencyServiceCategoryBitmask()
+                    != second.getEmergencyServiceCategoryBitmask()) {
+                return false;
+            }
+            if (!first.getEmergencyUrns().equals(second.getEmergencyUrns())) {
+                return false;
+            }
+        }
+        // Never merge two numbers if one of them is from test mode but the other one is not;
+        // This supports to remove a number from the test mode.
+        if (first.isFromSources(EMERGENCY_NUMBER_SOURCE_TEST)
+                ^ second.isFromSources(EMERGENCY_NUMBER_SOURCE_TEST)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Get a merged EmergencyNumber from two same emergency numbers. Two emergency numbers are
+     * the same if {@link #areSameEmergencyNumbers} returns {@code true}.
+     *
+     * @param first first EmergencyNumber to compare
+     * @param second second EmergencyNumber to compare
+     * @return a merged EmergencyNumber or null if they are not the same EmergencyNumber
+     *
+     * @hide
+     */
+    public static EmergencyNumber mergeSameEmergencyNumbers(@NonNull EmergencyNumber first,
+                                                            @NonNull EmergencyNumber second) {
+        if (areSameEmergencyNumbers(first, second, false)) {
+            int routing = first.getEmergencyCallRouting();
+
+            if (second.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
+                routing = second.getEmergencyCallRouting();
+            }
+
+            return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
+                    first.getEmergencyServiceCategoryBitmask(),
+                    first.getEmergencyUrns(),
+                    first.getEmergencyNumberSourceBitmask()
+                            | second.getEmergencyNumberSourceBitmask(),
+                    routing);
+        }
+        return null;
+    }
+
+    /**
+     * Get merged EmergencyUrns list from two same emergency numbers.
+     * By giving priority to the urns from first number.
+     *
+     * @param firstEmergencyUrns first number's Urns
+     * @param secondEmergencyUrns second number's Urns
+     * @return a merged Urns
+     *
+     * @hide
+     */
+    private static List<String> mergeEmergencyUrns(@NonNull List<String> firstEmergencyUrns,
+            @NonNull List<String> secondEmergencyUrns) {
+        List<String> mergedUrns = new ArrayList<String>();
+        mergedUrns.addAll(firstEmergencyUrns);
+        for (String urn : secondEmergencyUrns) {
+            if (!firstEmergencyUrns.contains(urn)) {
+                mergedUrns.add(urn);
+            }
+        }
+        return mergedUrns;
+    }
+
+    /**
+     * Get the highest precedence source of the given Emergency number. Then get service catergory
+     * and urns list fill in the respective map with key as source.
+     *
+     * @param num EmergencyNumber to get the source, service category & urns
+     * @param serviceCategoryArray Array to store the category of the given EmergencyNumber
+     * with key as highest precedence source
+     * @param urnsArray Array to store the list of Urns of the given EmergencyNumber
+     * with key as highest precedence source
+     *
+     * @hide
+     */
+    private static void fillServiceCategoryAndUrns(@NonNull EmergencyNumber num,
+            @NonNull SparseIntArray serviceCategoryArray,
+            @NonNull SparseArray<List<String>> urnsArray) {
+        int numberSrc = num.getEmergencyNumberSourceBitmask();
+        for (Integer source : EMERGENCY_NUMBER_SOURCE_PRECEDENCE) {
+            if ((numberSrc & source) == source) {
+                if (!num.isInEmergencyServiceCategories(EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED)) {
+                    serviceCategoryArray.put(source, num.getEmergencyServiceCategoryBitmask());
+                }
+                urnsArray.put(source, num.getEmergencyUrns());
+                break;
+            }
+        }
+    }
+
+    /**
+     * Get a merged EmergencyNumber from two same emergency numbers from
+     * Emergency number list. Two emergency numbers are the same if
+     * {@link #areSameEmergencyNumbers} returns {@code true}.
+     *
+     * @param first first EmergencyNumber to compare
+     * @param second second EmergencyNumber to compare
+     * @param mergeServiceCategoriesAndUrns {@code true} then determine service category and urns
+     * Service catetory : set from most precedence source number(N/W, SIM, DB, modem_cfg)
+     * Urns : merge from both with first priority from most precedence source number
+     * {@code false} then call {@link #mergeSameEmergencyNumbers} to merge.
+     * @return a merged EmergencyNumber or null if they are not the same EmergencyNumber
+     *
+     * @hide
+     */
+    public static @NonNull EmergencyNumber mergeSameEmergencyNumbers(
+            @NonNull EmergencyNumber first, @NonNull EmergencyNumber second,
+            boolean mergeServiceCategoriesAndUrns) {
+        if (!mergeServiceCategoriesAndUrns) {
+            return mergeSameEmergencyNumbers(first, second);
+        }
+
+        int routing = first.getEmergencyCallRouting();
+        int serviceCategory = first.getEmergencyServiceCategoryBitmask();
+        List<String> mergedEmergencyUrns = new ArrayList<String>();
+        //Maps to store the service category and urns of both the first and second emergency number
+        // with key as most precedent source
+        SparseIntArray serviceCategoryArray = new SparseIntArray(2);
+        SparseArray<List<String>> urnsArray = new SparseArray(2);
+
+        fillServiceCategoryAndUrns(first, serviceCategoryArray, urnsArray);
+        fillServiceCategoryAndUrns(second, serviceCategoryArray, urnsArray);
+
+        if (second.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
+            routing = second.getEmergencyCallRouting();
+        }
+
+        // Determine serviceCategory of most precedence number
+        for (int sourceOfCategory : EMERGENCY_NUMBER_SOURCE_PRECEDENCE) {
+            if (serviceCategoryArray.indexOfKey(sourceOfCategory) >= 0) {
+                serviceCategory = serviceCategoryArray.get(sourceOfCategory);
+                break;
+            }
+        }
+
+        // Merge Urns in precedence number
+        for (int sourceOfUrn : EMERGENCY_NUMBER_SOURCE_PRECEDENCE) {
+            if (urnsArray.contains(sourceOfUrn)) {
+                mergedEmergencyUrns = mergeEmergencyUrns(mergedEmergencyUrns,
+                        urnsArray.get(sourceOfUrn));
+            }
+        }
+
+        return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
+                serviceCategory, mergedEmergencyUrns,
+                first.getEmergencyNumberSourceBitmask()
+                        | second.getEmergencyNumberSourceBitmask(),
+                routing);
+    }
+
+    /**
+     * Validate Emergency Number address that only contains the dialable character
+     * {@link PhoneNumberUtils#isDialable(char)}
+     *
+     * @hide
+     */
+    public static boolean validateEmergencyNumberAddress(String address) {
+        if (address == null) {
+            return false;
+        }
+        for (char c : address.toCharArray()) {
+            if (!PhoneNumberUtils.isDialable(c)) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/android-35/android/telephony/euicc/DownloadableSubscription.java b/android-35/android/telephony/euicc/DownloadableSubscription.java
new file mode 100644
index 0000000..a5150b0
--- /dev/null
+++ b/android-35/android/telephony/euicc/DownloadableSubscription.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2017 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.euicc;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.UiccAccessRule;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Information about a subscription which is downloadable to an eUICC using
+ * {@link EuiccManager#downloadSubscription(DownloadableSubscription, boolean, PendingIntent).
+ *
+ * <p>For example, a DownloadableSubscription can be created through an activation code parsed from
+ * a QR code. A server address can be parsed from the activation code to download more information
+ * about the profile, such as carrier name, access rules, etc.
+ */
+public final class DownloadableSubscription implements Parcelable {
+
+    public static final @android.annotation.NonNull Creator<DownloadableSubscription> CREATOR =
+            new Creator<DownloadableSubscription>() {
+                @Override
+                public DownloadableSubscription createFromParcel(Parcel in) {
+                    return new DownloadableSubscription(in);
+                }
+
+                @Override
+                public DownloadableSubscription[] newArray(int size) {
+                    return new DownloadableSubscription[size];
+                }
+            };
+
+    /**
+     * Activation code. May be null for subscriptions which are not based on activation codes, e.g.
+     * to download a default subscription assigned to this device.
+     * Should use getEncodedActivationCode() instead.
+     * @hide
+     * @deprecated - Do not use. This will be private. Use getEncodedActivationCode() instead.
+     */
+    @Nullable
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public final String encodedActivationCode;
+
+    @Nullable private String confirmationCode;
+
+    // see getCarrierName and setCarrierName
+    @Nullable
+    private String carrierName;
+
+    // see getAccessRules and setAccessRules
+    @Nullable
+    private List<UiccAccessRule> accessRules;
+
+    /** Gets the activation code. */
+    @Nullable
+    public String getEncodedActivationCode() {
+        return encodedActivationCode;
+    }
+
+    /** @hide */
+    private DownloadableSubscription(String encodedActivationCode) {
+        this.encodedActivationCode = encodedActivationCode;
+    }
+
+    private DownloadableSubscription(Parcel in) {
+        encodedActivationCode = in.readString();
+        confirmationCode = in.readString();
+        carrierName = in.readString();
+        accessRules = new ArrayList<UiccAccessRule>();
+        in.readTypedList(accessRules, UiccAccessRule.CREATOR);
+    }
+
+    private DownloadableSubscription(String encodedActivationCode, String confirmationCode,
+            String carrierName, List<UiccAccessRule> accessRules) {
+        this.encodedActivationCode = encodedActivationCode;
+        this.confirmationCode = confirmationCode;
+        this.carrierName = carrierName;
+        this.accessRules = accessRules;
+    }
+
+    public static final class Builder {
+        @Nullable private String encodedActivationCode;
+        @Nullable private String confirmationCode;
+        @Nullable private String carrierName;
+        List<UiccAccessRule> accessRules;
+
+        /** @hide */
+        @SystemApi
+        public Builder() {}
+
+        public Builder(@NonNull DownloadableSubscription baseSubscription) {
+            encodedActivationCode = baseSubscription.getEncodedActivationCode();
+            confirmationCode = baseSubscription.getConfirmationCode();
+            carrierName = baseSubscription.getCarrierName();
+            accessRules = baseSubscription.getAccessRules();
+        }
+
+        public Builder(@NonNull String encodedActivationCode) {
+            this.encodedActivationCode = encodedActivationCode;
+        }
+
+        /**
+         * Builds a {@link DownloadableSubscription} object.
+         * @return a non-null {@link DownloadableSubscription} object.
+         */
+        @NonNull
+        public DownloadableSubscription build() {
+            return new DownloadableSubscription(encodedActivationCode, confirmationCode,
+                    carrierName, accessRules);
+        }
+
+        /**
+         * Sets the encoded activation code.
+         * @param value the activation code to use. An activation code can be parsed from a user
+         *              scanned QR code. The format of activation code is defined in SGP.22. For
+         *              example, "1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$1.3.6.1.4.1.31746". For
+         *              detail, see {@code com.android.euicc.data.ActivationCode}. Must not be null.
+         */
+        @NonNull
+        public Builder setEncodedActivationCode(@NonNull String value) {
+            encodedActivationCode = value;
+            return this;
+        }
+
+        /**
+         * Sets the confirmation code.
+         * @param value the confirmation code to use to authenticate the carrier server got
+         *              subscription download.
+         */
+        @NonNull
+        public Builder setConfirmationCode(@NonNull String value) {
+            confirmationCode = value;
+            return this;
+        }
+
+        /**
+         * Sets the user-visible carrier name.
+         * @param value carrier name.
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public Builder setCarrierName(@NonNull String value) {
+            carrierName = value;
+            return this;
+        }
+
+        /**
+         * Sets the {@link UiccAccessRule}s dictating access to this subscription.
+         * @param value A list of {@link UiccAccessRule}s.
+         * @hide
+         */
+        @NonNull
+        @SystemApi
+        public Builder setAccessRules(@NonNull List<UiccAccessRule> value) {
+            accessRules = value;
+            return this;
+        }
+    }
+
+    /**
+     * Create a DownloadableSubscription for the given activation code.
+     *
+     * <p>This fills the encodedActivationCode field. Other fields like confirmationCode,
+     * carrierName and accessRules may be filled in the implementation of
+     * {@code android.service.euicc.EuiccService} if exists.
+     *
+     * @param encodedActivationCode the activation code to use. An activation code can be parsed
+     *         from a user scanned QR code. The format of activation code is defined in SGP.22. For
+     *         example, "1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$1.3.6.1.4.1.31746". For detail, see
+     *         {@code com.android.euicc.data.ActivationCode}. Must not be null.
+     *
+     * @return the {@link DownloadableSubscription} which may be passed to
+     *     {@link EuiccManager#downloadSubscription}.
+     */
+    public static DownloadableSubscription forActivationCode(String encodedActivationCode) {
+        Preconditions.checkNotNull(encodedActivationCode, "Activation code may not be null");
+        return new DownloadableSubscription(encodedActivationCode);
+    }
+
+    /**
+     * Sets the confirmation code.
+     * @hide
+     * @deprecated - Do not use.
+     */
+    @Deprecated
+    public void setConfirmationCode(String confirmationCode) {
+        this.confirmationCode = confirmationCode;
+    }
+
+    /**
+     * Returns the confirmation code.
+     *
+     * <p>As an example, the confirmation code can be input by the user through a carrier app or the
+     * UI component of the eUICC local profile assistant (LPA) application.
+     */
+    @Nullable
+    public String getConfirmationCode() {
+        return confirmationCode;
+    }
+
+    /**
+     * Set the user-visible carrier name.
+     * @hide
+     * @deprecated - Do not use.
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void setCarrierName(String carrierName) {
+        this.carrierName = carrierName;
+    }
+
+    /**
+     * Returns the user-visible carrier name.
+     *
+     * <p>Only present for downloadable subscriptions that were queried from a server (as opposed to
+     * those created with {@link #forActivationCode}). May be populated with
+     * {@link EuiccManager#getDownloadableSubscriptionMetadata}.
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public String getCarrierName() {
+        return carrierName;
+    }
+
+    /**
+     * Returns the {@link UiccAccessRule}s in list dictating access to this subscription.
+     *
+     * <p>Only present for downloadable subscriptions that were queried from a server (as opposed to
+     * those created with {@link #forActivationCode}). May be populated with
+     * {@link EuiccManager#getDownloadableSubscriptionMetadata}.
+     * @hide
+     */
+    @SystemApi
+    public List<UiccAccessRule> getAccessRules() {
+        return accessRules;
+    }
+
+    /**
+     * Set the {@link UiccAccessRule}s dictating access to this subscription.
+     * @hide
+     * @deprecated - Do not use.
+     */
+    @Deprecated
+    public void setAccessRules(List<UiccAccessRule> accessRules) {
+        this.accessRules = accessRules;
+    }
+
+    /**
+     * @hide
+     * @deprecated - Do not use.
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public void setAccessRules(UiccAccessRule[] accessRules) {
+        this.accessRules = Arrays.asList(accessRules);
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(encodedActivationCode);
+        dest.writeString(confirmationCode);
+        dest.writeString(carrierName);
+        dest.writeTypedList(accessRules);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/android-35/android/telephony/euicc/EuiccCardManager.java b/android-35/android/telephony/euicc/EuiccCardManager.java
new file mode 100644
index 0000000..69594f2
--- /dev/null
+++ b/android-35/android/telephony/euicc/EuiccCardManager.java
@@ -0,0 +1,1009 @@
+/*
+ * Copyright (C) 2018 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.euicc;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.service.euicc.EuiccProfileInfo;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.telephony.euicc.IAuthenticateServerCallback;
+import com.android.internal.telephony.euicc.ICancelSessionCallback;
+import com.android.internal.telephony.euicc.IDeleteProfileCallback;
+import com.android.internal.telephony.euicc.IDisableProfileCallback;
+import com.android.internal.telephony.euicc.IEuiccCardController;
+import com.android.internal.telephony.euicc.IGetAllProfilesCallback;
+import com.android.internal.telephony.euicc.IGetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.IGetEuiccChallengeCallback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo1Callback;
+import com.android.internal.telephony.euicc.IGetEuiccInfo2Callback;
+import com.android.internal.telephony.euicc.IGetProfileCallback;
+import com.android.internal.telephony.euicc.IGetRulesAuthTableCallback;
+import com.android.internal.telephony.euicc.IGetSmdsAddressCallback;
+import com.android.internal.telephony.euicc.IListNotificationsCallback;
+import com.android.internal.telephony.euicc.ILoadBoundProfilePackageCallback;
+import com.android.internal.telephony.euicc.IPrepareDownloadCallback;
+import com.android.internal.telephony.euicc.IRemoveNotificationFromListCallback;
+import com.android.internal.telephony.euicc.IResetMemoryCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationCallback;
+import com.android.internal.telephony.euicc.IRetrieveNotificationListCallback;
+import com.android.internal.telephony.euicc.ISetDefaultSmdpAddressCallback;
+import com.android.internal.telephony.euicc.ISetNicknameCallback;
+import com.android.internal.telephony.euicc.ISwitchToProfileCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * EuiccCardManager is the application interface to an eSIM card.
+ * @hide
+ */
+@SystemApi
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
+public class EuiccCardManager {
+    private static final String TAG = "EuiccCardManager";
+
+    /**
+     * Reason for canceling a profile download session
+     *
+     * @removed mistakenly exposed previously
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CANCEL_REASON_"}, value = {
+            CANCEL_REASON_END_USER_REJECTED,
+            CANCEL_REASON_POSTPONED,
+            CANCEL_REASON_TIMEOUT,
+            CANCEL_REASON_PPR_NOT_ALLOWED
+    })
+    public @interface CancelReason {
+    }
+
+    /**
+     * The end user has rejected the download. The profile will be put into the error state and
+     * cannot be downloaded again without the operator's change.
+     */
+    public static final int CANCEL_REASON_END_USER_REJECTED = 0;
+
+    /** The download has been postponed and can be restarted later. */
+    public static final int CANCEL_REASON_POSTPONED = 1;
+
+    /** The download has been timed out and can be restarted later. */
+    public static final int CANCEL_REASON_TIMEOUT = 2;
+
+    /**
+     * The profile to be downloaded cannot be installed due to its policy rule is not allowed by
+     * the RAT (Rules Authorisation Table) on the eUICC or by other installed profiles. The
+     * download can be restarted later.
+     */
+    public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3;
+
+    /**
+     * Options for resetting eUICC memory
+     *
+     * @removed mistakenly exposed previously
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = {"RESET_OPTION_"}, value = {
+            RESET_OPTION_DELETE_OPERATIONAL_PROFILES,
+            RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES,
+            RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS
+    })
+    public @interface ResetOption {
+    }
+
+    /** Deletes all operational profiles. */
+    public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1;
+
+    /** Deletes all field-loaded testing profiles. */
+    public static final int RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES = 1 << 1;
+
+    /** Resets the default SM-DP+ address. */
+    public static final int RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS = 1 << 2;
+
+    /** Result code when the requested profile is not found.
+     * {@link #RESULT_PROFILE_NOT_FOUND} is not used in Android U+,
+     * use {@link #RESULT_PROFILE_DOES_NOT_EXIST} instead.
+     **/
+    public static final int RESULT_PROFILE_NOT_FOUND = 1;
+
+    /** Result code of execution with no error. */
+    public static final int RESULT_OK = 0;
+
+    /** Result code of an unknown error. */
+    public static final int RESULT_UNKNOWN_ERROR = -1;
+
+    /** Result code when the eUICC card with the given card Id is not found. */
+    public static final int RESULT_EUICC_NOT_FOUND = -2;
+
+    /** Result code indicating the caller is not the active LPA. */
+    public static final int RESULT_CALLER_NOT_ALLOWED = -3;
+
+    /** Result code when the requested profile does not exist */
+    public static final int RESULT_PROFILE_DOES_NOT_EXIST = -4;
+
+    /**
+     * Callback to receive the result of an eUICC card API.
+     *
+     * @param <T> Type of the result.
+     */
+    public interface ResultCallback<T> {
+        /**
+         * This method will be called when an eUICC card API call is completed.
+         *
+         * @param resultCode This can be {@link #RESULT_OK} or other positive values returned by the
+         *                   eUICC.
+         * @param result     The result object. It can be null if the {@code resultCode} is not
+         *                   {@link #RESULT_OK}.
+         */
+        void onComplete(int resultCode, T result);
+    }
+
+    private final Context mContext;
+
+    /** @hide */
+    public EuiccCardManager(Context context) {
+        mContext = context;
+    }
+
+    private IEuiccCardController getIEuiccCardController() {
+        return IEuiccCardController.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getEuiccCardControllerServiceRegisterer()
+                        .get());
+    }
+
+    /**
+     * Requests all the profiles on eUicc.
+     *
+     * @param cardId   The Id of the eUICC.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code and all the profiles.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void requestAllProfiles(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<EuiccProfileInfo[]> callback) {
+        try {
+            getIEuiccCardController().getAllProfiles(mContext.getOpPackageName(), cardId,
+                    new IGetAllProfilesCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccProfileInfo[] profiles) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profiles));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getAllProfiles", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Requests the profile of the given iccid.
+     *
+     * @param cardId   The Id of the eUICC.
+     * @param iccid    The iccid of the profile.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code and profile.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void requestProfile(String cardId, String iccid, @CallbackExecutor Executor executor,
+            ResultCallback<EuiccProfileInfo> callback) {
+        try {
+            getIEuiccCardController().getProfile(mContext.getOpPackageName(), cardId, iccid,
+                    new IGetProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccProfileInfo profile) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profile));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Requests the enabled profile for a given port on an eUicc. Callback with result code
+     * {@link RESULT_PROFILE_DOES_NOT_EXIST} and {@code NULL} EuiccProfile if there is no enabled
+     * profile on the target port.
+     *
+     * @param cardId    The Id of the eUICC.
+     * @param portIndex The portIndex to use. The port may be active or inactive. As long as the
+     *                  ICCID is known, an APDU will be sent through to read the enabled profile.
+     * @param executor  The executor through which the callback should be invoked.
+     * @param callback  The callback to get the result code and the profile.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void requestEnabledProfileForPort(@NonNull String cardId, int portIndex,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull ResultCallback<EuiccProfileInfo> callback) {
+        try {
+            getIEuiccCardController().getEnabledProfile(mContext.getOpPackageName(), cardId,
+                    portIndex,
+                    new IGetProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccProfileInfo profile) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profile));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling requestEnabledProfileForPort", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Disables the profile of the given iccid.
+     *
+     * @param cardId   The Id of the eUICC.
+     * @param iccid    The iccid of the profile.
+     * @param refresh  Whether sending the REFRESH command to modem.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void disableProfile(String cardId, String iccid, boolean refresh,
+            @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().disableProfile(mContext.getOpPackageName(), cardId, iccid,
+                    refresh, new IDisableProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling disableProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Switches from the current profile to another profile. The current profile will be disabled
+     * and the specified profile will be enabled.
+     *
+     * @param cardId   The Id of the eUICC.
+     * @param iccid    The iccid of the profile to switch to.
+     * @param refresh  Whether sending the REFRESH command to modem.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @deprecated instead use {@link #switchToProfile(String, String, int, boolean, Executor,
+     * ResultCallback)}
+     */
+    @Deprecated
+    public void switchToProfile(String cardId, String iccid, boolean refresh,
+            @CallbackExecutor Executor executor, ResultCallback<EuiccProfileInfo> callback) {
+        try {
+            getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid,
+                    TelephonyManager.DEFAULT_PORT_INDEX, refresh,
+                    new ISwitchToProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccProfileInfo profile) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profile));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling switchToProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Switches from the current profile to another profile. The current profile will be disabled
+     * and the specified profile will be enabled. Here portIndex specifies on which port the
+     * profile is to be enabled.
+     *
+     * @param cardId    The Id of the eUICC.
+     * @param iccid     The iccid of the profile to switch to.
+     * @param portIndex The Port index is the unique index referring to a port.
+     * @param refresh   Whether sending the REFRESH command to modem.
+     * @param executor  The executor through which the callback should be invoked.
+     * @param callback  The callback to get the result code and the EuiccProfileInfo enabled.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void switchToProfile(@Nullable String cardId, @Nullable String iccid, int portIndex,
+            boolean refresh, @NonNull @CallbackExecutor Executor executor,
+            @NonNull ResultCallback<EuiccProfileInfo> callback) {
+        try {
+            getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid,
+                    portIndex, refresh, new ISwitchToProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccProfileInfo profile) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profile));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling switchToProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the nickname of the profile of the given iccid.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param iccid The iccid of the profile.
+     * @param nickname The nickname of the profile.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void setNickname(String cardId, String iccid, String nickname,
+            @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().setNickname(mContext.getOpPackageName(), cardId, iccid,
+                    nickname, new ISetNicknameCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling setNickname", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Deletes the profile of the given iccid from eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param iccid The iccid of the profile.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void deleteProfile(String cardId, String iccid, @CallbackExecutor Executor executor,
+            ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().deleteProfile(mContext.getOpPackageName(), cardId, iccid,
+                    new IDeleteProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling deleteProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Resets the eUICC memory.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param options Bits of the options of resetting which parts of the eUICC memory. See
+     *     EuiccCard for details.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void resetMemory(String cardId, @ResetOption int options,
+            @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().resetMemory(mContext.getOpPackageName(), cardId, options,
+                    new IResetMemoryCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling resetMemory", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Requests the default SM-DP+ address from eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code and the default SM-DP+ address.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void requestDefaultSmdpAddress(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<String> callback) {
+        try {
+            getIEuiccCardController().getDefaultSmdpAddress(mContext.getOpPackageName(), cardId,
+                    new IGetDefaultSmdpAddressCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, String address) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, address));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getDefaultSmdpAddress", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Requests the SM-DS address from eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code and the SM-DS address.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void requestSmdsAddress(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<String> callback) {
+        try {
+            getIEuiccCardController().getSmdsAddress(mContext.getOpPackageName(), cardId,
+                    new IGetSmdsAddressCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, String address) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, address));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getSmdsAddress", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the default SM-DP+ address of eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param defaultSmdpAddress The default SM-DP+ address to set.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress,
+            @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().setDefaultSmdpAddress(mContext.getOpPackageName(), cardId,
+                    defaultSmdpAddress,
+                    new ISetDefaultSmdpAddressCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling setDefaultSmdpAddress", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Requests Rules Authorisation Table.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback the callback to get the result code and the rule authorisation table.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void requestRulesAuthTable(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<EuiccRulesAuthTable> callback) {
+        try {
+            getIEuiccCardController().getRulesAuthTable(mContext.getOpPackageName(), cardId,
+                    new IGetRulesAuthTableCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccRulesAuthTable rat) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, rat));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getRulesAuthTable", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Requests the eUICC challenge for new profile downloading.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback the callback to get the result code and the challenge.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void requestEuiccChallenge(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().getEuiccChallenge(mContext.getOpPackageName(), cardId,
+                    new IGetEuiccChallengeCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] challenge) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, challenge));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getEuiccChallenge", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Requests the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback the callback to get the result code and the info1.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void requestEuiccInfo1(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().getEuiccInfo1(mContext.getOpPackageName(), cardId,
+                    new IGetEuiccInfo1Callback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] info) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, info));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getEuiccInfo1", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the eUICC info2 defined in GSMA RSP v2.0+ for new profile downloading.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback the callback to get the result code and the info2.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void requestEuiccInfo2(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().getEuiccInfo2(mContext.getOpPackageName(), cardId,
+                    new IGetEuiccInfo2Callback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] info) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, info));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getEuiccInfo2", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Authenticates the SM-DP+ server by the eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param matchingId the activation code token defined in GSMA RSP v2.0+ or empty when it is not
+     *     required.
+     * @param serverSigned1 ASN.1 data in byte array signed and returned by the SM-DP+ server.
+     * @param serverSignature1 ASN.1 data in byte array indicating a SM-DP+ signature which is
+     *     returned by SM-DP+ server.
+     * @param euiccCiPkIdToBeUsed ASN.1 data in byte array indicating CI Public Key Identifier to be
+     *     used by the eUICC for signature which is returned by SM-DP+ server. This is defined in
+     *     GSMA RSP v2.0+.
+     * @param serverCertificate ASN.1 data in byte array indicating SM-DP+ Certificate returned by
+     *     SM-DP+ server.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback the callback to get the result code and a byte array which represents a
+     *     {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void authenticateServer(String cardId, String matchingId, byte[] serverSigned1,
+            byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate,
+            @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().authenticateServer(
+                    mContext.getOpPackageName(),
+                    cardId,
+                    matchingId,
+                    serverSigned1,
+                    serverSignature1,
+                    euiccCiPkIdToBeUsed,
+                    serverCertificate,
+                    new IAuthenticateServerCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] response) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling authenticateServer", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Prepares the profile download request sent to SM-DP+.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param hashCc the hash of confirmation code. It can be null if there is no confirmation code
+     *     required.
+     * @param smdpSigned2 ASN.1 data in byte array indicating the data to be signed by the SM-DP+
+     *     returned by SM-DP+ server.
+     * @param smdpSignature2 ASN.1 data in byte array indicating the SM-DP+ signature returned by
+     *     SM-DP+ server.
+     * @param smdpCertificate ASN.1 data in byte array indicating the SM-DP+ Certificate returned
+     *     by SM-DP+ server.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback the callback to get the result code and a byte array which represents a
+     *     {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void prepareDownload(String cardId, @Nullable byte[] hashCc, byte[] smdpSigned2,
+            byte[] smdpSignature2, byte[] smdpCertificate, @CallbackExecutor Executor executor,
+            ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().prepareDownload(
+                    mContext.getOpPackageName(),
+                    cardId,
+                    hashCc,
+                    smdpSigned2,
+                    smdpSignature2,
+                    smdpCertificate,
+                    new IPrepareDownloadCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] response) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling prepareDownload", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Loads a downloaded bound profile package onto the eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param boundProfilePackage the Bound Profile Package data returned by SM-DP+ server.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback the callback to get the result code and a byte array which represents a
+     *     {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void loadBoundProfilePackage(String cardId, byte[] boundProfilePackage,
+            @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().loadBoundProfilePackage(
+                    mContext.getOpPackageName(),
+                    cardId,
+                    boundProfilePackage,
+                    new ILoadBoundProfilePackageCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] response) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling loadBoundProfilePackage", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Cancels the current profile download session.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param transactionId the transaction ID returned by SM-DP+ server.
+     * @param reason the cancel reason.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback the callback to get the result code and an byte[] which represents a
+     *     {@code CancelSessionResponse} defined in GSMA RSP v2.0+.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void cancelSession(String cardId, byte[] transactionId, @CancelReason int reason,
+            @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) {
+        try {
+            getIEuiccCardController().cancelSession(
+                    mContext.getOpPackageName(),
+                    cardId,
+                    transactionId,
+                    reason,
+                    new ICancelSessionCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, byte[] response) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling cancelSession", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Lists all notifications of the given {@code events}.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback the callback to get the result code and the list of notifications.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void listNotifications(String cardId, @EuiccNotification.Event int events,
+            @CallbackExecutor Executor executor, ResultCallback<EuiccNotification[]> callback) {
+        try {
+            getIEuiccCardController().listNotifications(mContext.getOpPackageName(), cardId, events,
+                    new IListNotificationsCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccNotification[] notifications) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(
+                                        resultCode, notifications));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling listNotifications", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieves contents of all notification of the given {@code events}.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback the callback to get the result code and the list of notifications.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void retrieveNotificationList(String cardId, @EuiccNotification.Event int events,
+            @CallbackExecutor Executor executor, ResultCallback<EuiccNotification[]> callback) {
+        try {
+            getIEuiccCardController().retrieveNotificationList(mContext.getOpPackageName(), cardId,
+                    events, new IRetrieveNotificationListCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccNotification[] notifications) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(
+                                        resultCode, notifications));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling retrieveNotificationList", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Retrieves the content of a notification of the given {@code seqNumber}.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param seqNumber the sequence number of the notification.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback the callback to get the result code and the notification.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void retrieveNotification(String cardId, int seqNumber,
+            @CallbackExecutor Executor executor, ResultCallback<EuiccNotification> callback) {
+        try {
+            getIEuiccCardController().retrieveNotification(mContext.getOpPackageName(), cardId,
+                    seqNumber, new IRetrieveNotificationCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccNotification notification) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(
+                                        resultCode, notification));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling retrieveNotification", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Removes a notification from eUICC.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param seqNumber the sequence number of the notification.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback the callback to get the result code.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public void removeNotificationFromList(String cardId, int seqNumber,
+            @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().removeNotificationFromList(
+                    mContext.getOpPackageName(),
+                    cardId,
+                    seqNumber,
+                    new IRemoveNotificationFromListCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling removeNotificationFromList", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/android-35/android/telephony/euicc/EuiccInfo.java b/android-35/android/telephony/euicc/EuiccInfo.java
new file mode 100644
index 0000000..08de205
--- /dev/null
+++ b/android-35/android/telephony/euicc/EuiccInfo.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 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.euicc;
+
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information about an eUICC chip/device.
+ *
+ * @see EuiccManager#getEuiccInfo
+ */
+// WARNING: Do not add any privacy-sensitive fields to this class (such as an eUICC identifier)!
+// This API is accessible to all applications. Privacy-sensitive fields should be returned in their
+// own APIs guarded with appropriate permission checks.
+public final class EuiccInfo implements Parcelable {
+
+    public static final @android.annotation.NonNull Creator<EuiccInfo> CREATOR =
+            new Creator<EuiccInfo>() {
+                @Override
+                public EuiccInfo createFromParcel(Parcel in) {
+                    return new EuiccInfo(in);
+                }
+
+                @Override
+                public EuiccInfo[] newArray(int size) {
+                    return new EuiccInfo[size];
+                }
+            };
+
+    @Nullable
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private final String osVersion;
+
+    /**
+     * Gets the version of the operating system running on the eUICC. This field is
+     * hardware-specific and is not guaranteed to match any particular format.
+     */
+    @Nullable
+    public String getOsVersion() {
+        return osVersion;
+    }
+
+    public EuiccInfo(@Nullable String osVersion) {
+        this.osVersion = osVersion;
+    }
+
+    private EuiccInfo(Parcel in) {
+        osVersion = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(osVersion);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/android-35/android/telephony/euicc/EuiccManager.java b/android-35/android/telephony/euicc/EuiccManager.java
new file mode 100644
index 0000000..ca4a643
--- /dev/null
+++ b/android-35/android/telephony/euicc/EuiccManager.java
@@ -0,0 +1,1831 @@
+/*
+ * Copyright (C) 2017 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.euicc;
+
+import android.Manifest;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SuppressAutoDoc;
+import android.annotation.SystemApi;
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccCardInfo;
+import android.telephony.euicc.EuiccCardManager.ResetOption;
+import android.util.Log;
+
+import com.android.internal.telephony.euicc.IEuiccController;
+import com.android.internal.telephony.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * EuiccManager is the application interface to eUICCs, or eSIMs/embedded SIMs.
+ *
+ * <p>You do not instantiate this class directly; instead, you retrieve an instance through
+ * {@link Context#getSystemService(String)} and {@link Context#EUICC_SERVICE}. This instance will be
+ * created using the default eUICC.
+ *
+ * <p>On a device with multiple eUICCs, you may want to create multiple EuiccManagers. To do this
+ * you can call {@link #createForCardId}.
+ *
+ * <p>See {@link #isEnabled} before attempting to use these APIs.
+ */
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
+public class EuiccManager {
+    private static final String TAG = "EuiccManager";
+
+    /**
+     * Intent action to launch the embedded SIM (eUICC) management settings screen.
+     *
+     * <p>This screen shows a list of embedded profiles and offers the user the ability to switch
+     * between them, download new profiles, and delete unused profiles.
+     *
+     * <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if
+     * {@link #isEnabled} is false.
+     *
+     * This is ued by non-LPA app to bring up LUI.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
+            "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
+
+
+    /**
+     * Intent action to transfer an embedded subscriptions.
+     *
+     * <p> Action sent by apps (such as the Settings app) to the Telephony framework to transfer an
+     * embedded subscription.
+     *
+     * <p> Requires that the calling app has the
+     * {@code android.Manifest.permission#MODIFY_PHONE_STATE} permission.
+     *
+     * <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if
+     * {@link #isEnabled} is false.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public static final String ACTION_TRANSFER_EMBEDDED_SUBSCRIPTIONS =
+            "android.telephony.euicc.action.TRANSFER_EMBEDDED_SUBSCRIPTIONS";
+
+    /**
+     * Intent action to convert the physical subscription to an embedded subscription.
+     *
+     * <p> Action sent by apps (such as the Settings app) to the Telephony framework to convert
+     * physical sim to embedded sim.
+     *
+     * <p> Requires that the calling app has the
+     * {@code android.Manifest.permission#MODIFY_PHONE_STATE} permission.
+     *
+     * <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if
+     * {@link #isEnabled} is false.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public static final String ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION =
+            "android.telephony.euicc.action.CONVERT_TO_EMBEDDED_SUBSCRIPTION";
+
+    /**
+     * Broadcast Action: The eUICC OTA status is changed.
+     * <p class="note">
+     * Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public static final String ACTION_OTA_STATUS_CHANGED =
+            "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+
+    /**
+     * Broadcast Action: The action sent to carrier app so it knows the carrier setup is not
+     * completed.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE =
+            "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE";
+
+    /**
+     * Intent action to provision an embedded subscription.
+     *
+     * <p>May be called during device provisioning to launch a screen to perform embedded SIM
+     * provisioning, e.g. if no physical SIM is present and the user elects to configure their
+     * embedded SIM.
+     *
+     * <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if
+     * {@link #isEnabled} is false.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION =
+            "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+
+    /**
+     * Intent action to handle a resolvable error.
+     * @hide
+     */
+    public static final String ACTION_RESOLVE_ERROR =
+            "android.telephony.euicc.action.RESOLVE_ERROR";
+
+    /**
+     * Intent action sent by system apps (such as the Settings app) to the Telephony framework to
+     * enable or disable a subscription. Must be accompanied with {@link #EXTRA_SUBSCRIPTION_ID} and
+     * {@link #EXTRA_ENABLE_SUBSCRIPTION}, and optionally {@link #EXTRA_FROM_SUBSCRIPTION_ID}.
+     *
+     * <p>Requires the caller to be a privileged process with the
+     * {@link android.permission#CALL_PRIVILEGED} permission for the intent to reach the Telephony
+     * stack.
+     *
+     * <p>Unlike {@link #switchToSubscription(int, PendingIntent)}, using this action allows the
+     * underlying eUICC service (i.e. the LPA app) to control the UI experience during this
+     * operation. The action is received by the Telephony framework, which in turn selects and
+     * launches an appropriate LPA activity to present UI to the user. For example, the activity may
+     * show a confirmation dialog, a progress dialog, or an error dialog when necessary.
+     *
+     * <p>The launched activity will immediately finish with
+     * {@link android.app.Activity#RESULT_CANCELED} if {@link #isEnabled} is false.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED =
+            "android.telephony.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
+
+    /**
+     * Intent action sent by system apps (such as the Settings app) to the Telephony framework to
+     * delete a subscription. Must be accompanied with {@link #EXTRA_SUBSCRIPTION_ID}.
+     *
+     * <p>Requires the caller to be a privileged process with the
+     * {@link android.permission#CALL_PRIVILEGED} permission for the intent to reach the Telephony
+     * stack.
+     *
+     * <p>Unlike {@link #deleteSubscription(int, PendingIntent)}, using this action allows the
+     * underlying eUICC service (i.e. the LPA app) to control the UI experience during this
+     * operation. The action is received by the Telephony framework, which in turn selects and
+     * launches an appropriate LPA activity to present UI to the user. For example, the activity may
+     * show a confirmation dialog, a progress dialog, or an error dialog when necessary.
+     *
+     * <p>The launched activity will immediately finish with
+     * {@link android.app.Activity#RESULT_CANCELED} if {@link #isEnabled} is false.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED =
+            "android.telephony.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
+
+    /**
+     * Intent action sent by system apps (such as the Settings app) to the Telephony framework to
+     * rename a subscription. Must be accompanied with {@link #EXTRA_SUBSCRIPTION_ID} and
+     * {@link #EXTRA_SUBSCRIPTION_NICKNAME}.
+     *
+     * <p>Requires the caller to be a privileged process with the
+     * {@link android.permission#CALL_PRIVILEGED} permission for the intent to reach the Telephony
+     * stack.
+     *
+     * <p>Unlike {@link #updateSubscriptionNickname(int, String, PendingIntent)}, using this action
+     * allows the the underlying eUICC service (i.e. the LPA app) to control the UI experience
+     * during this operation. The action is received by the Telephony framework, which in turn
+     * selects and launches an appropriate LPA activity to present UI to the user. For example, the
+     * activity may show a confirmation dialog, a progress dialog, or an error dialog when
+     * necessary.
+     *
+     * <p>The launched activity will immediately finish with
+     * {@link android.app.Activity#RESULT_CANCELED} if {@link #isEnabled} is false.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED =
+            "android.telephony.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
+
+    /**
+     * Intent action sent by a carrier app to launch the eSIM activation flow provided by the LPA UI
+     * (LUI). The carrier app must send this intent with one of the following:
+     *
+     * <p>{@link #EXTRA_USE_QR_SCANNER} not set or set to false: The LPA should try to get an
+     * activation code from the carrier app by binding to the carrier app service implementing
+     * {@code android.service.euicc.EuiccService#ACTION_BIND_CARRIER_PROVISIONING_SERVICE}.
+     * <p>{@link #EXTRA_USE_QR_SCANNER} set to true: The LPA should launch a QR scanner for the user
+     * to scan an eSIM profile QR code.
+     *
+     * <p>Upon completion, the LPA should return one of the following results to the carrier app:
+     *
+     * <p>{@code Activity.RESULT_OK}: The LPA has succeeded in downloading the new eSIM profile.
+     * <p>{@code Activity.RESULT_CANCELED}: The carrier app should treat this as if the user pressed
+     * the back button.
+     * <p>Anything else: The carrier app should treat this as an error.
+     *
+     * <p>LPA needs to check if caller's package name is allowed to perform this action.
+     **/
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_START_EUICC_ACTIVATION =
+            "android.telephony.euicc.action.START_EUICC_ACTIVATION";
+
+    /**
+     * Result code for an operation indicating that the operation succeeded.
+     */
+    public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0;
+
+    /**
+     * Result code for an operation indicating that the user must take some action before the
+     * operation can continue.
+     *
+     * @see #startResolutionActivity
+     */
+    public static final int EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR = 1;
+
+    /**
+     * Result code for an operation indicating that an unresolvable error occurred.
+     *
+     * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} will be populated with a detailed error
+     * code for logging/debugging purposes only.
+     */
+    public static final int EMBEDDED_SUBSCRIPTION_RESULT_ERROR = 2;
+
+    /**
+     * Key for an extra set on the {@link #ACTION_PROVISION_EMBEDDED_SUBSCRIPTION} intent for which
+     * kind of activation flow will be evolved. (see {@code EUICC_ACTIVATION_})
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_ACTIVATION_TYPE =
+            "android.telephony.euicc.extra.ACTIVATION_TYPE";
+
+    /**
+     * Key for an extra set on {@link PendingIntent} result callbacks providing a detailed result
+     * code.
+     *
+     * <p>The value of this key is an integer and contains two portions. The first byte is
+     * OperationCode and the reaming three bytes is the ErrorCode.
+     *
+     * OperationCode is the first byte of the result code and is a categorization which defines what
+     * type of operation took place when an error occurred. e.g {@link #OPERATION_DOWNLOAD} means
+     * the error is related to download.Since the OperationCode only uses at most one byte, the
+     * maximum allowed quantity is 255(0xFF).
+     *
+     * ErrorCode is the remaining three bytes of the result code, and it denotes what happened.
+     * e.g a combination of {@link #OPERATION_DOWNLOAD} and {@link #ERROR_TIME_OUT} will suggest the
+     * download operation has timed out. The only exception here is
+     * {@link #OPERATION_SMDX_SUBJECT_REASON_CODE}, where instead of ErrorCode, SubjectCode[5.2.6.1
+     * from GSMA (SGP.22 v2.2) and ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2) are encoded. @see
+     * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE} and
+     * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE}
+     *
+     * In the case where ErrorCode contains a value of 0, it means it's an unknown error. E.g Intent
+     * only contains {@link #OPERATION_DOWNLOAD} and ErrorCode is 0 implies this is an unknown
+     * Download error.
+     *
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE
+     */
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
+
+    /**
+     * Key for an extra set on {@link PendingIntent} result callbacks providing a
+     * OperationCode of {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE},
+     * value will be an int.
+     */
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_OPERATION_CODE";
+
+    /**
+     * Key for an extra set on {@link PendingIntent} result callbacks providing a
+     * ErrorCode of {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE},
+     * value will be an int.
+     */
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_ERROR_CODE";
+
+    /**
+     * Key for an extra set on {@link PendingIntent} result callbacks providing a
+     * SubjectCode[5.2.6.1] from GSMA (SGP.22 v2.2) decoded from
+     * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}.
+     * The value of this extra will be a String.
+     */
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE";
+
+    /**
+     * Key for an extra set on {@link PendingIntent} result callbacks providing a
+     * ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2) decoded from
+     * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}.
+     * The value of this extra will be a String.
+     */
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE";
+
+    /**
+     * Key for an extra set on {@code #getDownloadableSubscriptionMetadata} PendingIntent result
+     * callbacks providing the downloadable subscription metadata.
+     */
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
+
+    /**
+     * Key for an extra set on {@link #getDefaultDownloadableSubscriptionList} PendingIntent result
+     * callbacks providing the list of available downloadable subscriptions.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
+
+    /**
+     * Key for an extra set on {@link PendingIntent} result callbacks providing the resolution
+     * pending intent for {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR}s.
+     * @hide
+     */
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT";
+
+    /**
+     * Key for an extra set on the {@link #EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT} intent
+     * containing the EuiccService action to launch for resolution.
+     * @hide
+     */
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_ACTION =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_RESOLUTION_ACTION";
+
+    /**
+     * Key for an extra set on the {@link #EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT} intent
+     * providing the callback to execute after resolution is completed.
+     * @hide
+     */
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT";
+
+    /**
+     * Key for an extra set on the {@link #ACTION_PROVISION_EMBEDDED_SUBSCRIPTION} intent for
+     * whether eSIM provisioning flow is forced to be started or not. If this extra hasn't been
+     * set, eSIM provisioning flow may be skipped and the corresponding carrier's app will be
+     * notified. Otherwise, eSIM provisioning flow will be started when
+     * {@link #ACTION_PROVISION_EMBEDDED_SUBSCRIPTION} has been received.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_FORCE_PROVISION =
+            "android.telephony.euicc.extra.FORCE_PROVISION";
+
+    /**
+     * Key for an extra set on privileged actions {@link #ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED},
+     * {@link #ACTION_DELETE_SUBSCRIPTION_PRIVILEGED}, and
+     * {@link #ACTION_RENAME_SUBSCRIPTION_PRIVILEGED} providing the ID of the targeted subscription.
+     *
+     * <p>Expected type of the extra data: int
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_SUBSCRIPTION_ID =
+            "android.telephony.euicc.extra.SUBSCRIPTION_ID";
+
+    /**
+     * Key for an extra set on {@link #ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED} providing a boolean
+     * value of whether to enable or disable the targeted subscription.
+     *
+     * <p>Expected type of the extra data: boolean
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_ENABLE_SUBSCRIPTION =
+            "android.telephony.euicc.extra.ENABLE_SUBSCRIPTION";
+
+    /**
+     * Key for an extra set on {@link #ACTION_RENAME_SUBSCRIPTION_PRIVILEGED} providing a new
+     * nickname for the targeted subscription.
+     *
+     * <p>Expected type of the extra data: String
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_SUBSCRIPTION_NICKNAME =
+            "android.telephony.euicc.extra.SUBSCRIPTION_NICKNAME";
+
+    /**
+     * Key for an extra set on {@link #ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED} providing the ID of
+     * the subscription we're toggling from. This extra is optional and is only used for UI
+     * purposes by the underlying eUICC service (i.e. the LPA app), such as displaying a dialog
+     * titled "Switch X with Y". If set, the provided subscription will be used as the "from"
+     * subscription in UI (the "X" in the dialog example). Otherwise, the currently active
+     * subscription that will be disabled is the "from" subscription.
+     *
+     * <p>Expected type of the extra data: int
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_FROM_SUBSCRIPTION_ID =
+            "android.telephony.euicc.extra.FROM_SUBSCRIPTION_ID";
+
+    /**
+     * Key for an extra set on privileged actions {@link #ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED}
+     * providing the physical slot ID of the target slot.
+     *
+     * <p>Expected type of the extra data: int
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_PHYSICAL_SLOT_ID =
+            "android.telephony.euicc.extra.PHYSICAL_SLOT_ID";
+
+
+    /**
+     * Key for an extra set on actions {@link #ACTION_START_EUICC_ACTIVATION} providing a boolean
+     * value of whether to start eSIM activation with QR scanner.
+     *
+     * <p>Expected type of the extra data: boolean
+     **/
+    public static final String EXTRA_USE_QR_SCANNER =
+            "android.telephony.euicc.extra.USE_QR_SCANNER";
+
+    /**
+     * Optional meta-data attribute for a carrier app providing an icon to use to represent the
+     * carrier. If not provided, the app's launcher icon will be used as a fallback.
+     */
+    public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
+
+    /**
+     * Euicc activation type which will be included in {@link #EXTRA_ACTIVATION_TYPE} and used to
+     * decide which kind of activation flow should be lauched.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"EUICC_ACTIVATION_"}, value = {
+            EUICC_ACTIVATION_TYPE_DEFAULT,
+            EUICC_ACTIVATION_TYPE_BACKUP,
+            EUICC_ACTIVATION_TYPE_TRANSFER,
+            EUICC_ACTIVATION_TYPE_ACCOUNT_REQUIRED,
+            EUICC_ACTIVATION_TYPE_TRANSFER_FINAL_HOLD,
+    })
+    public @interface EuiccActivationType{}
+
+
+    /**
+     * The default euicc activation type which includes checking server side and downloading the
+     * profile based on carrier's download configuration.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EUICC_ACTIVATION_TYPE_DEFAULT = 1;
+
+    /**
+     * The euicc activation type used when the default download process failed. LPA will start the
+     * backup flow and try to download the profile for the carrier.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EUICC_ACTIVATION_TYPE_BACKUP = 2;
+
+    /**
+     * The activation flow of eSIM seamless transfer will be used. LPA will start normal eSIM
+     * activation flow and if it's failed, the name of the carrier selected will be recorded. After
+     * the future device pairing, LPA will contact this carrier to transfer it from the other device
+     * to this device.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3;
+
+    /**
+     * The activation flow of eSIM requiring user account will be started. This can only be used
+     * when there is user account signed in. Otherwise, the flow will be failed.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EUICC_ACTIVATION_TYPE_ACCOUNT_REQUIRED = 4;
+
+    /**
+     * The activation flow of eSIM transfer to block the transfer process before B&R flow.
+     * This is needed to avoid connection overlapping between eSIM connection B&R connection.
+     *
+     * @hide
+     */
+     // TODO(b/329212614): add system api annotation during the allowed api timeline.
+     public static final int EUICC_ACTIVATION_TYPE_TRANSFER_FINAL_HOLD = 5;
+
+    /**
+     * Euicc OTA update status which can be got by {@link #getOtaStatus}
+     * @removed mistakenly exposed as system-api previously
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"EUICC_OTA_"}, value = {
+            EUICC_OTA_IN_PROGRESS,
+            EUICC_OTA_FAILED,
+            EUICC_OTA_SUCCEEDED,
+            EUICC_OTA_NOT_NEEDED,
+            EUICC_OTA_STATUS_UNAVAILABLE
+
+    })
+    public @interface OtaStatus{}
+
+    /**
+     * An OTA is in progress. During this time, the eUICC is not available and the user may lose
+     * network access.
+     * @hide
+     */
+    @SystemApi
+    public static final int EUICC_OTA_IN_PROGRESS = 1;
+
+    /**
+     * The OTA update failed.
+     * @hide
+     */
+    @SystemApi
+    public static final int EUICC_OTA_FAILED = 2;
+
+    /**
+     * The OTA update finished successfully.
+     * @hide
+     */
+    @SystemApi
+    public static final int EUICC_OTA_SUCCEEDED = 3;
+
+    /**
+     * The OTA update not needed since current eUICC OS is latest.
+     * @hide
+     */
+    @SystemApi
+    public static final int EUICC_OTA_NOT_NEEDED = 4;
+
+    /**
+     * The OTA status is unavailable since eUICC service is unavailable.
+     * @hide
+     */
+    @SystemApi
+    public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5;
+
+    /**
+     * List of OperationCode corresponding to {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}'s
+     * value, an integer. @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"OPERATION_"}, value = {
+            OPERATION_SYSTEM,
+            OPERATION_SIM_SLOT,
+            OPERATION_EUICC_CARD,
+            OPERATION_SWITCH,
+            OPERATION_DOWNLOAD,
+            OPERATION_METADATA,
+            OPERATION_EUICC_GSMA,
+            OPERATION_APDU,
+            OPERATION_SMDX,
+            OPERATION_HTTP,
+            OPERATION_SMDX_SUBJECT_REASON_CODE,
+    })
+    public @interface OperationCode {
+    }
+
+    /**
+     * Internal system error.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int OPERATION_SYSTEM = 1;
+
+    /**
+     * SIM slot error. Failed to switch slot, failed to access the physical slot etc.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int OPERATION_SIM_SLOT = 2;
+
+    /**
+     * eUICC card error.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int OPERATION_EUICC_CARD = 3;
+
+    /**
+     * Generic switching profile error
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int OPERATION_SWITCH = 4;
+
+    /**
+     * Download profile error.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int OPERATION_DOWNLOAD = 5;
+
+    /**
+     * Subscription's metadata error
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int OPERATION_METADATA = 6;
+
+    /**
+     * eUICC returned an error defined in GSMA (SGP.22 v2.2) while running one of the ES10x
+     * functions.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int OPERATION_EUICC_GSMA = 7;
+
+    /**
+     * The exception of failing to execute an APDU command. It can be caused by an error
+     * happening on opening the basic or logical channel, or the response of the APDU command is
+     * not success (0x9000).
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int OPERATION_APDU = 8;
+
+    /**
+     * SMDX(SMDP/SMDS) error
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int OPERATION_SMDX = 9;
+
+    /**
+     * SubjectCode[5.2.6.1] and ReasonCode[5.2.6.2] error from GSMA (SGP.22 v2.2)
+     * When {@link #OPERATION_SMDX_SUBJECT_REASON_CODE} is used as the
+     * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}, the remaining three bytes of the integer
+     * result from {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} will be used to stored the
+     * SubjectCode and ReasonCode from the GSMA spec and NOT ErrorCode.
+     *
+     * The encoding will follow the format of:
+     * 1. The first byte of the result will be 255(0xFF).
+     * 2. Remaining three bytes(24 bits) will be split into six sections, 4 bits in each section.
+     * 3. A SubjectCode/ReasonCode will take 12 bits each.
+     * 4. The maximum number can be represented per section is 15, as that is the maximum number
+     * allowed to be stored into 4 bits
+     * 5. Maximum supported nested category from GSMA is three layers. E.g 8.11.1.2 is not
+     * supported.
+     *
+     * E.g given SubjectCode(8.11.1) and ReasonCode(5.1)
+     *
+     * Base10:  0       10      8       11      1       0       5       1
+     * Base2:   0000    1010    1000    1011    0001    0000    0101    0001
+     * Base16:  0       A       8       B       1       0       5       1
+     *
+     * Thus the integer stored in {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} is
+     * 0xA8B1051(176885841)
+     *
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int OPERATION_SMDX_SUBJECT_REASON_CODE = 10;
+
+    /**
+     * HTTP error
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int OPERATION_HTTP = 11;
+
+    /**
+     * List of ErrorCode corresponding to {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"ERROR_"}, value = {
+            ERROR_CARRIER_LOCKED,
+            ERROR_INVALID_ACTIVATION_CODE,
+            ERROR_INVALID_CONFIRMATION_CODE,
+            ERROR_INCOMPATIBLE_CARRIER,
+            ERROR_EUICC_INSUFFICIENT_MEMORY,
+            ERROR_TIME_OUT,
+            ERROR_EUICC_MISSING,
+            ERROR_UNSUPPORTED_VERSION,
+            ERROR_SIM_MISSING,
+            ERROR_INSTALL_PROFILE,
+            ERROR_DISALLOWED_BY_PPR,
+            ERROR_ADDRESS_MISSING,
+            ERROR_CERTIFICATE_ERROR,
+            ERROR_NO_PROFILES_AVAILABLE,
+            ERROR_CONNECTION_ERROR,
+            ERROR_INVALID_RESPONSE,
+            ERROR_OPERATION_BUSY,
+    })
+    public @interface ErrorCode{}
+
+    /**
+     * Operation such as downloading/switching to another profile failed due to device being
+     * carrier locked.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_CARRIER_LOCKED = 10000;
+
+    /**
+     * The activation code(SGP.22 v2.2 section[4.1]) is invalid.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_INVALID_ACTIVATION_CODE = 10001;
+
+    /**
+     * The confirmation code(SGP.22 v2.2 section[4.7]) is invalid.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_INVALID_CONFIRMATION_CODE = 10002;
+
+    /**
+     * The profile's carrier is incompatible with the LPA.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_INCOMPATIBLE_CARRIER = 10003;
+
+    /**
+     * There is no more space available on the eUICC for new profiles.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_EUICC_INSUFFICIENT_MEMORY = 10004;
+
+    /**
+     * Timed out while waiting for an operation to complete. i.e restart, disable,
+     * switch reset etc.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_TIME_OUT = 10005;
+
+    /**
+     * eUICC is missing or defective on the device.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_EUICC_MISSING = 10006;
+
+    /**
+     * The eUICC card(hardware) version is incompatible with the software
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_UNSUPPORTED_VERSION = 10007;
+
+    /**
+     * No SIM card is available in the device.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_SIM_MISSING = 10008;
+
+    /**
+     * Failure to load the profile onto the eUICC card. e.g
+     * 1. iccid of the profile already exists on the eUICC.
+     * 2. GSMA(.22 v2.2) Profile Install Result - installFailedDueToDataMismatch
+     * 3. operation was interrupted
+     * 4. SIMalliance error in PEStatus(SGP.22 v2.2 section 2.5.6.1)
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_INSTALL_PROFILE = 10009;
+
+    /**
+     * Failed to load profile onto eUICC due to Profile Policy Rules.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_DISALLOWED_BY_PPR = 10010;
+
+
+    /**
+     * Address is missing e.g SMDS/SMDP address is missing.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_ADDRESS_MISSING = 10011;
+
+    /**
+     * Certificate needed for authentication is not valid or missing. E.g  SMDP/SMDS authentication
+     * failed.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_CERTIFICATE_ERROR = 10012;
+
+
+    /**
+     * No profiles available.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_NO_PROFILES_AVAILABLE = 10013;
+
+    /**
+     * Failure to create a connection.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_CONNECTION_ERROR = 10014;
+
+    /**
+     * Response format is invalid. e.g SMDP/SMDS response contains invalid json, header or/and ASN1.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_INVALID_RESPONSE = 10015;
+
+    /**
+     * The operation is currently busy, try again later.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
+     */
+    public static final int ERROR_OPERATION_BUSY = 10016;
+
+    /**
+     * Failure due to target port is not supported.
+     * @see #switchToSubscription(int, int, PendingIntent)
+     */
+    public static final int ERROR_INVALID_PORT = 10017;
+
+    /** Temporary failure to retrieve available memory because eUICC is not ready. */
+    @FlaggedApi(Flags.FLAG_ESIM_AVAILABLE_MEMORY)
+    public static final long EUICC_MEMORY_FIELD_UNAVAILABLE = -1L;
+
+    /**
+     * Apps targeting on Android T and beyond will get exception whenever switchToSubscription
+     * without portIndex is called for disable subscription.
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public static final long SWITCH_WITHOUT_PORT_INDEX_EXCEPTION_ON_DISABLE = 218393363L;
+
+    /**
+     * With support for MEP(multiple enabled profile) in Android T, a SIM card can enable multiple
+     * profile on different port. If apps are not target SDK T yet and keep calling the
+     * switchToSubscription or download API without specifying the port index, we should
+     * keep the existing behaviour by always use port index 0 even the device itself has MEP eUICC,
+     * this is for carrier app's backward compatibility.
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public static final long SHOULD_RESOLVE_PORT_INDEX_FOR_APPS = 224562872L;
+
+    /**
+     * Starting with Android U, a port is available if it is active without an enabled profile
+     * on it or calling app can activate a new profile on the selected port without any user
+     * interaction.
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final long INACTIVE_PORT_AVAILABILITY_CHECK = 240273417L;
+
+    private final Context mContext;
+    private int mCardId;
+
+    /** @hide */
+    public EuiccManager(Context context) {
+        mContext = context;
+        mCardId = getCardIdForDefaultEuicc();
+    }
+
+    /** @hide */
+    private EuiccManager(Context context, int cardId) {
+        mContext = context;
+        mCardId = cardId;
+    }
+
+    /**
+     * Create a new EuiccManager object pinned to the given card ID.
+     *
+     * @return an EuiccManager that uses the given card ID for all calls.
+     */
+    @NonNull
+    public EuiccManager createForCardId(int cardId) {
+        return new EuiccManager(mContext, cardId);
+    }
+
+    /**
+     * Whether embedded subscriptions are currently enabled.
+     *
+     * <p>Even on devices with the {@link PackageManager#FEATURE_TELEPHONY_EUICC} feature, embedded
+     * subscriptions may be turned off, e.g. because of a carrier restriction from an inserted
+     * physical SIM. Therefore, this runtime check should be used before accessing embedded
+     * subscription APIs.
+     *
+     * @return true if embedded subscriptions are currently enabled.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public boolean isEnabled() {
+        // In the future, this may reach out to IEuiccController (if non-null) to check any dynamic
+        // restrictions.
+        return getIEuiccController() != null && refreshCardIdIfUninitialized();
+    }
+
+    /**
+     * Returns the EID identifying the eUICC hardware.
+     *
+     * <p>Requires that the calling app has carrier privileges on the active subscription on the
+     * current eUICC. A calling app with carrier privileges for one eUICC may not necessarily have
+     * access to the EID of another eUICC.
+     *
+     * @return the EID. May be null if the eUICC is not ready.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    @Nullable
+    public String getEid() {
+        if (!isEnabled()) {
+            return null;
+        }
+        try {
+            return getIEuiccController().getEid(mCardId, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the available memory in bytes of the eUICC.
+     *
+     * @return the available memory in bytes. May be {@link #EUICC_MEMORY_FIELD_UNAVAILABLE} if the
+     *     eUICC is not ready. Check {@link #isEnabled} for more information.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC} or
+     *          device doesn't support querying this information from the eUICC.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @FlaggedApi(Flags.FLAG_ESIM_AVAILABLE_MEMORY)
+    @RequiresPermission(
+            anyOf = {
+                Manifest.permission.READ_PHONE_STATE,
+                Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+                "carrier privileges"
+            })
+    public long getAvailableMemoryInBytes() {
+        if (!isEnabled()) {
+            return EUICC_MEMORY_FIELD_UNAVAILABLE;
+        }
+        try {
+            return getIEuiccController()
+                    .getAvailableMemoryInBytes(mCardId, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current status of eUICC OTA.
+     *
+     * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @return the status of eUICC OTA. If the eUICC is not ready,
+     *         {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public int getOtaStatus() {
+        if (!isEnabled()) {
+            return EUICC_OTA_STATUS_UNAVAILABLE;
+        }
+        try {
+            return getIEuiccController().getOtaStatus(mCardId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Attempt to download the given {@link DownloadableSubscription}.
+     *
+     * <p>Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS}
+     * or the calling app must be authorized to manage both the currently-active
+     * subscription on the
+     * current eUICC and the subscription to be downloaded according to the subscription metadata.
+     * Without the former, an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be
+     * returned in the callback intent to prompt the user to accept the download.
+     *
+     * <p> Starting from Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM},
+     * if the caller has the
+     * {@code android.Manifest.permission#MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS} permission or
+     * is a profile owner or device owner, then the downloaded subscription
+     * will be managed by that caller.
+     * In case the caller is device owner or profile owner of an organization-owned device, {@code
+     * switchAfterDownload} can be set to true to automatically enable the subscription after
+     * download. If the caller is a profile owner on non organization owned device
+     * {@code switchAfterDownload} should be false otherwise the operation will fail with
+     * {@link #EMBEDDED_SUBSCRIPTION_RESULT_ERROR}.
+     *
+     * <p>On a multi-active SIM device, requires the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or a calling app
+     * only if the targeted eUICC does not currently have an active subscription or the calling app
+     * is authorized to manage the active subscription on the target eUICC, and the calling app is
+     * authorized to manage any active subscription on any SIM. Without it, an
+     * {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
+     * intent to prompt the user to accept the download. The caller should also be authorized to
+     * manage the subscription to be downloaded.
+     *
+     * <p>If device support {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP} and
+     * switchAfterDownload is {@code true}, the subscription will be enabled on an esim port based
+     * on the following selection rules:
+     * <ul>
+     *    <li>In SS(Single SIM) mode, if the embedded slot already has an active port, then download
+     *    and enable the subscription on this port.
+     *    <li>In SS mode, if the embedded slot is not active, then try to download and enable the
+     *    subscription on the default port 0 of eUICC.
+     *    <li>In DSDS mode, find first available port to download and enable the subscription.
+     *    (see {@link #isSimPortAvailable(int)})
+     *</ul>
+     * If there is no available port, an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR}
+     * will be returned in the callback intent to prompt the user to disable an already-active
+     * subscription.
+     *
+     * @param subscription the subscription to download.
+     * @param switchAfterDownload if true, the profile will be activated upon successful download.
+     * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS,
+            Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS})
+    public void downloadSubscription(DownloadableSubscription subscription,
+            boolean switchAfterDownload, PendingIntent callbackIntent) {
+        if (!isEnabled()) {
+            sendUnavailableError(callbackIntent);
+            return;
+        }
+        try {
+            getIEuiccController().downloadSubscription(mCardId, subscription, switchAfterDownload,
+                    mContext.getOpPackageName(), null /* resolvedBundle */, callbackIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Start an activity to resolve a user-resolvable error.
+     *
+     * <p>If an operation returns {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR}, this
+     * method may be called to prompt the user to resolve the issue.
+     *
+     * <p>This method may only be called once for a particular error.
+     *
+     * @param activity the calling activity (which should be in the foreground).
+     * @param requestCode an application-specific request code which will be provided to
+     *     {@link Activity#onActivityResult} upon completion. Note that the operation may still be
+     *     in progress when the resolution activity completes; it is not fully finished until the
+     *     callback intent is triggered.
+     * @param resultIntent the Intent provided to the initial callback intent which failed with
+     *     {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR}.
+     * @param callbackIntent a PendingIntent to launch when the operation completes. This is
+     *     trigered upon completion of the original operation that required user resolution.
+     * @throws android.content.IntentSender.SendIntentException if called more than once.
+     */
+    public void startResolutionActivity(Activity activity, int requestCode, Intent resultIntent,
+            PendingIntent callbackIntent) throws IntentSender.SendIntentException {
+        PendingIntent resolutionIntent =
+                resultIntent.getParcelableExtra(EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT, android.app.PendingIntent.class);
+        if (resolutionIntent == null) {
+            throw new IllegalArgumentException("Invalid result intent");
+        }
+        Intent fillInIntent = new Intent();
+        fillInIntent.putExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT,
+                callbackIntent);
+        activity.startIntentSenderForResult(resolutionIntent.getIntentSender(), requestCode,
+                fillInIntent, 0 /* flagsMask */, 0 /* flagsValues */, 0 /* extraFlags */);
+    }
+
+    /**
+     * Continue an operation after the user resolves an error.
+     *
+     * <p>To be called by the LUI upon completion of a resolvable error flow.
+     *
+     * <p>Requires that the calling app has the
+     * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @param resolutionIntent The original intent used to start the LUI.
+     * @param resolutionExtras Resolution-specific extras depending on the result of the resolution.
+     *     For example, this may indicate whether the user has consented or may include the input
+     *     they provided.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
+        if (!isEnabled()) {
+            PendingIntent callbackIntent =
+                    resolutionIntent.getParcelableExtra(
+                            EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT, android.app.PendingIntent.class);
+            if (callbackIntent != null) {
+                sendUnavailableError(callbackIntent);
+            }
+            return;
+        }
+        try {
+            getIEuiccController().continueOperation(mCardId, resolutionIntent, resolutionExtras);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Fills in the metadata for a DownloadableSubscription.
+     *
+     * <p>May be used in cases that a DownloadableSubscription was constructed to download a
+     * profile, but the metadata for the profile is unknown (e.g. we only know the activation code).
+     * The callback will be triggered with an Intent with
+     * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION} set to the
+     * downloadable subscription metadata upon success.
+     *
+     * <p>Requires that the calling app has the
+     * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for
+     * internal system use only.
+     *
+     * @param subscription the subscription which needs metadata filled in
+     * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public void getDownloadableSubscriptionMetadata(
+            DownloadableSubscription subscription, PendingIntent callbackIntent) {
+        if (!isEnabled()) {
+            sendUnavailableError(callbackIntent);
+            return;
+        }
+        try {
+            getIEuiccController().getDownloadableSubscriptionMetadata(mCardId, subscription,
+                    mContext.getOpPackageName(), callbackIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets metadata for subscription which are available for download on this device.
+     *
+     * <p>Subscriptions returned here may be passed to {@link #downloadSubscription}. They may have
+     * been pre-assigned to this particular device, for example. The callback will be triggered with
+     * an Intent with {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS} set to the
+     * list of available subscriptions upon success.
+     *
+     * <p>Requires that the calling app has the
+     * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for
+     * internal system use only.
+     *
+     * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
+        if (!isEnabled()) {
+            sendUnavailableError(callbackIntent);
+            return;
+        }
+        try {
+            getIEuiccController().getDefaultDownloadableSubscriptionList(mCardId,
+                    mContext.getOpPackageName(), callbackIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns information about the eUICC chip/device.
+     *
+     * @return the {@link EuiccInfo}. May be null if the eUICC is not ready.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    @Nullable
+    public EuiccInfo getEuiccInfo() {
+        if (!isEnabled()) {
+            return null;
+        }
+        try {
+            return getIEuiccController().getEuiccInfo(mCardId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Deletes the given subscription.
+     *
+     * <p>If this subscription is currently active, the device will first switch away from it onto
+     * an "empty" subscription.
+     *
+     * <p>Requires that the calling app has carrier privileges according to the metadata of the
+     * profile to be deleted, or the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     * Starting from Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, if the
+     * caller is a device owner, profile owner, or holds the
+     * {@code android.Manifest.permission#MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS} permission,
+     * then the caller can delete a subscription that was downloaded by that caller.
+     * If such a caller tries to delete any other subscription then the
+     * operation will fail with {@link #EMBEDDED_SUBSCRIPTION_RESULT_ERROR}.
+     *
+     * @param subscriptionId the ID of the subscription to delete.
+     * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS,
+            Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS})
+    public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
+        if (!isEnabled()) {
+            sendUnavailableError(callbackIntent);
+            return;
+        }
+        try {
+            getIEuiccController().deleteSubscription(mCardId,
+                    subscriptionId, mContext.getOpPackageName(), callbackIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Switch to (enable) the given subscription.
+     *
+     * <p>Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
+     * or the calling app must be authorized to manage both the currently-active subscription and
+     * the subscription to be enabled according to the subscription metadata. Without the former,
+     * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
+     * intent to prompt the user to accept the download.
+     *
+     * <p>On a multi-active SIM device, requires the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or a calling app
+     *  only if the targeted eUICC does not currently have an active subscription or the calling app
+     * is authorized to manage the active subscription on the target eUICC, and the calling app is
+     * authorized to manage any active subscription on any SIM. Without it, an
+     * {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
+     * intent to prompt the user to accept the download. The caller should also be authorized to
+     * manage the subscription to be enabled.
+     *
+     * <p> From Android T, devices might support {@link PackageManager#FEATURE_TELEPHONY_EUICC_MEP},
+     * the subscription can be installed on different port from the eUICC. Calling apps with
+     * carrier privilege (see {@link TelephonyManager#hasCarrierPrivileges}) over the currently
+     * active subscriptions can use {@link #switchToSubscription(int, int, PendingIntent)} to
+     * specify which port to enable the subscription. Otherwise, use this API to enable the
+     * subscription on the eUICC and the platform will internally resolve a port based on following
+     * rules:
+     * <ul>
+     *    <li>always use the default port 0 is eUICC does not support MEP or the apps are
+     *    not targeting on Android T.
+     *    <li>In SS(Single SIM) mode, if the embedded slot already has an active port, then enable
+     *    the subscription on this port.
+     *    <li>In SS mode, if the embedded slot is not active, then try to enable the subscription on
+     *    the default port 0 of eUICC.
+     *    <li>In DSDS mode, find first available port to enable the subscription.
+     *    (see {@link #isSimPortAvailable(int)})
+     *</ul>
+     * If there is no available port, an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR}
+     * will be returned in the callback intent to prompt the user to disable an already-active
+     * subscription.
+     *
+     * @param subscriptionId the ID of the subscription to enable. May be
+     *     {@link android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID} to deactivate the
+     *     current profile without activating another profile to replace it. Calling apps targeting
+     *     on android T must use {@link #switchToSubscription(int, int, PendingIntent)} API for
+     *     disable profile, port index can be found from {@link SubscriptionInfo#getPortIndex()}.
+     *     If it's a disable operation, requires the
+     *     {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or the
+     *     calling app must be authorized to manage the active subscription on the target eUICC.
+     * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
+        if (!isEnabled()) {
+            sendUnavailableError(callbackIntent);
+            return;
+        }
+        try {
+            if (subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
+                     && getIEuiccController().isCompatChangeEnabled(mContext.getOpPackageName(),
+                     SWITCH_WITHOUT_PORT_INDEX_EXCEPTION_ON_DISABLE)) {
+                // Apps targeting on Android T and beyond will get exception whenever
+                // switchToSubscription without portIndex is called with INVALID_SUBSCRIPTION_ID.
+                Log.e(TAG, "switchToSubscription without portIndex is not allowed for"
+                        + " disable operation");
+                throw new IllegalArgumentException("Must use switchToSubscription with portIndex"
+                        + " API for disable operation");
+            }
+            getIEuiccController().switchToSubscription(mCardId,
+                    subscriptionId, mContext.getOpPackageName(), callbackIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Switch to (enable) the given subscription.
+     *
+     * <p> Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
+     * or the caller must be having both the carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}) over any currently active subscriptions
+     * and the subscription to be enabled according to the subscription metadata.
+     * Without the former permissions, an SecurityException is thrown.
+     *
+     * <p> If the caller is passing invalid port index,
+     * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_ERROR} with detailed error code
+     * {@link #ERROR_INVALID_PORT} will be returned. The port index is invalid if one of the
+     * following requirements is met:
+     * <ul>
+     *     <li>index is beyond the range of {@link UiccCardInfo#getPorts()}.
+     *     <li>In SS(Single SIM) mode, the embedded slot already has an active port with different
+     *     port index.
+     *     <li>In DSDS mode, if the psim slot is active and the embedded slot already has an active
+     *     empty port with different port index.
+     * </ul>
+     *
+     * <p> Depending on the target port and permission check,
+     * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} might be returned to the callback
+     * intent to prompt the user to authorize before the switch.
+     *
+     * @param subscriptionId the ID of the subscription to enable. May be
+     *     {@link android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID} to deactivate the
+     *     current profile without activating another profile to replace it. If it's a disable
+     *     operation, requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS}
+     *     permission, or the calling app must be authorized to manage the active subscription on
+     *     the target eUICC. From Android T, multiple enabled profiles is supported. Calling apps
+     *     targeting on android T must use {@link #switchToSubscription(int, int, PendingIntent)}
+     *     API for disable profile, port index can be found from
+     *     {@link SubscriptionInfo#getPortIndex()}.
+     * @param portIndex the index of the port to target for the enabled subscription
+     * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public void switchToSubscription(int subscriptionId, int portIndex,
+            @NonNull PendingIntent callbackIntent) {
+        if (!isEnabled()) {
+            sendUnavailableError(callbackIntent);
+            return;
+        }
+        try {
+            boolean canWriteEmbeddedSubscriptions = mContext.checkCallingOrSelfPermission(
+                    Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+                    == PackageManager.PERMISSION_GRANTED;
+            // If the caller is not privileged caller and does not have the carrier privilege over
+            // any active subscription, do not continue.
+            if (!canWriteEmbeddedSubscriptions && !getIEuiccController()
+                    .hasCarrierPrivilegesForPackageOnAnyPhone(mContext.getOpPackageName())) {
+                Log.e(TAG, "Not permitted to use switchToSubscription with portIndex");
+                throw new SecurityException(
+                        "Must have carrier privileges to use switchToSubscription with portIndex");
+            }
+            getIEuiccController().switchToSubscriptionWithPort(mCardId,
+                    subscriptionId, portIndex, mContext.getOpPackageName(), callbackIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Update the nickname for the given subscription.
+     *
+     * <p>Requires that the calling app has carrier privileges according to the metadata of the
+     * profile to be updated, or the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @param subscriptionId the ID of the subscription to update.
+     * @param nickname the new nickname to apply.
+     * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public void updateSubscriptionNickname(
+            int subscriptionId, @Nullable String nickname, @NonNull PendingIntent callbackIntent) {
+        if (!isEnabled()) {
+            sendUnavailableError(callbackIntent);
+            return;
+        }
+        try {
+            getIEuiccController().updateSubscriptionNickname(mCardId,
+                    subscriptionId, nickname, mContext.getOpPackageName(), callbackIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Erase all operational subscriptions and reset the eUICC.
+     *
+     * <p>Requires that the calling app has the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @deprecated From R, callers should specify a flag for specific set of subscriptions to erase
+     * and use {@link #eraseSubscriptions(int, PendingIntent)} instead
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    @Deprecated
+    public void eraseSubscriptions(@NonNull PendingIntent callbackIntent) {
+        if (!isEnabled()) {
+            sendUnavailableError(callbackIntent);
+            return;
+        }
+        try {
+            getIEuiccController().eraseSubscriptions(mCardId, callbackIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Erase all specific subscriptions and reset the eUICC.
+     *
+     * <p>Requires that the calling app has the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @param options flag indicating specific set of subscriptions to erase
+     * @param callbackIntent a PendingIntent to launch when the operation completes.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public void eraseSubscriptions(
+            @ResetOption int options, @NonNull PendingIntent callbackIntent) {
+        if (!isEnabled()) {
+            sendUnavailableError(callbackIntent);
+            return;
+        }
+        try {
+            getIEuiccController().eraseSubscriptionsWithOptions(mCardId, options, callbackIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Ensure that subscriptions will be retained on the next factory reset.
+     *
+     * <p>By default, all subscriptions on the eUICC are erased the first time a device boots (ever
+     * and after factory resets). This ensures that the data is wiped after a factory reset is
+     * performed via fastboot or recovery mode, as these modes do not support the necessary radio
+     * communication needed to wipe the eSIM.
+     *
+     * <p>However, this method may be called right before a factory reset issued via settings when
+     * the user elects to retain subscriptions. Doing so will mark them for retention so that they
+     * are not cleared after the ensuing reset.
+     *
+     * <p>Requires that the calling app has the {@link android.Manifest.permission#MASTER_CLEAR}
+     * permission. This is for internal system use only.
+     *
+     * @param callbackIntent a PendingIntent to launch when the operation completes.
+     * @hide
+     */
+    public void retainSubscriptionsForFactoryReset(PendingIntent callbackIntent) {
+        if (!isEnabled()) {
+            sendUnavailableError(callbackIntent);
+            return;
+        }
+        try {
+            getIEuiccController().retainSubscriptionsForFactoryReset(mCardId, callbackIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the supported countries for eUICC.
+     *
+     * <p>Requires that the calling app has the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * <p>The supported country list will be replaced by {@code supportedCountries}. For how we
+     * determine whether a country is supported please check {@link #isSupportedCountry}.
+     *
+     * @param supportedCountries is a list of strings contains country ISO codes in uppercase.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public void setSupportedCountries(@NonNull List<String> supportedCountries) {
+        if (!isEnabled()) {
+            return;
+        }
+        try {
+            getIEuiccController().setSupportedCountries(
+                    true /* isSupported */,
+                    supportedCountries.stream()
+                        .map(String::toUpperCase).collect(Collectors.toList()));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the unsupported countries for eUICC.
+     *
+     * <p>Requires that the calling app has the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * <p>The unsupported country list will be replaced by {@code unsupportedCountries}. For how we
+     * determine whether a country is supported please check {@link #isSupportedCountry}.
+     *
+     * @param unsupportedCountries is a list of strings contains country ISO codes in uppercase.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public void setUnsupportedCountries(@NonNull List<String> unsupportedCountries) {
+        if (!isEnabled()) {
+            return;
+        }
+        try {
+            getIEuiccController().setSupportedCountries(
+                    false /* isSupported */,
+                    unsupportedCountries.stream()
+                        .map(String::toUpperCase).collect(Collectors.toList()));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the supported countries for eUICC.
+     *
+     * <p>Requires that the calling app has the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @return list of strings contains country ISO codes in uppercase.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    @NonNull
+    public List<String> getSupportedCountries() {
+        if (!isEnabled()) {
+            return Collections.emptyList();
+        }
+        try {
+            return getIEuiccController().getSupportedCountries(true /* isSupported */);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the unsupported countries for eUICC.
+     *
+     * <p>Requires that the calling app has the
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
+     * @return list of strings contains country ISO codes in uppercase.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    @NonNull
+    public List<String> getUnsupportedCountries() {
+        if (!isEnabled()) {
+            return Collections.emptyList();
+        }
+        try {
+            return getIEuiccController().getSupportedCountries(false /* isSupported */);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the given country supports eUICC.
+     *
+     * <p>Supported country list has a higher prority than unsupported country list. If the
+     * supported country list is not empty, {@code countryIso} will be considered as supported when
+     * it exists in the supported country list. Otherwise {@code countryIso} is not supported. If
+     * the supported country list is empty, {@code countryIso} will be considered as supported if it
+     * does not exist in the unsupported country list. Otherwise {@code countryIso} is not
+     * supported. If both supported and unsupported country lists are empty, then all countries are
+     * consider be supported. For how to set supported and unsupported country list, please check
+     * {@link #setSupportedCountries} and {@link #setUnsupportedCountries}.
+     *
+     * @param countryIso should be the ISO-3166 country code is provided in uppercase 2 character
+     * format.
+     * @return whether the given country supports eUICC or not.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public boolean isSupportedCountry(@NonNull String countryIso) {
+        if (!isEnabled()) {
+            return false;
+        }
+        try {
+            return getIEuiccController().isSupportedCountry(countryIso.toUpperCase(Locale.ROOT));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Refreshes the cardId if its uninitialized, and returns whether we should continue the
+     * operation.
+     * <p>
+     * Note that after a successful refresh, the mCardId may be TelephonyManager.UNSUPPORTED_CARD_ID
+     * on older HALs. For backwards compatability, we continue to the LPA and let it decide which
+     * card to use.
+     */
+    private boolean refreshCardIdIfUninitialized() {
+        // Refresh mCardId if its UNINITIALIZED_CARD_ID
+        if (mCardId == TelephonyManager.UNINITIALIZED_CARD_ID) {
+            mCardId = getCardIdForDefaultEuicc();
+        }
+        return mCardId != TelephonyManager.UNINITIALIZED_CARD_ID;
+    }
+
+    private static void sendUnavailableError(PendingIntent callbackIntent) {
+        try {
+            callbackIntent.send(EMBEDDED_SUBSCRIPTION_RESULT_ERROR);
+        } catch (PendingIntent.CanceledException e) {
+            // Caller canceled the callback; do nothing.
+        }
+    }
+
+    private static IEuiccController getIEuiccController() {
+        return IEuiccController.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getEuiccControllerService()
+                        .get());
+    }
+
+    private int getCardIdForDefaultEuicc() {
+        int cardId = TelephonyManager.UNINITIALIZED_CARD_ID;
+
+        if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
+            PackageManager pm = mContext.getPackageManager();
+            if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_EUICC)) {
+                TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+                cardId = tm.getCardIdForDefaultEuicc();
+            }
+        } else {
+            TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+            cardId = tm.getCardIdForDefaultEuicc();
+        }
+
+        return cardId;
+    }
+
+    /**
+     * Returns whether the passing portIndex is available.
+     * A port is available if it is active without enabled profile on it or
+     * calling app has carrier privilege over the profile installed on the selected port.
+     *
+     * <p> From Android U, a port is available if it is active without an enabled profile on it or
+     * calling app can activate a new profile on the selected port without any user interaction.
+     *
+     * Always returns false if the cardId is a physical card.
+     *
+     * @param portIndex is an enumeration of the ports available on the UICC.
+     * @return {@code true} if port is available
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     */
+    public boolean isSimPortAvailable(int portIndex) {
+        try {
+            return getIEuiccController().isSimPortAvailable(mCardId, portIndex,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the supported carrier ids for pSIM conversion.
+     *
+     * <p>Any existing pSIM conversion supported carrier list will be replaced
+     * by the {@code carrierIds} set here.
+     *
+     * @param carrierIds is a list of carrierIds that supports pSIM conversion
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @throws IllegalStateException if this method is called when {@link #isEnabled} is false.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION)
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public void setPsimConversionSupportedCarriers(@NonNull Set<Integer> carrierIds) {
+        if (!isEnabled()) {
+            throw new IllegalStateException("Euicc is not enabled");
+        }
+        try {
+            int[] arr = carrierIds.stream().mapToInt(Integer::intValue).toArray();
+            getIEuiccController().setPsimConversionSupportedCarriers(arr);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Returns whether the given carrier id supports pSIM conversion or not.
+     *
+     * @param carrierId to check whether pSIM conversion is supported or not
+     * @return whether the given carrier id supports pSIM conversion or not,
+     *         or false if {@link #isEnabled} is false
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_PSIM_TO_ESIM_CONVERSION)
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    public boolean isPsimConversionSupported(int carrierId) {
+        if (!isEnabled()) {
+            return false;
+        }
+        try {
+            return getIEuiccController().isPsimConversionSupported(carrierId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+}
diff --git a/android-35/android/telephony/euicc/EuiccNotification.java b/android-35/android/telephony/euicc/EuiccNotification.java
new file mode 100644
index 0000000..fcc0b6a
--- /dev/null
+++ b/android-35/android/telephony/euicc/EuiccNotification.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2018 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.euicc;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * This represents a signed notification which is defined in SGP.22. It can be either a profile
+ * installation result or a notification generated for profile operations (e.g., enabling,
+ * disabling, or deleting).
+ *
+ * @hide
+ */
+@SystemApi
+public final class EuiccNotification implements Parcelable {
+    /**
+     * Event
+     *
+     * @removed mistakenly exposed previously
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "EVENT_" }, value = {
+            EVENT_INSTALL,
+            EVENT_ENABLE,
+            EVENT_DISABLE,
+            EVENT_DELETE
+    })
+    public @interface Event {}
+
+    /** A profile is downloaded and installed. */
+    public static final int EVENT_INSTALL = 1;
+
+    /** A profile is enabled. */
+    public static final int EVENT_ENABLE = 1 << 1;
+
+    /** A profile is disabled. */
+    public static final int EVENT_DISABLE = 1 << 2;
+
+    /** A profile is deleted. */
+    public static final int EVENT_DELETE = 1 << 3;
+
+    /** Value of the bits of all the events including install, enable, disable and delete. */
+    @Event
+    public static final int ALL_EVENTS =
+            EVENT_INSTALL | EVENT_ENABLE | EVENT_DISABLE | EVENT_DELETE;
+
+    private final int mSeq;
+    private final String mTargetAddr;
+    @Event private final int mEvent;
+    @Nullable private final byte[] mData;
+
+    /**
+     * Creates an instance.
+     *
+     * @param seq The sequence number of this notification.
+     * @param targetAddr The target server where to send this notification.
+     * @param event The event which causes this notification.
+     * @param data The data which needs to be sent to the target server. This can be null for
+     *     building a list of notification metadata without data.
+     */
+    public EuiccNotification(int seq, String targetAddr, @Event int event, @Nullable byte[] data) {
+        mSeq = seq;
+        mTargetAddr = targetAddr;
+        mEvent = event;
+        mData = data;
+    }
+
+    /** @return The sequence number of this notification. */
+    public int getSeq() {
+        return mSeq;
+    }
+
+    /** @return The target server address where this notification should be sent to. */
+    public String getTargetAddr() {
+        return mTargetAddr;
+    }
+
+    /** @return The event of this notification. */
+    @Event
+    public int getEvent() {
+        return mEvent;
+    }
+
+    /** @return The notification data which needs to be sent to the target server. */
+    @Nullable
+    public byte[] getData() {
+        return mData;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        EuiccNotification that = (EuiccNotification) obj;
+        return mSeq == that.mSeq
+                && Objects.equals(mTargetAddr, that.mTargetAddr)
+                && mEvent == that.mEvent
+                && Arrays.equals(mData, that.mData);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 1;
+        result = 31 * result + mSeq;
+        result = 31 * result + Objects.hashCode(mTargetAddr);
+        result = 31 * result + mEvent;
+        result = 31 * result + Arrays.hashCode(mData);
+        return result;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "EuiccNotification (seq="
+                + mSeq
+                + ", targetAddr="
+                + mTargetAddr
+                + ", event="
+                + mEvent
+                + ", data="
+                + (mData == null ? "null" : "byte[" + mData.length + "]")
+                + ")";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSeq);
+        dest.writeString(mTargetAddr);
+        dest.writeInt(mEvent);
+        dest.writeByteArray(mData);
+    }
+
+    private EuiccNotification(Parcel source) {
+        mSeq = source.readInt();
+        mTargetAddr = source.readString();
+        mEvent = source.readInt();
+        mData = source.createByteArray();
+    }
+
+    public static final @android.annotation.NonNull Creator<EuiccNotification> CREATOR =
+            new Creator<EuiccNotification>() {
+                @Override
+                public EuiccNotification createFromParcel(Parcel source) {
+                    return new EuiccNotification(source);
+                }
+
+                @Override
+                public EuiccNotification[] newArray(int size) {
+                    return new EuiccNotification[size];
+                }
+            };
+}
diff --git a/android-35/android/telephony/euicc/EuiccRulesAuthTable.java b/android-35/android/telephony/euicc/EuiccRulesAuthTable.java
new file mode 100644
index 0000000..c35242d
--- /dev/null
+++ b/android-35/android/telephony/euicc/EuiccRulesAuthTable.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2018 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.euicc;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.carrier.CarrierIdentifier;
+import android.service.euicc.EuiccProfileInfo;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This represents the RAT (Rules Authorisation Table) stored on eUICC.
+ * @hide
+ */
+@SystemApi
+public final class EuiccRulesAuthTable implements Parcelable {
+    /**
+     * Profile policy rule flags
+     *
+     * @removed mistakenly exposed previously
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "POLICY_RULE_FLAG_" }, value = {
+            POLICY_RULE_FLAG_CONSENT_REQUIRED
+    })
+    public @interface PolicyRuleFlag {}
+
+    /** User consent is required to install the profile. */
+    public static final int POLICY_RULE_FLAG_CONSENT_REQUIRED = 1;
+
+    private final int[] mPolicyRules;
+    private final CarrierIdentifier[][] mCarrierIds;
+    private final int[] mPolicyRuleFlags;
+
+    /** This is used to build new {@link EuiccRulesAuthTable} instance. */
+    public static final class Builder {
+        private int[] mPolicyRules;
+        private CarrierIdentifier[][] mCarrierIds;
+        private int[] mPolicyRuleFlags;
+        private int mPosition;
+
+        /**
+         * Creates a new builder.
+         *
+         * @param ruleNum The number of authorisation rules in the table.
+         */
+        public Builder(int ruleNum) {
+            mPolicyRules = new int[ruleNum];
+            mCarrierIds = new CarrierIdentifier[ruleNum][];
+            mPolicyRuleFlags = new int[ruleNum];
+        }
+
+        /**
+         * Builds the RAT instance. This builder should not be used anymore after this method is
+         * called, otherwise {@link NullPointerException} will be thrown.
+         */
+        public EuiccRulesAuthTable build() {
+            if (mPosition != mPolicyRules.length) {
+                throw new IllegalStateException(
+                        "Not enough rules are added, expected: "
+                                + mPolicyRules.length
+                                + ", added: "
+                                + mPosition);
+            }
+            return new EuiccRulesAuthTable(mPolicyRules, mCarrierIds, mPolicyRuleFlags);
+        }
+
+        /**
+         * Adds an authorisation rule.
+         *
+         * @throws ArrayIndexOutOfBoundsException If the {@code mPosition} is larger than the size
+         *     this table.
+         */
+        public Builder add(int policyRules, List<CarrierIdentifier> carrierId, int policyRuleFlags) {
+            if (mPosition >= mPolicyRules.length) {
+                throw new ArrayIndexOutOfBoundsException(mPosition);
+            }
+            mPolicyRules[mPosition] = policyRules;
+            if (carrierId != null && carrierId.size() > 0) {
+                mCarrierIds[mPosition] = carrierId.toArray(new CarrierIdentifier[carrierId.size()]);
+            }
+            mPolicyRuleFlags[mPosition] = policyRuleFlags;
+            mPosition++;
+            return this;
+        }
+    }
+
+    /**
+     * @param mccRule A 2-character or 3-character string which can be either MCC or MNC. The
+     *     character 'E' is used as a wild char to match any digit.
+     * @param mcc A 2-character or 3-character string which can be either MCC or MNC.
+     * @return Whether the {@code mccRule} matches {@code mcc}.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static boolean match(String mccRule, String mcc) {
+        if (mccRule.length() < mcc.length()) {
+            return false;
+        }
+        for (int i = 0; i < mccRule.length(); i++) {
+            // 'E' is the wild char to match any digit.
+            if (mccRule.charAt(i) == 'E'
+                    || (i < mcc.length() && mccRule.charAt(i) == mcc.charAt(i))) {
+                continue;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    private EuiccRulesAuthTable(int[] policyRules, CarrierIdentifier[][] carrierIds,
+            int[] policyRuleFlags) {
+        mPolicyRules = policyRules;
+        mCarrierIds = carrierIds;
+        mPolicyRuleFlags = policyRuleFlags;
+    }
+
+    /**
+     * Finds the index of the first authorisation rule matching the given policy and carrier id. If
+     * the returned index is not negative, the carrier is allowed to apply this policy to its
+     * profile.
+     *
+     * @param policy The policy rule.
+     * @param carrierId The carrier id.
+     * @return The index of authorization rule. If no rule is found, -1 will be returned.
+     */
+    public int findIndex(@EuiccProfileInfo.PolicyRule int policy, CarrierIdentifier carrierId) {
+        for (int i = 0; i < mPolicyRules.length; i++) {
+            if ((mPolicyRules[i] & policy) == 0) {
+                continue;
+            }
+            CarrierIdentifier[] carrierIds = mCarrierIds[i];
+            if (carrierIds == null || carrierIds.length == 0) {
+                continue;
+            }
+            for (int j = 0; j < carrierIds.length; j++) {
+                CarrierIdentifier ruleCarrierId = carrierIds[j];
+                if (!match(ruleCarrierId.getMcc(), carrierId.getMcc())
+                        || !match(ruleCarrierId.getMnc(), carrierId.getMnc())) {
+                    continue;
+                }
+                String gid = ruleCarrierId.getGid1();
+                if (!TextUtils.isEmpty(gid) && !gid.equals(carrierId.getGid1())) {
+                    continue;
+                }
+                gid = ruleCarrierId.getGid2();
+                if (!TextUtils.isEmpty(gid) && !gid.equals(carrierId.getGid2())) {
+                    continue;
+                }
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Tests if the entry in the table has the given policy rule flag.
+     *
+     * @param index The index of the entry.
+     * @param flag The policy rule flag to be tested.
+     * @throws ArrayIndexOutOfBoundsException If the {@code index} is negative or larger than the
+     *     size of this table.
+     */
+    public boolean hasPolicyRuleFlag(int index, @PolicyRuleFlag int flag) {
+        if (index < 0 || index >= mPolicyRules.length) {
+            throw new ArrayIndexOutOfBoundsException(index);
+        }
+        return (mPolicyRuleFlags[index] & flag) != 0;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeIntArray(mPolicyRules);
+        for (CarrierIdentifier[] ids : mCarrierIds) {
+            dest.writeTypedArray(ids, flags);
+        }
+        dest.writeIntArray(mPolicyRuleFlags);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        EuiccRulesAuthTable that = (EuiccRulesAuthTable) obj;
+        if (mCarrierIds.length != that.mCarrierIds.length) {
+            return false;
+        }
+        for (int i = 0; i < mCarrierIds.length; i++) {
+            CarrierIdentifier[] carrierIds = mCarrierIds[i];
+            CarrierIdentifier[] thatCarrierIds = that.mCarrierIds[i];
+            if (carrierIds != null && thatCarrierIds != null) {
+                if (carrierIds.length != thatCarrierIds.length) {
+                    return false;
+                }
+                for (int j = 0; j < carrierIds.length; j++) {
+                    if (!carrierIds[j].equals(thatCarrierIds[j])) {
+                        return false;
+                    }
+                }
+                continue;
+            } else if (carrierIds == null && thatCarrierIds == null) {
+                continue;
+            }
+            return false;
+        }
+
+        return Arrays.equals(mPolicyRules, that.mPolicyRules)
+                && Arrays.equals(mPolicyRuleFlags, that.mPolicyRuleFlags);
+    }
+
+    private EuiccRulesAuthTable(Parcel source) {
+        mPolicyRules = source.createIntArray();
+        int len = mPolicyRules.length;
+        mCarrierIds = new CarrierIdentifier[len][];
+        for (int i = 0; i < len; i++) {
+            mCarrierIds[i] = source.createTypedArray(CarrierIdentifier.CREATOR);
+        }
+        mPolicyRuleFlags = source.createIntArray();
+    }
+
+    public static final @android.annotation.NonNull Creator<EuiccRulesAuthTable> CREATOR =
+            new Creator<EuiccRulesAuthTable>() {
+                @Override
+                public EuiccRulesAuthTable createFromParcel(Parcel source) {
+                    return new EuiccRulesAuthTable(source);
+                }
+
+                @Override
+                public EuiccRulesAuthTable[] newArray(int size) {
+                    return new EuiccRulesAuthTable[size];
+                }
+            };
+}
diff --git a/android-35/android/telephony/gba/GbaAuthRequest.java b/android-35/android/telephony/gba/GbaAuthRequest.java
new file mode 100644
index 0000000..2c6021a
--- /dev/null
+++ b/android-35/android/telephony/gba/GbaAuthRequest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.gba;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.IBootstrapAuthenticationCallback;
+
+import com.android.internal.telephony.uicc.IccUtils;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * GBA authentication request
+ * {@hide}
+ */
+public final class GbaAuthRequest implements Parcelable {
+    private int mToken;
+    private int mSubId;
+    private int mAppType;
+    private Uri mNafUrl;
+    private byte[] mSecurityProtocol;
+    private boolean mForceBootStrapping;
+    private IBootstrapAuthenticationCallback mCallback;
+
+    private static AtomicInteger sUniqueToken = new AtomicInteger(0);
+
+    public GbaAuthRequest(int subId, int appType, Uri nafUrl, byte[] securityProtocol,
+            boolean forceBootStrapping, IBootstrapAuthenticationCallback callback) {
+        this(nextUniqueToken(), subId, appType, nafUrl,
+                securityProtocol, forceBootStrapping, callback);
+    }
+
+    public GbaAuthRequest(GbaAuthRequest request) {
+        this(request.mToken, request.mSubId, request.mAppType, request.mNafUrl,
+                request.mSecurityProtocol, request.mForceBootStrapping, request.mCallback);
+    }
+
+    public GbaAuthRequest(int token, int subId, int appType, Uri nafUrl, byte[] securityProtocol,
+            boolean forceBootStrapping, IBootstrapAuthenticationCallback callback) {
+        mToken = token;
+        mSubId = subId;
+        mAppType = appType;
+        mNafUrl = nafUrl;
+        mSecurityProtocol = securityProtocol;
+        mCallback = callback;
+        mForceBootStrapping = forceBootStrapping;
+    }
+
+    public int getToken() {
+        return mToken;
+    }
+
+    public int getSubId() {
+        return mSubId;
+    }
+
+    public int getAppType() {
+        return mAppType;
+    }
+
+    public Uri getNafUrl() {
+        return mNafUrl;
+    }
+
+    public byte[] getSecurityProtocol() {
+        return mSecurityProtocol;
+    }
+
+    public boolean isForceBootStrapping() {
+        return mForceBootStrapping;
+    }
+
+    public void setCallback(IBootstrapAuthenticationCallback cb) {
+        mCallback = cb;
+    }
+
+    public IBootstrapAuthenticationCallback getCallback() {
+        return mCallback;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mToken);
+        out.writeInt(mSubId);
+        out.writeInt(mAppType);
+        out.writeParcelable(mNafUrl, 0);
+        out.writeInt(mSecurityProtocol.length);
+        out.writeByteArray(mSecurityProtocol);
+        out.writeBoolean(mForceBootStrapping);
+        out.writeStrongInterface(mCallback);
+    }
+
+    /**
+     * {@link Parcelable.Creator}
+     *
+     */
+    public static final @android.annotation.NonNull Parcelable.Creator<
+            GbaAuthRequest> CREATOR = new Creator<GbaAuthRequest>() {
+                @Override
+                public GbaAuthRequest createFromParcel(Parcel in) {
+                    int token = in.readInt();
+                    int subId = in.readInt();
+                    int appType = in.readInt();
+                    Uri nafUrl = in.readParcelable(GbaAuthRequest.class.getClassLoader(), android.net.Uri.class);
+                    int len = in.readInt();
+                    byte[] protocol = new byte[len];
+                    in.readByteArray(protocol);
+                    boolean forceBootStrapping = in.readBoolean();
+                    IBootstrapAuthenticationCallback callback =
+                            IBootstrapAuthenticationCallback.Stub
+                            .asInterface(in.readStrongBinder());
+                    return new GbaAuthRequest(token, subId, appType, nafUrl, protocol,
+                            forceBootStrapping, callback);
+                }
+
+                @Override
+                public GbaAuthRequest[] newArray(int size) {
+                    return new GbaAuthRequest[size];
+                }
+            };
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    private static int nextUniqueToken() {
+        return sUniqueToken.getAndIncrement() << 16 | (0xFFFF & (int) System.currentTimeMillis());
+    }
+
+    @Override
+    public String toString() {
+        String str = "Token: " +  mToken + "SubId:" + mSubId + ", AppType:"
+                + mAppType + ", NafUrl:" + mNafUrl + ", SecurityProtocol:"
+                + IccUtils.bytesToHexString(mSecurityProtocol)
+                + ", ForceBootStrapping:" + mForceBootStrapping
+                + ", CallBack:" + mCallback;
+        return str;
+    }
+}
diff --git a/android-35/android/telephony/gba/GbaService.java b/android-35/android/telephony/gba/GbaService.java
new file mode 100644
index 0000000..3962aff
--- /dev/null
+++ b/android-35/android/telephony/gba/GbaService.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.gba;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.Annotation.UiccAppTypeExt;
+import android.telephony.IBootstrapAuthenticationCallback;
+import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.AuthenticationFailureReason;
+import android.util.Log;
+import android.util.SparseArray;
+
+/**
+  * Base class for GBA Service. Any implementation which wants to provide
+  * GBA service must extend this class.
+  *
+  * <p>Note that the application to implement the service must declare to use
+  * the permission {@link android.Manifest.permission#BIND_GBA_SERVICE},
+  * and filter the intent of {@link #SERVICE_INTERFACE}.
+  * The manifest of the service must follow the format below:
+  *
+  * <p>...
+  * <service
+  *     android:name=".EgGbaService"
+  *     android:directBootAware="true"
+  *     android:permission="android.permission.BIND_GBA_SERVICE" >
+  *     ...
+  *     <intent-filter>
+  *         <action android:name="android.telephony.gba.GbaService"/>
+  *     </intent-filter>
+  * </service>
+  * ...
+  *
+  * <p>The service should also be file-based encryption (FBE) aware.
+  * {@hide}
+  */
+@SystemApi
+public class GbaService extends Service  {
+    private static final boolean DBG = Build.IS_DEBUGGABLE;
+    private static final String TAG = "GbaService";
+
+    /**
+    * The intent must be defined as an intent-filter in the
+    * AndroidManifest of the GbaService.
+    */
+    public static final String SERVICE_INTERFACE = "android.telephony.gba.GbaService";
+
+    private static final int EVENT_GBA_AUTH_REQUEST = 1;
+
+    private final HandlerThread mHandlerThread;
+    private final GbaServiceHandler mHandler;
+
+    private final SparseArray<IBootstrapAuthenticationCallback> mCallbacks = new SparseArray<>();
+    private final IGbaServiceWrapper mBinder = new IGbaServiceWrapper();
+
+    /**
+     * Default constructor.
+     */
+    public GbaService() {
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+
+        mHandler = new GbaServiceHandler(mHandlerThread.getLooper());
+        Log.d(TAG, "GBA service created");
+    }
+
+    private class GbaServiceHandler extends Handler {
+
+        GbaServiceHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_GBA_AUTH_REQUEST:
+                    GbaAuthRequest req = (GbaAuthRequest) msg.obj;
+                    synchronized (mCallbacks) {
+                        mCallbacks.put(req.getToken(), req.getCallback());
+                    }
+                    onAuthenticationRequest(req.getSubId(), req.getToken(), req.getAppType(),
+                            req.getNafUrl(), req.getSecurityProtocol(), req.isForceBootStrapping());
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Called by the platform when a GBA authentication request is received from
+     * {@link TelephonyManager#bootstrapAuthenticationRequest} to get the KsNAF for
+     * a particular NAF.
+     *
+     * @param subscriptionId the ICC card to be used for the bootstrapping authentication.
+     * @param token the identification of the authentication request.
+     * @param appType icc application type, like {@link #APPTYPE_USIM} or {@link
+     * #APPTYPE_ISIM} or {@link#APPTYPE_UNKNOWN}
+     * @param nafUrl Network Application Function(NAF) fully qualified domain name and
+     * the selected GBA mode. It shall contain two parts delimited by "@" sign. The first
+     * part is the constant string "3GPP-bootstrapping" (GBA_ME),
+     * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest),
+     * and the latter part shall be the FQDN of the NAF (e.g.
+     * "[email protected]" or "[email protected]",
+     * or "[email protected]").
+     * @param securityProtocol Security protocol identifier between UE and NAF.  See
+     * 3GPP TS 33.220 Annex H. Application can use
+     * {@link UaSecurityProtocolIdentifier#createDefaultUaSpId},
+     * {@link UaSecurityProtocolIdentifier#create3GppUaSpId},
+     * to create the ua security protocol identifier as needed
+     * @param forceBootStrapping true=force bootstrapping, false=do not force
+     * bootstrapping. Bootstrapping shouldn't be forced unless the application sees
+     * authentication errors from the server.
+     * Response is returned via {@link TelephonyManager#BootstrapAuthenticationCallback}
+     * along with the token to identify the request.
+     *
+     * <p>Note that this is not called in the main thread.
+     */
+    public void onAuthenticationRequest(int subscriptionId, int token, @UiccAppTypeExt int appType,
+            @NonNull Uri nafUrl, @NonNull byte[] securityProtocol, boolean forceBootStrapping) {
+        //Default implementation should be overridden by vendor Gba Service. Vendor Gba Service
+        //should handle the gba bootstrap authentication request, and call reportKeysAvailable or
+        //reportAuthenticationFailure to notify the caller accordingly.
+        reportAuthenticationFailure(
+                token, TelephonyManager.GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED);
+    }
+
+    /**
+     * Called by {@link GbaService} when the previously requested GBA keys are available
+     * (@see onAuthenticationRequest())
+     *
+     * @param token unique identifier of the request.
+     * @param gbaKey KsNaf Response.
+     * @param transactionId Bootstrapping Transaction ID.
+     * @throws RuntimeException when there is remote failure of callback.
+     */
+    public final void reportKeysAvailable(int token, @NonNull byte[] gbaKey,
+            @NonNull String transactionId) throws RuntimeException {
+        IBootstrapAuthenticationCallback cb = null;
+        synchronized (mCallbacks) {
+            cb = mCallbacks.get(token);
+            mCallbacks.remove(token);
+        }
+        if (cb != null) {
+            try {
+                cb.onKeysAvailable(token, gbaKey, transactionId);
+            } catch (RemoteException exception) {
+                throw exception.rethrowAsRuntimeException();
+            }
+        }
+    }
+
+    /**
+     * Invoked when the previously requested GBA key authentication failed
+     * (@see onAuthenticationRequest())
+     *
+     * @param token unique identifier of the request.
+     * @param reason The reason for the authentication failure.
+     * @throws RuntimeException when there is remote failure of callback.
+     */
+    public final void reportAuthenticationFailure(int token,
+            @AuthenticationFailureReason int reason) throws RuntimeException {
+        IBootstrapAuthenticationCallback cb = null;
+        synchronized (mCallbacks) {
+            cb = mCallbacks.get(token);
+            mCallbacks.remove(token);
+        }
+        if (cb != null) {
+            try {
+                cb.onAuthenticationFailure(token, reason);
+            } catch (RemoteException exception) {
+                throw exception.rethrowAsRuntimeException();
+            }
+        }
+    }
+
+    /** @hide */
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            Log.d(TAG, "GbaService Bound.");
+            return mBinder;
+        }
+        return null;
+    }
+
+    /** @hide */
+    @Override
+    public void onDestroy() {
+        mHandlerThread.quit();
+        super.onDestroy();
+    }
+
+    private class IGbaServiceWrapper extends IGbaService.Stub {
+        @Override
+        public void authenticationRequest(GbaAuthRequest request) {
+            if (DBG) Log.d(TAG, "receive request: " + request);
+            mHandler.obtainMessage(EVENT_GBA_AUTH_REQUEST, request).sendToTarget();
+        }
+    }
+}
diff --git a/android-35/android/telephony/gba/TlsParams.java b/android-35/android/telephony/gba/TlsParams.java
new file mode 100644
index 0000000..922f4bb
--- /dev/null
+++ b/android-35/android/telephony/gba/TlsParams.java
@@ -0,0 +1,281 @@
+/*
+ * 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.gba;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * Defines the TLS parameters for GBA as per IANA and TS 33.210, which are used
+ * by some UA security protocol identifiers defined in 3GPP TS 33.220 Annex H,
+ * and 3GPP TS 33.222.
+ *
+ * @hide
+ */
+@SystemApi
+public class TlsParams {
+
+    private TlsParams() {}
+
+    /**
+     * TLS protocol version supported by GBA
+     */
+    public static final int PROTOCOL_VERSION_TLS_1_2 = 0x0303;
+    public static final int PROTOCOL_VERSION_TLS_1_3 = 0x0304;
+
+    /**
+     * TLS cipher suites are used to create {@link UaSecurityProtocolIdentifier}
+     * by {@link UaSecurityProtocolIdentifier#create3GppUaSpId}
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = {"TLS_"},
+        value = {
+            TLS_NULL_WITH_NULL_NULL,
+            TLS_RSA_WITH_NULL_MD5,
+            TLS_RSA_WITH_NULL_SHA,
+            TLS_RSA_WITH_RC4_128_MD5,
+            TLS_RSA_WITH_RC4_128_SHA,
+            TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+            TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA,
+            TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA,
+            TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+            TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+            TLS_DH_ANON_WITH_RC4_128_MD5,
+            TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA,
+            TLS_RSA_WITH_AES_128_CBC_SHA,
+            TLS_DH_DSS_WITH_AES_128_CBC_SHA,
+            TLS_DH_RSA_WITH_AES_128_CBC_SHA,
+            TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+            TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+            TLS_DH_ANON_WITH_AES_128_CBC_SHA,
+            TLS_RSA_WITH_AES_256_CBC_SHA,
+            TLS_DH_DSS_WITH_AES_256_CBC_SHA,
+            TLS_DH_RSA_WITH_AES_256_CBC_SHA,
+            TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+            TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+            TLS_DH_ANON_WITH_AES_256_CBC_SHA,
+            TLS_RSA_WITH_NULL_SHA256,
+            TLS_RSA_WITH_AES_128_CBC_SHA256,
+            TLS_RSA_WITH_AES_256_CBC_SHA256,
+            TLS_DH_DSS_WITH_AES_128_CBC_SHA256,
+            TLS_DH_RSA_WITH_AES_128_CBC_SHA256,
+            TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
+            TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+            TLS_DH_DSS_WITH_AES_256_CBC_SHA256,
+            TLS_DH_RSA_WITH_AES_256_CBC_SHA256,
+            TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
+            TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+            TLS_DH_ANON_WITH_AES_128_CBC_SHA256,
+            TLS_DH_ANON_WITH_AES_256_CBC_SHA256,
+            TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+            TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
+            TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
+            TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
+            TLS_AES_128_GCM_SHA256,
+            TLS_AES_256_GCM_SHA384,
+            TLS_CHACHA20_POLY1305_SHA256,
+            TLS_AES_128_CCM_SHA256,
+            TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+            TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+            TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+            TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+            TLS_DHE_RSA_WITH_AES_128_CCM,
+            TLS_DHE_RSA_WITH_AES_256_CCM,
+            TLS_DHE_PSK_WITH_AES_128_CCM,
+            TLS_DHE_PSK_WITH_AES_256_CCM,
+            TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+            TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+            TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+            TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+            TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+            TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
+            TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384,
+            TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256
+        })
+    public @interface TlsCipherSuite {}
+
+    // Cipher suites for TLS v1.2 per RFC5246
+    public static final int TLS_NULL_WITH_NULL_NULL = 0x0000;
+    public static final int TLS_RSA_WITH_NULL_MD5 = 0x0001;
+    public static final int TLS_RSA_WITH_NULL_SHA = 0x0002;
+    public static final int TLS_RSA_WITH_RC4_128_MD5 = 0x0004;
+    public static final int TLS_RSA_WITH_RC4_128_SHA = 0x0005;
+    public static final int TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A;
+    public static final int TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D;
+    public static final int TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010;
+    public static final int TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013;
+    public static final int TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016;
+    public static final int TLS_DH_ANON_WITH_RC4_128_MD5 = 0x0018;
+    public static final int TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA = 0x001B;
+    public static final int TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F;
+    public static final int TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030;
+    public static final int TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031;
+    public static final int TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032;
+    public static final int TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033;
+    public static final int TLS_DH_ANON_WITH_AES_128_CBC_SHA = 0x0034;
+    public static final int TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035;
+    public static final int TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036;
+    public static final int TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037;
+    public static final int TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038;
+    public static final int TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039;
+    public static final int TLS_DH_ANON_WITH_AES_256_CBC_SHA = 0x003A;
+    public static final int TLS_RSA_WITH_NULL_SHA256 = 0x003B;
+    public static final int TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C;
+    public static final int TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D;
+    public static final int TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E;
+    public static final int TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F;
+    public static final int TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040;
+    public static final int TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067;
+    public static final int TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068;
+    public static final int TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069;
+    public static final int TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A;
+    public static final int TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B;
+    public static final int TLS_DH_ANON_WITH_AES_128_CBC_SHA256 = 0x006C;
+    public static final int TLS_DH_ANON_WITH_AES_256_CBC_SHA256 = 0x006D;
+
+    // Cipher suites for TLS v1.3 per RFC8446 and recommended by IANA
+    public static final int TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E;
+    public static final int TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F;
+    public static final int TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA;
+    public static final int TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB;
+    public static final int TLS_AES_128_GCM_SHA256 = 0x1301;
+    public static final int TLS_AES_256_GCM_SHA384 = 0x1302;
+    public static final int TLS_CHACHA20_POLY1305_SHA256 = 0x1303;
+    public static final int TLS_AES_128_CCM_SHA256 = 0x1304;
+    public static final int TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B;
+    public static final int TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C;
+    public static final int TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F;
+    public static final int TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030;
+    public static final int TLS_DHE_RSA_WITH_AES_128_CCM = 0xC09E;
+    public static final int TLS_DHE_RSA_WITH_AES_256_CCM = 0xC09F;
+    public static final int TLS_DHE_PSK_WITH_AES_128_CCM = 0xC0A6;
+    public static final int TLS_DHE_PSK_WITH_AES_256_CCM = 0xC0A7;
+    public static final int TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8;
+    public static final int TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9;
+    public static final int TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAA;
+    public static final int TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAC;
+    public static final int TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAD;
+    public static final int TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 = 0xD001;
+    public static final int TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384 = 0xD002;
+    public static final int TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256 = 0xD005;
+
+    private static final int[] CS_EXPECTED = {
+        TLS_NULL_WITH_NULL_NULL,
+        TLS_RSA_WITH_NULL_MD5,
+        TLS_RSA_WITH_NULL_SHA,
+        TLS_RSA_WITH_RC4_128_MD5,
+        TLS_RSA_WITH_RC4_128_SHA,
+        TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+        TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA,
+        TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA,
+        TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+        TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+        TLS_DH_ANON_WITH_RC4_128_MD5,
+        TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA,
+        TLS_RSA_WITH_AES_128_CBC_SHA,
+        TLS_DH_DSS_WITH_AES_128_CBC_SHA,
+        TLS_DH_RSA_WITH_AES_128_CBC_SHA,
+        TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+        TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+        TLS_DH_ANON_WITH_AES_128_CBC_SHA,
+        TLS_RSA_WITH_AES_256_CBC_SHA,
+        TLS_DH_DSS_WITH_AES_256_CBC_SHA,
+        TLS_DH_RSA_WITH_AES_256_CBC_SHA,
+        TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+        TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+        TLS_DH_ANON_WITH_AES_256_CBC_SHA,
+        TLS_RSA_WITH_NULL_SHA256,
+        TLS_RSA_WITH_AES_128_CBC_SHA256,
+        TLS_RSA_WITH_AES_256_CBC_SHA256,
+        TLS_DH_DSS_WITH_AES_128_CBC_SHA256,
+        TLS_DH_RSA_WITH_AES_128_CBC_SHA256,
+        TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
+        TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+        TLS_DH_DSS_WITH_AES_256_CBC_SHA256,
+        TLS_DH_RSA_WITH_AES_256_CBC_SHA256,
+        TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
+        TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+        TLS_DH_ANON_WITH_AES_128_CBC_SHA256,
+        TLS_DH_ANON_WITH_AES_256_CBC_SHA256,
+        TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+        TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
+        TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
+        TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
+        TLS_AES_128_GCM_SHA256,
+        TLS_AES_256_GCM_SHA384,
+        TLS_CHACHA20_POLY1305_SHA256,
+        TLS_AES_128_CCM_SHA256,
+        TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+        TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+        TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+        TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+        TLS_DHE_RSA_WITH_AES_128_CCM,
+        TLS_DHE_RSA_WITH_AES_256_CCM,
+        TLS_DHE_PSK_WITH_AES_128_CCM,
+        TLS_DHE_PSK_WITH_AES_256_CCM,
+        TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+        TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+        TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+        TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+        TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+        TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
+        TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384,
+        TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256
+    };
+
+    /**
+     * TLS supported groups required by TS 33.210
+     */
+    public static final int GROUP_SECP256R1 = 23;
+    public static final int GROUP_SECP384R1 = 24;
+    public static final int GROUP_X25519 = 29;
+    public static final int GROUP_X448 = 30;
+
+    /**
+     * Signature algorithms shall be supported as per TS 33.210
+     */
+    public static final int SIG_RSA_PKCS1_SHA1 = 0X0201;
+    public static final int SIG_ECDSA_SHA1 = 0X0203;
+    public static final int SIG_RSA_PKCS1_SHA256 = 0X0401;
+    public static final int SIG_ECDSA_SECP256R1_SHA256 = 0X0403;
+    public static final int SIG_RSA_PKCS1_SHA256_LEGACY = 0X0420;
+    public static final int SIG_RSA_PKCS1_SHA384 = 0X0501;
+    public static final int SIG_ECDSA_SECP384R1_SHA384 = 0X0503;
+    public static final int SIG_RSA_PKCS1_SHA384_LEGACY = 0X0520;
+    public static final int SIG_RSA_PKCS1_SHA512 = 0X0601;
+    public static final int SIG_ECDSA_SECP521R1_SHA512 = 0X0603;
+    public static final int SIG_RSA_PKCS1_SHA512_LEGACY = 0X0620;
+    public static final int SIG_RSA_PSS_RSAE_SHA256 = 0X0804;
+    public static final int SIG_RSA_PSS_RSAE_SHA384 = 0X0805;
+    public static final int SIG_RSA_PSS_RSAE_SHA512 = 0X0806;
+    public static final int SIG_ECDSA_BRAINPOOLP256R1TLS13_SHA256 = 0X081A;
+    public static final int SIG_ECDSA_BRAINPOOLP384R1TLS13_SHA384 = 0X081B;
+    public static final int SIG_ECDSA_BRAINPOOLP512R1TLS13_SHA512 = 0X081C;
+
+    /**
+     * Returns whether the TLS cipher suite id is supported
+     */
+    public static boolean isTlsCipherSuiteSupported(int csId) {
+        return Arrays.binarySearch(CS_EXPECTED, csId) >= 0;
+    }
+}
diff --git a/android-35/android/telephony/gba/UaSecurityProtocolIdentifier.java b/android-35/android/telephony/gba/UaSecurityProtocolIdentifier.java
new file mode 100644
index 0000000..c141875
--- /dev/null
+++ b/android-35/android/telephony/gba/UaSecurityProtocolIdentifier.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.gba;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.gba.TlsParams.TlsCipherSuite;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+/**
+ * Description of ua security protocol identifier defined in 3GPP TS 33.220 H.2
+ * @hide
+ */
+@SystemApi
+public final class UaSecurityProtocolIdentifier implements Parcelable {
+
+    /**
+     * Organization code defined in 3GPP TS 33.220 H.3
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"ORG_"}, value = {
+        ORG_NONE,
+        ORG_3GPP,
+        ORG_3GPP2,
+        ORG_OMA,
+        ORG_GSMA,
+        ORG_LOCAL})
+    public @interface OrganizationCode {}
+
+    /**
+     * Organization octet value for default ua security protocol
+     */
+    public static final int ORG_NONE  = 0;
+    /**
+     * Organization octet value for 3GPP ua security protocol
+     */
+    public static final int ORG_3GPP  = 0x01;
+    /**
+     * Organization octet value for 3GPP2 ua security protocol
+     */
+    public static final int ORG_3GPP2 = 0x02;
+    /**
+     * Organization octet value for OMA ua security protocol
+     */
+    public static final int ORG_OMA   = 0x03;
+    /**
+     * Organization octet value for GSMA ua security protocol
+     */
+    public static final int ORG_GSMA  = 0x04;
+    /**
+     * Internal organization octet value for local/experimental protocols
+     */
+    public static final int ORG_LOCAL = 0xFF;
+
+    /**
+     * 3GPP UA Security Protocol ID defined in 3GPP TS 33.220 H.3
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"UA_SECURITY_PROTOCOL_3GPP_"}, value = {
+        UA_SECURITY_PROTOCOL_3GPP_SUBSCRIBER_CERTIFICATE,
+        UA_SECURITY_PROTOCOL_3GPP_MBMS,
+        UA_SECURITY_PROTOCOL_3GPP_HTTP_DIGEST_AUTHENTICATION,
+        UA_SECURITY_PROTOCOL_3GPP_HTTP_BASED_MBMS,
+        UA_SECURITY_PROTOCOL_3GPP_SIP_BASED_MBMS,
+        UA_SECURITY_PROTOCOL_3GPP_GENERIC_PUSH_LAYER,
+        UA_SECURITY_PROTOCOL_3GPP_IMS_MEDIA_PLANE,
+        UA_SECURITY_PROTOCOL_3GPP_GENERATION_TMPI,
+        UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT,
+        UA_SECURITY_PROTOCOL_3GPP_TLS_BROWSER})
+    public @interface UaSecurityProtocol3gpp {}
+
+    /**
+     * Security protocol param according to TS 33.221 as described in TS
+     * 33.220 Annex H. Mapped to byte stream "0x01,0x00,0x00,0x00,0x00".
+     */
+    public static final int UA_SECURITY_PROTOCOL_3GPP_SUBSCRIBER_CERTIFICATE = 0;
+
+    /**
+     * Security protocol param according to TS 33.246 for Multimedia
+     * broadcast/Multimedia services (MBMS) as described in TS
+     * 33.220 Annex H. Mapped to byte stream "0x01,0x00,0x00,0x00,0x01".
+     */
+    public static final int UA_SECURITY_PROTOCOL_3GPP_MBMS = 1;
+
+    /**
+     * Security protocol param based on HTTP digest authentication
+     * according to TS 24.109 as described in TS 33.220 Annex H. Mapped to
+     * byte stream "0x01,0x00,0x00,0x00,0x02".
+     */
+    public static final int UA_SECURITY_PROTOCOL_3GPP_HTTP_DIGEST_AUTHENTICATION = 2;
+
+    /**
+     * Security protocol param used with HTTP-based security procedures for
+     * Multimedia broadcast/Multimedia services (MBMS) user services
+     * according to TS 26.237 as described in TS 33.220 Annex H.
+     * Mapped to byte stream "0x01,0x00,0x00,0x00,0x03".
+     */
+    public static final int UA_SECURITY_PROTOCOL_3GPP_HTTP_BASED_MBMS = 3;
+
+    /**
+     * Security protocol param used with SIP-based security procedures for
+     * Multimedia broadcast/Multimedia services (MBMS) user services
+     * according to TS 26.237 as described in TS 33.220 Annex H.
+     * Mapped to byte stream "0x01,0x00,0x00,0x00,0x04".
+     */
+    public static final int UA_SECURITY_PROTOCOL_3GPP_SIP_BASED_MBMS = 4;
+
+    /**
+     * Security protocol param used with Generic Push Layer according to TS
+     * 33.224  as described in TS 33.220 Annex H. Mapped to byte stream
+     * "0x01,0x00,0x00,0x00,0x05".
+     */
+    public static final int UA_SECURITY_PROTOCOL_3GPP_GENERIC_PUSH_LAYER = 5;
+
+    /**
+     * Security protocol param used for IMS UE to KMS http based message
+     * exchanges according to "IMS media plane security", TS 33.328   as
+     * described in TS 33.220 Annex H. Mapped to byte stream
+     * "0x01,0x00,0x00,0x00,0x06".
+     */
+    public static final int UA_SECURITY_PROTOCOL_3GPP_IMS_MEDIA_PLANE = 6;
+
+    /**
+     * Security protocol param used for Generation of Temporary IP
+     * Multimedia Private Identity (TMPI) according to TS 33.220 Annex B.4
+     * Mapped to byte stream "0x01,0x00,0x00,0x01,0x00".
+     */
+    public static final int UA_SECURITY_PROTOCOL_3GPP_GENERATION_TMPI = 0x0100;
+
+    /**
+     * Security protocol param used for Shared key-based UE authentication with
+     * certificate-based NAF authentication, according to TS 33.222 section 5.3,
+     * or Shared key-based mutual authentication between UE and NAF, according to
+     * TS 33.222 section 5.4. Mapped to byte stream "0x01,0x00,0x01,yy,zz".
+     * "yy, zz" is the TLS CipherSuite code.
+     */
+    public static final int UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT = 0x010000;
+
+    /**
+     * Security protocol param used for Shared key-based UE authentication with
+     * certificate-based NAF authentication, according to TS 33.222 Annex D.
+     * Mapped to byte stream "0x01,0x00,0x02,yy,zz".
+     * "yy, zz" is the TLS CipherSuite code.
+     */
+    public static final int UA_SECURITY_PROTOCOL_3GPP_TLS_BROWSER = 0x020000;
+
+    private static final int PROTOCOL_SIZE = 5;
+    private static final int[] sUaSp3gppIds = new int[] {
+            UA_SECURITY_PROTOCOL_3GPP_SUBSCRIBER_CERTIFICATE,
+            UA_SECURITY_PROTOCOL_3GPP_MBMS,
+            UA_SECURITY_PROTOCOL_3GPP_HTTP_DIGEST_AUTHENTICATION,
+            UA_SECURITY_PROTOCOL_3GPP_HTTP_BASED_MBMS,
+            UA_SECURITY_PROTOCOL_3GPP_SIP_BASED_MBMS,
+            UA_SECURITY_PROTOCOL_3GPP_GENERIC_PUSH_LAYER,
+            UA_SECURITY_PROTOCOL_3GPP_IMS_MEDIA_PLANE,
+            UA_SECURITY_PROTOCOL_3GPP_GENERATION_TMPI,
+            UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT,
+            UA_SECURITY_PROTOCOL_3GPP_TLS_BROWSER};
+
+    private int mOrg;
+    private int mProtocol;
+    private int mTlsCipherSuite;
+
+    private UaSecurityProtocolIdentifier() {}
+
+    private UaSecurityProtocolIdentifier(UaSecurityProtocolIdentifier sp) {
+        mOrg = sp.mOrg;
+        mProtocol = sp.mProtocol;
+        mTlsCipherSuite = sp.mTlsCipherSuite;
+    }
+
+    /**
+     * Returns the byte array representing the ua security protocol
+     */
+    @NonNull
+    public byte[] toByteArray() {
+        byte[] data = new byte[PROTOCOL_SIZE];
+        ByteBuffer buf = ByteBuffer.wrap(data);
+        buf.put((byte) mOrg);
+        buf.putInt(mProtocol | mTlsCipherSuite);
+        return data;
+    }
+
+    /**
+     * Returns the organization code
+     */
+    public @OrganizationCode int getOrg() {
+        return mOrg;
+    }
+
+    /**
+     * Returns the security procotol id
+     *
+     * <p>Note that only 3GPP UA Security Protocols are supported for now
+     */
+    public @UaSecurityProtocol3gpp int getProtocol() {
+        return mProtocol;
+    }
+
+    /**
+     * Returns the TLS cipher suite
+     */
+    public @TlsCipherSuite int getTlsCipherSuite() {
+        return mTlsCipherSuite;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mOrg);
+        out.writeInt(mProtocol);
+        out.writeInt(mTlsCipherSuite);
+    }
+
+    /**
+     * {@link Parcelable.Creator}
+     *
+     */
+    public static final @NonNull Parcelable.Creator<
+            UaSecurityProtocolIdentifier> CREATOR = new Creator<UaSecurityProtocolIdentifier>() {
+                @Nullable
+                @Override
+                public UaSecurityProtocolIdentifier createFromParcel(Parcel in) {
+                    int org = in.readInt();
+                    int protocol = in.readInt();
+                    int cs = in.readInt();
+                    if (org < 0 || protocol < 0 || cs < 0) {
+                        return null;
+                    }
+                    Builder builder = new Builder();
+                    try {
+                        if (org > 0) {
+                            builder.setOrg(org);
+                        }
+                        if (protocol > 0) {
+                            builder.setProtocol(protocol);
+                        }
+                        if (cs > 0) {
+                            builder.setTlsCipherSuite(cs);
+                        }
+                    } catch (IllegalArgumentException e) {
+                        return null;
+                    }
+                    return builder.build();
+                }
+
+                @NonNull
+                @Override
+                public UaSecurityProtocolIdentifier[] newArray(int size) {
+                    return new UaSecurityProtocolIdentifier[size];
+                }
+            };
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "UaSecurityProtocolIdentifier[" + mOrg + " , " + (mProtocol | mTlsCipherSuite) + "]";
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof UaSecurityProtocolIdentifier)) {
+            return false;
+        }
+
+        UaSecurityProtocolIdentifier other = (UaSecurityProtocolIdentifier) obj;
+
+        return mOrg == other.mOrg && mProtocol == other.mProtocol
+                && mTlsCipherSuite == other.mTlsCipherSuite;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mOrg, mProtocol, mTlsCipherSuite);
+    }
+
+    private boolean isTlsSupported() {
+        //TODO May update to support non 3gpp protocol in the future
+        if (mOrg == ORG_3GPP && (mProtocol == UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT
+                    || mProtocol == UA_SECURITY_PROTOCOL_3GPP_TLS_BROWSER)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Builder class for UaSecurityProtocolIdentifier
+     */
+    public static final class Builder {
+        private final UaSecurityProtocolIdentifier mSp;
+
+        /**
+         * Creates a Builder with default UaSecurityProtocolIdentifier, a.k.a 0x00 00 00 00 00
+         */
+        public Builder() {
+            mSp = new UaSecurityProtocolIdentifier();
+        }
+
+        /**
+         * Creates a Builder from a UaSecurityProtocolIdentifier
+         */
+        public Builder(@NonNull final UaSecurityProtocolIdentifier sp) {
+            Objects.requireNonNull(sp);
+            mSp = new UaSecurityProtocolIdentifier(sp);
+        }
+
+        /**
+         * Sets the organization code
+         *
+         * @param orgCode the organization code with the following value
+         * <ol>
+         * <li>{@link #ORG_NONE} </li>
+         * <li>{@link #ORG_3GPP} </li>
+         * <li>{@link #ORG_3GPP2} </li>
+         * <li>{@link #ORG_OMA} </li>
+         * <li>{@link #ORG_GSMA} </li>
+         * <li>{@link #ORG_LOCAL} </li>
+         * </ol>
+         * @throws IllegalArgumentException if it is not one of the value above.
+         *
+         * <p>Note that this method will reset the security protocol and TLS cipher suite
+         * if they have been set.
+         */
+        @NonNull
+        public Builder setOrg(@OrganizationCode int orgCode) {
+            if (orgCode < ORG_NONE || orgCode > ORG_LOCAL) {
+                throw new IllegalArgumentException("illegal organization code");
+            }
+            mSp.mOrg = orgCode;
+            mSp.mProtocol = 0;
+            mSp.mTlsCipherSuite = 0;
+            return this;
+        }
+
+        /**
+         * Sets the UA security protocol for 3GPP
+         *
+         * @param protocol only 3GPP ua security protocol ID is supported for now, which
+         * is one of the following value
+         * <ol>
+         * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_SUBSCRIBER_CERTIFICATE} </li>
+         * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_MBMS} </li>
+         * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_HTTP_DIGEST_AUTHENTICATION} </li>
+         * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_HTTP_BASED_MBMS} </li>
+         * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_SIP_BASED_MBMS} </li>
+         * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_GENERIC_PUSH_LAYER} </li>
+         * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_IMS_MEDIA_PLANE} </li>
+         * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_GENERATION_TMPI} </li>
+         * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT} </li>
+         * <li>{@link #UA_SECURITY_PROTOCOL_3GPP_TLS_BROWSER} </li>
+         * </ol>
+         * @throws IllegalArgumentException if the protocol is not one of the value above.
+         *
+         * <p>Note that this method will reset TLS cipher suite if it has been set.
+         */
+        @NonNull
+        public Builder setProtocol(@UaSecurityProtocol3gpp int protocol) {
+            //TODO May update to support non 3gpp protocol in the future
+            if (protocol < UA_SECURITY_PROTOCOL_3GPP_SUBSCRIBER_CERTIFICATE
+                    || (protocol > UA_SECURITY_PROTOCOL_3GPP_IMS_MEDIA_PLANE
+                    && protocol != UA_SECURITY_PROTOCOL_3GPP_GENERATION_TMPI
+                    && protocol != UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT
+                    && protocol != UA_SECURITY_PROTOCOL_3GPP_TLS_BROWSER)
+                    || mSp.mOrg != ORG_3GPP) {
+                throw new IllegalArgumentException("illegal protocol code");
+            }
+            mSp.mProtocol = protocol;
+            mSp.mTlsCipherSuite = 0;
+            return this;
+        }
+
+        /**
+         * Sets the UA security protocol for 3GPP
+         *
+         * @param cs TLS cipher suite value defined by {@link TlsParams#TlsCipherSuite}
+         * @throws IllegalArgumentException if it is not a 3GPP ua security protocol,
+         * the protocol does not support TLS, or does not support the cipher suite.
+         */
+        @NonNull
+        public Builder setTlsCipherSuite(@TlsCipherSuite int cs) {
+            if (!mSp.isTlsSupported()) {
+                throw new IllegalArgumentException("The protocol does not support TLS");
+            }
+            if (!TlsParams.isTlsCipherSuiteSupported(cs)) {
+                throw new IllegalArgumentException("TLS cipher suite is not supported");
+            }
+            mSp.mTlsCipherSuite = cs;
+            return this;
+        }
+
+        /**
+         * Builds the instance of UaSecurityProtocolIdentifier
+         *
+         * @return the built instance of UaSecurityProtocolIdentifier
+         */
+        @NonNull
+        public UaSecurityProtocolIdentifier build() {
+            return new UaSecurityProtocolIdentifier(mSp);
+        }
+    }
+}
diff --git a/android-35/android/telephony/gsm/GsmCellLocation.java b/android-35/android/telephony/gsm/GsmCellLocation.java
new file mode 100644
index 0000000..2eee4ce
--- /dev/null
+++ b/android-35/android/telephony/gsm/GsmCellLocation.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2006 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.gsm;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Bundle;
+import android.telephony.CellLocation;
+
+/**
+ * Represents the cell location on a GSM phone.
+ *
+ * @deprecated use {@link android.telephony.CellIdentity CellIdentity}.
+ */
+@Deprecated
+public class GsmCellLocation extends CellLocation {
+    private int mLac;
+    private int mCid;
+    private int mPsc;
+
+    /**
+     * Empty constructor.  Initializes the LAC and CID to -1.
+     */
+    public GsmCellLocation() {
+        mLac = -1;
+        mCid = -1;
+        mPsc = -1;
+    }
+
+    /**
+     * Initialize the object from a bundle.
+     */
+    public GsmCellLocation(Bundle bundle) {
+        mLac = bundle.getInt("lac", -1);
+        mCid = bundle.getInt("cid", -1);
+        mPsc = bundle.getInt("psc", -1);
+    }
+
+    /**
+     * @return gsm location area code, -1 if unknown, 0xffff max legal value
+     */
+    public int getLac() {
+        return mLac;
+    }
+
+    /**
+     * @return gsm cell id, -1 if unknown or invalid, 0xffff max legal value
+     */
+    public int getCid() {
+        return mCid;
+    }
+
+    /**
+     * On a UMTS network, returns the primary scrambling code of the serving
+     * cell.
+     *
+     * @return primary scrambling code for UMTS, -1 if unknown or GSM
+     */
+    public int getPsc() {
+        return mPsc;
+    }
+
+    /**
+     * Invalidate this object.  The location area code and the cell id are set to -1.
+     */
+    @Override
+    public void setStateInvalid() {
+        mLac = -1;
+        mCid = -1;
+        mPsc = -1;
+    }
+
+    /**
+     * Set the location area code and the cell id.
+     */
+    public void setLacAndCid(int lac, int cid) {
+        mLac = lac;
+        mCid = cid;
+    }
+
+    /**
+     * Set the primary scrambling code.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    public void setPsc(int psc) {
+        mPsc = psc;
+    }
+
+    @Override
+    public int hashCode() {
+        return mLac ^ mCid;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        GsmCellLocation s;
+
+        try {
+            s = (GsmCellLocation)o;
+        } catch (ClassCastException ex) {
+            return false;
+        }
+
+        if (o == null) {
+            return false;
+        }
+
+        return equalsHandlesNulls(mLac, s.mLac) && equalsHandlesNulls(mCid, s.mCid)
+            && equalsHandlesNulls(mPsc, s.mPsc);
+    }
+
+    @Override
+    public String toString() {
+        return "["+ mLac + "," + mCid + "," + mPsc + "]";
+    }
+
+    /**
+     * Test whether two objects hold the same data values or both are null
+     *
+     * @param a first obj
+     * @param b second obj
+     * @return true if two objects equal or both are null
+     */
+    private static boolean equalsHandlesNulls(Object a, Object b) {
+        return (a == null) ? (b == null) : a.equals (b);
+    }
+
+    /**
+     * Set intent notifier Bundle based on service state
+     *
+     * @param m intent notifier Bundle
+     */
+    public void fillInNotifierBundle(Bundle m) {
+        m.putInt("lac", mLac);
+        m.putInt("cid", mCid);
+        m.putInt("psc", mPsc);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean isEmpty() {
+        return (mLac == -1 && mCid == -1 && mPsc == -1);
+    }
+}
diff --git a/android-35/android/telephony/gsm/SmsManager.java b/android-35/android/telephony/gsm/SmsManager.java
new file mode 100644
index 0000000..777802a
--- /dev/null
+++ b/android-35/android/telephony/gsm/SmsManager.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2007 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.gsm;
+
+import android.app.PendingIntent;
+
+import java.util.ArrayList;
+
+
+/**
+ * Manages SMS operations such as sending data, text, and pdu SMS messages.
+ * Get this object by calling the static method SmsManager.getDefault().
+ * @deprecated Replaced by android.telephony.SmsManager that supports both GSM and CDMA.
+ */
+@Deprecated public final class SmsManager {
+    private static SmsManager sInstance;
+    private android.telephony.SmsManager mSmsMgrProxy;
+
+    /** Get the default instance of the SmsManager
+     *
+     * @return the default instance of the SmsManager
+     * @deprecated Use android.telephony.SmsManager.
+     */
+    @Deprecated
+    public static final SmsManager getDefault() {
+        if (sInstance == null) {
+            sInstance = new SmsManager();
+        }
+        return sInstance;
+    }
+
+    @Deprecated
+    private SmsManager() {
+        mSmsMgrProxy = android.telephony.SmsManager.getDefault();
+    }
+
+    /**
+     * Send a text based SMS.
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *  the current default SMSC
+     * @param text the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK<code> for success,
+     *  or one of these errors:
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
+     *  <code>RESULT_ERROR_RADIO_OFF</code>
+     *  <code>RESULT_ERROR_NULL_PDU</code>.
+     *  The per-application based SMS control checks sentIntent. If sentIntent
+     *  is NULL the caller will be checked against all unknown applications,
+     *  which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     *
+     * @throws IllegalArgumentException if destinationAddress or text are empty
+     * @deprecated Use android.telephony.SmsManager.
+     */
+    @Deprecated
+    public final void sendTextMessage(
+            String destinationAddress, String scAddress, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        mSmsMgrProxy.sendTextMessage(destinationAddress, scAddress, text,
+                sentIntent, deliveryIntent);
+    }
+
+    /**
+     * Divide a text message into several messages, none bigger than
+     * the maximum SMS message size.
+     *
+     * @param text the original message.  Must not be null.
+     * @return an <code>ArrayList</code> of strings that, in order,
+     *   comprise the original message
+     * @deprecated Use android.telephony.SmsManager.
+     */
+    @Deprecated
+    public final ArrayList<String> divideMessage(String text) {
+        return mSmsMgrProxy.divideMessage(text);
+    }
+
+    /**
+     * Send a multi-part text based SMS.  The callee should have already
+     * divided the message into correctly sized parts by calling
+     * <code>divideMessage</code>.
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *   the current default SMSC
+     * @param parts an <code>ArrayList</code> of strings that, in order,
+     *   comprise the original message
+     * @param sentIntents if not null, an <code>ArrayList</code> of
+     *   <code>PendingIntent</code>s (one for each message part) that is
+     *   broadcast when the corresponding message part has been sent.
+     *   The result code will be <code>Activity.RESULT_OK<code> for success,
+     *   or one of these errors:
+     *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
+     *   <code>RESULT_ERROR_RADIO_OFF</code>
+     *   <code>RESULT_ERROR_NULL_PDU</code>.
+     *   The per-application based SMS control checks sentIntent. If sentIntent
+     *   is NULL the caller will be checked against all unknown applicaitons,
+     *   which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntents if not null, an <code>ArrayList</code> of
+     *   <code>PendingIntent</code>s (one for each message part) that is
+     *   broadcast when the corresponding message part has been delivered
+     *   to the recipient.  The raw pdu of the status report is in the
+     *   extended data ("pdu").
+     *
+     * @throws IllegalArgumentException if destinationAddress or data are empty
+     * @deprecated Use android.telephony.SmsManager.
+     */
+    @Deprecated
+    public final void sendMultipartTextMessage(
+            String destinationAddress, String scAddress, ArrayList<String> parts,
+            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
+        mSmsMgrProxy.sendMultipartTextMessage(destinationAddress, scAddress, parts,
+                sentIntents, deliveryIntents);
+    }
+
+    /**
+     * Send a data based SMS to a specific application port.
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *  the current default SMSC
+     * @param destinationPort the port to deliver the message to
+     * @param data the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is sucessfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK<code> for success,
+     *  or one of these errors:
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
+     *  <code>RESULT_ERROR_RADIO_OFF</code>
+     *  <code>RESULT_ERROR_NULL_PDU</code>.
+     *  The per-application based SMS control checks sentIntent. If sentIntent
+     *  is NULL the caller will be checked against all unknown applicaitons,
+     *  which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     *
+     * @throws IllegalArgumentException if destinationAddress or data are empty
+     * @deprecated Use android.telephony.SmsManager.
+     */
+    @Deprecated
+    public final void sendDataMessage(
+            String destinationAddress, String scAddress, short destinationPort,
+            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        mSmsMgrProxy.sendDataMessage(destinationAddress, scAddress, destinationPort,
+                data, sentIntent, deliveryIntent);
+    }
+
+    /**
+     * Copy a raw SMS PDU to the SIM.
+     *
+     * @param smsc the SMSC for this message, or NULL for the default SMSC
+     * @param pdu the raw PDU to store
+     * @param status message status (STATUS_ON_SIM_READ, STATUS_ON_SIM_UNREAD,
+     *               STATUS_ON_SIM_SENT, STATUS_ON_SIM_UNSENT)
+     * @return true for success
+     * @deprecated Use android.telephony.SmsManager.
+     * {@hide}
+     */
+    @Deprecated
+    public final boolean copyMessageToSim(byte[] smsc, byte[] pdu, int status) {
+        return mSmsMgrProxy.copyMessageToIcc(smsc, pdu, status);
+    }
+
+    /**
+     * Delete the specified message from the SIM.
+     *
+     * @param messageIndex is the record index of the message on SIM
+     * @return true for success
+     * @deprecated Use android.telephony.SmsManager.
+     * {@hide}
+     */
+    @Deprecated
+    public final boolean deleteMessageFromSim(int messageIndex) {
+        return mSmsMgrProxy.deleteMessageFromIcc(messageIndex);
+    }
+
+    /**
+     * Update the specified message on the SIM.
+     *
+     * @param messageIndex record index of message to update
+     * @param newStatus new message status (STATUS_ON_SIM_READ,
+     *                  STATUS_ON_SIM_UNREAD, STATUS_ON_SIM_SENT,
+     *                  STATUS_ON_SIM_UNSENT, STATUS_ON_SIM_FREE)
+     * @param pdu the raw PDU to store
+     * @return true for success
+     * @deprecated Use android.telephony.SmsManager.
+     * {@hide}
+     */
+    @Deprecated
+    public final boolean updateMessageOnSim(int messageIndex, int newStatus, byte[] pdu) {
+        return mSmsMgrProxy.updateMessageOnIcc(messageIndex, newStatus, pdu);
+    }
+
+    /**
+     * Retrieves all messages currently stored on SIM.
+     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
+     * @deprecated Use android.telephony.SmsManager.
+     * {@hide}
+     */
+    @Deprecated
+    public final ArrayList<android.telephony.SmsMessage> getAllMessagesFromSim() {
+        return android.telephony.SmsManager.getDefault().getAllMessagesFromIcc();
+    }
+
+    /** Free space (TS 51.011 10.5.3).
+     *  @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int STATUS_ON_SIM_FREE      = 0;
+
+    /** Received and read (TS 51.011 10.5.3).
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int STATUS_ON_SIM_READ      = 1;
+
+    /** Received and unread (TS 51.011 10.5.3).
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int STATUS_ON_SIM_UNREAD    = 3;
+
+    /** Stored and sent (TS 51.011 10.5.3).
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int STATUS_ON_SIM_SENT      = 5;
+
+    /** Stored and unsent (TS 51.011 10.5.3).
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int STATUS_ON_SIM_UNSENT    = 7;
+
+    /** Generic failure cause
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int RESULT_ERROR_GENERIC_FAILURE    = 1;
+
+    /** Failed because radio was explicitly turned off
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int RESULT_ERROR_RADIO_OFF          = 2;
+
+    /** Failed because no pdu provided
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int RESULT_ERROR_NULL_PDU           = 3;
+
+    /** Failed because service is currently unavailable
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int RESULT_ERROR_NO_SERVICE         = 4;
+
+}
diff --git a/android-35/android/telephony/gsm/SmsMessage.java b/android-35/android/telephony/gsm/SmsMessage.java
new file mode 100644
index 0000000..8d5dac7
--- /dev/null
+++ b/android-35/android/telephony/gsm/SmsMessage.java
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2008 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.gsm;
+
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
+
+import java.util.Arrays;
+
+import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
+
+
+/**
+ * A Short Message Service message.
+ * @deprecated Replaced by android.telephony.SmsMessage that supports both GSM and CDMA.
+ */
+@Deprecated
+public class SmsMessage {
+    /**
+     * SMS Class enumeration.
+     * See TS 23.038.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public enum MessageClass{
+        UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
+    }
+
+    /** Unknown encoding scheme (see TS 23.038)
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int ENCODING_UNKNOWN = 0;
+
+    /** 7-bit encoding scheme (see TS 23.038)
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int ENCODING_7BIT = 1;
+
+    /** 8-bit encoding scheme (see TS 23.038)
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int ENCODING_8BIT = 2;
+
+    /** 16-bit encoding scheme (see TS 23.038)
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int ENCODING_16BIT = 3;
+
+    /** The maximum number of payload bytes per message
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int MAX_USER_DATA_BYTES = 140;
+
+    /**
+     * The maximum number of payload bytes per message if a user data header
+     * is present.  This assumes the header only contains the
+     * CONCATENATED_8_BIT_REFERENCE element.
+     *
+     * @deprecated Use android.telephony.SmsMessage.
+     * @hide pending API Council approval to extend the public API
+     */
+    @Deprecated public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134;
+
+    /** The maximum number of payload septets per message
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int MAX_USER_DATA_SEPTETS = 160;
+
+    /**
+     * The maximum number of payload septets per message if a user data header
+     * is present.  This assumes the header only contains the
+     * CONCATENATED_8_BIT_REFERENCE element.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
+
+    /** Contains actual SmsMessage. Only public for debugging and for framework layer.
+     * @deprecated Use android.telephony.SmsMessage.
+     * {@hide}
+     */
+    @Deprecated public SmsMessageBase mWrappedSmsMessage;
+
+    /** @deprecated Use android.telephony.SmsMessage. */
+    @Deprecated
+    public static class SubmitPdu {
+        /** @deprecated Use android.telephony.SmsMessage. */
+        @Deprecated public byte[] encodedScAddress; // Null if not applicable.
+        /** @deprecated Use android.telephony.SmsMessage. */
+        @Deprecated public byte[] encodedMessage;
+
+        //Constructor
+        /** @deprecated Use android.telephony.SmsMessage. */
+        @Deprecated
+        public SubmitPdu() {
+        }
+
+        /** @deprecated Use android.telephony.SmsMessage.
+         * {@hide}
+         */
+        @Deprecated
+        protected SubmitPdu(SubmitPduBase spb) {
+            this.encodedMessage = spb.encodedMessage;
+            this.encodedScAddress = spb.encodedScAddress;
+        }
+
+        /** @deprecated Use android.telephony.SmsMessage. */
+        @Override
+        @Deprecated
+        public String toString() {
+            return "SubmitPdu: encodedScAddress = "
+                    + Arrays.toString(encodedScAddress)
+                    + ", encodedMessage = "
+                    + Arrays.toString(encodedMessage);
+        }
+    }
+
+    // Constructor
+    /** @deprecated Use android.telephony.SmsMessage. */
+    @Deprecated
+    public SmsMessage() {
+        this(getSmsFacility());
+    }
+
+    private SmsMessage(SmsMessageBase smb) {
+        mWrappedSmsMessage = smb;
+    }
+
+    /**
+     * Create an SmsMessage from a raw PDU.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public static SmsMessage createFromPdu(byte[] pdu) {
+        SmsMessageBase wrappedMessage;
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
+        } else {
+            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
+        }
+
+        return new SmsMessage(wrappedMessage);
+    }
+
+    /**
+     * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
+     * length in bytes (not hex chars) less the SMSC header
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public static int getTPLayerLengthForPDU(String pdu) {
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            return com.android.internal.telephony.cdma.SmsMessage.getTPLayerLengthForPDU(pdu);
+        } else {
+            return com.android.internal.telephony.gsm.SmsMessage.getTPLayerLengthForPDU(pdu);
+        }
+    }
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and
+     * the number of characters remaining until the next message, given the
+     * current encoding.
+     *
+     * @param messageBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the GSM
+     *         alphabet are counted as a single space char.  If false, a
+     *         messageBody containing non-GSM alphabet characters is calculated
+     *         for 16-bit encoding.
+     * @return an int[4] with int[0] being the number of SMS's required, int[1]
+     *         the number of code units used, and int[2] is the number of code
+     *         units remaining until the next message. int[3] is the encoding
+     *         type that should be used for the message.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public static int[] calculateLength(CharSequence messageBody, boolean use7bitOnly) {
+        GsmAlphabet.TextEncodingDetails ted =
+                com.android.internal.telephony.gsm.SmsMessage
+                        .calculateLength(messageBody, use7bitOnly);
+        int ret[] = new int[4];
+        ret[0] = ted.msgCount;
+        ret[1] = ted.codeUnitCount;
+        ret[2] = ted.codeUnitsRemaining;
+        ret[3] = ted.codeUnitSize;
+        return ret;
+    }
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and
+     * the number of characters remaining until the next message, given the
+     * current encoding.
+     *
+     * @param messageBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the GSM
+     *         alphabet are counted as a single space char.  If false, a
+     *         messageBody containing non-GSM alphabet characters is calculated
+     *         for 16-bit encoding.
+     * @return an int[4] with int[0] being the number of SMS's required, int[1]
+     *         the number of code units used, and int[2] is the number of code
+     *         units remaining until the next message. int[3] is the encoding
+     *         type that should be used for the message.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public static int[] calculateLength(String messageBody, boolean use7bitOnly) {
+        return calculateLength((CharSequence)messageBody, use7bitOnly);
+    }
+
+    /**
+     * Get an SMS-SUBMIT PDU for a destination address and a message
+     *
+     * @param scAddress Service Centre address.  Null means use default.
+     * @return a <code>SubmitPdu</code> containing the encoded SC
+     *         address, if applicable, and the encoded message.
+     *         Returns null on encode error.
+     * @deprecated Use android.telephony.SmsMessage.
+     * @hide
+     */
+    @Deprecated
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, String message,
+            boolean statusReportRequested, byte[] header) {
+        SubmitPduBase spb;
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested,
+                    SmsHeader.fromByteArray(header));
+        } else {
+            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested, header);
+        }
+
+        return new SubmitPdu(spb);
+    }
+
+    /**
+     * Get an SMS-SUBMIT PDU for a destination address and a message
+     *
+     * @param scAddress Service Centre address.  Null means use default.
+     * @return a <code>SubmitPdu</code> containing the encoded SC
+     *         address, if applicable, and the encoded message.
+     *         Returns null on encode error.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, String message, boolean statusReportRequested) {
+        SubmitPduBase spb;
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested, null);
+        } else {
+            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested);
+        }
+
+        return new SubmitPdu(spb);
+    }
+
+    /**
+     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
+     *
+     * @param scAddress Service Centre address. null == use default
+     * @param destinationAddress the address of the destination for the message
+     * @param destinationPort the port to deliver the message to at the
+     *        destination
+     * @param data the dat for the message
+     * @return a <code>SubmitPdu</code> containing the encoded SC
+     *         address, if applicable, and the encoded message.
+     *         Returns null on encode error.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, short destinationPort, byte[] data,
+            boolean statusReportRequested) {
+        SubmitPduBase spb;
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, destinationPort, data, statusReportRequested);
+        } else {
+            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, destinationPort, data, statusReportRequested);
+        }
+
+        return new SubmitPdu(spb);
+    }
+
+    /**
+     * Returns the address of the SMS service center that relayed this message
+     * or null if there is none.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getServiceCenterAddress() {
+        return mWrappedSmsMessage.getServiceCenterAddress();
+    }
+
+    /**
+     * Returns the originating address (sender) of this SMS message in String
+     * form or null if unavailable
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getOriginatingAddress() {
+        return mWrappedSmsMessage.getOriginatingAddress();
+    }
+
+    /**
+     * Returns the originating address, or email from address if this message
+     * was from an email gateway. Returns null if originating address
+     * unavailable.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getDisplayOriginatingAddress() {
+        return mWrappedSmsMessage.getDisplayOriginatingAddress();
+    }
+
+    /**
+     * Returns the message body as a String, if it exists and is text based.
+     * @return message body is there is one, otherwise null
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getMessageBody() {
+        return mWrappedSmsMessage.getMessageBody();
+    }
+
+    /**
+     * Returns the class of this message.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public MessageClass getMessageClass() {
+        int index = mWrappedSmsMessage.getMessageClass().ordinal();
+
+        return MessageClass.values()[index];
+    }
+
+    /**
+     * Returns the message body, or email message body if this message was from
+     * an email gateway. Returns null if message body unavailable.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getDisplayMessageBody() {
+        return mWrappedSmsMessage.getDisplayMessageBody();
+    }
+
+    /**
+     * Unofficial convention of a subject line enclosed in parens empty string
+     * if not present
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getPseudoSubject() {
+        return mWrappedSmsMessage.getPseudoSubject();
+    }
+
+    /**
+     * Returns the service centre timestamp in currentTimeMillis() format
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public long getTimestampMillis() {
+        return mWrappedSmsMessage.getTimestampMillis();
+    }
+
+    /**
+     * Returns true if message is an email.
+     *
+     * @return true if this message came through an email gateway and email
+     *         sender / subject / parsed body are available
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isEmail() {
+        return mWrappedSmsMessage.isEmail();
+    }
+
+     /**
+     * @return if isEmail() is true, body of the email sent through the gateway.
+     *         null otherwise
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getEmailBody() {
+        return mWrappedSmsMessage.getEmailBody();
+    }
+
+    /**
+     * @return if isEmail() is true, email from address of email sent through
+     *         the gateway. null otherwise
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getEmailFrom() {
+        return mWrappedSmsMessage.getEmailFrom();
+    }
+
+    /**
+     * Get protocol identifier.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public int getProtocolIdentifier() {
+        return mWrappedSmsMessage.getProtocolIdentifier();
+    }
+
+    /**
+     * See TS 23.040 9.2.3.9 returns true if this is a "replace short message" SMS
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isReplace() {
+        return mWrappedSmsMessage.isReplace();
+    }
+
+    /**
+     * Returns true for CPHS MWI toggle message.
+     *
+     * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section B.4.2
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isCphsMwiMessage() {
+        return mWrappedSmsMessage.isCphsMwiMessage();
+    }
+
+    /**
+     * returns true if this message is a CPHS voicemail / message waiting
+     * indicator (MWI) clear message
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isMWIClearMessage() {
+        return mWrappedSmsMessage.isMWIClearMessage();
+    }
+
+    /**
+     * returns true if this message is a CPHS voicemail / message waiting
+     * indicator (MWI) set message
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isMWISetMessage() {
+        return mWrappedSmsMessage.isMWISetMessage();
+    }
+
+    /**
+     * returns true if this message is a "Message Waiting Indication Group:
+     * Discard Message" notification and should not be stored.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isMwiDontStore() {
+        return mWrappedSmsMessage.isMwiDontStore();
+    }
+
+    /**
+     * returns the user data section minus the user data header if one was present.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public byte[] getUserData() {
+        return mWrappedSmsMessage.getUserData();
+    }
+
+    /* Not part of the SDK interface and only needed by specific classes:
+       protected SmsHeader getUserDataHeader()
+    */
+
+    /**
+     * Returns the raw PDU for the message.
+     *
+     * @return the raw PDU for the message.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public byte[] getPdu() {
+        return mWrappedSmsMessage.getPdu();
+    }
+
+    /**
+     * Returns the status of the message on the SIM (read, unread, sent, unsent).
+     *
+     * @return the status of the message on the SIM.  These are:
+     *         SmsManager.STATUS_ON_SIM_FREE
+     *         SmsManager.STATUS_ON_SIM_READ
+     *         SmsManager.STATUS_ON_SIM_UNREAD
+     *         SmsManager.STATUS_ON_SIM_SEND
+     *         SmsManager.STATUS_ON_SIM_UNSENT
+     * @deprecated Use android.telephony.SmsMessage and getStatusOnIcc instead.
+     */
+    @Deprecated
+    public int getStatusOnSim() {
+        return mWrappedSmsMessage.getStatusOnIcc();
+    }
+
+    /**
+     * Returns the status of the message on the ICC (read, unread, sent, unsent).
+     *
+     * @return the status of the message on the ICC.  These are:
+     *         SmsManager.STATUS_ON_ICC_FREE
+     *         SmsManager.STATUS_ON_ICC_READ
+     *         SmsManager.STATUS_ON_ICC_UNREAD
+     *         SmsManager.STATUS_ON_ICC_SEND
+     *         SmsManager.STATUS_ON_ICC_UNSENT
+     * @deprecated Use android.telephony.SmsMessage.
+     * @hide
+     */
+    @Deprecated
+    public int getStatusOnIcc() {
+
+        return mWrappedSmsMessage.getStatusOnIcc();
+    }
+
+    /**
+     * Returns the record index of the message on the SIM (1-based index).
+     * @return the record index of the message on the SIM, or -1 if this
+     *         SmsMessage was not created from a SIM SMS EF record.
+     * @deprecated Use android.telephony.SmsMessage and getIndexOnIcc instead.
+     */
+    @Deprecated
+    public int getIndexOnSim() {
+        return mWrappedSmsMessage.getIndexOnIcc();
+    }
+
+    /**
+     * Returns the record index of the message on the ICC (1-based index).
+     * @return the record index of the message on the ICC, or -1 if this
+     *         SmsMessage was not created from a ICC SMS EF record.
+     * @deprecated Use android.telephony.SmsMessage.
+     * @hide
+     */
+    @Deprecated
+    public int getIndexOnIcc() {
+
+        return mWrappedSmsMessage.getIndexOnIcc();
+    }
+
+    /**
+     * GSM:
+     * For an SMS-STATUS-REPORT message, this returns the status field from
+     * the status report.  This field indicates the status of a previously
+     * submitted SMS, if requested.  See TS 23.040, 9.2.3.15 TP-Status for a
+     * description of values.
+     * CDMA:
+     * For not interfering with status codes from GSM, the value is
+     * shifted to the bits 31-16.
+     * The value is composed of an error class (bits 25-24) and a status code (bits 23-16).
+     * Possible codes are described in C.S0015-B, v2.0, 4.5.21.
+     *
+     * @return 0 indicates the previously sent message was received.
+     *         See TS 23.040, 9.9.2.3.15 and C.S0015-B, v2.0, 4.5.21
+     *         for a description of other possible values.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public int getStatus() {
+        return mWrappedSmsMessage.getStatus();
+    }
+
+    /**
+     * Return true iff the message is a SMS-STATUS-REPORT message.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isStatusReportMessage() {
+        return mWrappedSmsMessage.isStatusReportMessage();
+    }
+
+    /**
+     * Returns true iff the <code>TP-Reply-Path</code> bit is set in
+     * this message.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isReplyPathPresent() {
+        return mWrappedSmsMessage.isReplyPathPresent();
+    }
+
+    /** This method returns the reference to a specific
+     *  SmsMessage object, which is used for accessing its static methods.
+     * @return Specific SmsMessage.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    private static final SmsMessageBase getSmsFacility(){
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+        if (PHONE_TYPE_CDMA == activePhone) {
+            return new com.android.internal.telephony.cdma.SmsMessage();
+        } else {
+            return new com.android.internal.telephony.gsm.SmsMessage();
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/AudioCodecAttributes.java b/android-35/android/telephony/ims/AudioCodecAttributes.java
new file mode 100644
index 0000000..7b6ab00
--- /dev/null
+++ b/android-35/android/telephony/ims/AudioCodecAttributes.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Range;
+
+/**
+ * Parcelable object to handle audio codec attributes.
+ * It provides the audio codec bitrate, bandwidth and their upper/lower bound.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AudioCodecAttributes implements Parcelable {
+    // The audio codec bitrate in kbps.
+    private float mBitrateKbps;
+    // The range of the audio codec bitrate in kbps.
+    private Range<Float> mBitrateRangeKbps;
+    // The audio codec bandwidth in kHz.
+    private float mBandwidthKhz;
+    // The range of the audio codec bandwidth in kHz.
+    private Range<Float> mBandwidthRangeKhz;
+
+
+    /**
+     * Constructor.
+     *
+     * @param bitrateKbps        The audio codec bitrate in kbps.
+     * @param bitrateRangeKbps  The range of the audio codec bitrate in kbps.
+     * @param bandwidthKhz      The audio codec bandwidth in kHz.
+     * @param bandwidthRangeKhz The range of the audio codec bandwidth in kHz.
+     */
+
+    public AudioCodecAttributes(float bitrateKbps, @NonNull Range<Float> bitrateRangeKbps,
+            float bandwidthKhz, @NonNull Range<Float> bandwidthRangeKhz) {
+        mBitrateKbps = bitrateKbps;
+        mBitrateRangeKbps = bitrateRangeKbps;
+        mBandwidthKhz = bandwidthKhz;
+        mBandwidthRangeKhz = bandwidthRangeKhz;
+    }
+
+    private AudioCodecAttributes(Parcel in) {
+        mBitrateKbps = in.readFloat();
+        mBitrateRangeKbps = new Range<>(in.readFloat(), in.readFloat());
+        mBandwidthKhz = in.readFloat();
+        mBandwidthRangeKhz = new Range<>(in.readFloat(), in.readFloat());
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeFloat(mBitrateKbps);
+        out.writeFloat(mBitrateRangeKbps.getLower());
+        out.writeFloat(mBitrateRangeKbps.getUpper());
+        out.writeFloat(mBandwidthKhz);
+        out.writeFloat(mBandwidthRangeKhz.getLower());
+        out.writeFloat(mBandwidthRangeKhz.getUpper());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Creator<AudioCodecAttributes> CREATOR =
+            new Creator<AudioCodecAttributes>() {
+                @Override
+                public AudioCodecAttributes createFromParcel(Parcel in) {
+                    return new AudioCodecAttributes(in);
+                }
+
+                @Override
+                public AudioCodecAttributes[] newArray(int size) {
+                    return new AudioCodecAttributes[size];
+                }
+            };
+
+    /**
+     * @return the exact value of the audio codec bitrate in kbps.
+     */
+    public float getBitrateKbps() {
+        return mBitrateKbps;
+    }
+
+    /**
+     * @return the range of the audio codec bitrate in kbps
+     */
+    public @NonNull Range<Float> getBitrateRangeKbps() {
+        return mBitrateRangeKbps;
+    }
+
+    /**
+     * @return the exact value of the audio codec bandwidth in kHz.
+     */
+    public float getBandwidthKhz() {
+        return mBandwidthKhz;
+    }
+
+    /**
+     * @return the range of the audio codec bandwidth in kHz.
+     */
+    public @NonNull Range<Float> getBandwidthRangeKhz() {
+        return mBandwidthRangeKhz;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "{ bitrateKbps=" + mBitrateKbps
+                + ", bitrateRangeKbps=" + mBitrateRangeKbps
+                + ", bandwidthKhz=" + mBandwidthKhz
+                + ", bandwidthRangeKhz=" + mBandwidthRangeKhz + " }";
+    }
+}
diff --git a/android-35/android/telephony/ims/DelegateMessageCallback.java b/android-35/android/telephony/ims/DelegateMessageCallback.java
new file mode 100644
index 0000000..a008cfd
--- /dev/null
+++ b/android-35/android/telephony/ims/DelegateMessageCallback.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.telephony.ims.stub.SipDelegate;
+
+/**
+ * Callback interface provided to the SipTransport implementation to notify a remote application of
+ * the following:
+ * <ul>
+ *     <li>A new incoming SIP message associated with the feature tags the SipDelegate registered
+ *     with has been received or an in-dialog request to this SipDelegate has been received.</li>
+ *     <li>Acknowledge that an outgoing SIP message from the RCS application has been sent
+ *     successfully or notify the application of the reason why it was not sent</li>
+ * </ul>
+ * @hide
+ */
+@SystemApi
+public interface DelegateMessageCallback {
+
+    /**
+     * Sends a new incoming SIP message to the remote application for processing.
+     */
+    void onMessageReceived(@NonNull SipMessage message);
+
+    /**
+     * Notifies the remote application that a previous request to send a SIP message using
+     * {@link SipDelegate#sendMessage} has succeeded.
+     *
+     * @param viaTransactionId The transaction ID found in the via header field of the
+     *         previously sent {@link SipMessage}.
+     */
+    void onMessageSent(@NonNull String viaTransactionId);
+
+    /**
+     * Notifies the remote application that a previous request to send a SIP message using
+     * {@link SipDelegate#sendMessage} has failed.
+     *
+     * @param viaTransactionId The Transaction ID found in the via header field of the previously
+     *         sent {@link SipMessage}.
+     * @param reason The reason for the failure.
+     */
+    void onMessageSendFailure(@NonNull String viaTransactionId,
+            @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/android-35/android/telephony/ims/DelegateRegistrationState.java b/android-35/android/telephony/ims/DelegateRegistrationState.java
new file mode 100644
index 0000000..c2c9497
--- /dev/null
+++ b/android-35/android/telephony/ims/DelegateRegistrationState.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Contains the full state of the IMS feature tags associated with a SipDelegate and managed by the
+ * ImsService.
+ * @hide
+ */
+@SystemApi
+public final class DelegateRegistrationState implements Parcelable {
+
+    /**
+     * This feature tag has been deregistered for an unknown reason. Outgoing out-of-dialog SIP
+     * messages associated with feature tags that are not registered will fail.
+     */
+    public static final int DEREGISTERED_REASON_UNKNOWN = 0;
+
+    /**
+     * This feature tag has been deregistered because it is not provisioned to be used on this radio
+     * access technology or PDN. Outgoing out-of-dialog SIP messages associated with feature tags
+     * that are not registered will fail.
+     * <p>
+     * There may be new incoming SIP dialog requests on a feature that that is not provisioned. It
+     * is still expected that the SipDelegateConnection responds to the request.
+     */
+    public static final int DEREGISTERED_REASON_NOT_PROVISIONED = 1;
+
+    /**
+     * This feature tag has been deregistered because IMS has been deregistered. All outgoing SIP
+     * messages will fail until IMS registration occurs.
+     */
+    public static final int DEREGISTERED_REASON_NOT_REGISTERED = 2;
+
+    /**
+     * This feature tag is being deregistered because the PDN that the IMS registration is on is
+     *changing.
+     * All open SIP dialogs need to be closed before the PDN change can proceed using
+     * {@link SipDelegateConnection#cleanupSession(String)}.
+     */
+    public static final int DEREGISTERING_REASON_PDN_CHANGE = 3;
+
+    /**
+     * This feature tag is being deregistered due to a provisioning change. This can be triggered by
+     * many things, such as a provisioning change triggered by the carrier network, a radio access
+     * technology change by the modem causing a different set of feature tags to be provisioned, or
+     * a user triggered hange, such as data being enabled/disabled.
+     * <p>
+     * All open SIP dialogs associated with the new deprovisioned feature tag need to be closed
+     * using {@link SipDelegateConnection#cleanupSession(String)} before the IMS registration
+     * modification can proceed.
+     */
+    public static final int DEREGISTERING_REASON_PROVISIONING_CHANGE = 4;
+
+    /**
+     * This feature tag is deregistering because the SipDelegate associated with this feature tag
+     * needs to change its supported feature set.
+     * <p>
+     * All open SIP Dialogs associated with this feature tag must be  closed
+     * using {@link SipDelegateConnection#cleanupSession(String)} before this operation can proceed.
+     */
+    public static final int DEREGISTERING_REASON_FEATURE_TAGS_CHANGING = 5;
+
+    /**
+     * This feature tag is deregistering because the SipDelegate is in the process of being
+     * destroyed.
+     * <p>
+     * All open SIP Dialogs associated with this feature tag must be closed
+     * using {@link SipDelegateConnection#cleanupSession(String)} before this operation can proceed.
+     */
+    public static final int DEREGISTERING_REASON_DESTROY_PENDING = 6;
+
+    /**
+     * This feature tag is deregistering because the PDN that the IMS registration is on
+     * is being torn down.
+     * <p>
+     * All open SIP Dialogs associated with this feature tag must be  closed
+     * using {@link SipDelegateConnection#cleanupSession(String)} before this operation can proceed.
+     */
+    public static final int DEREGISTERING_REASON_LOSING_PDN = 7;
+
+    /**
+     * This feature tag is deregistering because of an unspecified reason.
+     * <p>
+     * All open SIP Dialogs associated with this feature tag must be  closed
+     * using {@link SipDelegateConnection#cleanupSession(String)} before this operation can proceed.
+     */
+    public static final int DEREGISTERING_REASON_UNSPECIFIED = 8;
+
+/** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "DEREGISTERED_REASON_", value = {
+            DEREGISTERED_REASON_UNKNOWN,
+            DEREGISTERED_REASON_NOT_PROVISIONED,
+            DEREGISTERED_REASON_NOT_REGISTERED
+    })
+    public @interface DeregisteredReason {}
+
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "DEREGISTERING_REASON_", value = {
+            DEREGISTERING_REASON_PDN_CHANGE,
+            DEREGISTERING_REASON_PROVISIONING_CHANGE,
+            DEREGISTERING_REASON_FEATURE_TAGS_CHANGING,
+            DEREGISTERING_REASON_DESTROY_PENDING,
+            DEREGISTERING_REASON_LOSING_PDN,
+            DEREGISTERING_REASON_UNSPECIFIED
+    })
+    public @interface DeregisteringReason {}
+
+    private ArraySet<String> mRegisteringTags = new ArraySet<>();
+    private ArraySet<String> mRegisteredTags = new ArraySet<>();
+    private final ArraySet<FeatureTagState> mDeregisteringTags = new ArraySet<>();
+    private final ArraySet<FeatureTagState> mDeregisteredTags = new ArraySet<>();
+
+    /**
+     * Builder used to create new instances of {@link DelegateRegistrationState}.
+     */
+    public static final class Builder {
+
+        private final DelegateRegistrationState mState;
+
+        /* Create a new instance of {@link Builder} */
+        public Builder() {
+            mState = new DelegateRegistrationState();
+        }
+
+        /**
+         * Add the set of feature tags that are associated with this SipDelegate and
+         * the IMS stack is actively trying to register on the carrier network.
+         *
+         * The feature tags will either move to the registered or deregistered state
+         * depending on the result of the registration.
+         * @param featureTags The IMS media feature tags that are in the progress of registering.
+         * @return The in-progress Builder instance for RegistrationState. ]
+         */
+        public @NonNull Builder addRegisteringFeatureTags(@NonNull Set<String> featureTags) {
+            mState.mRegisteringTags.addAll(featureTags);
+            return this;
+        }
+
+        /**
+         * Add a feature tag that is currently included in the current network IMS Registration.
+         * @param featureTag The IMS media feature tag included in the current IMS registration.
+         * @return The in-progress Builder instance for RegistrationState.
+         */
+        public @NonNull Builder addRegisteredFeatureTag(@NonNull String featureTag) {
+            mState.mRegisteredTags.add(featureTag);
+            return this;
+        }
+
+        /**
+         * Add a list of feature tags that are currently included in the current network IMS
+         * Registration.
+         * @param featureTags The IMS media feature tags included in the current IMS registration.
+         * @return The in-progress Builder instance for RegistrationState.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder addRegisteredFeatureTags(@NonNull Set<String> featureTags) {
+            mState.mRegisteredTags.addAll(featureTags);
+            return this;
+        }
+
+        /**
+         * Add a feature tag that is in the current network IMS Registration, but is in the progress
+         * of being deregistered and requires action from the RCS application before the IMS
+         * registration can be modified.
+         *
+         * See {@link DeregisteringReason} for more information regarding what is required by the
+         * RCS application to proceed.
+         *
+         * @param featureTag The media feature tag that has limited or no availability due to its
+         *         current deregistering state.
+         * @param reason The reason why the media feature tag has moved to the deregistering state.
+         *         The availability of the feature tag depends on the {@link DeregisteringReason}.
+         * @return The in-progress Builder instance for RegistrationState.
+         */
+        public @NonNull Builder addDeregisteringFeatureTag(@NonNull String featureTag,
+                @DeregisteringReason int reason) {
+            mState.mDeregisteringTags.add(new FeatureTagState(featureTag, reason));
+            return this;
+        }
+
+        /**
+         * Add a feature tag that is currently not included in the network RCS registration. See
+         * {@link DeregisteredReason} for more information regarding the reason for why the feature
+         * tag is not registered.
+         * @param featureTag The media feature tag that is not registered.
+         * @param reason The reason why the media feature tag has been deregistered.
+         * @return The in-progress Builder instance for RegistrationState.
+         */
+        public @NonNull Builder addDeregisteredFeatureTag(@NonNull String featureTag,
+                @DeregisteredReason int reason) {
+            mState.mDeregisteredTags.add(new FeatureTagState(featureTag, reason));
+            return this;
+        }
+
+        /**
+         * @return the finalized instance.
+         */
+        public @NonNull DelegateRegistrationState build() {
+            return mState;
+        }
+    }
+
+    /**
+     * The builder should be used to construct a new instance of this class.
+     */
+    private DelegateRegistrationState() {}
+
+    /**
+     * Used for unparcelling only.
+     */
+    private DelegateRegistrationState(Parcel source) {
+        mRegisteredTags = (ArraySet<String>) source.readArraySet(null);
+        readStateFromParcel(source, mDeregisteringTags);
+        readStateFromParcel(source, mDeregisteredTags);
+        mRegisteringTags = (ArraySet<String>) source.readArraySet(null);
+    }
+
+    /**
+     * Get the feature tags that are associated with this SipDelegate that the IMS stack is actively
+     * trying to register on the carrier network.
+     * @return A Set of feature tags associated with this SipDelegate that the IMS service is
+     * currently trying to register on the  carrier network.
+     */
+    public @NonNull Set<String> getRegisteringFeatureTags() {
+        return new ArraySet<>(mRegisteringTags);
+    }
+
+    /**
+     * Get the feature tags that this SipDelegate is associated with that are currently part of the
+     * network IMS registration. SIP Messages both in and out of a SIP Dialog may be sent and
+     * received using these feature tags.
+     * @return A Set of feature tags that the SipDelegate has associated with that are included in
+     * the network IMS registration.
+     */
+    public @NonNull Set<String> getRegisteredFeatureTags() {
+        return new ArraySet<>(mRegisteredTags);
+    }
+
+    /**
+     * Get the feature tags that this SipDelegate is associated with that are currently part of the
+     * network IMS registration but are in the process of being deregistered.
+     * <p>
+     * Any incoming SIP messages associated with a feature tag included in this list will still be
+     * delivered. Outgoing SIP messages that are still in-dialog will be delivered to the
+     * SipDelegate, but outgoing out-of-dialog SIP messages with  a feature tag that is included in
+     * this list will fail.
+     * <p>
+     * The SipDelegate will stay in this state for a limited period of time while it waits for the
+     * RCS application to perform a specific action. More details on the actions that can cause this
+     * state as well as the expected response are included in the reason codes and can be found in
+     * {@link DeregisteringReason}.
+     * @return A Set of feature tags that the SipDelegate has associated with that are included in
+     * the network IMS registration but are in the process of deregistering.
+     */
+    public @NonNull Set<FeatureTagState> getDeregisteringFeatureTags() {
+        return new ArraySet<>(mDeregisteringTags);
+    }
+
+    /**
+     * Get the list of feature tags that are associated with this SipDelegate but are not currently
+     * included in the network IMS registration.
+     * <p>
+     * See {@link DeregisteredReason} codes for more information related to the reasons why this may
+     * occur.
+     * <p>
+     * Due to network race conditions, there may still be onditions where an incoming out-of-dialog
+     * SIP message is delivered for a feature tag that is considered deregistered. Due to this
+     * condition, in-dialog outgoing SIP messages for deregistered feature tags will still be
+     * allowed as long as they are in response to a dialog started by a remote party. Any outgoing
+     * out-of-dialog SIP messages associated with feature tags included in this list will fail to be
+     * sent.
+     * @return A list of feature tags that the SipDelegate has associated with that not included in
+     * the network IMS registration.
+     */
+    public @NonNull Set<FeatureTagState> getDeregisteredFeatureTags() {
+        return new ArraySet<>(mDeregisteredTags);
+    }
+
+
+    public static final @NonNull Creator<DelegateRegistrationState> CREATOR =
+            new Creator<DelegateRegistrationState>() {
+        @Override
+        public DelegateRegistrationState createFromParcel(Parcel source) {
+            return new DelegateRegistrationState(source);
+        }
+
+        @Override
+        public DelegateRegistrationState[] newArray(int size) {
+            return new DelegateRegistrationState[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeArraySet(mRegisteredTags);
+        writeStateToParcel(dest, mDeregisteringTags);
+        writeStateToParcel(dest, mDeregisteredTags);
+        dest.writeArraySet(mRegisteringTags);
+    }
+
+    private void writeStateToParcel(Parcel dest, Set<FeatureTagState> state) {
+        dest.writeInt(state.size());
+        for (FeatureTagState s : state) {
+            dest.writeString(s.getFeatureTag());
+            dest.writeInt(s.getState());
+        }
+    }
+
+    private void readStateFromParcel(Parcel source, Set<FeatureTagState> emptyState) {
+        int len = source.readInt();
+        for (int i = 0; i < len; i++) {
+            String ft = source.readString();
+            int reason = source.readInt();
+
+            emptyState.add(new FeatureTagState(ft, reason));
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DelegateRegistrationState that = (DelegateRegistrationState) o;
+        return mRegisteringTags.equals(that.mRegisteringTags)
+                && mRegisteredTags.equals(that.mRegisteredTags)
+                && mDeregisteringTags.equals(that.mDeregisteringTags)
+                && mDeregisteredTags.equals(that.mDeregisteredTags);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRegisteringTags, mRegisteredTags,
+                mDeregisteringTags, mDeregisteredTags);
+    }
+
+    @Override
+    public String toString() {
+        return "DelegateRegistrationState{ registered={" + mRegisteredTags
+                + "}, registering={" + mRegisteringTags
+                + "}, deregistering={" + mDeregisteringTags + "}, deregistered={"
+                + mDeregisteredTags + "}}";
+    }
+}
diff --git a/android-35/android/telephony/ims/DelegateRequest.java b/android-35/android/telephony/ims/DelegateRequest.java
new file mode 100644
index 0000000..c5c9200
--- /dev/null
+++ b/android-35/android/telephony/ims/DelegateRequest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Contains information required for the creation of a {@link SipDelegate} and the associated
+ * SipDelegateConnection given back to the requesting application.
+ * @hide
+ */
+@SystemApi
+public final class DelegateRequest implements Parcelable {
+
+    private final ArrayList<String> mFeatureTags;
+
+    /**
+     * Create a new DelegateRequest, which will be used to create a SipDelegate by the ImsService.
+     * @param featureTags The list of IMS feature tags that will be associated with the SipDelegate
+     *                    created using this DelegateRequest. All feature tags are expected to be in
+     *                    the format defined in RCC.07 section 2.6.1.3.
+     */
+    public DelegateRequest(@NonNull Set<String> featureTags) {
+        if (featureTags == null) {
+            throw new IllegalStateException("Invalid arguments, featureTags List can not be null");
+        }
+        mFeatureTags = new ArrayList<>(featureTags);
+    }
+
+    /**
+     * @return the list of IMS feature tag associated with this DelegateRequest in the format
+     * defined in RCC.07 section 2.6.1.3.
+     */
+    public @NonNull Set<String> getFeatureTags() {
+        return new ArraySet<>(mFeatureTags);
+    }
+
+    /**
+     * Internal constructor used only for unparcelling.
+     */
+    private DelegateRequest(Parcel in) {
+        mFeatureTags = new ArrayList<>();
+        in.readList(mFeatureTags, null /*classLoader*/, java.lang.String.class);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeList(mFeatureTags);
+    }
+
+    public static final @NonNull Creator<DelegateRequest> CREATOR = new Creator<DelegateRequest>() {
+        @Override
+        public DelegateRequest createFromParcel(Parcel source) {
+            return new DelegateRequest(source);
+        }
+
+        @Override
+        public DelegateRequest[] newArray(int size) {
+            return new DelegateRequest[size];
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DelegateRequest that = (DelegateRequest) o;
+        return mFeatureTags.equals(that.mFeatureTags);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mFeatureTags);
+    }
+
+    @Override
+    public String toString() {
+        return "DelegateRequest{mFeatureTags=" + mFeatureTags + '}';
+    }
+}
diff --git a/android-35/android/telephony/ims/DelegateStateCallback.java b/android-35/android/telephony/ims/DelegateStateCallback.java
new file mode 100644
index 0000000..734b520
--- /dev/null
+++ b/android-35/android/telephony/ims/DelegateStateCallback.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.telephony.ims.stub.SipDelegate;
+import android.telephony.ims.stub.SipTransportImplBase;
+
+import java.util.Set;
+
+/**
+ * Callback interface to notify a remote application of the following:
+ * <ul>
+ *     <li>the {@link SipDelegate} associated with this callback has been created or destroyed in
+ *         response to a creation or destruction request from the framework</li>
+ *     <li>the SIP IMS configuration associated with this {@link SipDelegate} has changed</li>
+ *     <li>the IMS registration of the feature tags associated with this {@link SipDelegate} have
+ *         changed.</li>
+ * </ul>
+ * @hide
+ */
+@SystemApi
+public interface DelegateStateCallback {
+
+    /**
+     * This must be called by the ImsService after {@link SipTransportImplBase#createSipDelegate} is
+     * called by the framework to notify the framework and remote application that the
+     * {@link SipDelegate} has been successfully created.
+     *  @param delegate The SipDelegate created to service the DelegateRequest.
+     * @param deniedTags A Set of {@link FeatureTagState}s, which contain the feature tags
+     *    associated with this {@link SipDelegate} that have no access to send/receive SIP messages
+     *    as well as a reason for why the feature tag is denied. For more information on the reason
+     *    why the feature tag was denied access, see the
+     *    {@link SipDelegateManager.DeniedReason} reasons. This is considered a permanent denial due
+     *    to this {@link SipDelegate} not supporting a feature or this ImsService already
+     *    implementing this feature elsewhere. If all features of this {@link SipDelegate} are
+     *    denied, this method should still be called.
+     */
+    void onCreated(@NonNull SipDelegate delegate,
+            @SuppressLint("NullableCollection")  // TODO(b/154763999): Mark deniedTags @Nonnull
+            @Nullable Set<FeatureTagState> deniedTags);
+
+    /**
+     * This must be called by the ImsService after the framework calls
+     * {@link SipTransportImplBase#destroySipDelegate} to notify the framework and remote
+     * application that the procedure to destroy the {@link SipDelegate} has been completed.
+     * @param reasonCode The reason for closing this delegate.
+     */
+    void onDestroyed(@SipDelegateManager.SipDelegateDestroyReason int reasonCode);
+
+    /**
+     * Call to notify the remote application of a configuration change associated with this
+     * {@link SipDelegate}.
+     * <p>
+     * The remote application will not be able to proceed sending SIP messages until after this
+     * configuration is sent the first time, so this configuration should be sent as soon as the
+     * {@link SipDelegate} has access to these configuration parameters.
+     * <p>
+     * Incoming SIP messages should not be routed to the remote application until AFTER this
+     * configuration change is sent to ensure that the remote application can respond correctly.
+     * Similarly, if there is an event that triggers the IMS configuration to change, incoming SIP
+     * messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration
+     * change event to reduce conditions where the remote application is using a stale IMS
+     * configuration.
+     * @removed This is being removed from API surface, Use
+     * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead.
+     */
+    @Deprecated
+    void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration config);
+
+    /**
+     * Call to notify the remote application of a configuration change associated with this
+     * {@link SipDelegate}.
+     * <p>
+     * The remote application will not be able to proceed sending SIP messages until after this
+     * configuration is sent the first time, so this configuration should be sent as soon as the
+     * {@link SipDelegate} has access to these configuration parameters.
+     * <p>
+     * Incoming SIP messages should not be routed to the remote application until AFTER this
+     * configuration change is sent to ensure that the remote application can respond correctly.
+     * Similarly, if there is an event that triggers the IMS configuration to change, incoming SIP
+     * messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration
+     * change event to reduce conditions where the remote application is using a stale IMS
+     * configuration.
+     */
+    void onConfigurationChanged(@NonNull SipDelegateConfiguration config);
+
+    /**
+     * Call to notify the remote application that the {@link SipDelegate} has modified the IMS
+     * registration state of the RCS feature tags that were requested as part of the initial
+     * {@link DelegateRequest}.
+     * <p>
+     * See {@link DelegateRegistrationState} for more information about how IMS Registration state
+     * should be communicated the associated SipDelegateConnection in cases such as
+     * IMS deregistration, handover, PDN change, provisioning changes, etc…
+     * <p>
+     * Note: Even after the status of the feature tags are updated here to deregistered, the
+     * SipDelegate must still be able to handle these messages and call
+     * {@link DelegateMessageCallback#onMessageSendFailure} to notify the RCS application that the
+     * message was not sent.
+     *
+     * @param registrationState The current network IMS registration state for all feature tags
+     *         associated with this SipDelegate.
+     */
+    void onFeatureTagRegistrationChanged(@NonNull DelegateRegistrationState registrationState);
+}
diff --git a/android-35/android/telephony/ims/FeatureTagState.java b/android-35/android/telephony/ims/FeatureTagState.java
new file mode 100644
index 0000000..3622065
--- /dev/null
+++ b/android-35/android/telephony/ims/FeatureTagState.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.telephony.ims.stub.SipDelegate;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Maps an IMS media feature tag 3gpp universal resource name (URN) previously mapped to a
+ * {@link SipDelegate} in the associated {@link DelegateRequest} to its current availability
+ * state as set by the ImsService managing the related IMS registration.
+ *
+ * This class is only used to report more information about a IMS feature tag that is not fully
+ * available at this time.
+ * <p>
+ * Please see {@link DelegateRegistrationState}, {@link DelegateStateCallback}, and
+ * {@link DelegateConnectionStateCallback} for more information about how this class is used to
+ * convey the state of IMS feature tags that were requested by {@link DelegateRequest} but are not
+ * currently available.
+ * @hide
+ */
+@SystemApi
+public final class FeatureTagState implements Parcelable {
+
+    private final String mFeatureTag;
+    private final int mState;
+
+    /**
+     * Associate an IMS feature tag with its current state. See {@link DelegateRegistrationState}
+     * and {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged(
+     * DelegateRegistrationState, List)} and
+     * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} for examples on how and
+     * when this is used.
+     *
+     * @param featureTag The IMS feature tag that is deregistered, in the process of
+     *                   deregistering, or denied.
+     * @param state The {@link DelegateRegistrationState.DeregisteredReason},
+     *         {@link DelegateRegistrationState.DeregisteringReason}, or
+     *         {@link SipDelegateManager.DeniedReason} associated with this feature tag.
+     */
+    public FeatureTagState(@NonNull String featureTag, int state) {
+        mFeatureTag = featureTag;
+        mState = state;
+    }
+
+    /**
+     * Used for constructing instances during un-parcelling.
+     */
+    private FeatureTagState(Parcel source) {
+        mFeatureTag = source.readString();
+        mState = source.readInt();
+    }
+
+    /**
+     * @return The IMS feature tag string that is in the process of deregistering,
+     * deregistered, or denied.
+     */
+    public @NonNull String getFeatureTag() {
+        return mFeatureTag;
+    }
+
+    /**
+     * @return The reason for why the feature tag is currently in the process of deregistering,
+     * has been deregistered, or has been denied. See {@link DelegateRegistrationState} and
+     * {@link DelegateConnectionStateCallback} for more information.
+     */
+    public int getState() {
+        return mState;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mFeatureTag);
+        dest.writeInt(mState);
+    }
+
+    public static final @NonNull Creator<FeatureTagState> CREATOR = new Creator<FeatureTagState>() {
+        @Override
+        public FeatureTagState createFromParcel(Parcel source) {
+            return new FeatureTagState(source);
+        }
+
+        @Override
+        public FeatureTagState[] newArray(int size) {
+            return new FeatureTagState[size];
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        FeatureTagState that = (FeatureTagState) o;
+        return mState == that.mState
+                && mFeatureTag.equals(that.mFeatureTag);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mFeatureTag, mState);
+    }
+
+    @Override
+    public String toString() {
+        return "FeatureTagState{" + "mFeatureTag='" + mFeatureTag + ", mState=" + mState + '}';
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsCallForwardInfo.java b/android-35/android/telephony/ims/ImsCallForwardInfo.java
new file mode 100644
index 0000000..1c2cac2
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsCallForwardInfo.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides the call forward information for the supplementary service configuration.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsCallForwardInfo implements Parcelable {
+
+    /**
+     * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for unconditional call
+     * forwarding. See TC 27.007
+     */
+    public static final int CDIV_CF_REASON_UNCONDITIONAL = 0;
+
+    /**
+     * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for call forwarding
+     * when the user is busy.
+     */
+    public static final int CDIV_CF_REASON_BUSY = 1;
+
+    /**
+     * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for call forwarding
+     * when there is no reply from the user.
+     */
+    public static final int CDIV_CF_REASON_NO_REPLY = 2;
+
+    /**
+     * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for call forwarding
+     * when the user is not reachable.
+     */
+    public static final int CDIV_CF_REASON_NOT_REACHABLE = 3;
+
+    /**
+     * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for setting all call
+     * forwarding reasons simultaneously (i.e. unconditional, busy, no reply, and not reachable).
+     */
+    public static final int CDIV_CF_REASON_ALL = 4;
+
+    /**
+     * CDIV (Communication Diversion, 3GPP TS 24.604) call forwarding reason for setting all
+     * conditional call forwarding reasons simultaneously (i.e. if busy, if no reply, and if not
+     * reachable).
+     */
+    public static final int CDIV_CF_REASON_ALL_CONDITIONAL = 5;
+
+    /**
+     * CDIV (Communication Diversion) IMS only call forwarding reason for call forwarding when the
+     * user is not logged in.
+     */
+    public static final int CDIV_CF_REASON_NOT_LOGGED_IN = 6;
+
+    /**@hide*/
+    @IntDef(prefix = {"CDIV_CF_REASON_"}, value = {
+            CDIV_CF_REASON_UNCONDITIONAL,
+            CDIV_CF_REASON_BUSY,
+            CDIV_CF_REASON_NO_REPLY,
+            CDIV_CF_REASON_NOT_REACHABLE,
+            CDIV_CF_REASON_ALL,
+            CDIV_CF_REASON_ALL_CONDITIONAL,
+            CDIV_CF_REASON_NOT_LOGGED_IN})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallForwardReasons{}
+
+    /**
+     * Call forwarding is not active for any service class.
+     */
+    public static final int STATUS_NOT_ACTIVE = 0;
+
+    /**
+     * Call forwarding is active for one or more service classes.
+     */
+    public static final int STATUS_ACTIVE = 1;
+
+    /**@hide*/
+    @IntDef(prefix = {"STATUS_"}, value = {
+            STATUS_NOT_ACTIVE,
+            STATUS_ACTIVE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallForwardStatus{}
+
+    /**
+     * The address defined in {@link #getNumber()} is in an unknown format.
+     *
+     * See TS 27.007, section 7.11 for more information.
+     */
+    public static final int TYPE_OF_ADDRESS_UNKNOWN = 0x81;
+    /**
+     * The address defined in {@link #getNumber()} is in E.164 international format, which includes
+     * international access code "+".
+     *
+     * See TS 27.007, section 7.11 for more information.
+     */
+    public static final int TYPE_OF_ADDRESS_INTERNATIONAL = 0x91;
+
+    /**@hide*/
+    @IntDef(prefix = {"TYPE_OF_ADDRESS_"}, value = {
+            TYPE_OF_ADDRESS_INTERNATIONAL,
+            TYPE_OF_ADDRESS_UNKNOWN})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TypeOfAddress{}
+
+    /**@hide*/
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public @CallForwardReasons int mCondition;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public @CallForwardStatus int mStatus;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public @TypeOfAddress int mToA;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public @ImsSsData.ServiceClassFlags int mServiceClass;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public String mNumber;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int mTimeSeconds;
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public ImsCallForwardInfo() {
+    }
+
+    /**
+     * IMS Call Forward Information.
+     */
+    public ImsCallForwardInfo(@CallForwardReasons int reason, @CallForwardStatus int status,
+            @TypeOfAddress int toA, @ImsSsData.ServiceClassFlags int serviceClass,
+            @NonNull String number, int replyTimerSec) {
+        mCondition = reason;
+        mStatus = status;
+        mToA = toA;
+        mServiceClass = serviceClass;
+        mNumber = number;
+        mTimeSeconds = replyTimerSec;
+    }
+
+    /** @hide */
+    public ImsCallForwardInfo(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mCondition);
+        out.writeInt(mStatus);
+        out.writeInt(mToA);
+        out.writeString(mNumber);
+        out.writeInt(mTimeSeconds);
+        out.writeInt(mServiceClass);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return super.toString() + ", Condition: " + mCondition
+            + ", Status: " + ((mStatus == 0) ? "disabled" : "enabled")
+            + ", ToA: " + mToA
+            + ", Service Class: " + mServiceClass
+            + ", Number=" + mNumber
+            + ", Time (seconds): " + mTimeSeconds;
+    }
+
+    private void readFromParcel(Parcel in) {
+        mCondition = in.readInt();
+        mStatus = in.readInt();
+        mToA = in.readInt();
+        mNumber = in.readString();
+        mTimeSeconds = in.readInt();
+        mServiceClass = in.readInt();
+    }
+
+    public static final @android.annotation.NonNull Creator<ImsCallForwardInfo> CREATOR =
+            new Creator<ImsCallForwardInfo>() {
+        @Override
+        public ImsCallForwardInfo createFromParcel(Parcel in) {
+            return new ImsCallForwardInfo(in);
+        }
+
+        @Override
+        public ImsCallForwardInfo[] newArray(int size) {
+            return new ImsCallForwardInfo[size];
+        }
+    };
+
+    /**
+     * @return the condition of call forwarding for the service classes specified.
+     */
+    public @CallForwardReasons int getCondition() {
+        return mCondition;
+    }
+
+    /**
+     * @return The call forwarding status.
+     */
+    public @CallForwardStatus int getStatus() {
+        return mStatus;
+    }
+
+    /**
+     * @return the type of address (ToA) for the number.
+     * @see #getNumber()
+     */
+    public @TypeOfAddress int getToA() {
+        return mToA;
+    }
+
+    /**
+     * @return a bitfield containing the service classes that are enabled for call forwarding.
+     */
+    public @ImsSsData.ServiceClassFlags int getServiceClass() {
+        return mServiceClass;
+    }
+
+    /**
+     * @return the call forwarding number associated with call forwarding, with a number type
+     * defined by {@link #getToA()}.
+     */
+    public String getNumber() {
+        return mNumber;
+    }
+
+    /**
+     * @return the number in seconds to wait before the call is forwarded for call forwarding
+     * condition {@link #CDIV_CF_REASON_NO_REPLY}
+     */
+    public int getTimeSeconds() {
+        return mTimeSeconds;
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsCallProfile.java b/android-35/android/telephony/ims/ImsCallProfile.java
new file mode 100644
index 0000000..cebfe01
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsCallProfile.java
@@ -0,0 +1,1296 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telecom.VideoProfile;
+import android.telephony.CallState;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting;
+import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
+import android.telephony.ims.feature.MmTelFeature;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * A Parcelable object to handle the IMS call profile, which provides the service, call type, and
+ * additional information related to the call.
+ * <p>
+ * See the following specifications for more information about this class: GSMA IR.92/IR.94,
+ * 3GPP TS 24.229/TS 26.114/TS26.111.
+ * @hide
+ */
+@SystemApi
+public final class ImsCallProfile implements Parcelable {
+    private static final String TAG = "ImsCallProfile";
+
+    /**
+     * Service types
+     */
+    /**
+     * It is for a special case. It helps that the application can make a call
+     * without IMS connection (not registered).
+     * In the moment of the call initiation, the device try to connect to the IMS network
+     * and initiates the call.
+     */
+    public static final int SERVICE_TYPE_NONE = 0;
+    /**
+     * It is a default type and can be selected when the device is connected to the IMS network.
+     */
+    public static final int SERVICE_TYPE_NORMAL = 1;
+    /**
+     * It is for an emergency call.
+     */
+    public static final int SERVICE_TYPE_EMERGENCY = 2;
+
+    /**
+     * This value is returned if there is no valid IMS call type defined for the call. For example,
+     * if an ongoing call is circuit-switched and {@link CallState#getImsCallType()} is called, this
+     * value will be returned.
+     */
+    public static final int CALL_TYPE_NONE = 0;
+    /**
+     * IMSPhone to support IR.92 & IR.94 (voice + video upgrade/downgrade)
+     */
+    public static final int CALL_TYPE_VOICE_N_VIDEO = 1;
+    /**
+     * IR.92 (Voice only)
+     */
+    public static final int CALL_TYPE_VOICE = 2;
+    /**
+     * VT to support IR.92 & IR.94 (voice + video upgrade/downgrade)
+     */
+    public static final int CALL_TYPE_VIDEO_N_VOICE = 3;
+    /**
+     * Video Telephony (audio / video two way)
+     */
+    public static final int CALL_TYPE_VT = 4;
+    /**
+     * Video Telephony (audio two way / video TX one way)
+     */
+    public static final int CALL_TYPE_VT_TX = 5;
+    /**
+     * Video Telephony (audio two way / video RX one way)
+     */
+    public static final int CALL_TYPE_VT_RX = 6;
+    /**
+     * Video Telephony (audio two way / video inactive)
+     */
+    public static final int CALL_TYPE_VT_NODIR = 7;
+    /**
+     * VideoShare (video two way)
+     */
+    public static final int CALL_TYPE_VS = 8;
+    /**
+     * VideoShare (video TX one way)
+     */
+    public static final int CALL_TYPE_VS_TX = 9;
+    /**
+     * VideoShare (video RX one way)
+     */
+    public static final int CALL_TYPE_VS_RX = 10;
+
+    /**
+     * Extra properties for IMS call.
+     */
+    /**
+     * Boolean extra properties - "true" / "false"
+     *  conference : Indicates if the session is for the conference call or not.
+     *  e_call : Indicates if the session is for the emergency call or not.
+     *  vms : Indicates if the session is connected to the voice mail system or not.
+     *  call_mode_changeable : Indicates if the session is able to upgrade/downgrade
+     *      the video during voice call.
+     *  conference_avail : Indicates if the session can be extended to the conference.
+     */
+
+    /**
+     * Indicates if the session is for a conference call or not. If not defined, should be
+     * considered {@code false}.
+     * Boolean extra properties - {@code true} / {@code false}.
+     *
+     * This extra is set on an instance of {@link ImsCallProfile} via {@link #setCallExtraBoolean}.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_CONFERENCE = "android.telephony.ims.extra.CONFERENCE";
+
+    /**
+     * The previous string of EXTRA_CONFERENCE. Use EXTRA_CONFERENCE whenever possible.
+     * For external app or vendor code backward compatibility, we should always set value for both
+     * EXTRA_CONFERENCE_DEPRECATED and EXTRA_CONFERENCE.
+     *
+     * @deprecated Remove when not needed anymore.
+     *
+     * @hide
+     */
+    public static final String EXTRA_CONFERENCE_DEPRECATED = "conference";
+
+    /**
+     * Boolean extra property set on an {@link ImsCallProfile} to indicate that this call is an
+     * emergency call.  The {@link ImsService} sets this on a call to indicate that the network has
+     * identified the call as an emergency call.
+     */
+    public static final String EXTRA_EMERGENCY_CALL = "e_call";
+
+    /**
+     * @hide
+     */
+    public static final String EXTRA_VMS = "vms";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_CALL_MODE_CHANGEABLE = "call_mode_changeable";
+
+    /**
+     * Indicates if the session can be extended to a conference call. If not defined, should be
+     * considered {@code false}.
+     * Boolean extra properties - {@code true} / {@code false}.
+     *
+     * This extra is set on an instance of {@link ImsCallProfile} via {@link #setCallExtraBoolean}.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED =
+            "android.telephony.ims.extra.EXTENDING_TO_CONFERENCE_SUPPORTED";
+
+    /**
+     * The previous string of EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED.
+     * Use EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED whenever possible.
+     * For backward compatibility, we should always set value for both
+     * EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED and EXTRA_CONFERENCE_AVAIL.
+     *
+     * @deprecated Remove when not needed anymore.
+     *
+     * @hide
+     */
+    public static final String EXTRA_CONFERENCE_AVAIL = "conference_avail";
+
+    /**
+     * Extra key used to store a Bundle containing proprietary extras to send to the ImsService.
+     * Use {@link #getProprietaryCallExtras()} instead.
+     * @hide
+     */
+    @TestApi
+    public static final String EXTRA_OEM_EXTRAS = "android.telephony.ims.extra.OEM_EXTRAS";
+
+    /**
+     * Rule for originating identity (number) presentation, MO/MT.
+     *      {@link ImsCallProfile#OIR_DEFAULT}
+     *      {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
+     *      {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
+     */
+    public static final String EXTRA_OIR = "oir";
+    /**
+     * Rule for calling name presentation
+     *      {@link ImsCallProfile#OIR_DEFAULT}
+     *      {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
+     *      {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
+     */
+    public static final String EXTRA_CNAP = "cnap";
+    /**
+     * To identify the Ims call type, MO
+     *      {@link ImsCallProfile#DIALSTRING_NORMAL}
+     *      {@link ImsCallProfile#DIALSTRING_SS_CONF}
+     *      {@link ImsCallProfile#DIALSTRING_USSD}
+     */
+    public static final String EXTRA_DIALSTRING = "dialstring";
+    /**
+     * This extra holds call fail cause because of which redial is attempted.
+     * see {@link android.telephony.ims.ImsReasonInfo} {@code CODE_*}
+     * for possible values this extra can hold.
+     *
+     * @hide
+     */
+    public static final String EXTRA_RETRY_CALL_FAIL_REASON =
+            "android.telephony.ims.extra.RETRY_CALL_FAIL_REASON";
+    /**
+     * This extra holds call network type on which lower layers
+     * may try attempting redial.
+     * See {@link TelephonyManager} {@code NETWORK_TYPE_*}
+     * for possible values this extra can hold.
+     *
+     * @hide
+     */
+    public static final String EXTRA_RETRY_CALL_FAIL_NETWORKTYPE =
+            "android.telephony.ims.extra.RETRY_CALL_FAIL_NETWORKTYPE";
+
+    /**
+     * Extra for the call composer call priority, either {@link ImsCallProfile#PRIORITY_NORMAL} or
+     * {@link ImsCallProfile#PRIORITY_URGENT}. It can be set via
+     * {@link #setCallExtraInt(String, int)}.
+     *
+     * Reference: RCC.20 Section 2.4.4.2
+     */
+    public static final String EXTRA_PRIORITY = "android.telephony.ims.extra.PRIORITY";
+
+    // TODO(hallliu) remove the reference to the maximum length and update it later.
+    /**
+     * Extra for the call composer call subject, a string of maximum length 60 characters.
+     * It can be set via {@link #setCallExtra(String, String)}.
+     *
+     * Reference: RCC.20 Section 2.4.3.2
+     */
+    public static final String EXTRA_CALL_SUBJECT = "android.telephony.ims.extra.CALL_SUBJECT";
+
+    /**
+     * Extra for the call composer call location, an {@Link android.location.Location} parcelable
+     * class to represent the geolocation as a latitude and longitude pair. It can be set via
+     * {@link #setCallExtraParcelable(String, Parcelable)}.
+     *
+     * Reference: RCC.20 Section 2.4.3.2
+     */
+    public static final String EXTRA_LOCATION = "android.telephony.ims.extra.LOCATION";
+
+    /**
+     * Extra for the call composer picture URL, a String that indicates the URL on the carrier’s
+     * server infrastructure to get the picture. It can be set via
+     * {@link #setCallExtra(String, String)}.
+     *
+     * Note that this URL is not intended to be parsed by the IMS stack -- it should be sent
+     * directly to the network for consumption by the called party or forwarded directly from the
+     * network to the platform for caching and download.
+     *
+     * Reference: RCC.20 Section 2.4.3.2
+     */
+    public static final String EXTRA_PICTURE_URL = "android.telephony.ims.extra.PICTURE_URL";
+
+    /**
+     * Boolean extra indicating whether the call is a business call.
+     *
+     * This extra will be set to {@code true} if and only if the SIP INVITE headers contain the
+     * "Organization" header.
+     */
+    public static final String EXTRA_IS_BUSINESS_CALL =
+            "android.telephony.ims.extra.IS_BUSINESS_CALL";
+
+    /**
+     * The vendor IMS stack populates this {@code string} extra; it is used to hold the display name
+     * passed via the P-Asserted-Identity SIP header’s display-name field
+     *
+     * Reference: RFC3325
+     */
+    @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_BUSINESS_CALL_COMPOSER)
+    public static final String EXTRA_ASSERTED_DISPLAY_NAME =
+            "android.telephony.ims.extra.ASSERTED_DISPLAY_NAME";
+
+    /**
+     * Values for EXTRA_OIR / EXTRA_CNAP
+     */
+    /**
+     * Default presentation for Originating Identity.
+     */
+    public static final int OIR_DEFAULT = 0;    // "user subscription default value"
+    /**
+     * Restricted presentation for Originating Identity.
+     */
+    public static final int OIR_PRESENTATION_RESTRICTED = 1;
+    /**
+     * Not restricted presentation for Originating Identity.
+     */
+    public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2;
+    /**
+     * Presentation unknown for Originating Identity.
+     */
+    public static final int OIR_PRESENTATION_UNKNOWN = 3;
+    /**
+     * Payphone presentation for Originating Identity.
+     */
+    public static final int OIR_PRESENTATION_PAYPHONE = 4;
+    /**
+     * Unavailable presentation for Originating Identity.
+     */
+    public static final int OIR_PRESENTATION_UNAVAILABLE = 5;
+
+    //Values for EXTRA_DIALSTRING
+    /**
+     * A default or normal normal call.
+     */
+    public static final int DIALSTRING_NORMAL = 0;
+    /**
+     * Call for SIP-based user configuration
+     */
+    public static final int DIALSTRING_SS_CONF = 1;
+    /**
+     * Call for USSD message
+     */
+    public static final int DIALSTRING_USSD = 2;
+
+    // Values for EXTRA_PRIORITY
+    /**
+     * Indicates the call composer call priority is normal.
+     *
+     * Reference: RCC.20 Section 2.4.4.2
+     */
+    public static final int PRIORITY_NORMAL = 0;
+
+    /**
+     * Indicates the call composer call priority is urgent.
+     *
+     * Reference: RCC.20 Section 2.4.4.2
+     */
+    public static final int PRIORITY_URGENT = 1;
+
+    /**
+     * Call is not restricted on peer side and High Definition media is supported
+     */
+    public static final int CALL_RESTRICT_CAUSE_NONE = 0;
+
+    /**
+     * High Definition media is not supported on the peer side due to the Radio Access Technology
+     * (RAT) it is are connected to.
+     */
+    public static final int CALL_RESTRICT_CAUSE_RAT = 1;
+
+    /**
+     * The service has been disabled on the peer side.
+     */
+    public static final int CALL_RESTRICT_CAUSE_DISABLED = 2;
+
+    /**
+     * High definition media is not currently supported.
+     */
+    public static final int CALL_RESTRICT_CAUSE_HD = 3;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CALL_RESTRICT_CAUSE_", value = {
+            CALL_RESTRICT_CAUSE_NONE,
+            CALL_RESTRICT_CAUSE_RAT,
+            CALL_RESTRICT_CAUSE_DISABLED,
+            CALL_RESTRICT_CAUSE_HD
+    })
+    public @interface CallRestrictCause {}
+
+    /**
+     * String extra properties
+     *  oi : Originating identity (number), MT only
+     *  cna : Calling name
+     *  ussd : For network-initiated USSD, MT only
+     *  remote_uri : Connected user identity (it can be used for the conference)
+     *  ChildNum: Child number info.
+     *  Codec: Codec info.
+     *  DisplayText: Display text for the call.
+     *  AdditionalCallInfo: Additional call info.
+     *  CallPull: Boolean value specifying if the call is a pulled call.
+     */
+    public static final String EXTRA_OI = "oi";
+    public static final String EXTRA_CNA = "cna";
+    public static final String EXTRA_USSD = "ussd";
+    public static final String EXTRA_REMOTE_URI = "remote_uri";
+    public static final String EXTRA_CHILD_NUMBER = "ChildNum";
+    public static final String EXTRA_CODEC = "Codec";
+    public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
+    public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
+    public static final String EXTRA_IS_CALL_PULL = "CallPull";
+
+    /**
+     * String extra property
+     *  Containing fields from the SIP INVITE message for an IMS call
+     */
+    public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS =
+                                  "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
+
+    /**
+     * CallDisconnectCause: Specify call disconnect cause. This extra should be a code
+     * corresponding to ImsReasonInfo and should only be populated in the case that the
+     * call has already been missed
+     */
+    public static final String EXTRA_CALL_DISCONNECT_CAUSE =
+                                 "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE";
+
+    /**
+     * Extra key which the RIL can use to indicate the radio technology used for a call.
+     * Valid values are:
+     * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE},
+     * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_IWLAN}, and the other defined
+     * {@code RIL_RADIO_TECHNOLOGY_*} constants.
+     * Note: Despite the fact the {@link android.telephony.ServiceState} values are integer
+     * constants, the values passed for the {@link #EXTRA_CALL_RAT_TYPE} should be strings (e.g.
+     * "14" vs (int) 14).
+     * Note: This is used by {@link com.android.internal.telephony.imsphone.ImsPhoneConnection#
+     *      updateImsCallRatFromExtras(Bundle)} to determine whether to set the
+     * {@link android.telecom.TelecomManager#EXTRA_CALL_NETWORK_TYPE} extra value and
+     * {@link android.telecom.Connection#PROPERTY_WIFI} property on a connection.
+     * @deprecated the constants associated with this extra are hidden, instead use
+     * {@link #EXTRA_CALL_NETWORK_TYPE}.
+     */
+    @Deprecated
+    public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+
+    /**
+     * Extra key with an {@code int} value which can be set in {@link #setCallExtraInt(String, int)}
+     * to indicate the network type used for a call.
+     * <p>
+     * Valid values are defined by {@code TelephonyManager.NETWORK_TYPE_*} constants. An example may
+     * be {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
+     */
+    public static final String EXTRA_CALL_NETWORK_TYPE =
+            "android.telephony.ims.extra.CALL_NETWORK_TYPE";
+
+    /**
+     * Similar to {@link #EXTRA_CALL_RAT_TYPE}, except with a lowercase 'c'.  Used to ensure
+     * compatibility with modems that are non-compliant with the {@link #EXTRA_CALL_RAT_TYPE}
+     * extra key.  Should be removed when the non-compliant modems are fixed.
+     * @hide
+     * @deprecated Use {@link #EXTRA_CALL_NETWORK_TYPE} instead.
+     */
+    @Deprecated
+    public static final String EXTRA_CALL_RAT_TYPE_ALT = "callRadioTech";
+
+    /**
+     * String extra property containing forwarded numbers associated with the current connection
+     * for an IMS call. The value is string array, and it can include multiple numbers, and
+     * the array values are expected E164 (e.g. +1 (650) 253-0000) format.
+     */
+    public static final String EXTRA_FORWARDED_NUMBER =
+            "android.telephony.ims.extra.FORWARDED_NUMBER";
+
+    /**
+     * Extra key with an {@code boolean} value which can be set in
+     * {@link #setCallExtraBoolean(String, boolean)} to indicate whether call is a cross sim call.
+     * <p>
+     * Valid values are true if call is cross sim call else false.
+     */
+    public static final String EXTRA_IS_CROSS_SIM_CALL =
+            "android.telephony.ims.extra.IS_CROSS_SIM_CALL";
+
+    /** @hide */
+    public int mServiceType;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int mCallType;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public @CallRestrictCause int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
+
+    /**
+     * The VERSTAT for an incoming call's phone number.
+     */
+    private @VerificationStatus int mCallerNumberVerificationStatus;
+
+    /**
+     * Indicates that the network could not perform verification.
+     */
+    public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0;
+
+    /**
+     * Indicates that verification by the network passed.  This indicates there is a high likelihood
+     * that the call originated from a valid source.
+     */
+    public static final int VERIFICATION_STATUS_PASSED = 1;
+
+    /**
+     * Indicates that verification by the network failed.  This indicates there is a high likelihood
+     * that the call did not originate from a valid source.
+     */
+    public static final int VERIFICATION_STATUS_FAILED = 2;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "VERIFICATION_STATUS_", value = {
+            VERIFICATION_STATUS_NOT_VERIFIED,
+            VERIFICATION_STATUS_PASSED,
+            VERIFICATION_STATUS_FAILED
+    })
+    public @interface VerificationStatus {}
+
+    /**
+     * The emergency service categories, only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * If valid, the value is the bitwise-OR combination of the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+     * </ol>
+     *
+     * Reference: 3gpp 23.167, Section 6 - Functional description;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     */
+    private @EmergencyServiceCategories int mEmergencyServiceCategories =
+            EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+
+    /**
+     * The emergency Uniform Resource Names (URN), only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}.
+     *
+     * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     */
+    private List<String> mEmergencyUrns = new ArrayList<>();
+
+    /**
+     * The emergency call routing, only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * If valid, the value is any of the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+     * </ol>
+     */
+    private @EmergencyCallRouting int mEmergencyCallRouting =
+            EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+
+    /** Indicates if the call is for testing purpose */
+    private boolean mEmergencyCallTesting = false;
+
+    /** Indicates if we have known the intent of the user for the call is emergency */
+    private boolean mHasKnownUserIntentEmergency = false;
+
+    private Set<RtpHeaderExtensionType> mAcceptedRtpHeaderExtensionTypes = new ArraySet<>();
+
+    /**
+     * Extras associated with this {@link ImsCallProfile}.
+     * <p>
+     * Valid data types include:
+     * <ul>
+     *     <li>{@link Integer} (and int)</li>
+     *     <li>{@link Long} (and long)</li>
+     *     <li>{@link Double} (and double)</li>
+     *     <li>{@link String}</li>
+     *     <li>{@code int[]}</li>
+     *     <li>{@code long[]}</li>
+     *     <li>{@code double[]}</li>
+     *     <li>{@code String[]}</li>
+     *     <li>{@link android.os.PersistableBundle}</li>
+     *     <li>{@link Boolean} (and boolean)</li>
+     *     <li>{@code boolean[]}</li>
+     *     <li>Other {@link Parcelable} classes in the {@code android.*} namespace.</li>
+     * </ul>
+     * <p>
+     * Invalid types will be removed when the {@link ImsCallProfile} is parceled for transmit across
+     * a {@link android.os.Binder}.
+     */
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public Bundle mCallExtras;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public ImsStreamMediaProfile mMediaProfile;
+
+    /** @hide */
+    public ImsCallProfile(Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * Default Constructor that initializes the call profile with service type
+     * {@link #SERVICE_TYPE_NORMAL} and call type {@link #CALL_TYPE_VIDEO_N_VOICE}
+     */
+    public ImsCallProfile() {
+        mServiceType = SERVICE_TYPE_NORMAL;
+        mCallType = CALL_TYPE_VOICE_N_VIDEO;
+        mCallExtras = new Bundle();
+        mMediaProfile = new ImsStreamMediaProfile();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param serviceType the service type for the call. Can be one of the following:
+     *                    {@link #SERVICE_TYPE_NONE},
+     *                    {@link #SERVICE_TYPE_NORMAL},
+     *                    {@link #SERVICE_TYPE_EMERGENCY}
+     * @param callType the call type. Can be one of the following:
+     *                 {@link #CALL_TYPE_VOICE_N_VIDEO},
+     *                 {@link #CALL_TYPE_VOICE},
+     *                 {@link #CALL_TYPE_VIDEO_N_VOICE},
+     *                 {@link #CALL_TYPE_VT},
+     *                 {@link #CALL_TYPE_VT_TX},
+     *                 {@link #CALL_TYPE_VT_RX},
+     *                 {@link #CALL_TYPE_VT_NODIR},
+     *                 {@link #CALL_TYPE_VS},
+     *                 {@link #CALL_TYPE_VS_TX},
+     *                 {@link #CALL_TYPE_VS_RX}
+     */
+    public ImsCallProfile(int serviceType, int callType) {
+        mServiceType = serviceType;
+        mCallType = callType;
+        mCallExtras = new Bundle();
+        mMediaProfile = new ImsStreamMediaProfile();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param serviceType the service type for the call. Can be one of the following:
+     *                    {@link #SERVICE_TYPE_NONE},
+     *                    {@link #SERVICE_TYPE_NORMAL},
+     *                    {@link #SERVICE_TYPE_EMERGENCY}
+     * @param callType the call type. Can be one of the following:
+     *                 {@link #CALL_TYPE_VOICE_N_VIDEO},
+     *                 {@link #CALL_TYPE_VOICE},
+     *                 {@link #CALL_TYPE_VIDEO_N_VOICE},
+     *                 {@link #CALL_TYPE_VT},
+     *                 {@link #CALL_TYPE_VT_TX},
+     *                 {@link #CALL_TYPE_VT_RX},
+     *                 {@link #CALL_TYPE_VT_NODIR},
+     *                 {@link #CALL_TYPE_VS},
+     *                 {@link #CALL_TYPE_VS_TX},
+     *                 {@link #CALL_TYPE_VS_RX}
+     * @param callExtras A bundle with the call extras.
+     * @param mediaProfile The IMS stream media profile.
+     */
+    public ImsCallProfile(int serviceType, int callType, Bundle callExtras,
+            ImsStreamMediaProfile mediaProfile) {
+        mServiceType = serviceType;
+        mCallType = callType;
+        mCallExtras = callExtras;
+        mMediaProfile = mediaProfile;
+    }
+
+    public String getCallExtra(String name) {
+        return getCallExtra(name, "");
+    }
+
+    public String getCallExtra(String name, String defaultValue) {
+        if (mCallExtras == null) {
+            return defaultValue;
+        }
+
+        return mCallExtras.getString(name, defaultValue);
+    }
+
+    public boolean getCallExtraBoolean(String name) {
+        return getCallExtraBoolean(name, false);
+    }
+
+    public boolean getCallExtraBoolean(String name, boolean defaultValue) {
+        if (mCallExtras == null) {
+            return defaultValue;
+        }
+
+        return mCallExtras.getBoolean(name, defaultValue);
+    }
+
+    public int getCallExtraInt(String name) {
+        return getCallExtraInt(name, -1);
+    }
+
+    public int getCallExtraInt(String name, int defaultValue) {
+        if (mCallExtras == null) {
+            return defaultValue;
+        }
+
+        return mCallExtras.getInt(name, defaultValue);
+    }
+
+    /**
+     * Get the call extras (Parcelable), given the extra name.
+     * @param name call extra name
+     * @return the corresponding call extra Parcelable or null if not applicable
+     */
+    @Nullable
+    public <T extends Parcelable> T getCallExtraParcelable(@Nullable String name) {
+        if (mCallExtras != null) {
+            return mCallExtras.getParcelable(name);
+        }
+        return null;
+    }
+
+    public void setCallExtra(String name, String value) {
+        if (mCallExtras != null) {
+            mCallExtras.putString(name, value);
+        }
+    }
+
+    public void setCallExtraBoolean(String name, boolean value) {
+        if (mCallExtras != null) {
+            mCallExtras.putBoolean(name, value);
+        }
+    }
+
+    public void setCallExtraInt(String name, int value) {
+        if (mCallExtras != null) {
+            mCallExtras.putInt(name, value);
+        }
+    }
+
+    /**
+     * Set the call extra value (Parcelable), given the call extra name.
+     *
+     * Note that the {@link Parcelable} provided must be a class defined in the Android API surface,
+     * as opposed to a class defined by your app.
+     *
+     * @param name call extra name
+     * @param parcelable call extra value
+     */
+    public void setCallExtraParcelable(@NonNull String name, @NonNull Parcelable parcelable) {
+        if (mCallExtras != null) {
+            mCallExtras.putParcelable(name, parcelable);
+        }
+    }
+
+    /**
+     * Set the call restrict cause, which provides the reason why a call has been restricted from
+     * using High Definition media.
+     */
+    public void setCallRestrictCause(@CallRestrictCause int cause) {
+        mRestrictCause = cause;
+    }
+
+    public void updateCallType(ImsCallProfile profile) {
+        mCallType = profile.mCallType;
+    }
+
+    public void updateCallExtras(ImsCallProfile profile) {
+        mCallExtras.clear();
+        mCallExtras = (Bundle) profile.mCallExtras.clone();
+    }
+
+    /**
+     * Updates the media profile for the call.
+     *
+     * @param profile Call profile with new media profile.
+     */
+    public void updateMediaProfile(ImsCallProfile profile) {
+        mMediaProfile = profile.mMediaProfile;
+    }
+
+    /**
+     * Sets the verification status for the phone number of an incoming call as identified in
+     * ATIS-1000082.
+     * <p>
+     * The ImsService should parse the verstat information from the SIP INVITE headers for the call
+     * to determine this information.  It is typically found in the P-Asserted-Identity OR From
+     * header fields.
+     * @param callerNumberVerificationStatus the new verification status.
+     */
+    public void setCallerNumberVerificationStatus(
+            @VerificationStatus int callerNumberVerificationStatus) {
+        mCallerNumberVerificationStatus = callerNumberVerificationStatus;
+    }
+
+    /**
+     * Gets the verification status for the phone number of an incoming call as identified in
+     * ATIS-1000082.
+     * @return the verification status.
+     */
+    public @VerificationStatus int getCallerNumberVerificationStatus() {
+        return mCallerNumberVerificationStatus;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "{ serviceType=" + mServiceType
+                + ", callType=" + mCallType
+                + ", restrictCause=" + mRestrictCause
+                + ", mediaProfile=" + (mMediaProfile != null ? mMediaProfile.toString() : "null")
+                + ", emergencyServiceCategories=" + mEmergencyServiceCategories
+                + ", emergencyUrns=" + mEmergencyUrns
+                + ", emergencyCallRouting=" + mEmergencyCallRouting
+                + ", emergencyCallTesting=" + mEmergencyCallTesting
+                + ", hasKnownUserIntentEmergency=" + mHasKnownUserIntentEmergency
+                + ", mRestrictCause=" + mRestrictCause
+                + ", mCallerNumberVerstat= " + mCallerNumberVerificationStatus
+                + ", mAcceptedRtpHeaderExtensions= " + mAcceptedRtpHeaderExtensionTypes
+                + " }";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        Bundle filteredExtras = maybeCleanseExtras(mCallExtras);
+        out.writeInt(mServiceType);
+        out.writeInt(mCallType);
+        out.writeBundle(filteredExtras);
+        out.writeParcelable(mMediaProfile, 0);
+        out.writeInt(mEmergencyServiceCategories);
+        out.writeStringList(mEmergencyUrns);
+        out.writeInt(mEmergencyCallRouting);
+        out.writeBoolean(mEmergencyCallTesting);
+        out.writeBoolean(mHasKnownUserIntentEmergency);
+        out.writeInt(mRestrictCause);
+        out.writeInt(mCallerNumberVerificationStatus);
+        out.writeArray(mAcceptedRtpHeaderExtensionTypes.toArray());
+    }
+
+    private void readFromParcel(Parcel in) {
+        mServiceType = in.readInt();
+        mCallType = in.readInt();
+        mCallExtras = in.readBundle();
+        mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader(), android.telephony.ims.ImsStreamMediaProfile.class);
+        mEmergencyServiceCategories = in.readInt();
+        mEmergencyUrns = in.createStringArrayList();
+        mEmergencyCallRouting = in.readInt();
+        mEmergencyCallTesting = in.readBoolean();
+        mHasKnownUserIntentEmergency = in.readBoolean();
+        mRestrictCause = in.readInt();
+        mCallerNumberVerificationStatus = in.readInt();
+        Object[] accepted = in.readArray(RtpHeaderExtensionType.class.getClassLoader(),
+                RtpHeaderExtensionType.class);
+        mAcceptedRtpHeaderExtensionTypes = Arrays.stream(accepted)
+                .map(o -> (RtpHeaderExtensionType) o).collect(Collectors.toSet());
+    }
+
+    public static final @android.annotation.NonNull Creator<ImsCallProfile> CREATOR =
+            new Creator<ImsCallProfile>() {
+        @Override
+        public ImsCallProfile createFromParcel(Parcel in) {
+            return new ImsCallProfile(in);
+        }
+
+        @Override
+        public ImsCallProfile[] newArray(int size) {
+            return new ImsCallProfile[size];
+        }
+    };
+
+    public int getServiceType() {
+        return mServiceType;
+    }
+
+    public int getCallType() {
+        return mCallType;
+    }
+
+    /**
+     * @return The call restrict cause, which provides the reason why a call has been restricted
+     * from using High Definition media.
+     */
+    public @CallRestrictCause int getRestrictCause() {
+        return mRestrictCause;
+    }
+
+    public Bundle getCallExtras() {
+        return mCallExtras;
+    }
+
+    /**
+     * Get the proprietary extras set for this ImsCallProfile.
+     * @return A {@link Bundle} containing proprietary call extras that were not set by the
+     * platform.
+     */
+    public @NonNull Bundle getProprietaryCallExtras() {
+        if (mCallExtras == null) {
+            return new Bundle();
+        }
+        Bundle proprietaryExtras = mCallExtras.getBundle(EXTRA_OEM_EXTRAS);
+        if (proprietaryExtras == null) {
+            return new Bundle();
+        }
+        // Make a copy so users do not accidentally change this copy of the extras.
+        return new Bundle(proprietaryExtras);
+    }
+
+    public ImsStreamMediaProfile getMediaProfile() {
+        return mMediaProfile;
+    }
+
+    /**
+     * Converts from the call types defined in {@link ImsCallProfile} to the
+     * video state values defined in {@link VideoProfile}.
+     *
+     * @param callProfile The call profile.
+     * @return The video state.
+     */
+    public static int getVideoStateFromImsCallProfile(ImsCallProfile callProfile) {
+        int videostate = getVideoStateFromCallType(callProfile.mCallType);
+        if (callProfile.isVideoPaused() && !VideoProfile.isAudioOnly(videostate)) {
+            videostate |= VideoProfile.STATE_PAUSED;
+        } else {
+            videostate &= ~VideoProfile.STATE_PAUSED;
+        }
+        return videostate;
+    }
+
+    /**
+     * Translates a {@link ImsCallProfile} {@code CALL_TYPE_*} constant into a video state.
+     * @param callType The call type.
+     * @return The video state.
+     */
+    public static int getVideoStateFromCallType(int callType) {
+        int videostate = VideoProfile.STATE_AUDIO_ONLY;
+        switch (callType) {
+            case CALL_TYPE_VT_TX:
+                videostate = VideoProfile.STATE_TX_ENABLED;
+                break;
+            case CALL_TYPE_VT_RX:
+                videostate = VideoProfile.STATE_RX_ENABLED;
+                break;
+            case CALL_TYPE_VT:
+                videostate = VideoProfile.STATE_BIDIRECTIONAL;
+                break;
+            case CALL_TYPE_VOICE:
+                videostate = VideoProfile.STATE_AUDIO_ONLY;
+                break;
+            default:
+                videostate = VideoProfile.STATE_AUDIO_ONLY;
+                break;
+        }
+        return videostate;
+    }
+
+    /**
+     * Converts from the video state values defined in {@link VideoProfile}
+     * to the call types defined in {@link ImsCallProfile}.
+     *
+     * @param videoState The video state.
+     * @return The call type.
+     */
+    public static int getCallTypeFromVideoState(int videoState) {
+        boolean videoTx = isVideoStateSet(videoState, VideoProfile.STATE_TX_ENABLED);
+        boolean videoRx = isVideoStateSet(videoState, VideoProfile.STATE_RX_ENABLED);
+        boolean isPaused = isVideoStateSet(videoState, VideoProfile.STATE_PAUSED);
+        if (isPaused) {
+            return ImsCallProfile.CALL_TYPE_VT_NODIR;
+        } else if (videoTx && !videoRx) {
+            return ImsCallProfile.CALL_TYPE_VT_TX;
+        } else if (!videoTx && videoRx) {
+            return ImsCallProfile.CALL_TYPE_VT_RX;
+        } else if (videoTx && videoRx) {
+            return ImsCallProfile.CALL_TYPE_VT;
+        }
+        return ImsCallProfile.CALL_TYPE_VOICE;
+    }
+
+    /**
+     * Badly named old method, kept for compatibility.
+     * See {@link #presentationToOir(int)}.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public static int presentationToOIR(int presentation) {
+        switch (presentation) {
+            case PhoneConstants.PRESENTATION_RESTRICTED:
+                return ImsCallProfile.OIR_PRESENTATION_RESTRICTED;
+            case PhoneConstants.PRESENTATION_ALLOWED:
+                return ImsCallProfile.OIR_PRESENTATION_NOT_RESTRICTED;
+            case PhoneConstants.PRESENTATION_PAYPHONE:
+                return ImsCallProfile.OIR_PRESENTATION_PAYPHONE;
+            case PhoneConstants.PRESENTATION_UNKNOWN:
+                return ImsCallProfile.OIR_PRESENTATION_UNKNOWN;
+            case PhoneConstants.PRESENTATION_UNAVAILABLE:
+                return ImsCallProfile.OIR_PRESENTATION_UNAVAILABLE;
+            default:
+                return ImsCallProfile.OIR_DEFAULT;
+        }
+    }
+
+    /**
+     * Translate presentation value to OIR value
+     * @param presentation
+     * @return OIR values
+     */
+    public static int presentationToOir(int presentation) {
+        return presentationToOIR(presentation);
+    }
+
+    /**
+     * Translate OIR value to presentation value
+     * @param oir value
+     * @return presentation value
+     * @hide
+     */
+    public static int OIRToPresentation(int oir) {
+        switch(oir) {
+            case ImsCallProfile.OIR_PRESENTATION_RESTRICTED:
+                return PhoneConstants.PRESENTATION_RESTRICTED;
+            case ImsCallProfile.OIR_PRESENTATION_NOT_RESTRICTED:
+                return PhoneConstants.PRESENTATION_ALLOWED;
+            case ImsCallProfile.OIR_PRESENTATION_PAYPHONE:
+                return PhoneConstants.PRESENTATION_PAYPHONE;
+            case ImsCallProfile.OIR_PRESENTATION_UNAVAILABLE:
+                return PhoneConstants.PRESENTATION_UNAVAILABLE;
+            case ImsCallProfile.OIR_PRESENTATION_UNKNOWN:
+                return PhoneConstants.PRESENTATION_UNKNOWN;
+            default:
+                return PhoneConstants.PRESENTATION_UNKNOWN;
+        }
+    }
+
+    /**
+     * Checks if video call is paused
+     * @return true if call is video paused
+     */
+    public boolean isVideoPaused() {
+        return mMediaProfile.mVideoDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE;
+    }
+
+    /**
+     * Determines if the {@link ImsCallProfile} represents a video call.
+     *
+     * @return {@code true} if the profile is for a video call, {@code false} otherwise.
+     */
+    public boolean isVideoCall() {
+        return VideoProfile.isVideo(getVideoStateFromCallType(mCallType));
+    }
+
+    /**
+     * Cleanses a {@link Bundle} to ensure that it contains only data of type:
+     * 1. Primitive data types (e.g. int, bool, and other values determined by
+     * {@link android.os.PersistableBundle#isValidType(Object)}).
+     * 2. Other Bundles.
+     * 3. {@link Parcelable} objects in the {@code android.*} namespace.
+     * @param extras the source {@link Bundle}
+     * @return where all elements are valid types the source {@link Bundle} is returned unmodified,
+     *      otherwise a copy of the {@link Bundle} with the invalid elements is returned.
+     */
+    private Bundle maybeCleanseExtras(Bundle extras) {
+        if (extras == null) {
+            return null;
+        }
+
+        int startSize = extras.size();
+        Bundle filtered = TelephonyUtils.filterValues(extras);
+        int endSize = filtered.size();
+        if (startSize != endSize) {
+            Log.i(TAG, "maybeCleanseExtras: " + (startSize - endSize) + " extra values were "
+                    + "removed - only primitive types and system parcelables are permitted.");
+        }
+        return filtered;
+    }
+
+    /**
+     * Determines if a video state is set in a video state bit-mask.
+     *
+     * @param videoState The video state bit mask.
+     * @param videoStateToCheck The particular video state to check.
+     * @return True if the video state is set in the bit-mask.
+     */
+    private static boolean isVideoStateSet(int videoState, int videoStateToCheck) {
+        return (videoState & videoStateToCheck) == videoStateToCheck;
+    }
+
+    /**
+     * Set the emergency number information. The set value is valid
+     * only if {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * Reference: 3gpp 23.167, Section 6 - Functional description;
+     *            3gpp 24.503, Section 5.1.6.8.1 - General;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     *
+     * @hide
+     */
+    public void setEmergencyCallInfo(EmergencyNumber num, boolean hasKnownUserIntentEmergency) {
+        setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmaskInternalDial());
+        setEmergencyUrns(num.getEmergencyUrns());
+        setEmergencyCallRouting(num.getEmergencyCallRouting());
+        setEmergencyCallTesting(num.getEmergencyNumberSourceBitmask()
+                == EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST);
+        setHasKnownUserIntentEmergency(hasKnownUserIntentEmergency);
+    }
+
+    /**
+     * Set the emergency service categories. The set value is valid only if
+     * {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * If valid, the value is the bitwise-OR combination of the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+     * </ol>
+     *
+     * Reference: 3gpp 23.167, Section 6 - Functional description;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     */
+    @VisibleForTesting
+    public void setEmergencyServiceCategories(
+            @EmergencyServiceCategories int emergencyServiceCategories) {
+        mEmergencyServiceCategories = emergencyServiceCategories;
+    }
+
+    /**
+     * Set the emergency Uniform Resource Names (URN), only valid if {@link #getServiceType}
+     * returns {@link #SERVICE_TYPE_EMERGENCY}.
+     *
+     * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     */
+    @VisibleForTesting
+    public void setEmergencyUrns(@NonNull List<String> emergencyUrns) {
+        mEmergencyUrns = emergencyUrns;
+    }
+
+    /**
+     * Set the emergency call routing, only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * If valid, the value is any of the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+     * </ol>
+     */
+    @VisibleForTesting
+    public void setEmergencyCallRouting(@EmergencyCallRouting int emergencyCallRouting) {
+        mEmergencyCallRouting = emergencyCallRouting;
+    }
+
+    /**
+     * Set if this is for testing emergency call, only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}.
+     */
+    @VisibleForTesting
+    public void setEmergencyCallTesting(boolean isTesting) {
+        mEmergencyCallTesting = isTesting;
+    }
+
+    /**
+     * Set if we have known the user intent of the call is emergency.
+     *
+     * This is only used to specify when the dialed number is ambiguous when it can be identified
+     * as both emergency number and any other non-emergency number; e.g. in some situation, 611
+     * could be both an emergency number in a country and a non-emergency number of a carrier's
+     * customer service hotline.
+     */
+    @VisibleForTesting
+    public void setHasKnownUserIntentEmergency(boolean hasKnownUserIntentEmergency) {
+        mHasKnownUserIntentEmergency = hasKnownUserIntentEmergency;
+    }
+
+    /**
+     * Get the emergency service categories, only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * @return the emergency service categories,
+     *
+     * If valid, the value is the bitwise-OR combination of the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+     * </ol>
+     *
+     * Reference: 3gpp 23.167, Section 6 - Functional description;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     */
+    public @EmergencyServiceCategories int getEmergencyServiceCategories() {
+        return mEmergencyServiceCategories;
+    }
+
+    /**
+     * Get the emergency Uniform Resource Names (URN), only valid if {@link #getServiceType}
+     * returns {@link #SERVICE_TYPE_EMERGENCY}.
+     *
+     * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     */
+    public @NonNull List<String> getEmergencyUrns() {
+        return mEmergencyUrns;
+    }
+
+    /**
+     * Get the emergency call routing, only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * If valid, the value is any of the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+     * </ol>
+     */
+    public @EmergencyCallRouting int getEmergencyCallRouting() {
+        return mEmergencyCallRouting;
+    }
+
+    /**
+     * Get if the emergency call is for testing purpose.
+     */
+    public boolean isEmergencyCallTesting() {
+        return mEmergencyCallTesting;
+    }
+
+    /**
+     * Checks if we have known the user intent of the call is emergency.
+     *
+     * This is only used to specify when the dialed number is ambiguous when it can be identified
+     * as both emergency number and any other non-emergency number; e.g. in some situation, 611
+     * could be both an emergency number in a country and a non-emergency number of a carrier's
+     * customer service hotline.
+     */
+    public boolean hasKnownUserIntentEmergency() {
+        return mHasKnownUserIntentEmergency;
+    }
+
+    /**
+     * Gets the {@link RtpHeaderExtensionType}s which have been accepted by both ends of the call.
+     * <p>
+     * According to RFC8285, RTP header extensions available to a call are determined using the
+     * offer/accept phase of the SDP protocol (see RFC4566).
+     * <p>
+     * The offered header extension types supported by the framework and exposed to the
+     * {@link ImsService} via {@link MmTelFeature#changeOfferedRtpHeaderExtensionTypes(Set)}.
+     *
+     * @return the {@link RtpHeaderExtensionType}s which were accepted by the other end of the call.
+     */
+    public @NonNull Set<RtpHeaderExtensionType> getAcceptedRtpHeaderExtensionTypes() {
+        return mAcceptedRtpHeaderExtensionTypes;
+    }
+
+    /**
+     * Sets the accepted {@link RtpHeaderExtensionType}s for this call.
+     * <p>
+     * According to RFC8285, RTP header extensions available to a call are determined using the
+     * offer/accept phase of the SDP protocol (see RFC4566).
+     *
+     * @param rtpHeaderExtensions
+     */
+    public void setAcceptedRtpHeaderExtensionTypes(@NonNull Set<RtpHeaderExtensionType>
+            rtpHeaderExtensions) {
+        mAcceptedRtpHeaderExtensionTypes.clear();
+        mAcceptedRtpHeaderExtensionTypes.addAll(rtpHeaderExtensions);
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsCallSession.java b/android-35/android/telephony/ims/ImsCallSession.java
new file mode 100755
index 0000000..d30078d
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsCallSession.java
@@ -0,0 +1,1793 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.CallQuality;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+/**
+ * Provides the call initiation/termination, and media exchange between two IMS endpoints.
+ * It directly communicates with IMS service which implements the IMS protocol behavior.
+ *
+ * @hide
+ */
+public class ImsCallSession {
+    private static final String TAG = "ImsCallSession";
+
+    /**
+     * Defines IMS call session state. Please use
+     * {@link android.telephony.ims.stub.ImsCallSessionImplBase.State} definition.
+     * This is kept around for capability reasons.
+     */
+    public static class State {
+        public static final int IDLE = 0;
+        public static final int INITIATED = 1;
+        public static final int NEGOTIATING = 2;
+        public static final int ESTABLISHING = 3;
+        public static final int ESTABLISHED = 4;
+
+        public static final int RENEGOTIATING = 5;
+        public static final int REESTABLISHING = 6;
+
+        public static final int TERMINATING = 7;
+        public static final int TERMINATED = 8;
+
+        public static final int INVALID = (-1);
+
+        /**
+         * Converts the state to string.
+         */
+        public static String toString(int state) {
+            switch (state) {
+                case IDLE:
+                    return "IDLE";
+                case INITIATED:
+                    return "INITIATED";
+                case NEGOTIATING:
+                    return "NEGOTIATING";
+                case ESTABLISHING:
+                    return "ESTABLISHING";
+                case ESTABLISHED:
+                    return "ESTABLISHED";
+                case RENEGOTIATING:
+                    return "RENEGOTIATING";
+                case REESTABLISHING:
+                    return "REESTABLISHING";
+                case TERMINATING:
+                    return "TERMINATING";
+                case TERMINATED:
+                    return "TERMINATED";
+                default:
+                    return "UNKNOWN";
+            }
+        }
+
+        private State() {
+        }
+    }
+
+    /**
+     * Listener for events relating to an IMS session, such as when a session is being
+     * recieved ("on ringing") or a call is outgoing ("on calling").
+     * <p>Many of these events are also received by {@link ImsCall.Listener}.</p>
+     */
+    public static class Listener {
+        /**
+         * Called when the session is initiating.
+         *
+         * see: {@link ImsCallSessionListener#callSessionInitiating(ImsCallProfile)}
+         */
+        public void callSessionInitiating(ImsCallSession session,
+                ImsCallProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the session failed before initiating was called.
+         *
+         * see: {@link ImsCallSessionListener#callSessionInitiatingFailed(ImsReasonInfo)}
+         */
+        public void callSessionInitiatingFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when the session is progressing.
+         *
+         * see: {@link ImsCallSessionListener#callSessionProgressing(ImsStreamMediaProfile)}
+         */
+        public void callSessionProgressing(ImsCallSession session,
+                ImsStreamMediaProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the session is established.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionStarted(ImsCallSession session,
+                ImsCallProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the session establishment is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session establishment failure
+         */
+        public void callSessionStartFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session is terminated.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session termination
+         */
+        public void callSessionTerminated(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session is in hold.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionHeld(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session hold is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session hold failure
+         */
+        public void callSessionHoldFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session hold is received from the remote user.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionHoldReceived(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session resume is done.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionResumed(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session resume is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session resume failure
+         */
+        public void callSessionResumeFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session resume is received from the remote user.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionResumeReceived(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session merge has been started.  At this point, the {@code newSession}
+         * represents the session which has been initiated to the IMS conference server for the
+         * new merged conference.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param newSession the session object that is merged with an active & hold session
+         */
+        public void callSessionMergeStarted(ImsCallSession session,
+                ImsCallSession newSession, ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session merge is successful and the merged session is active.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionMergeComplete(ImsCallSession session) {
+        }
+
+        /**
+         * Called when the session merge has failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the call merge failure
+         */
+        public void callSessionMergeFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session is updated (except for hold/unhold).
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionUpdated(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session update is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session update failure
+         */
+        public void callSessionUpdateFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session update is received from the remote user.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionUpdateReceived(ImsCallSession session,
+                ImsCallProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the session is extended to the conference session.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param newSession the session object that is extended to the conference
+         *      from the active session
+         */
+        public void callSessionConferenceExtended(ImsCallSession session,
+                ImsCallSession newSession, ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the conference extension is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the conference extension failure
+         */
+        public void callSessionConferenceExtendFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the conference extension is received from the remote user.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionConferenceExtendReceived(ImsCallSession session,
+                ImsCallSession newSession, ImsCallProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the invitation request of the participants is delivered to the conference
+         * server.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionInviteParticipantsRequestDelivered(ImsCallSession session) {
+            // no-op
+        }
+
+        /**
+         * Called when the invitation request of the participants is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the conference invitation failure
+         */
+        public void callSessionInviteParticipantsRequestFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when the removal request of the participants is delivered to the conference
+         * server.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionRemoveParticipantsRequestDelivered(ImsCallSession session) {
+            // no-op
+        }
+
+        /**
+         * Called when the removal request of the participants is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the conference removal failure
+         */
+        public void callSessionRemoveParticipantsRequestFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when the conference state is updated.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionConferenceStateUpdated(ImsCallSession session,
+                ImsConferenceState state) {
+            // no-op
+        }
+
+        /**
+         * Called when the USSD message is received from the network.
+         *
+         * @param mode mode of the USSD message (REQUEST / NOTIFY)
+         * @param ussdMessage USSD message
+         */
+        public void callSessionUssdMessageReceived(ImsCallSession session,
+                int mode, String ussdMessage) {
+            // no-op
+        }
+
+        /**
+         * Called when an {@link ImsCallSession} may handover from one network type to another.
+         * For example, the session may handover from WIFI to LTE if conditions are right.
+         * <p>
+         * If handover is attempted,
+         * {@link #callSessionHandover(ImsCallSession, int, int, ImsReasonInfo)} or
+         * {@link #callSessionHandoverFailed(ImsCallSession, int, int, ImsReasonInfo)} will be
+         * called to indicate the success or failure of the handover.
+         *
+         * @param session IMS session object
+         * @param srcNetworkType original network type
+         * @param targetNetworkType new network type
+         */
+        public void callSessionMayHandover(ImsCallSession session, int srcNetworkType,
+                int targetNetworkType) {
+            // no-op
+        }
+
+        /**
+         * Called when session network type changes
+         *
+         * @param session IMS session object
+         * @param srcNetworkType original network type
+         * @param targetNetworkType new network type
+         * @param reasonInfo
+         */
+        public void callSessionHandover(ImsCallSession session,
+                                 int srcNetworkType, int targetNetworkType,
+                                 ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when session access technology change fails
+         *
+         * @param session IMS session object
+         * @param srcNetworkType original access technology
+         * @param targetNetworkType new access technology
+         * @param reasonInfo handover failure reason
+         */
+        public void callSessionHandoverFailed(ImsCallSession session,
+                                       int srcNetworkType, int targetNetworkType,
+                                       ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when TTY mode of remote party changed
+         *
+         * @param session IMS session object
+         * @param mode TTY mode of remote party
+         */
+        public void callSessionTtyModeReceived(ImsCallSession session,
+                                       int mode) {
+            // no-op
+        }
+
+        /**
+         * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+         *
+         * @param session The call session.
+         * @param isMultiParty {@code true} if the session became multiparty, {@code false}
+         *      otherwise.
+         */
+        public void callSessionMultipartyStateChanged(ImsCallSession session,
+                boolean isMultiParty) {
+            // no-op
+        }
+
+        /**
+         * Called when the session supplementary service is received
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionSuppServiceReceived(ImsCallSession session,
+                ImsSuppServiceNotification suppServiceInfo) {
+        }
+
+        /**
+         * Received RTT modify request from Remote Party
+         */
+        public void callSessionRttModifyRequestReceived(ImsCallSession session,
+            ImsCallProfile callProfile) {
+            // no-op
+        }
+
+        /**
+         * Received response for RTT modify request
+         */
+        public void callSessionRttModifyResponseReceived(int status) {
+            // no -op
+        }
+
+        /**
+         * Device received RTT message from Remote UE
+         */
+        public void callSessionRttMessageReceived(String rttMessage) {
+            // no-op
+        }
+
+        /**
+         * While in call, there has been a change in RTT audio indicator.
+         */
+        public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Received success response for call transfer request.
+         */
+        public void callSessionTransferred(@NonNull ImsCallSession session) {
+            // no-op
+        }
+
+        /**
+         * Received failure response for call transfer request.
+         */
+        public void callSessionTransferFailed(@NonNull ImsCallSession session,
+                @Nullable ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Informs the framework of a DTMF digit which was received from the network.
+         * <p>
+         * According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833 sec 3.10</a>,
+         * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to
+         * 12 ~ 15.
+         * @param digit the DTMF digit
+         */
+        public void callSessionDtmfReceived(char digit) {
+            // no-op
+        }
+
+        /**
+         * Called when the IMS service reports a change to the call quality.
+         */
+        public void callQualityChanged(CallQuality callQuality) {
+            // no-op
+        }
+
+        /**
+         * Called when the IMS service reports incoming RTP header extension data.
+         */
+        public void callSessionRtpHeaderExtensionsReceived(
+                @NonNull Set<RtpHeaderExtension> extensions) {
+            // no-op
+        }
+
+        /**
+         * Called when radio to send ANBRQ message to the access network to query the desired
+         * bitrate.
+         */
+        public void callSessionSendAnbrQuery(int mediaType, int direction, int bitsPerSecond) {
+            // no-op
+        }
+    }
+
+    private final IImsCallSession miSession;
+    private boolean mClosed = false;
+    private String mCallId = null;
+    private Listener mListener;
+    private Executor mListenerExecutor = Runnable::run;
+    private IImsCallSessionListenerProxy mIImsCallSessionListenerProxy = null;
+
+    public ImsCallSession(IImsCallSession iSession) {
+        miSession = iSession;
+        mIImsCallSessionListenerProxy = new IImsCallSessionListenerProxy();
+
+        if (iSession != null) {
+            try {
+                iSession.setListener(mIImsCallSessionListenerProxy);
+            } catch (RemoteException e) {
+                if (Flags.ignoreAlreadyTerminatedIncomingCallBeforeRegisteringListener()) {
+                    // Registering listener failed, so other operations are not allowed.
+                    Log.e(TAG, "ImsCallSession : " + e);
+                    mClosed = true;
+                }
+            }
+        } else {
+            mClosed = true;
+        }
+    }
+
+    public ImsCallSession(IImsCallSession iSession, Listener listener, Executor executor) {
+        this(iSession);
+        setListener(listener, executor);
+    }
+
+    /**
+     * returns the IImsCallSessionListenerProxy for the ImsCallSession
+     */
+    public final IImsCallSessionListenerProxy getIImsCallSessionListenerProxy() {
+        return mIImsCallSessionListenerProxy;
+    }
+
+    /**
+     * Closes this object. This object is not usable after being closed.
+     */
+    public void close() {
+        synchronized (this) {
+            if (mClosed) {
+                return;
+            }
+
+            try {
+                miSession.close();
+                mClosed = true;
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    /**
+     * Gets the call ID of the session.
+     *
+     * @return the call ID
+     * If null is returned for getCallId, then that means that the call ID has not been set yet.
+     */
+    public String getCallId() {
+        if (mClosed) {
+            return null;
+        }
+
+        if (mCallId != null) {
+            return mCallId;
+        } else {
+            try {
+                return mCallId = miSession.getCallId();
+            } catch (RemoteException e) {
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Sets the call ID of the session.
+     *
+     * @param callId Call ID of the session, which is transferred from
+     * {@link android.telephony.ims.feature.MmTelFeature#notifyIncomingCall(
+     * ImsCallSessionImplBase, String, Bundle)}
+     */
+    public void setCallId(String callId) {
+        if (callId != null) {
+            mCallId = callId;
+        }
+    }
+
+    /**
+     * Gets the call profile that this session is associated with
+     *
+     * @return the call profile that this session is associated with
+     */
+    public ImsCallProfile getCallProfile() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getCallProfile();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the local call profile that this session is associated with
+     *
+     * @return the local call profile that this session is associated with
+     */
+    public ImsCallProfile getLocalCallProfile() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getLocalCallProfile();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the remote call profile that this session is associated with
+     *
+     * @return the remote call profile that this session is associated with
+     */
+    public ImsCallProfile getRemoteCallProfile() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getRemoteCallProfile();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the video call provider for the session.
+     *
+     * @return The video call provider.
+     */
+    public IImsVideoCallProvider getVideoCallProvider() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getVideoCallProvider();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the value associated with the specified property of this session.
+     *
+     * @return the string value associated with the specified property
+     */
+    public String getProperty(String name) {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getProperty(name);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the session state.
+     * The value returned must be one of the states in {@link State}.
+     *
+     * @return the session state
+     */
+    public int getState() {
+        if (mClosed) {
+            return State.INVALID;
+        }
+
+        try {
+            return miSession.getState();
+        } catch (RemoteException e) {
+            return State.INVALID;
+        }
+    }
+
+    /**
+     * Determines if the {@link ImsCallSession} is currently alive (e.g. not in a terminated or
+     * closed state).
+     *
+     * @return {@code True} if the session is alive.
+     */
+    public boolean isAlive() {
+        if (mClosed) {
+            return false;
+        }
+
+        int state = getState();
+        switch (state) {
+            case State.IDLE:
+            case State.INITIATED:
+            case State.NEGOTIATING:
+            case State.ESTABLISHING:
+            case State.ESTABLISHED:
+            case State.RENEGOTIATING:
+            case State.REESTABLISHING:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Gets the native IMS call session.
+     */
+    public IImsCallSession getSession() {
+        return miSession;
+    }
+
+    /**
+     * Checks if the session is in call.
+     *
+     * @return true if the session is in call
+     */
+    public boolean isInCall() {
+        if (mClosed) {
+            return false;
+        }
+
+        try {
+            return miSession.isInCall();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Sets the listener to listen to the session events. A {@link ImsCallSession}
+     * can only hold one listener at a time. Subsequent calls to this method
+     * override the previous listener.
+     *
+     * @param listener to listen to the session events of this object
+     * @param executor an Executor that will execute callbacks
+     */
+    public void setListener(Listener listener, Executor executor) {
+        mListener = listener;
+        if (executor != null) {
+            mListenerExecutor = executor;
+        }
+    }
+
+    /**
+     * Mutes or unmutes the mic for the active call.
+     *
+     * @param muted true if the call is muted, false otherwise
+     */
+    public void setMute(boolean muted) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.setMute(muted);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Initiates an IMS call with the specified target and call profile.
+     * The session listener is called back upon defined session events.
+     * The method is only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param callee dial string to make the call to.  The platform passes the dialed number
+     *               entered by the user as-is.  The {@link ImsService} should ensure that the
+     *               number is formatted in SIP messages appropriately (e.g. using
+     *               {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}).
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see Listener#callSessionStarted, Listener#callSessionStartFailed
+     */
+    public void start(String callee, ImsCallProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.start(callee, profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Initiates an IMS conference call with the specified target and call profile.
+     * The session listener is called back upon defined session events.
+     * The method is only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param participants participant list to initiate an IMS conference call.  The platform passes
+     *               the dialed numbers entered by the user as-is.  The {@link ImsService} should
+     *               ensure that the number is formatted in SIP messages appropriately (e.g. using
+     *               {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}).
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see Listener#callSessionStarted, Listener#callSessionStartFailed
+     */
+    public void start(String[] participants, ImsCallProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.startConference(participants, profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Accepts an incoming call or session update.
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be answered
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+     * @see Listener#callSessionStarted
+     */
+    public void accept(int callType, ImsStreamMediaProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.accept(callType, profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Deflects an incoming call.
+     *
+     * @param number number to be deflected to
+     */
+    public void deflect(String number) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.deflect(number);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Rejects an incoming call or session update.
+     *
+     * @param reason reason code to reject an incoming call
+     * @see Listener#callSessionStartFailed
+     */
+    public void reject(int reason) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.reject(reason);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Transfers an ongoing call.
+     *
+     * @param number number to be transferred to.
+     * @param isConfirmationRequired indicates whether confirmation of the transfer is required.
+     */
+    public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.transfer(number, isConfirmationRequired);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Transfers a call to another ongoing call.
+     *
+     * @param transferToSession the other ImsCallSession to which this session will be transferred.
+     */
+    public void transfer(@NonNull ImsCallSession transferToSession) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            if (transferToSession != null) {
+                miSession.consultativeTransfer(transferToSession.getSession());
+            }
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Terminates a call.
+     *
+     * @see Listener#callSessionTerminated
+     */
+    public void terminate(int reason) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.terminate(reason);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Puts a call on hold. When it succeeds, {@link Listener#callSessionHeld} is called.
+     *
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+     * @see Listener#callSessionHeld, Listener#callSessionHoldFailed
+     */
+    public void hold(ImsStreamMediaProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.hold(profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Continues a call that's on hold. When it succeeds,
+     * {@link Listener#callSessionResumed} is called.
+     *
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to resume the call
+     * @see Listener#callSessionResumed, Listener#callSessionResumeFailed
+     */
+    public void resume(ImsStreamMediaProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.resume(profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Merges the active & hold call. When it succeeds,
+     * {@link Listener#callSessionMergeStarted} is called.
+     *
+     * @see Listener#callSessionMergeStarted , Listener#callSessionMergeFailed
+     */
+    public void merge() {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.merge();
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be updated
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+     * @see Listener#callSessionUpdated, Listener#callSessionUpdateFailed
+     */
+    public void update(int callType, ImsStreamMediaProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.update(callType, profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Extends this call to the conference call with the specified recipients.
+     *
+     * @param participants list to be invited to the conference call after extending the call
+     * @see Listener#callSessionConferenceExtended
+     * @see Listener#callSessionConferenceExtendFailed
+     */
+    public void extendToConference(String[] participants) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.extendToConference(participants);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Requests the conference server to invite an additional participants to the conference.
+     *
+     * @param participants list to be invited to the conference call
+     * @see Listener#callSessionInviteParticipantsRequestDelivered
+     * @see Listener#callSessionInviteParticipantsRequestFailed
+     */
+    public void inviteParticipants(String[] participants) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.inviteParticipants(participants);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Requests the conference server to remove the specified participants from the conference.
+     *
+     * @param participants participant list to be removed from the conference call
+     * @see Listener#callSessionRemoveParticipantsRequestDelivered
+     * @see Listener#callSessionRemoveParticipantsRequestFailed
+     */
+    public void removeParticipants(String[] participants) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.removeParticipants(participants);
+        } catch (RemoteException e) {
+        }
+    }
+
+
+    /**
+     * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    public void sendDtmf(char c, Message result) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.sendDtmf(c, result);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Starts a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    public void startDtmf(char c) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.startDtmf(c);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Stops a DTMF code.
+     */
+    public void stopDtmf() {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.stopDtmf();
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Sends an USSD message.
+     *
+     * @param ussdMessage USSD message to send
+     */
+    public void sendUssd(String ussdMessage) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.sendUssd(ussdMessage);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Determines if the session is multiparty.
+     *
+     * @return {@code True} if the session is multiparty.
+     */
+    public boolean isMultiparty() {
+        if (mClosed) {
+            return false;
+        }
+
+        try {
+            return miSession.isMultiparty();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Sends Rtt Message
+     *
+     * @param rttMessage rtt text to be sent
+     */
+    public void sendRttMessage(String rttMessage) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.sendRttMessage(rttMessage);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Sends RTT Upgrade or downgrade request
+     *
+     * @param to Profile with the RTT flag set to the desired value
+     */
+    public void sendRttModifyRequest(ImsCallProfile to) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.sendRttModifyRequest(to);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Sends RTT Upgrade response
+     *
+     * @param response : response for upgrade
+     */
+    public void sendRttModifyResponse(boolean response) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.sendRttModifyResponse(response);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Requests that {@code rtpHeaderExtensions} are sent as a header extension with the next
+     * RTP packet sent by the IMS stack.
+     * <p>
+     * The {@link RtpHeaderExtensionType}s negotiated during SDP (Session Description Protocol)
+     * signalling determine the {@link RtpHeaderExtension}s which can be sent using this method.
+     * See RFC8285 for more information.
+     * <p>
+     * By specification, the RTP header extension is an unacknowledged transmission and there is no
+     * guarantee that the header extension will be delivered by the network to the other end of the
+     * call.
+     * @param rtpHeaderExtensions The header extensions to be included in the next RTP header.
+     */
+    public void sendRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.sendRtpHeaderExtensions(
+                    new ArrayList<RtpHeaderExtension>(rtpHeaderExtensions));
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Deliver the bitrate for the indicated media type, direction and bitrate to the upper layer.
+     *
+     * @param mediaType MediaType is used to identify media stream such as audio or video.
+     * @param direction Direction of this packet stream (e.g. uplink or downlink).
+     * @param bitsPerSecond This value is the bitrate received from the NW through the Recommended
+     *        bitrate MAC Control Element message and ImsStack converts this value from MAC bitrate
+     *        to audio/video codec bitrate (defined in TS26.114).
+     */
+    public void callSessionNotifyAnbr(int mediaType, int direction, int bitsPerSecond) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.callSessionNotifyAnbr(mediaType, direction, bitsPerSecond);
+        } catch (RemoteException e) {
+            Log.e(TAG, "callSessionNotifyAnbr" + e);
+        }
+    }
+
+    /**
+     * A listener type for receiving notification on IMS call session events.
+     * When an event is generated for an {@link IImsCallSession},
+     * the application is notified by having one of the methods called on
+     * the {@link IImsCallSessionListener}.
+     */
+    private class IImsCallSessionListenerProxy extends IImsCallSessionListener.Stub {
+        /**
+         * Notifies the result of the basic session operation (setup / terminate).
+         */
+        @Override
+        public void callSessionInitiating(ImsCallProfile profile) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionInitiating(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionProgressing(ImsStreamMediaProfile profile) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionProgressing(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionInitiated(ImsCallProfile profile) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionStarted(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionInitiatingFailed(ImsReasonInfo reasonInfo) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionTerminated(ImsReasonInfo reasonInfo) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Notifies the result of the call hold/resume operation.
+         */
+        @Override
+        public void callSessionHeld(ImsCallProfile profile) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionHeld(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionHoldReceived(ImsCallProfile profile) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionHoldReceived(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionResumed(ImsCallProfile profile) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionResumed(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionResumeReceived(ImsCallProfile profile) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionResumeReceived(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Notifies the start of a call merge operation.
+         *
+         * @param newSession The merged call session.
+         * @param profile The call profile.
+         */
+        @Override
+        public void callSessionMergeStarted(IImsCallSession newSession, ImsCallProfile profile) {
+            // This callback can be used for future use to add additional
+            // functionality that may be needed between conference start and complete
+            Log.d(TAG, "callSessionMergeStarted");
+        }
+
+        /**
+         * Notifies the successful completion of a call merge operation.
+         *
+         * @param newSession The call session.
+         */
+        @Override
+        public void callSessionMergeComplete(IImsCallSession newSession) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    if (newSession != null) {
+                        // New session created after conference
+                        mListener.callSessionMergeComplete(new ImsCallSession(newSession));
+                    } else {
+                        // Session already exists. Hence no need to pass
+                        mListener.callSessionMergeComplete(null);
+                    }
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Notifies of a failure to perform a call merge operation.
+         *
+         * @param reasonInfo The merge failure reason.
+         */
+        @Override
+        public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Notifies the result of call upgrade / downgrade or any other call updates.
+         */
+        @Override
+        public void callSessionUpdated(ImsCallProfile profile) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionUpdated(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionUpdateReceived(ImsCallProfile profile) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Notifies the result of conference extension.
+         */
+        @Override
+        public void callSessionConferenceExtended(IImsCallSession newSession,
+                ImsCallProfile profile) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionConferenceExtended(ImsCallSession.this,
+                            new ImsCallSession(newSession), profile);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionConferenceExtendFailed(
+                            ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionConferenceExtendReceived(IImsCallSession newSession,
+                ImsCallProfile profile) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
+                            new ImsCallSession(newSession), profile);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Notifies the result of the participant invitation / removal to/from
+         * the conference session.
+         */
+        @Override
+        public void callSessionInviteParticipantsRequestDelivered() {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionInviteParticipantsRequestDelivered(
+                            ImsCallSession.this);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
+                            reasonInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestDelivered() {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
+                            reasonInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Notifies the changes of the conference info. in the conference session.
+         */
+        @Override
+        public void callSessionConferenceStateUpdated(ImsConferenceState state) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Notifies the incoming USSD message.
+         */
+        @Override
+        public void callSessionUssdMessageReceived(int mode, String ussdMessage) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode,
+                            ussdMessage);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Notifies of a case where a {@link ImsCallSession} may
+         * potentially handover from one radio technology to another.
+         * @param srcNetworkType The source network type; one of the network type constants defined
+         *                       in {@link android.telephony.TelephonyManager}.  For example
+         *                      {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
+         * @param targetNetworkType The target radio access technology; one of the network type
+         *                          constants defined in {@link android.telephony.TelephonyManager}.
+         *                          For example
+         *                          {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
+         */
+        @Override
+        public void callSessionMayHandover(int srcNetworkType, int targetNetworkType) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionMayHandover(ImsCallSession.this, srcNetworkType,
+                            targetNetworkType);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Notifies of handover information for this call
+         */
+        @Override
+        public void callSessionHandover(int srcNetworkType, int targetNetworkType,
+                ImsReasonInfo reasonInfo) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionHandover(ImsCallSession.this, srcNetworkType,
+                            targetNetworkType, reasonInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Notifies of handover failure info for this call
+         */
+        @Override
+        public void callSessionHandoverFailed(int srcNetworkType, int targetNetworkType,
+                ImsReasonInfo reasonInfo) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionHandoverFailed(ImsCallSession.this, srcNetworkType,
+                            targetNetworkType, reasonInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Notifies the TTY mode received from remote party.
+         */
+        @Override
+        public void callSessionTtyModeReceived(int mode) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+         *
+         * @param isMultiParty {@code true} if the session became multiparty, {@code false}
+         *      otherwise.
+         */
+        public void callSessionMultipartyStateChanged(boolean isMultiParty) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionMultipartyStateChanged(ImsCallSession.this,
+                            isMultiParty);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppServiceInfo ) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionSuppServiceReceived(ImsCallSession.this,
+                            suppServiceInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Received RTT modify request from remote party
+         */
+        @Override
+        public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRttModifyRequestReceived(ImsCallSession.this,
+                            callProfile);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Received response for RTT modify request
+         */
+        @Override
+        public void callSessionRttModifyResponseReceived(int status) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRttModifyResponseReceived(status);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * RTT Message received
+         */
+        @Override
+        public void callSessionRttMessageReceived(String rttMessage) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRttMessageReceived(rttMessage);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * While in call, there has been a change in RTT audio indicator.
+         */
+        @Override
+        public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRttAudioIndicatorChanged(profile);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionTransferred() {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionTransferred(ImsCallSession.this);
+                }
+            }, mListenerExecutor);
+        }
+
+        @Override
+        public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * DTMF digit received.
+         * @param dtmf The DTMF digit.
+         */
+        @Override
+        public void callSessionDtmfReceived(char dtmf) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionDtmfReceived(dtmf);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * Call quality updated
+         */
+        @Override
+        public void callQualityChanged(CallQuality callQuality) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callQualityChanged(callQuality);
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * RTP header extension data received.
+         * @param extensions The header extension data.
+         */
+        @Override
+        public void callSessionRtpHeaderExtensionsReceived(
+                @NonNull List<RtpHeaderExtension> extensions) {
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionRtpHeaderExtensionsReceived(
+                            new ArraySet<RtpHeaderExtension>(extensions));
+                }
+            }, mListenerExecutor);
+        }
+
+        /**
+         * ANBR Query received.
+         *
+         * @param mediaType MediaType is used to identify media stream such as audio or video.
+         * @param direction Direction of this packet stream (e.g. uplink or downlink).
+         * @param bitsPerSecond This value is the bitrate requested by the other party UE through
+         *        RTP CMR, RTCPAPP or TMMBR, and ImsStack converts this value to the MAC bitrate
+         *        (defined in TS36.321, range: 0 ~ 8000 kbit/s).
+         */
+        @Override
+        public void callSessionSendAnbrQuery(int mediaType, int direction,
+                int bitsPerSecond) {
+            Log.d(TAG, "callSessionSendAnbrQuery in ImsCallSession");
+            TelephonyUtils.runWithCleanCallingIdentity(()-> {
+                if (mListener != null) {
+                    mListener.callSessionSendAnbrQuery(mediaType, direction, bitsPerSecond);
+                }
+            }, mListenerExecutor);
+        }
+    }
+
+    /**
+     * Provides a string representation of the {@link ImsCallSession}.  Primarily intended for
+     * use in log statements.
+     *
+     * @return String representation of session.
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[ImsCallSession objId:");
+        sb.append(System.identityHashCode(this));
+        sb.append(" callId:");
+        sb.append(mCallId != null ? mCallId : "[UNINITIALIZED]");
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsCallSessionListener.java b/android-35/android/telephony/ims/ImsCallSessionListener.java
new file mode 100644
index 0000000..5946535
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsCallSessionListener.java
@@ -0,0 +1,868 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.Annotation;
+import android.telephony.CallQuality;
+import android.telephony.ServiceState;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+import android.telephony.ims.stub.ImsCallSessionImplBase.MediaStreamDirection;
+import android.telephony.ims.stub.ImsCallSessionImplBase.MediaStreamType;
+import android.util.Log;
+
+import com.android.ims.internal.IImsCallSession;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+/**
+ * Listener interface for notifying the Framework's {@link ImsCallSession} for updates to an ongoing
+ * IMS call.
+ *
+ * @hide
+ */
+// DO NOT remove or change the existing APIs, only add new ones to this implementation or you
+// will break other implementations of ImsCallSessionListener maintained by other ImsServices.
+// TODO: APIs in here do not conform to API guidelines yet. This can be changed if
+// ImsCallSessionListenerConverter is also changed.
+@SystemApi
+public class ImsCallSessionListener {
+    private static final String TAG = "ImsCallSessionListener";
+    private final IImsCallSessionListener mListener;
+    private Executor mExecutor = null;
+
+    /** @hide */
+    public ImsCallSessionListener(IImsCallSessionListener l) {
+        mListener = l;
+    }
+
+    /**
+     * Called when the network first begins to establish the call session and is now connecting
+     * to the remote party. This must be called once after {@link ImsCallSessionImplBase#start} and
+     * before any other method on this listener.  After this is called,
+     * {@link #callSessionProgressing(ImsStreamMediaProfile)} must be called to communicate any
+     * further updates.
+     * <p/>
+     * Once this is called, {@link #callSessionTerminated} must be called
+     * to end the call session.  In the event that the session failed before the remote party
+     * was contacted, {@link #callSessionInitiatingFailed} must be called.
+     *
+     * @param profile the associated {@link ImsCallProfile}.
+     */
+    public void callSessionInitiating(@NonNull ImsCallProfile profile) {
+        try {
+            mListener.callSessionInitiating(profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session establishment has failed while initiating.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the IMS call session
+     * establishment failure.
+     */
+    public void callSessionInitiatingFailed(@NonNull ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionInitiatingFailed(reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Called after the network has contacted the remote party and the call state should move to
+     * ALERTING.
+     *
+     * @param profile the associated {@link ImsCallProfile}.
+     */
+    public void callSessionProgressing(ImsStreamMediaProfile profile) {
+        try {
+            mListener.callSessionProgressing(profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Called once the outgoing IMS call session has been begun between the local and remote party.
+     * The call state should move to ACTIVE.
+     *
+     * @param profile the associated {@link ImsCallProfile}.
+     */
+    public void callSessionInitiated(ImsCallProfile profile) {
+        try {
+            mListener.callSessionInitiated(profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session establishment has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the IMS call session
+     * establishment failure.
+     * @deprecated {@link #callSessionInitiated(ImsCallProfile)} is called immediately after
+     * the session is first started which meant that there was no time in which a call to this
+     * method was technically valid.  This method is replaced starting Android S in favor of
+     * {@link #callSessionInitiatingFailed(ImsReasonInfo)}.
+     */
+    @Deprecated
+    public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionInitiatedFailed(reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session has been terminated.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the session termination.
+     */
+    public void callSessionTerminated(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionTerminated(reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session has started the process of holding the call. If it fails,
+     * {@link #callSessionHoldFailed(ImsReasonInfo)} should be called.
+     *
+     * If the IMS call session is resumed, call {@link #callSessionResumed(ImsCallProfile)}.
+     *
+     * @param profile The associated {@link ImsCallProfile} of the call session that has been put
+     * on hold.
+     */
+    public void callSessionHeld(ImsCallProfile profile) {
+        try {
+            mListener.callSessionHeld(profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session has failed to be held.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the session hold failure.
+     */
+    public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionHoldFailed(reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This IMS Call session has been put on hold by the remote party.
+     *
+     * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+     */
+    public void callSessionHoldReceived(ImsCallProfile profile) {
+        try {
+            mListener.callSessionHoldReceived(profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session has started the process of resuming the call. If the process of resuming
+     * the call fails, call {@link #callSessionResumeFailed(ImsReasonInfo)}.
+     *
+     * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+     */
+    public void callSessionResumed(ImsCallProfile profile) {
+        try {
+            mListener.callSessionResumed(profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session resume has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} containing the detailed reason of the session resume
+     * failure.
+     */
+    public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionResumeFailed(reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The remote party has resumed this IMS call session.
+     *
+     * @param profile {@link ImsCallProfile} associated with the IMS call session.
+     */
+    public void callSessionResumeReceived(ImsCallProfile profile) {
+        try {
+            mListener.callSessionResumeReceived(profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session merge has been started.  At this point, the {@code newSession}
+     * represents the IMS call session which represents the new merged conference and has been
+     * initiated to the IMS conference server.
+     *
+     * @param newSession the {@link ImsCallSessionImplBase} that represents the merged active & held
+     * sessions.
+     * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+     */
+    public void callSessionMergeStarted(ImsCallSessionImplBase newSession, ImsCallProfile profile)
+    {
+        try {
+            if (newSession != null && mExecutor != null) {
+                newSession.setDefaultExecutor(mExecutor);
+            }
+            mListener.callSessionMergeStarted(newSession != null ?
+                            newSession.getServiceImpl() : null, profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Compatibility method for older implementations.
+     * See {@link #callSessionMergeStarted(ImsCallSessionImplBase, ImsCallProfile)}.
+     *
+     * @hide
+     */
+    public void callSessionMergeStarted(IImsCallSession newSession, ImsCallProfile profile)
+    {
+        try {
+            mListener.callSessionMergeStarted(newSession, profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The session merge is successful and the merged {@link ImsCallSession} is active.
+     *
+     * @param newSession the new {@link ImsCallSessionImplBase}
+     *                  that represents the conference IMS call
+     * session.
+     */
+    public void callSessionMergeComplete(ImsCallSessionImplBase newSession) {
+        try {
+            if (newSession != null && mExecutor != null) {
+                newSession.setDefaultExecutor(mExecutor);
+            }
+            mListener.callSessionMergeComplete(newSession != null ?
+                    newSession.getServiceImpl() : null);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Compatibility method for older implementations of ImsService.
+     *
+     * See {@link #callSessionMergeComplete(ImsCallSessionImplBase)}}.
+     *
+     * @hide
+     */
+    public void callSessionMergeComplete(IImsCallSession newSession) {
+        try {
+            mListener.callSessionMergeComplete(newSession);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session merge has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} contining the reason for the call merge failure.
+     */
+    public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionMergeFailed(reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session profile has been updated. Does not include holding or resuming a call.
+     *
+     * @param profile The {@link ImsCallProfile} associated with the updated IMS call session.
+     */
+    public void callSessionUpdated(ImsCallProfile profile) {
+        try {
+            mListener.callSessionUpdated(profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session profile update has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} containing a reason for the session update failure.
+     */
+    public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionUpdateFailed(reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session profile has received an update from the remote user.
+     *
+     * @param profile The new {@link ImsCallProfile} associated with the update.
+     */
+    public void callSessionUpdateReceived(ImsCallProfile profile) {
+        try {
+            mListener.callSessionUpdateReceived(profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Called when the session has been extended to a conference session.
+     *
+     * If the conference extension fails, call
+     * {@link #callSessionConferenceExtendFailed(ImsReasonInfo)}.
+     *
+     * @param newSession the session object that is extended to the conference from the active
+     * IMS Call session.
+     * @param profile The {@link ImsCallProfile} associated with the IMS call session.
+     */
+    public void callSessionConferenceExtended(ImsCallSessionImplBase newSession,
+            ImsCallProfile profile) {
+        try {
+            if (newSession != null && mExecutor != null) {
+                newSession.setDefaultExecutor(mExecutor);
+            }
+            mListener.callSessionConferenceExtended(
+                    newSession != null ? newSession.getServiceImpl() : null, profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Compatibility method to interface with older versions of ImsService.
+     * See {@link #callSessionConferenceExtended(ImsCallSessionImplBase, ImsCallProfile)}.
+     *
+     * @hide
+     */
+    public void callSessionConferenceExtended(IImsCallSession newSession, ImsCallProfile profile) {
+        try {
+            mListener.callSessionConferenceExtended(newSession, profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The previous conference extension has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} containing the detailed reason of the conference
+     * extension failure.
+     */
+    public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionConferenceExtendFailed(reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * A conference extension has been received received from the remote party.
+     *
+     * @param newSession An {@link ImsCallSessionImplBase}
+     *                   representing the extended IMS call session.
+     * @param profile The {@link ImsCallProfile} associated with the new IMS call session.
+     */
+    public void callSessionConferenceExtendReceived(ImsCallSessionImplBase newSession,
+            ImsCallProfile profile) {
+        try {
+            if (newSession != null && mExecutor != null) {
+                newSession.setDefaultExecutor(mExecutor);
+            }
+            mListener.callSessionConferenceExtendReceived(newSession != null
+                    ? newSession.getServiceImpl() : null, profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Compatibility method to interface with older versions of ImsService.
+     * See {@link #callSessionConferenceExtendReceived(ImsCallSessionImplBase, ImsCallProfile)}.
+     *
+     * @hide
+     */
+    public void callSessionConferenceExtendReceived(IImsCallSession newSession,
+            ImsCallProfile profile) {
+        try {
+            mListener.callSessionConferenceExtendReceived(newSession, profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The request to invite participants to the conference has been delivered to the conference
+     * server.
+     */
+    public void callSessionInviteParticipantsRequestDelivered() {
+        try {
+            mListener.callSessionInviteParticipantsRequestDelivered();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The previous request to invite participants to the conference (see
+     * {@link #callSessionInviteParticipantsRequestDelivered()}) has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason forthe conference invitation
+     * failure.
+     */
+    public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+    {
+        try {
+            mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The request to remove participants from the conference has been delivered to the conference
+     * server.
+     */
+    public void callSessionRemoveParticipantsRequestDelivered() {
+        try {
+            mListener.callSessionRemoveParticipantsRequestDelivered();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The previous request to remove participants from the conference (see
+     * {@link #callSessionRemoveParticipantsRequestDelivered()}) has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason for the conference removal
+     * failure.
+     */
+    public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+    {
+        try {
+            mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session's conference state has changed.
+     *
+     * @param state The new {@link ImsConferenceState} associated with the conference.
+     */
+    public void callSessionConferenceStateUpdated(ImsConferenceState state) {
+        try {
+            mListener.callSessionConferenceStateUpdated(state);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session has received a Ussd message.
+     *
+     * @param mode The mode of the USSD message, either
+     * {@link ImsCallSessionImplBase#USSD_MODE_NOTIFY} or
+     * {@link ImsCallSessionImplBase#USSD_MODE_REQUEST}.
+     * @param ussdMessage The USSD message.
+     */
+    public void callSessionUssdMessageReceived(int mode, String ussdMessage)
+    {
+        try {
+            mListener.callSessionUssdMessageReceived(mode, ussdMessage);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * An {@link ImsCallSession} may potentially handover from one radio
+     * technology to another.
+     *
+     * @param srcAccessTech The source radio access technology; one of the access technology
+     * constants defined in {@link android.telephony.ServiceState}. For example
+     * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+     * @param targetAccessTech The target radio access technology; one of the access technology
+     * constants defined in {@link android.telephony.ServiceState}. For example
+     * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+     * @deprecated Uses hidden constants for radio access technology, use
+     * {@link #onMayHandover(int, int)} instead.
+     */
+    @Deprecated
+    public void callSessionMayHandover(int srcAccessTech, int targetAccessTech) {
+        // Use new API internally.
+        onMayHandover(ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+                ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech));
+    }
+
+    /**
+     * Notify the framework that the associated {@link ImsCallSession} may handover from one network
+     * type to another.
+     *
+     * @param srcNetworkType The source network type.
+     * @param targetNetworkType The target network type.
+     */
+    public void onMayHandover(@Annotation.NetworkType int srcNetworkType,
+            @Annotation.NetworkType int targetNetworkType) {
+        try {
+            mListener.callSessionMayHandover(srcNetworkType, targetNetworkType);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session's access technology has changed.
+     *
+     * @param srcAccessTech original access technology, defined in
+     * {@link android.telephony.ServiceState}.
+     * @param targetAccessTech new access technology, defined in
+     * {@link android.telephony.ServiceState}.
+     * @param reasonInfo The {@link ImsReasonInfo} associated with this handover.
+     * @deprecated Uses hidden radio access technology constants, use
+     * {@link #onHandover(int, int, ImsReasonInfo)} instead.
+     */
+    @Deprecated
+    public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+            ImsReasonInfo reasonInfo) {
+        // Use new API internally.
+        onHandover(ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+                ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
+    }
+
+    /**
+     * Notify the framework that the associated {@link ImsCallSession} has handed over from one
+     * network type to another.
+     *
+     * @param srcNetworkType original network type.
+     * @param targetNetworkType target network type after handover..
+     * @param reasonInfo An optional {@link ImsReasonInfo} associated with this handover.
+     */
+    public void onHandover(@Annotation.NetworkType int srcNetworkType,
+            @Annotation.NetworkType int targetNetworkType, @Nullable ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionHandover(srcNetworkType, targetNetworkType, reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session's access technology change has failed..
+     *
+     * @param srcAccessTech original access technology
+     * @param targetAccessTech new access technology
+     * @param reasonInfo An {@link ImsReasonInfo} detailing the reason for the failure.
+     * @deprecated Uses hidden radio access technology constants, use
+     * {@link #onHandoverFailed(int, int, ImsReasonInfo)} instead
+     */
+    @Deprecated
+    public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+            ImsReasonInfo reasonInfo) {
+        // Use new API internally.
+        onHandoverFailed(ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+                ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
+    }
+
+    /**
+     * The IMS call session's access technology change has failed..
+     *
+     * @param srcNetworkType original network type.
+     * @param targetNetworkType target network type that the handover failed for.
+     * @param reasonInfo An {@link ImsReasonInfo} detailing the reason for the failure.
+     */
+    public void onHandoverFailed(@Annotation.NetworkType int srcNetworkType,
+            @Annotation.NetworkType int targetNetworkType, @NonNull ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionHandoverFailed(srcNetworkType, targetNetworkType, reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The TTY mode has been changed by the remote party.
+     *
+     * @param mode one of the following: -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+     */
+    public void callSessionTtyModeReceived(int mode) {
+        try {
+            mListener.callSessionTtyModeReceived(mode);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The multiparty state has been changed for this {@code ImsCallSession}.
+     *
+     * @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise.
+     */
+    public void callSessionMultipartyStateChanged(boolean isMultiParty) {
+        try {
+            mListener.callSessionMultipartyStateChanged(isMultiParty);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Supplementary service information has been received for the current IMS call session.
+     *
+     * @param suppSrvNotification The {@link ImsSuppServiceNotification} containing the change.
+     */
+    public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification)
+    {
+        try {
+            mListener.callSessionSuppServiceReceived(suppSrvNotification);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * An RTT modify request has been received from the remote party.
+     *
+     * @param callProfile An {@link ImsCallProfile} with the updated attributes
+     */
+    public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile)
+    {
+        try {
+            mListener.callSessionRttModifyRequestReceived(callProfile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * An RTT modify response has been received.
+     *
+     * @param status the received response for RTT modify request.
+     */
+    public void callSessionRttModifyResponseReceived(int status) {
+        try {
+            mListener.callSessionRttModifyResponseReceived(status);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * An RTT message has been received from the remote party.
+     *
+     * @param rttMessage The RTT message that has been received.
+     */
+    public void callSessionRttMessageReceived(String rttMessage) {
+        try {
+            mListener.callSessionRttMessageReceived(rttMessage);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * While in call, there has been a change in RTT audio indicator.
+     *
+     * @param profile updated ImsStreamMediaProfile
+     */
+    public void callSessionRttAudioIndicatorChanged(@NonNull ImsStreamMediaProfile profile) {
+        try {
+            mListener.callSessionRttAudioIndicatorChanged(profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The call quality has changed.
+     *
+     * @param callQuality The new call quality
+     */
+    public void callQualityChanged(@NonNull CallQuality callQuality) {
+        try {
+            mListener.callQualityChanged(callQuality);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The {@link ImsService} calls this method to inform the framework of a DTMF digit which was
+     * received from the network.
+     * <p>
+     * According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833 sec 3.10</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15.
+     * <p>
+     * <em>Note:</em> Alpha DTMF digits are converted from lower-case to upper-case.
+     *
+     * @param dtmf The DTMF digit received, '0'-'9', *, #, A, B, C, or D.
+     * @throws IllegalArgumentException If an invalid DTMF character is provided.
+     */
+    public void callSessionDtmfReceived(char dtmf) {
+        if (!(dtmf >= '0' && dtmf <= '9'
+                || dtmf >= 'A' && dtmf <= 'D'
+                || dtmf >= 'a' && dtmf <= 'd'
+                || dtmf == '*'
+                || dtmf == '#')) {
+            throw new IllegalArgumentException("DTMF digit must be 0-9, *, #, A, B, C, D");
+        }
+        try {
+            mListener.callSessionDtmfReceived(Character.toUpperCase(dtmf));
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The {@link ImsService} calls this method to inform the framework of RTP header extension data
+     * which was received from the network.
+     * <p>
+     * The set of {@link RtpHeaderExtension} data are identified by local identifiers which were
+     * negotiated during SDP signalling.  See RFC8285,
+     * {@link ImsCallProfile#getAcceptedRtpHeaderExtensionTypes()} and
+     * {@link RtpHeaderExtensionType} for more information.
+     * <p>
+     * By specification, the RTP header extension is an unacknowledged transmission and there is no
+     * guarantee that the header extension will be delivered by the network to the other end of the
+     * call.
+     *
+     * @param extensions The RTP header extension data received.
+     */
+    public void callSessionRtpHeaderExtensionsReceived(
+            @NonNull Set<RtpHeaderExtension> extensions) {
+        Objects.requireNonNull(extensions, "extensions are required.");
+        try {
+            mListener.callSessionRtpHeaderExtensionsReceived(
+                    new ArrayList<RtpHeaderExtension>(extensions));
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notifies the result of transfer request.
+     * @hide
+     */
+    public void callSessionTransferred() {
+        try {
+            mListener.callSessionTransferred();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notifies the result of transfer request.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} containing a reason for the
+     * session transfer failure
+     * @hide
+     */
+    public void callSessionTransferFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionTransferFailed(reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Access Network Bitrate Recommendation Query (ANBRQ), see 3GPP TS 26.114.
+     * This API triggers radio to send ANBRQ message to the access network to query the
+     * desired bitrate.
+     *
+     * @param mediaType {@link ImsCallSessionImplBase.MediaStreamType} is used to identify
+     *        media stream such as audio or video.
+     * @param direction {@link ImsCallSessionImplBase.MediaStreamDirection} of this packet
+     *        stream (e.g. uplink or downlink).
+     * @param bitsPerSecond This value is the bitrate requested by the other party UE through
+     *        RTP CMR, RTCPAPP or TMMBR, and ImsStack converts this value to the MAC bitrate
+     *        (defined in TS36.321, range: 0 ~ 8000 kbit/s).
+     * @hide
+     */
+    public final void callSessionSendAnbrQuery(@MediaStreamType int mediaType,
+                @MediaStreamDirection int direction, @IntRange(from = 0) int bitsPerSecond) {
+        Log.d(TAG, "callSessionSendAnbrQuery in imscallsessonListener");
+        try {
+            mListener.callSessionSendAnbrQuery(mediaType, direction, bitsPerSecond);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set default Executor from ImsService.
+     * @param executor The default executor to use when executing the methods by the vendor
+     *                 implementation of {@link ImsCallSessionImplBase} for conference call.
+     *                 This executor is dedicated to set vendor CallSessionImpl
+     *                 only when conference call is established.
+     * @hide
+     */
+    public final void setDefaultExecutor(@NonNull Executor executor) {
+        if (mExecutor == null) {
+            mExecutor = executor;
+        }
+    }
+}
+
diff --git a/android-35/android/telephony/ims/ImsConferenceState.java b/android-35/android/telephony/ims/ImsConferenceState.java
new file mode 100644
index 0000000..d4d8c44
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsConferenceState.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telecom.Call;
+import android.telecom.Connection;
+
+import com.android.telephony.Rlog;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Provides the conference information (defined in RFC 4575) for IMS conference call.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsConferenceState implements Parcelable {
+    private static final String TAG = "ImsConferenceState";
+    /**
+     * conference-info : user
+     */
+    // user (String) : Tel or SIP URI
+    public static final String USER = "user";
+    // user > display text (String)
+    public static final String DISPLAY_TEXT = "display-text";
+    // user > endpoint (String) : URI or GRUU or Phone number
+    public static final String ENDPOINT = "endpoint";
+    // user > endpoint > status
+    public static final String STATUS = "status";
+
+    /**
+     * status-type (String) :
+     * "pending" : Endpoint is not yet in the session, but it is anticipated that he/she will
+     *      join in the near future.
+     * "dialing-out" : Focus has dialed out to connect the endpoint to the conference,
+     *      but the endpoint is not yet in the roster (probably being authenticated).
+     * "dialing-in" : Endpoint is dialing into the conference, not yet in the roster
+     *      (probably being authenticated).
+     * "alerting" : PSTN alerting or SIP 180 Ringing was returned for the outbound call;
+     *      endpoint is being alerted.
+     * "on-hold" : Active signaling dialog exists between an endpoint and a focus,
+     *      but endpoint is "on-hold" for this conference, i.e., he/she is neither "hearing"
+     *      the conference mix nor is his/her media being mixed in the conference.
+     * "connected" : Endpoint is a participant in the conference. Depending on the media policies,
+     *      he/she can send and receive media to and from other participants.
+     * "disconnecting" : Focus is in the process of disconnecting the endpoint
+     *      (e.g. in SIP a DISCONNECT or BYE was sent to the endpoint).
+     * "disconnected" : Endpoint is not a participant in the conference, and no active dialog
+     *      exists between the endpoint and the focus.
+     * "muted-via-focus" : Active signaling dialog exists beween an endpoint and a focus and
+     *      the endpoint can "listen" to the conference, but the endpoint's media is not being
+     *      mixed into the conference.
+     * "connect-fail" : Endpoint fails to join the conference by rejecting the conference call.
+     */
+    public static final String STATUS_PENDING = "pending";
+    public static final String STATUS_DIALING_OUT = "dialing-out";
+    public static final String STATUS_DIALING_IN = "dialing-in";
+    public static final String STATUS_ALERTING = "alerting";
+    public static final String STATUS_ON_HOLD = "on-hold";
+    public static final String STATUS_CONNECTED = "connected";
+    public static final String STATUS_DISCONNECTING = "disconnecting";
+    public static final String STATUS_DISCONNECTED = "disconnected";
+    public static final String STATUS_MUTED_VIA_FOCUS = "muted-via-focus";
+    public static final String STATUS_CONNECT_FAIL = "connect-fail";
+    public static final String STATUS_SEND_ONLY = "sendonly";
+    public static final String STATUS_SEND_RECV = "sendrecv";
+
+    /**
+     * conference-info : SIP status code (integer)
+     */
+    public static final String SIP_STATUS_CODE = "sipstatuscode";
+
+    public final HashMap<String, Bundle> mParticipants = new HashMap<String, Bundle>();
+
+    /** @hide */
+    public ImsConferenceState() {
+    }
+
+    private ImsConferenceState(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mParticipants.size());
+
+        if (mParticipants.size() > 0) {
+            Set<Entry<String, Bundle>> entries = mParticipants.entrySet();
+
+            if (entries != null) {
+                Iterator<Entry<String, Bundle>> iterator = entries.iterator();
+
+                while (iterator.hasNext()) {
+                    Entry<String, Bundle> entry = iterator.next();
+
+                    out.writeString(entry.getKey());
+                    out.writeParcelable(entry.getValue(), 0);
+                }
+            }
+        }
+    }
+
+    private void readFromParcel(Parcel in) {
+        int size = in.readInt();
+
+        for (int i = 0; i < size; ++i) {
+            String user = in.readString();
+            Bundle state = in.readParcelable(null, android.os.Bundle.class);
+            mParticipants.put(user, state);
+        }
+    }
+
+    public static final @android.annotation.NonNull Creator<ImsConferenceState> CREATOR =
+            new Creator<ImsConferenceState>() {
+        @Override
+        public ImsConferenceState createFromParcel(Parcel in) {
+            return new ImsConferenceState(in);
+        }
+
+        @Override
+        public ImsConferenceState[] newArray(int size) {
+            return new ImsConferenceState[size];
+        }
+    };
+
+    /**
+     * Translates an {@code ImsConferenceState} status type to a telecom connection state.
+     *
+     * @param status The status type.
+     * @return The corresponding {@link android.telecom.Connection} state.
+     */
+    public static int getConnectionStateForStatus(String status) {
+        if (status.equals(STATUS_PENDING)) {
+            return Connection.STATE_INITIALIZING;
+        } else if (status.equals(STATUS_DIALING_IN)) {
+            return Connection.STATE_RINGING;
+        } else if (status.equals(STATUS_ALERTING) ||
+                status.equals(STATUS_DIALING_OUT)) {
+            return Connection.STATE_DIALING;
+        } else if (status.equals(STATUS_ON_HOLD) ||
+                status.equals(STATUS_SEND_ONLY)) {
+            return Connection.STATE_HOLDING;
+        } else if (status.equals(STATUS_CONNECTED) ||
+                status.equals(STATUS_MUTED_VIA_FOCUS) ||
+                status.equals(STATUS_DISCONNECTING) ||
+                status.equals(STATUS_SEND_RECV)) {
+            return Connection.STATE_ACTIVE;
+        } else if (status.equals(STATUS_DISCONNECTED)) {
+            return Connection.STATE_DISCONNECTED;
+        }
+        return Call.STATE_ACTIVE;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        sb.append(ImsConferenceState.class.getSimpleName());
+        sb.append(" ");
+        if (mParticipants.size() > 0) {
+            Set<Entry<String, Bundle>> entries = mParticipants.entrySet();
+
+            if (entries != null) {
+                Iterator<Entry<String, Bundle>> iterator = entries.iterator();
+                sb.append("<");
+                while (iterator.hasNext()) {
+                    Entry<String, Bundle> entry = iterator.next();
+                    sb.append(Rlog.pii(TAG, entry.getKey()));
+                    sb.append(": ");
+                    Bundle participantData = entry.getValue();
+
+                    for (String key : participantData.keySet()) {
+                        sb.append(key);
+                        sb.append("=");
+                        if (STATUS.equals(key)) {
+                            sb.append(participantData.get(key));
+                        } else {
+                            sb.append(Rlog.pii(TAG, participantData.get(key)));
+                        }
+                        sb.append(", ");
+                    }
+                }
+                sb.append(">");
+            }
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsException.java b/android-35/android/telephony/ims/ImsException.java
new file mode 100644
index 0000000..50fb828
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsException.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2019 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.ims;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.pm.PackageManager;
+import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class defines an IMS-related exception that has been thrown while interacting with a
+ * device or carrier provided ImsService implementation.
+ */
+public final class ImsException extends Exception {
+
+    /**
+     * The operation has failed due to an unknown or unspecified error.
+     */
+    public static final int CODE_ERROR_UNSPECIFIED = 0;
+    /**
+     * The operation has failed because there is no remote process available to service it. This
+     * may be due to a process crash or other illegal state.
+     * <p>
+     * This is a temporary error and the operation may be retried until the connection to the
+     * remote process is restored.
+     */
+    public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1;
+
+    /**
+     * This device or carrier configuration does not support this feature for this subscription.
+     * <p>
+     * This is a permanent configuration error and there should be no retry until the subscription
+     * changes if this operation is denied due to a carrier configuration. If this is due to a
+     * device configuration, the feature {@link PackageManager#FEATURE_TELEPHONY_IMS} is not
+     * available or the device has no ImsService implementation to service this request.
+     */
+    public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2;
+
+    /**
+     * The subscription ID associated with this operation is invalid or not active.
+     * <p>
+     * This is a configuration error and there should be no retry. The subscription used for this
+     * operation is either invalid or has become inactive. The active subscriptions can be queried
+     * with {@link SubscriptionManager#getActiveSubscriptionInfoList()}.
+     */
+    public static final int CODE_ERROR_INVALID_SUBSCRIPTION = 3;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CODE_ERROR_", value = {
+            CODE_ERROR_UNSPECIFIED,
+            CODE_ERROR_SERVICE_UNAVAILABLE,
+            CODE_ERROR_UNSUPPORTED_OPERATION,
+            CODE_ERROR_INVALID_SUBSCRIPTION
+    })
+    public @interface ImsErrorCode {}
+
+    private int mCode = CODE_ERROR_UNSPECIFIED;
+
+    /**
+     * A new {@link ImsException} with an unspecified {@link ImsErrorCode} code.
+     * @param message an optional message to detail the error condition more specifically.
+     * @hide
+     */
+    @SystemApi
+    public ImsException(@Nullable String message) {
+        super(getMessage(message, CODE_ERROR_UNSPECIFIED));
+    }
+
+    /**
+     * A new {@link ImsException} that includes an {@link ImsErrorCode} error code.
+     * @param message an optional message to detail the error condition more specifically.
+     * @hide
+     */
+    @SystemApi
+    public ImsException(@Nullable String message, @ImsErrorCode int code) {
+        super(getMessage(message, code));
+        mCode = code;
+    }
+
+    /**
+     * A new {@link ImsException} that includes an {@link ImsErrorCode} error code and a
+     * {@link Throwable} that contains the original error that was thrown to lead to this Exception.
+     * @param message an optional message to detail the error condition more specifically.
+     * @param cause the {@link Throwable} that caused this {@link ImsException} to be created.
+     * @hide
+     */
+    @SystemApi
+    public ImsException(@Nullable String message, @ImsErrorCode  int code,
+            @Nullable Throwable cause) {
+        super(getMessage(message, code), cause);
+        mCode = code;
+    }
+
+    /**
+     * @return the IMS Error code that is associated with this {@link ImsException}.
+     */
+    public @ImsErrorCode int getCode() {
+        return mCode;
+    }
+
+    private static String getMessage(String message, int code) {
+        StringBuilder builder;
+        if (!TextUtils.isEmpty(message)) {
+            builder = new StringBuilder(message);
+            builder.append(" (code: ");
+            builder.append(code);
+            builder.append(")");
+            return builder.toString();
+        } else {
+            return "code: " + code;
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsExternalCallState.java b/android-35/android/telephony/ims/ImsExternalCallState.java
new file mode 100644
index 0000000..d451107
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsExternalCallState.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Parcelable object to handle MultiEndpoint Dialog Event Package Information.
+ * @hide
+ */
+@SystemApi
+public final class ImsExternalCallState implements Parcelable {
+
+    private static final String TAG = "ImsExternalCallState";
+
+    // Dialog States
+    /**
+     * The external call is in the confirmed dialog state.
+     */
+    public static final int CALL_STATE_CONFIRMED = 1;
+    /**
+     * The external call is in the terminated dialog state.
+     */
+    public static final int CALL_STATE_TERMINATED = 2;
+
+    /**@hide*/
+    @IntDef(value = {
+                    CALL_STATE_CONFIRMED,
+                    CALL_STATE_TERMINATED
+            },
+            prefix = "CALL_STATE_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ExternalCallState {}
+
+    /**@hide*/
+    @IntDef(value = {
+                    ImsCallProfile.CALL_TYPE_VOICE,
+                    ImsCallProfile.CALL_TYPE_VT_TX,
+                    ImsCallProfile.CALL_TYPE_VT_RX,
+                    ImsCallProfile.CALL_TYPE_VT
+            },
+            prefix = "CALL_TYPE_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ExternalCallType {}
+
+
+
+    // Dialog Id
+    private int mCallId;
+    // Number
+    private Uri mAddress;
+    private Uri mLocalAddress;
+    private boolean mIsPullable;
+    // CALL_STATE_CONFIRMED / CALL_STATE_TERMINATED
+    private int mCallState;
+    // ImsCallProfile#CALL_TYPE_*
+    private int mCallType;
+    private boolean mIsHeld;
+
+    /** @hide */
+    public ImsExternalCallState() {
+    }
+
+    /**@hide*/
+    public ImsExternalCallState(int callId, Uri address, boolean isPullable,
+            @ExternalCallState int callState, int callType, boolean isCallheld) {
+        mCallId = callId;
+        mAddress = address;
+        mIsPullable = isPullable;
+        mCallState = callState;
+        mCallType = callType;
+        mIsHeld = isCallheld;
+        Rlog.d(TAG, "ImsExternalCallState = " + this);
+    }
+
+    /**@hide*/
+    public ImsExternalCallState(int callId, Uri address, Uri localAddress,
+            boolean isPullable, @ExternalCallState int callState, int callType,
+            boolean isCallheld) {
+        mCallId = callId;
+        mAddress = address;
+        mLocalAddress = localAddress;
+        mIsPullable = isPullable;
+        mCallState = callState;
+        mCallType = callType;
+        mIsHeld = isCallheld;
+        Rlog.d(TAG, "ImsExternalCallState = " + this);
+    }
+
+    /**
+     * Create a new ImsExternalCallState instance to contain Multiendpoint Dialog information.
+     * @param callId The unique ID of the call, which will be used to identify this external
+     *               connection.
+     * @param address A {@link Uri} containing the remote address of this external connection.
+     * @param localAddress A {@link Uri} containing the local address information.
+     * @param isPullable A flag determining if this external connection can be pulled to the current
+     *         device.
+     * @param callState The state of the external call.
+     * @param callType The type of external call.
+     * @param isCallheld A flag determining if the external connection is currently held.
+     */
+    public ImsExternalCallState(@NonNull String callId, @NonNull Uri address,
+            @Nullable Uri localAddress, boolean isPullable, @ExternalCallState int callState,
+            @ExternalCallType int callType, boolean isCallheld) {
+        mCallId = getIdForString(callId);
+        mAddress = address;
+        mLocalAddress = localAddress;
+        mIsPullable = isPullable;
+        mCallState = callState;
+        mCallType = callType;
+        mIsHeld = isCallheld;
+        Rlog.d(TAG, "ImsExternalCallState = " + this);
+    }
+
+    /** @hide */
+    public ImsExternalCallState(Parcel in) {
+        mCallId = in.readInt();
+        ClassLoader classLoader = ImsExternalCallState.class.getClassLoader();
+        mAddress = in.readParcelable(classLoader, android.net.Uri.class);
+        mLocalAddress = in.readParcelable(classLoader, android.net.Uri.class);
+        mIsPullable = (in.readInt() != 0);
+        mCallState = in.readInt();
+        mCallType = in.readInt();
+        mIsHeld = (in.readInt() != 0);
+        Rlog.d(TAG, "ImsExternalCallState const = " + this);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mCallId);
+        out.writeParcelable(mAddress, 0);
+        out.writeParcelable(mLocalAddress, 0);
+        out.writeInt(mIsPullable ? 1 : 0);
+        out.writeInt(mCallState);
+        out.writeInt(mCallType);
+        out.writeInt(mIsHeld ? 1 : 0);
+        Rlog.d(TAG, "ImsExternalCallState writeToParcel = " + out.toString());
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ImsExternalCallState> CREATOR =
+            new Parcelable.Creator<ImsExternalCallState>() {
+        @Override
+        public ImsExternalCallState createFromParcel(Parcel in) {
+            return new ImsExternalCallState(in);
+        }
+
+        @Override
+        public ImsExternalCallState[] newArray(int size) {
+            return new ImsExternalCallState[size];
+        }
+    };
+
+    public int getCallId() {
+        return mCallId;
+    }
+
+    public @NonNull Uri getAddress() {
+        return mAddress;
+    }
+
+    /**
+     * @return A {@link Uri} containing the local address from the Multiendpoint Dialog Information.
+     */
+    public @Nullable Uri getLocalAddress() {
+        return mLocalAddress;
+    }
+
+    public boolean isCallPullable() {
+        return mIsPullable;
+    }
+
+    public @ExternalCallState int getCallState() {
+        return mCallState;
+    }
+
+    public @ExternalCallType int getCallType() {
+        return mCallType;
+    }
+
+    public boolean isCallHeld() {
+        return mIsHeld;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "ImsExternalCallState { mCallId = " + mCallId +
+                ", mAddress = " + Rlog.pii(TAG, mAddress) +
+                ", mLocalAddress = " + Rlog.pii(TAG, mLocalAddress) +
+                ", mIsPullable = " + mIsPullable +
+                ", mCallState = " + mCallState +
+                ", mCallType = " + mCallType +
+                ", mIsHeld = " + mIsHeld + "}";
+    }
+
+    private int getIdForString(String idString) {
+        try {
+            return Integer.parseInt(idString);
+        } catch (NumberFormatException e) {
+            // In the case that there are alphanumeric characters, we will create a hash of the
+            // String value as a backup.
+            // TODO: Modify call IDs to use Strings as keys instead of integers in telephony/telecom
+            return idString.hashCode();
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsManager.java b/android-35/android/telephony/ims/ImsManager.java
new file mode 100644
index 0000000..b0ff949
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsManager.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2019 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.ims;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresFeature;
+import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.telephony.BinderCacheManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.ims.aidl.IImsRcsController;
+
+import com.android.internal.telephony.ITelephony;
+
+/**
+ * Provides access to information about Telephony IMS services on the device.
+ */
+@SystemService(Context.TELEPHONY_IMS_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
+public class ImsManager {
+
+    /**
+     * <p>Broadcast Action: Indicates that a previously allowed IMS operation was rejected by the
+     * network due to the network returning a "forbidden" response. This may be due to a
+     * provisioning change from the network.
+     * May include the {@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX} extra to also specify
+     * which subscription the operation was rejected for.
+     * <p class="note">
+     * Carrier applications may listen to this broadcast to be notified of possible IMS provisioning
+     * issues.
+     * @hide
+     */
+    // Moved from TelephonyIntents, need to keep backwards compatibility with OEM apps that have
+    // this value hard-coded in BroadcastReceiver.
+    @SuppressLint("ActionValue")
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION =
+            "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
+
+    /**
+     * An intent action indicating that IMS registration for WiFi calling has resulted in an error.
+     * Contains error information that should be displayed to the user.
+     * <p>
+     * This intent will contain the following extra key/value pairs:
+     * {@link #EXTRA_WFC_REGISTRATION_FAILURE_TITLE}
+     * and {@link #EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE}, which contain carrier specific
+     * error information that should be displayed to the user.
+     * <p>
+     * Usage: This intent is sent as an ordered broadcast. If the settings application is going
+     * to show the error information specified to the user, it should respond to
+     * {@link android.content.BroadcastReceiver#setResultCode(int)} with
+     * {@link android.app.Activity#RESULT_CANCELED}, which will signal to the framework that the
+     * event was handled. If the framework does not receive a response to the ordered broadcast,
+     * it will then show a notification to the user indicating that there was a registration
+     * failure.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_WFC_IMS_REGISTRATION_ERROR =
+            "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+
+    /**
+     * An extra key corresponding to a {@link CharSequence} value which contains the carrier
+     * specific title to be displayed as part of the message shown to the user when there is an
+     * error registering for WiFi calling.
+     */
+    public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE =
+            "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
+
+    /**
+     * An extra key corresponding to a {@link CharSequence} value which contains the carrier
+     * specific message to  be displayed as part of the message shown to the user when there is an
+     * error registering for WiFi calling.
+     */
+    public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE =
+            "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+
+    // Cache Telephony Binder interfaces, one cache per process.
+    private static final BinderCacheManager<ITelephony> sTelephonyCache =
+            new BinderCacheManager<>(ImsManager::getITelephonyInterface);
+    private static final BinderCacheManager<IImsRcsController> sRcsCache =
+            new BinderCacheManager<>(ImsManager::getIImsRcsControllerInterface);
+
+    private final Context mContext;
+
+    /**
+     * Use {@link Context#getSystemService(String)} to get an instance of this class.
+     * @hide
+     */
+    public ImsManager(@NonNull Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Create an instance of ImsRcsManager for the subscription id specified.
+     *
+     * @param subscriptionId The ID of the subscription that this ImsRcsManager will use.
+     * @throws IllegalArgumentException if the subscription is invalid.
+     * @return a ImsRcsManager instance with the specific subscription ID.
+     */
+    @NonNull
+    public ImsRcsManager getImsRcsManager(int subscriptionId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+            throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
+        }
+
+        return new ImsRcsManager(mContext, subscriptionId, sRcsCache, sTelephonyCache);
+    }
+
+    /**
+     * Create an instance of ImsMmTelManager for the subscription id specified.
+     *
+     * @param subscriptionId The ID of the subscription that this ImsMmTelManager will use.
+     * @throws IllegalArgumentException if the subscription is invalid.
+     * @return a ImsMmTelManager instance with the specific subscription ID.
+     */
+    @NonNull
+    public ImsMmTelManager getImsMmTelManager(int subscriptionId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+            throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
+        }
+
+        return new ImsMmTelManager(mContext, subscriptionId, sTelephonyCache);
+    }
+
+    /**
+     * Create an instance of {@link SipDelegateManager} for the subscription id specified.
+     * <p>
+     * Allows an IMS application to forward SIP traffic through the device's IMS service,
+     * which is used for cellular carriers that require the device to share a single IMS
+     * registration for both MMTEL and RCS features.
+     * @param subscriptionId The ID of the subscription that this {@link SipDelegateManager} will
+     *                       be bound to.
+     * @throws IllegalArgumentException if the subscription is invalid.
+     * @return a {@link SipDelegateManager} instance for the specified subscription ID.
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public SipDelegateManager getSipDelegateManager(int subscriptionId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+            throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
+        }
+
+        return new SipDelegateManager(mContext, subscriptionId, sRcsCache, sTelephonyCache);
+    }
+
+
+    /**
+     * Create an instance of {@link ProvisioningManager} for the subscription id specified.
+     * <p>
+     * Provides a ProvisioningManager instance to carrier apps to update carrier provisioning
+     * information, as well as provides a callback so that apps can listen for changes
+     * in MMTEL/RCS provisioning
+     * @param subscriptionId The ID of the subscription that this ProvisioningManager will use.
+     * @throws IllegalArgumentException if the subscription is invalid.
+     * @return a ProvisioningManager instance with the specific subscription ID.
+     */
+    @NonNull
+    public ProvisioningManager getProvisioningManager(int subscriptionId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+            throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
+        }
+
+        return new ProvisioningManager(subscriptionId);
+    }
+
+    private static IImsRcsController getIImsRcsControllerInterface() {
+        return IImsRcsController.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getTelephonyImsServiceRegisterer()
+                        .get());
+    }
+
+    private static ITelephony getITelephonyInterface() {
+        return ITelephony.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getTelephonyServiceRegisterer()
+                        .get());
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsMmTelManager.java b/android-35/android/telephony/ims/ImsMmTelManager.java
new file mode 100644
index 0000000..551057f
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsMmTelManager.java
@@ -0,0 +1,1751 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressAutoDoc;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.BinderCacheManager;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated
+ * subscription.
+ *
+ * Allows a user to query the IMS MmTel feature information for a subscription, register for
+ * registration and MmTel capability status callbacks, as well as query/modify user settings for the
+ * associated subscription.
+ *
+ * Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an instance of this
+ * manager.
+ */
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
+public class ImsMmTelManager implements RegistrationManager {
+    private static final String TAG = "ImsMmTelManager";
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "WIFI_MODE_", value = {
+            WIFI_MODE_UNKNOWN,
+            WIFI_MODE_WIFI_ONLY,
+            WIFI_MODE_CELLULAR_PREFERRED,
+            WIFI_MODE_WIFI_PREFERRED
+            })
+    public @interface WiFiCallingMode {}
+
+    /**
+     * Wifi calling mode is unknown. This is for initialization only.
+     * @hide
+     */
+    public static final int WIFI_MODE_UNKNOWN = -1;
+
+    /**
+     * Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE
+     * registration if signal quality degrades.
+     */
+    public static final int WIFI_MODE_WIFI_ONLY = 0;
+
+    /**
+     * Prefer registering for IMS over LTE if LTE signal quality is high enough.
+     */
+    public static final int WIFI_MODE_CELLULAR_PREFERRED = 1;
+
+    /**
+     * Prefer registering for IMS over IWLAN if possible if WiFi signal quality is high enough.
+     */
+    public static final int WIFI_MODE_WIFI_PREFERRED = 2;
+
+    /**
+     * Callback class for receiving IMS network Registration callback events.
+     * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
+     * @see #unregisterImsRegistrationCallback(RegistrationCallback)
+     * @deprecated Use {@link RegistrationManager.RegistrationCallback} instead.
+     * @hide
+     */
+    // Do not add to this class, add to RegistrationManager.RegistrationCallback instead.
+    @Deprecated
+    @SystemApi
+    public static class RegistrationCallback extends RegistrationManager.RegistrationCallback {
+
+        /**
+         * Notifies the framework when the IMS Provider is registered to the IMS network.
+         *
+         * @param imsTransportType the radio access technology.
+         */
+        @Override
+        public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is trying to register the IMS network.
+         *
+         * @param imsTransportType the radio access technology.
+         */
+        @Override
+        public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is deregistered from the IMS network.
+         *
+         * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+         */
+        @Override
+        public void onUnregistered(@NonNull ImsReasonInfo info) {
+        }
+
+        /**
+         * A failure has occurred when trying to handover registration to another technology type.
+         *
+         * @param imsTransportType The transport type that has failed to handover registration to.
+         * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
+         */
+        @Override
+        public void onTechnologyChangeFailed(
+                @AccessNetworkConstants.TransportType int imsTransportType,
+                @NonNull ImsReasonInfo info) {
+        }
+    }
+
+    /**
+     * Receives IMS capability status updates from the ImsService.
+     *
+     * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
+     * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
+     */
+    public static class CapabilityCallback {
+
+        private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+
+            private final CapabilityCallback mLocalCallback;
+            private Executor mExecutor;
+
+            CapabilityBinder(CapabilityCallback c) {
+                mLocalCallback = c;
+            }
+
+            @Override
+            public void onCapabilitiesStatusChanged(int config) {
+                if (mLocalCallback == null) return;
+
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged(
+                            new MmTelFeature.MmTelCapabilities(config)));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            @Override
+            public void onQueryCapabilityConfiguration(int capability, int radioTech,
+                    boolean isEnabled) {
+                // This is not used for public interfaces.
+            }
+
+            @Override
+            public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                    @ImsFeature.ImsCapabilityError int reason) {
+                // This is not used for public interfaces
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final CapabilityBinder mBinder = new CapabilityBinder(this);
+
+        /**
+         * The status of the feature's capabilities has changed to either available or unavailable.
+         * If unavailable, the feature is not able to support the unavailable capability at this
+         * time.
+         *
+         * @param capabilities The new availability of the capabilities.
+         */
+        public void onCapabilitiesStatusChanged(
+                @NonNull MmTelFeature.MmTelCapabilities capabilities) {
+        }
+
+        /**@hide*/
+        public final IImsCapabilityCallback getBinder() {
+            return mBinder;
+        }
+
+        /**@hide*/
+        // Only exposed as public method for compatibility with deprecated ImsManager APIs.
+        // TODO: clean up dependencies and change back to private visibility.
+        public final void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+    }
+
+    private final Context mContext;
+    private final int mSubId;
+    private final BinderCacheManager<ITelephony> mBinderCache;
+
+    // Cache Telephony Binder interfaces, one cache per process.
+    private static final BinderCacheManager<ITelephony> sTelephonyCache =
+            new BinderCacheManager<>(ImsMmTelManager::getITelephonyInterface);
+
+    /**
+     * Create an instance of {@link ImsMmTelManager} for the subscription id specified.
+     *
+     * @param subId The ID of the subscription that this ImsMmTelManager will use.
+     * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @throws IllegalArgumentException if the subscription is invalid.
+     * @deprecated Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an
+     * instance of this class.
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE
+    })
+    @SuppressLint("ManagerLookup")
+    public static @NonNull ImsMmTelManager createForSubscriptionId(int subId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid subscription ID");
+        }
+
+        return new ImsMmTelManager(subId, sTelephonyCache);
+    }
+
+    /**
+     * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead.
+     * @hide
+     */
+    @VisibleForTesting
+    public ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache) {
+        this(null, subId, binderCache);
+    }
+
+    /**
+     * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead.
+     * @hide
+     */
+    @VisibleForTesting
+    public ImsMmTelManager(Context context, int subId, BinderCacheManager<ITelephony> binderCache) {
+        mContext = context;
+        mSubId = subId;
+        mBinderCache = binderCache;
+    }
+
+    /**
+     * Registers a {@link RegistrationCallback} with the system, which will provide registration
+     * updates for the subscription specified in {@link ImsManager#getImsMmTelManager(int)}. Use
+     * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
+     * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
+     *
+     * When the callback is registered, it will initiate the callback c to be called with the
+     * current registration state.
+     *
+     * @param executor The executor the callback events should be run on.
+     * @param c The {@link RegistrationCallback} to be added.
+     * @see #unregisterImsRegistrationCallback(RegistrationCallback)
+     * @throws IllegalArgumentException if the subscription associated with this callback is not
+     * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or
+     * {@link CapabilityCallback} callback.
+     * @throws ImsException if the subscription associated with this callback is valid, but
+     * the {@link ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+     * reason.
+     * @deprecated Use {@link RegistrationManager#registerImsRegistrationCallback(Executor,
+     * RegistrationManager.RegistrationCallback)} instead.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull RegistrationCallback c) throws ImsException {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        c.setExecutor(executor);
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder());
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new ImsException(e.getMessage(), e.errorCode);
+            }
+        } catch (RemoteException | IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+     /**
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * {@inheritDoc}
+     *
+     */
+    @Override
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+    public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull RegistrationManager.RegistrationCallback c) throws ImsException {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        c.setExecutor(executor);
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException | IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Removes an existing {@link RegistrationCallback}.
+     *
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be removed. If this method is called for an
+     * inactive subscription, it will result in a no-op.
+     *
+     * @param c The {@link RegistrationCallback} to be removed.
+     * @see SubscriptionManager.OnSubscriptionsChangedListener
+     * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
+     * @deprecated Use {@link #unregisterImsRegistrationCallback(
+     * RegistrationManager.RegistrationCallback)}.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+     /**
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     * Access by profile owners is deprecated and will be removed in a future release.
+     *
+     *{@inheritDoc}
+     */
+    @Override
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+    public void unregisterImsRegistrationCallback(
+            @NonNull RegistrationManager.RegistrationCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Registers a {@link RegistrationCallback} with the system, which will provide IMS emergency
+     * registration updates for the subscription specified in
+     * {@link ImsManager#getImsMmTelManager(int)}. Use
+     * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
+     * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
+     *
+     * When the callback is registered, it will initiate the callback c to be called with the
+     * current emergency registration state.
+     * Emergency registration callback is available when there is valid SIM card.
+     * <p>This API requires one of the following:
+     * <ul>
+     *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+     *     <li>If the caller is the device or profile owner, the caller holds the
+     *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+     *     <li>The caller has carrier privileges (see
+     *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+     *     active subscription.</li>
+     * </ul>
+     * <p>The profile owner is an app that owns a managed profile on the device; for more details
+     * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+     * Access by profile owners is deprecated and will be removed in a future release.
+     *
+     * @param executor The executor the callback events should be run on.
+     * @param c The {@link RegistrationCallback} to be added.
+     * @see #unregisterImsEmergencyRegistrationCallback
+     * @throws ImsException if the subscription associated with this callback is valid, but
+     * the {@link ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+     * reason.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE)
+    public void registerImsEmergencyRegistrationCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull RegistrationManager.RegistrationCallback c) throws ImsException {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        c.setExecutor(executor);
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            iTelephony.registerImsEmergencyRegistrationCallback(mSubId, c.getBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }  catch (IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Removes an existing {@link RegistrationCallback} for Emergency IMS registration.
+     *
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be removed. If this method is called for an
+     * inactive subscription, it will result in a no-op.
+     * <p>This API requires one of the following:
+     * <ul>
+     *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+     *     <li>If the caller is the device or profile owner, the caller holds the
+     *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+     *     <li>The caller has carrier privileges (see
+     *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+     *     active subscription.</li>
+     * </ul>
+     * <p>The profile owner is an app that owns a managed profile on the device; for more details
+     * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+     * Access by profile owners is deprecated and will be removed in a future release.
+     *
+     * @param c The {@link RegistrationCallback} to be removed.
+     * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener
+     * @see #registerImsEmergencyRegistrationCallback(Executor,
+     *                                 RegistrationManager.RegistrationCallback)
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE)
+    public void unregisterImsEmergencyRegistrationCallback(
+            @NonNull RegistrationManager.RegistrationCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            Log.w("ImsMmTelManager", "Could not find Telephony Service.");
+            return;
+        }
+
+        try {
+            iTelephony.unregisterImsEmergencyRegistrationCallback(mSubId, c.getBinder());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @Override
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
+            @NonNull @ImsRegistrationState Consumer<Integer> stateCallback) {
+        if (stateCallback == null) {
+            throw new IllegalArgumentException("Must include a non-null callback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            iTelephony.getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() {
+                @Override
+                public void accept(int result) {
+                    final long identity = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> stateCallback.accept(result));
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
+                }
+            });
+        } catch (ServiceSpecificException | RemoteException e) {
+            Log.w("ImsMmTelManager", "Error getting registration state: " + e);
+            executor.execute(() -> stateCallback.accept(REGISTRATION_STATE_NOT_REGISTERED));
+        }
+    }
+
+    /**
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     * Access by profile owners is deprecated and will be removed in a future release.
+     *
+     *{@inheritDoc}
+     */
+    @Override
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+    public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
+            @NonNull @AccessNetworkConstants.TransportType
+                    Consumer<Integer> transportTypeCallback) {
+        if (transportTypeCallback == null) {
+            throw new IllegalArgumentException("Must include a non-null callback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            iTelephony.getImsMmTelRegistrationTransportType(mSubId,
+                    new IIntegerConsumer.Stub() {
+                        @Override
+                        public void accept(int result) {
+                            final long identity = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> transportTypeCallback.accept(result));
+                            } finally {
+                                Binder.restoreCallingIdentity(identity);
+                            }
+                        }
+                    });
+        } catch (ServiceSpecificException | RemoteException e) {
+            Log.w("ImsMmTelManager", "Error getting transport type: " + e);
+            executor.execute(() -> transportTypeCallback.accept(
+                    AccessNetworkConstants.TRANSPORT_TYPE_INVALID));
+        }
+    }
+
+    /**
+     * Registers a {@link CapabilityCallback} with the system, which will provide MmTel service
+     * availability updates for the subscription specified in
+     * {@link ImsManager#getImsMmTelManager(int)}.
+     *
+     * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
+     * subscription changed events and call
+     * {@link #unregisterMmTelCapabilityCallback(CapabilityCallback)} to clean up.
+     * <p>This API requires one of the following:
+     * <ul>
+     *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+     *     <li>If the caller is the device or profile owner, the caller holds the
+     *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+     *     <li>The caller has carrier privileges (see
+     *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+     *     active subscription.</li>
+     * </ul>
+     * <p>The profile owner is an app that owns a managed profile on the device; for more details
+     * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+     * Access by profile owners is deprecated and will be removed in a future release.
+     *
+     * When the callback is registered, it will initiate the callback c to be called with the
+     * current capabilities.
+     *
+     * @param executor The executor the callback events should be run on.
+     * @param c The MmTel {@link CapabilityCallback} to be registered.
+     * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
+     * @throws ImsException if the subscription associated with this callback is valid, but
+     * the {@code ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+     * reason.
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+    public void registerMmTelCapabilityCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull CapabilityCallback c) throws ImsException {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        c.setExecutor(executor);
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            iTelephony.registerMmTelCapabilityCallback(mSubId, c.getBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }  catch (IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Removes an existing MmTel {@link CapabilityCallback}.
+     *
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be removed. If this method is called for an
+     * inactive subscription, it will result in a no-op.
+     * <p>This API requires one of the following:
+     * <ul>
+     *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+     *     <li>If the caller is the device or profile owner, the caller holds the
+     *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+     *     <li>The caller has carrier privileges (see
+     *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+     *     active subscription.</li>
+     * </ul>
+     * <p>The profile owner is an app that owns a managed profile on the device; for more details
+     * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+     * Access by profile owners is deprecated and will be removed in a future release.
+     *
+     * @param c The MmTel {@link CapabilityCallback} to be removed.
+     * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback)
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+    public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            Log.w("ImsMmTelManager", "Could not find Telephony Service.");
+            return;
+        }
+        try {
+            iTelephony.unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
+     * enable MmTel IMS features, depending on the carrier configuration for the current
+     * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
+     * be enabled as long as the carrier has provisioned these services for the specified
+     * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
+     * carrier requirements.
+     * <p>
+     * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+     * method will always return the default value.
+     * <p>This API requires one of the following:
+     * <ul>
+     *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+     *     <li>If the caller is the device or profile owner, the caller holds the
+     *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+     *     <li>The caller has carrier privileges (see
+     *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+     *     active subscription.</li>
+     * </ul>
+     * <p>The profile owner is an app that owns a managed profile on the device; for more details
+     * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+     * Access by profile owners is deprecated and will be removed in a future release.
+     *
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @return true if the user's setting for advanced calling is enabled, false otherwise.
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+    public boolean isAdvancedCallingSettingEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            return iTelephony.isAdvancedCallingSettingEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
+     * enable MmTel IMS features, depending on the carrier configuration for the current
+     * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
+     * be enabled as long as the carrier has provisioned these services for the specified
+     * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
+     * carrier requirements.
+     *
+     * Modifying this value may also trigger an IMS registration or deregistration, depending on
+     * whether or not the new value is enabled or disabled.
+     *
+     * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+     * method will do nothing and will instead always use the default value.
+     *
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+     * @see #isAdvancedCallingSettingEnabled()
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
+    public void setAdvancedCallingSettingEnabled(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            iTelephony.setAdvancedCallingSettingEnabled(mSubId, isEnabled);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query the IMS MmTel capability for a given registration technology. This does not
+     * necessarily mean that we are registered and the capability is available, but rather the
+     * subscription is capable of this service over IMS.
+     *
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VT_AVAILABLE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_IMS_GBA_REQUIRED_BOOL
+     * @see #isAvailable(int, int)
+     *
+     * @param imsRegTech The IMS registration technology.
+     * @param capability The IMS MmTel capability to query.
+     * @return {@code true} if the MmTel IMS capability is capable for this subscription, false
+     *         otherwise.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            return iTelephony.isCapable(mSubId, capability, imsRegTech);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query the availability of an IMS MmTel capability for a given registration technology. If
+     * a capability is available, IMS is registered and the service is currently available over IMS.
+     *
+     * @see #isCapable(int, int)
+     *
+     * @param imsRegTech The IMS registration technology.
+     * @param capability The IMS MmTel capability to query.
+     * @return {@code true} if the MmTel IMS capability is available for this subscription, false
+     *         otherwise.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            return iTelephony.isAvailable(mSubId, capability, imsRegTech);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query whether or not the requested MmTel capability is supported by the carrier on the
+     * specified network transport.
+     * <p>
+     * This is a configuration option and does not change. The only time this may change is if a
+     * new IMS configuration is loaded when there is a
+     * {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED} broadcast for this subscription.
+     * @param capability The capability that is being queried for support on the carrier network.
+     * @param transportType The transport type of the capability to check support for.
+     * @param executor The executor that the callback will be called with.
+     * @param callback A consumer containing a Boolean result specifying whether or not the
+     *                 capability is supported on this carrier network for the transport specified.
+     * @throws ImsException if the subscription is no longer valid or the IMS service is not
+     * available.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void isSupported(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+            @AccessNetworkConstants.TransportType int transportType,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Boolean> callback) throws ImsException {
+        if (callback == null) {
+            throw new IllegalArgumentException("Must include a non-null Consumer.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            iTelephony.isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
+                @Override
+                public void accept(int result) {
+                    final long identity = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.accept(result == 1));
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
+                }
+            }, capability, transportType);
+        } catch (ServiceSpecificException sse) {
+            throw new ImsException(sse.getMessage(), sse.errorCode);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * The user's setting for whether or not they have enabled the "Video Calling" setting.
+     *
+     * <p>
+     * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+     * method will always return the default value.
+     * <p>This API requires one of the following:
+     * <ul>
+     *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+     *     <li>If the caller is the device or profile owner, the caller holds the
+     *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+     *     <li>The caller has carrier privileges (see
+     *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+     *     active subscription.</li>
+     * </ul>
+     * <p>The profile owner is an app that owns a managed profile on the device; for more details
+     * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+     * Access by profile owners is deprecated and will be removed in a future release.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @return true if the user’s “Video Calling” setting is currently enabled.
+     */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    public boolean isVtSettingEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            return iTelephony.isVtSettingEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Change the user's setting for Video Telephony and enable the Video Telephony capability.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @see #isVtSettingEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVtSettingEnabled(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            iTelephony.setVtSettingEnabled(mSubId, isEnabled);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
+     *
+     * <p>This API requires one of the following:
+     * <ul>
+     *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+     *     <li>If the caller is the device or profile owner, the caller holds the
+     *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+     *     <li>The caller has carrier privileges (see
+     *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+     *     active subscription.</li>
+     * </ul>
+     * <p>The profile owner is an app that owns a managed profile on the device; for more details
+     * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+     * Access by profile owners is deprecated and will be removed in a future release.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+    public boolean isVoWiFiSettingEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            return iTelephony.isVoWiFiSettingEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Sets the user's setting for whether or not Voice over WiFi is enabled.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
+     * @see #isVoWiFiSettingEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiSettingEnabled(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            iTelephony.setVoWiFiSettingEnabled(mSubId, isEnabled);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * This configuration is meaningful only on dual sim device.
+     * If enabled, this will result in the device setting up IMS of all other
+     * active subscriptions over the INTERNET APN of the primary default data subscription
+     * when any of those subscriptions are roaming or out of service and if wifi is not available
+     * for VoWifi. This feature will be disabled if
+     * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false.
+     * <p>Following are the conditions in which system will try to register IMS over
+     * cross sim
+     * <ul>
+     *     <li>Wifi is not available, one SIM is roaming and the default data
+     *     SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the
+     *     default data subscription </li>
+     *     <li>Wifi is not available, one SIM is out of service and the default data
+     *     SIM is in home network. Then out of service SIM IMS will be registered over INTERNET
+     *     APN of the default data subscription </li>
+     * </ul>
+     * <p>This API requires one of the following:
+     * <ul>
+     *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+     *     <li>If the caller is the device or profile owner, the caller holds the
+     *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+     *     <li>The caller has carrier privileges (see
+     *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+     *     active subscription.</li>
+     * </ul>
+     * <p>The profile owner is an app that owns a managed profile on the device; for more details
+     * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+     * Access by profile owners is deprecated and will be removed in a future release.
+     *
+     * @throws ImsException if the IMS service associated with this subscription is not available or
+     * the IMS service is not available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @return true if the user's setting for Voice over Cross SIM is enabled and false if it is not
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+    public boolean isCrossSimCallingEnabled() throws ImsException {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            return iTelephony.isCrossSimCallingEnabledByUser(mSubId);
+        } catch (ServiceSpecificException sse) {
+            throw new ImsException(sse.getMessage(), sse.errorCode);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+        // Not reachable. Adding return to make compiler happy.
+        return false;
+    }
+
+    /**
+     * Sets the user's setting for whether or not Voice over Cross SIM is enabled.
+     * If enabled, this will result in the device setting up IMS of all other
+     * active subscriptions over the INTERNET APN of the primary default data subscription
+     * when any of those subscriptions are roaming or out of service and if wifi is not available
+     * for VoWifi. This feature will be disabled if
+     * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false.
+     *
+     * <p>Following are the conditions in which system will try to register IMS over
+     * cross sim
+     * <ul>
+     *     <li>Wifi is not available, one SIM is roaming and the default data
+     *     SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the
+     *     default data subscription </li>
+     *     <li>Wifi is not available, one SIM is out of service and the default data
+     *     SIM is in home network. Then out of service SIM IMS will be registered over INTERNET
+     *     APN of the default data subscription </li>
+     * </ul>
+     * @throws ImsException if the IMS service associated with this subscription is not available or
+     * the IMS service is not available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @param isEnabled true if the user's setting for Voice over Cross SIM is enabled,
+     *                 false otherwise
+     * @see #isCrossSimCallingEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setCrossSimCallingEnabled(boolean isEnabled) throws ImsException {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            iTelephony.setCrossSimCallingEnabled(mSubId, isEnabled);
+        } catch (ServiceSpecificException sse) {
+            throw new ImsException(sse.getMessage(), sse.errorCode);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Returns the user's voice over WiFi roaming setting associated with the current subscription.
+     *
+     * <p>This API requires one of the following:
+     * <ul>
+     *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+     *     <li>If the caller is the device or profile owner, the caller holds the
+     *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+     *     <li>The caller has carrier privileges (see
+     *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+     *     active subscription.</li>
+     * </ul>
+     * <p>The profile owner is an app that owns a managed profile on the device; for more details
+     * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+     * Access by profile owners is deprecated and will be removed in a future release.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
+     * if disabled.
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+    public boolean isVoWiFiRoamingSettingEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            return iTelephony.isVoWiFiRoamingSettingEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Change the user's setting for Voice over WiFi while roaming.
+     *
+     * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
+     *     false otherwise.
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @see #isVoWiFiRoamingSettingEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            iTelephony.setVoWiFiRoamingSettingEnabled(mSubId, isEnabled);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Overrides the Voice over WiFi capability to true for IMS, but do not persist the setting.
+     * Typically used during the Voice over WiFi registration process for some carriers.
+     *
+     * @param isCapable true if the IMS stack should try to register for IMS over IWLAN, false
+     *     otherwise.
+     * @param mode the Voice over WiFi mode preference to set, which can be one of the following:
+     * - {@link #WIFI_MODE_WIFI_ONLY}
+     * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     * - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @see #setVoWiFiSettingEnabled(boolean)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            iTelephony.setVoWiFiNonPersistent(mSubId, isCapable, mode);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Returns the user's voice over WiFi Roaming mode setting associated with the device.
+     *
+     * <p>This API requires one of the following:
+     * <ul>
+     *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+     *     <li>If the caller is the device or profile owner, the caller holds the
+     *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+     *     <li>The caller has carrier privileges (see
+     *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+     *     active subscription.</li>
+     * </ul>
+     * <p>The profile owner is an app that owns a managed profile on the device; for more details
+     * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+     * Access by profile owners is deprecated and will be removed in a future release.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @return The Voice over WiFi Mode preference set by the user, which can be one of the
+     * following:
+     * - {@link #WIFI_MODE_WIFI_ONLY}
+     * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     * - {@link #WIFI_MODE_WIFI_PREFERRED}
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+    public @WiFiCallingMode int getVoWiFiModeSetting() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            return iTelephony.getVoWiFiModeSetting(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the user's preference for Voice over WiFi calling mode.
+     * @param mode The user's preference for the technology to register for IMS over, can be one of
+     *    the following:
+     * - {@link #WIFI_MODE_WIFI_ONLY}
+     * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     * - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @see #getVoWiFiModeSetting()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            iTelephony.setVoWiFiModeSetting(mSubId, mode);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the user's preference for Voice over WiFi calling mode while the device is roaming on
+     * another network.
+     *
+     * @return The user's preference for the technology to register for IMS over when roaming on
+     *     another network, can be one of the following:
+     *     - {@link #WIFI_MODE_WIFI_ONLY}
+     *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     *     - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @see #setVoWiFiRoamingSettingEnabled(boolean)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            return iTelephony.getVoWiFiRoamingModeSetting(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the user's preference for Voice over WiFi mode while the device is roaming on another
+     * network.
+     *
+     * @param mode The user's preference for the technology to register for IMS over when roaming on
+     *     another network, can be one of the following:
+     *     - {@link #WIFI_MODE_WIFI_ONLY}
+     *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     *     - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @see #getVoWiFiRoamingModeSetting()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            iTelephony.setVoWiFiRoamingModeSetting(mSubId, mode);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Sets the capability of RTT for IMS calls placed on this subscription.
+     *
+     * Note: This does not affect the value of
+     * {@link android.provider.Settings.Secure#RTT_CALLING_MODE}, which is the global user setting
+     * for RTT. That value is enabled/disabled separately by the user through the Accessibility
+     * settings.
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @param isEnabled if true RTT should be enabled during calls made on this subscription.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setRttCapabilitySetting(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            iTelephony.setRttCapabilitySetting(mSubId, isEnabled);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * @return true if TTY over VoLTE is supported
+     *
+     * <p>This API requires one of the following:
+     * <ul>
+     *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+     *     <li>If the caller is the device or profile owner, the caller holds the
+     *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+     *     <li>The caller has carrier privileges (see
+     *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+     *     active subscription.</li>
+     * </ul>
+     * <p>The profile owner is an app that owns a managed profile on the device; for more details
+     * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+     * Access by profile owners is deprecated and will be removed in a future release.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
+     */
+    @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+    public boolean isTtyOverVolteEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
+        try {
+            return iTelephony.isTtyOverVolteEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Get the status of the MmTel Feature registered on this subscription.
+     * @param executor The executor that will be used to call the callback.
+     * @param callback A callback containing an Integer describing the current state of the
+     *                 MmTel feature, Which will be one of the following:
+     *                 {@link ImsFeature#STATE_UNAVAILABLE},
+     *                {@link ImsFeature#STATE_INITIALIZING},
+     *                {@link ImsFeature#STATE_READY}. Will be called using the executor
+     *                 specified when the service state has been retrieved from the IMS service.
+     * @throws ImsException if the IMS service associated with this subscription is not available or
+     * the IMS service is not available.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void getFeatureState(@NonNull @CallbackExecutor Executor executor,
+            @NonNull @ImsFeature.ImsState Consumer<Integer> callback) throws ImsException {
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("Must include a non-null Consumer.");
+        }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            iTelephony.getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() {
+                @Override
+                public void accept(int result) {
+                    final long identity = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.accept(result));
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
+                }
+            });
+        } catch (ServiceSpecificException sse) {
+            throw new ImsException(sse.getMessage(), sse.errorCode);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Register a new callback, which is used to notify the registrant of changes to
+     * the state of the underlying IMS service that is attached to telephony to
+     * implement IMS functionality. If the manager is created for
+     * the {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
+     * this throws an {@link ImsException}.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
+     * or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
+     * @param callback The callback instance being registered.
+     * @throws ImsException in the case that the callback can not be registered.
+     * See {@link ImsException#getCode} for more information on when this is called.
+     */
+    @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+    public void registerImsStateCallback(@NonNull Executor executor,
+            @NonNull ImsStateCallback callback) throws ImsException {
+        Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+        Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+        callback.init(executor);
+        ITelephony telephony = mBinderCache.listenOnBinder(callback, callback::binderDied);
+        if (telephony == null) {
+            throw new ImsException("Telephony server is down",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            telephony.registerImsStateCallback(
+                    mSubId, ImsFeature.FEATURE_MMTEL,
+                    callback.getCallbackBinder(), getOpPackageName());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException | IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Unregisters a previously registered callback.
+     *
+     * @param callback The callback instance to be unregistered.
+     */
+    public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
+        Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+
+        ITelephony telephony = mBinderCache.removeRunnable(callback);
+        try {
+            if (telephony != null) {
+                telephony.unregisterImsStateCallback(callback.getCallbackBinder());
+            }
+        } catch (RemoteException ignore) {
+            // ignore it
+        }
+    }
+
+    private String getOpPackageName() {
+        if (mContext != null) {
+            return mContext.getOpPackageName();
+        } else {
+            return null;
+        }
+    }
+
+    private ITelephony getITelephony() {
+        return mBinderCache.getBinder();
+    }
+
+    private static ITelephony getITelephonyInterface() {
+        ITelephony binder = ITelephony.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getTelephonyServiceRegisterer()
+                        .get());
+        return binder;
+    }
+
+    /**
+     * Convert Wi-Fi calling mode to string.
+     *
+     * @param mode Wi-Fi calling mode.
+     * @return The Wi-Fi calling mode in string format.
+     *
+     * @hide
+     */
+    @NonNull
+    public static String wifiCallingModeToString(@ImsMmTelManager.WiFiCallingMode int mode) {
+        switch (mode) {
+            case ImsMmTelManager.WIFI_MODE_UNKNOWN: return "UNKNOWN";
+            case ImsMmTelManager.WIFI_MODE_WIFI_ONLY: return "WIFI_ONLY";
+            case ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED: return "CELLULAR_PREFERRED";
+            case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED: return "WIFI_PREFERRED";
+            default:
+                return "UNKNOWN(" + mode + ")";
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsRcsManager.java b/android-35/android/telephony/ims/ImsRcsManager.java
new file mode 100644
index 0000000..62d4263
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsRcsManager.java
@@ -0,0 +1,705 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.BinderCacheManager;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRcsController;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.ITelephony;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Manager for interfacing with the framework RCS services, including the User Capability Exchange
+ * (UCE) service, as well as managing user settings.
+ *
+ * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this manager.
+ */
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
+public class ImsRcsManager {
+    private static final String TAG = "ImsRcsManager";
+
+    /**
+     * Activity Action: Show the opt-in dialog for enabling or disabling RCS contact discovery
+     * using User Capability Exchange (UCE), which enables a service that periodically shares the
+     * phone numbers of all of the contacts in the user's address book with the carrier to refresh
+     * the RCS capabilities associated with those contacts as the local cache becomes stale.
+     * <p>
+     * An application that depends on RCS contact discovery being enabled must send this intent
+     * using {@link Context#startActivity(Intent)} to ask the user to opt-in for contacts upload for
+     * capability exchange if it is currently disabled. Whether or not RCS contact discovery has
+     * been enabled by the user can be queried using {@link RcsUceAdapter#isUceSettingEnabled()}.
+     * <p>
+     * This intent will always be handled by the system, however the application should only send
+     * this Intent if the carrier supports bulk RCS contact exchange, which will be true if either
+     * key {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL}
+     * or {@link android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is set to true.
+     * Otherwise, the RCS contact discovery opt-in dialog will not be shown.
+     * <p>
+     * Input: A mandatory {@link Settings#EXTRA_SUB_ID} extra containing the subscription that the
+     * setting will be be shown for.
+     * <p>
+     * Output: Nothing
+     * @see RcsUceAdapter
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN =
+            "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
+
+    /**
+     * This carrier supports User Capability Exchange as, defined by the framework using a
+     * presence server. If set, the RcsFeature should support capability exchange. If not set, this
+     * RcsFeature should not publish capabilities or service capability requests.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = {
+            CAPABILITY_TYPE_NONE,
+            CAPABILITY_TYPE_OPTIONS_UCE,
+            CAPABILITY_TYPE_PRESENCE_UCE
+    })
+    public @interface RcsImsCapabilityFlag {}
+
+    /**
+     * Undefined capability type for initialization
+     */
+    public static final int CAPABILITY_TYPE_NONE = 0;
+
+    /**
+     * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
+     * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
+     * If not set, this RcsFeature should not service capability requests.
+     */
+    public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
+
+    /**
+     * This carrier supports User Capability Exchange using a presence server as defined by the
+     * framework. If set, the RcsFeature should support capability exchange using a presence
+     * server. If not set, this RcsFeature should not publish capabilities or service capability
+     * requests using presence.
+     */
+    public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
+
+    /**
+     * This is used to check the upper range of RCS capability
+     * @hide
+     */
+    public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_PRESENCE_UCE + 1;
+
+    /**
+     * An application can use {@link #addOnAvailabilityChangedListener} to register a
+     * {@link OnAvailabilityChangedListener}, which will notify the user when the RCS feature
+     * availability status updates from the ImsService.
+     * @hide
+     */
+    @SystemApi
+    public interface OnAvailabilityChangedListener {
+        /**
+         * The availability of the feature's capabilities has changed to either available or
+         * unavailable.
+         * <p>
+         * If unavailable, the feature does not support the capability at the current time. This may
+         * be due to network or subscription provisioning changes, such as the IMS registration
+         * being lost, network type changing, or OMA-DM provisioning updates.
+         *
+         * @param capabilities The new availability of the capabilities.
+         */
+        void onAvailabilityChanged(@RcsImsCapabilityFlag int capabilities);
+    }
+
+    /**
+     * Receive the availability status changed from the ImsService and pass the status change to
+     * the associated {@link OnAvailabilityChangedListener}
+     */
+    private static class AvailabilityCallbackAdapter {
+
+        private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+            private final OnAvailabilityChangedListener mOnAvailabilityChangedListener;
+            private final Executor mExecutor;
+
+            CapabilityBinder(OnAvailabilityChangedListener listener, Executor executor) {
+                mExecutor = executor;
+                mOnAvailabilityChangedListener = listener;
+            }
+
+            @Override
+            public void onCapabilitiesStatusChanged(int config) {
+                if (mOnAvailabilityChangedListener == null) return;
+
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() ->
+                            mOnAvailabilityChangedListener.onAvailabilityChanged(config));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            @Override
+            public void onQueryCapabilityConfiguration(int capability, int radioTech,
+                    boolean isEnabled) {
+                // This is not used.
+            }
+
+            @Override
+            public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                    @ImsFeature.ImsCapabilityError int reason) {
+                // This is not used.
+            }
+        }
+
+        private final CapabilityBinder mBinder;
+
+        AvailabilityCallbackAdapter(@NonNull Executor executor,
+                @NonNull OnAvailabilityChangedListener listener) {
+            mBinder = new CapabilityBinder(listener, executor);
+        }
+
+        /**@hide*/
+        public final IImsCapabilityCallback getBinder() {
+            return mBinder;
+        }
+    }
+
+    private final int mSubId;
+    private final Context mContext;
+    private final BinderCacheManager<IImsRcsController> mBinderCache;
+    private final BinderCacheManager<ITelephony> mTelephonyBinderCache;
+    private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter>
+            mAvailabilityChangedCallbacks;
+
+    /**
+     * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class.
+     * @hide
+     */
+    public ImsRcsManager(Context context, int subId,
+            BinderCacheManager<IImsRcsController> binderCache,
+            BinderCacheManager<ITelephony> telephonyBinderCache) {
+        mSubId = subId;
+        mContext = context;
+        mBinderCache = binderCache;
+        mAvailabilityChangedCallbacks = new HashMap<>();
+        mTelephonyBinderCache = telephonyBinderCache;
+    }
+
+    /**
+     * @return A {@link RcsUceAdapter} used for User Capability Exchange (UCE) operations for
+     * this subscription.
+     */
+    @NonNull
+    public RcsUceAdapter getUceAdapter() {
+        return new RcsUceAdapter(mContext, mSubId);
+    }
+
+    /**
+     * Registers a {@link RegistrationManager.RegistrationCallback} with the system. When the
+     * callback is registered, it will initiate the callback c to be called with the current
+     * registration state.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param executor The executor the callback events should be run on.
+     * @param c The {@link RegistrationManager.RegistrationCallback} to be added.
+     * @see #unregisterImsRegistrationCallback(RegistrationManager.RegistrationCallback)
+     * @throws ImsException if the subscription associated with this callback is valid, but
+     * the {@code ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+     * reason.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public void registerImsRegistrationCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull RegistrationManager.RegistrationCallback c)
+            throws ImsException {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.w(TAG, "Register registration callback: IImsRcsController is null");
+            throw new ImsException("Cannot find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        c.setExecutor(executor);
+        try {
+            imsRcsController.registerImsRegistrationCallback(mSubId, c.getBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.toString(), e.errorCode);
+        } catch (RemoteException | IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Removes an existing {@link RegistrationManager.RegistrationCallback}.
+     *
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be removed. If this method is called for an
+     * inactive subscription, it will result in a no-op.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param c The {@link RegistrationManager.RegistrationCallback} to be removed.
+     * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener
+     * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public void unregisterImsRegistrationCallback(
+            @NonNull RegistrationManager.RegistrationCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.w(TAG, "Unregister registration callback: IImsRcsController is null");
+            throw new IllegalStateException("Cannot find remote IMS service");
+        }
+
+        try {
+            imsRcsController.unregisterImsRegistrationCallback(mSubId, c.getBinder());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Gets the registration state of the IMS service.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param executor The {@link Executor} that will be used to call the IMS registration state
+     * callback.
+     * @param stateCallback A callback called on the supplied {@link Executor} that will contain the
+     * registration state of the IMS service, which will be one of the
+     * following: {@link RegistrationManager#REGISTRATION_STATE_NOT_REGISTERED},
+     * {@link RegistrationManager#REGISTRATION_STATE_REGISTERING}, or
+     * {@link RegistrationManager#REGISTRATION_STATE_REGISTERED}.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
+            @NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback) {
+        if (stateCallback == null) {
+            throw new IllegalArgumentException("Must include a non-null stateCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.w(TAG, "Get registration state error: IImsRcsController is null");
+            throw new IllegalStateException("Cannot find remote IMS service");
+        }
+
+        try {
+            imsRcsController.getImsRcsRegistrationState(mSubId, new IIntegerConsumer.Stub() {
+                @Override
+                public void accept(int result) {
+                    final long identity = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> stateCallback.accept(result));
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
+                }
+            });
+        } catch (ServiceSpecificException | RemoteException e) {
+            Log.w(TAG, "Get registration state error: " + e);
+            executor.execute(() -> stateCallback.accept(
+                    RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED));
+        }
+    }
+
+    /**
+     * Gets the Transport Type associated with the current IMS registration.
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+     * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param executor The {@link Executor} that will be used to call the transportTypeCallback.
+     * @param transportTypeCallback The transport type associated with the current IMS registration,
+     * which will be one of following:
+     * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN},
+     * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or
+     * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
+            @NonNull @AccessNetworkConstants.TransportType
+                    Consumer<Integer> transportTypeCallback) {
+        if (transportTypeCallback == null) {
+            throw new IllegalArgumentException("Must include a non-null transportTypeCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.w(TAG, "Get registration transport type error: IImsRcsController is null");
+            throw new IllegalStateException("Cannot find remote IMS service");
+        }
+
+        try {
+            imsRcsController.getImsRcsRegistrationTransportType(mSubId,
+                    new IIntegerConsumer.Stub() {
+                        @Override
+                        public void accept(int result) {
+                            final long identity = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> transportTypeCallback.accept(result));
+                            } finally {
+                                Binder.restoreCallingIdentity(identity);
+                            }
+                        }
+                    });
+        } catch (ServiceSpecificException | RemoteException e) {
+            Log.w(TAG, "Get registration transport type error: " + e);
+            executor.execute(() -> transportTypeCallback.accept(
+                    AccessNetworkConstants.TRANSPORT_TYPE_INVALID));
+        }
+    }
+
+    /**
+     * Add an {@link OnAvailabilityChangedListener} with the system, which will provide RCS
+     * availability updates for the subscription specified.
+     *
+     * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
+     * subscription changed events and call
+     * {@link #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)} to clean up
+     * after a subscription is removed.
+     * <p>
+     * When the listener is registered, it will initiate the callback listener to be called with
+     * the current capabilities.
+     *
+     * @param executor The executor the callback events should be run on.
+     * @param listener The RCS {@link OnAvailabilityChangedListener} to be registered.
+     * @see #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void addOnAvailabilityChangedListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OnAvailabilityChangedListener listener) throws ImsException {
+        if (listener == null) {
+            throw new IllegalArgumentException("Must include a non-null"
+                    + "OnAvailabilityChangedListener.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.w(TAG, "Add availability changed listener: IImsRcsController is null");
+            throw new ImsException("Cannot find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        AvailabilityCallbackAdapter adapter =
+                addAvailabilityChangedListenerToCollection(executor, listener);
+        try {
+            imsRcsController.registerRcsAvailabilityCallback(mSubId, adapter.getBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.toString(), e.errorCode);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e);
+            throw new ImsException("Remote IMS Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+     /**
+     * Removes an existing RCS {@link OnAvailabilityChangedListener}.
+     * <p>
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be unregistered. If this method is called for an
+     * inactive subscription, it will result in a no-op.
+     * @param listener The RCS {@link OnAvailabilityChangedListener} to be removed.
+     * @see #addOnAvailabilityChangedListener(Executor, OnAvailabilityChangedListener)
+     * @throws ImsException if the IMS service is not available when calling this method.
+     * See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void removeOnAvailabilityChangedListener(
+            @NonNull OnAvailabilityChangedListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("Must include a non-null"
+                    + "OnAvailabilityChangedListener.");
+        }
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.w(TAG, "Remove availability changed listener: IImsRcsController is null");
+            return;
+        }
+
+        AvailabilityCallbackAdapter callback =
+                removeAvailabilityChangedListenerFromCollection(listener);
+        if (callback == null) {
+            return;
+        }
+
+        try {
+            imsRcsController.unregisterRcsAvailabilityCallback(mSubId, callback.getBinder());
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e);
+        }
+    }
+
+    /**
+     * Query for the capability of an IMS RCS service provided by the framework.
+     * <p>
+     * This only reports the status of RCS capabilities provided by the framework, not necessarily
+     * RCS capabilities provided over-the-top by applications.
+     *
+     * @param capability The RCS capability to query.
+     * @param radioTech The radio technology type that we are querying.
+     * @return true if the RCS capability is capable for this subscription, false otherwise. This
+     * does not necessarily mean that we are registered for IMS and the capability is available, but
+     * rather the subscription is capable of this service over IMS.
+     * @see #isAvailable(int, int)
+     * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
+     * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL
+     * @throws ImsException if the IMS service is not available when calling this method.
+     * See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isCapable(@RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.w(TAG, "isCapable: IImsRcsController is null");
+            throw new ImsException("Cannot find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            return imsRcsController.isCapable(mSubId, capability, radioTech);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error calling IImsRcsController#isCapable", e);
+            throw new ImsException("Remote IMS Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Query the availability of an IMS RCS capability.
+     * <p>
+     * This only reports the status of RCS capabilities provided by the framework, not necessarily
+     * RCS capabilities provided by over-the-top by applications.
+     *
+     * @param capability the RCS capability to query.
+     * @param radioTech The radio technology type that we are querying.
+     * @return true if the RCS capability is currently available for the associated subscription,
+     * false otherwise. If the capability is available, IMS is registered and the service is
+     * currently available over IMS.
+     * @see #isCapable(int, int)
+     * @throws ImsException if the IMS service is not available when calling this method.
+     * See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isAvailable(@RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)
+            throws ImsException {
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.w(TAG, "isAvailable: IImsRcsController is null");
+            throw new ImsException("Cannot find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            return imsRcsController.isAvailable(mSubId, capability, radioTech);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error calling IImsRcsController#isAvailable", e);
+            throw new ImsException("Remote IMS Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Register a new callback, which is used to notify the registrant of changes to
+     * the state of the underlying IMS service that is attached to telephony to
+     * implement IMS functionality. If the manager is created for
+     * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
+     * this throws an {@link ImsException}.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
+     * or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
+     * @param callback The callback instance being registered.
+     * @throws ImsException in the case that the callback can not be registered.
+     * See {@link ImsException#getCode} for more information on when this is called.
+     */
+    @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE})
+    public void registerImsStateCallback(@NonNull Executor executor,
+            @NonNull ImsStateCallback callback) throws ImsException {
+        Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+        Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+        callback.init(executor);
+        ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied);
+        if (telephony == null) {
+            throw new ImsException("Telephony server is down",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            telephony.registerImsStateCallback(
+                    mSubId, ImsFeature.FEATURE_RCS,
+                    callback.getCallbackBinder(), mContext.getOpPackageName());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException | IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Unregisters a previously registered callback.
+     *
+     * @param callback The callback instance to be unregistered.
+     */
+    public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
+        Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+
+        ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback);
+        try {
+            if (telephony != null) {
+                telephony.unregisterImsStateCallback(callback.getCallbackBinder());
+            }
+        } catch (RemoteException ignore) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Add the {@link OnAvailabilityChangedListener} to collection for tracking.
+     * @param executor The executor that will be used when the publish state is changed and the
+     * {@link OnAvailabilityChangedListener} is called.
+     * @param listener The {@link OnAvailabilityChangedListener} to call the publish state changed.
+     * @return The {@link AvailabilityCallbackAdapter} to wrapper the
+     * {@link OnAvailabilityChangedListener}
+     */
+    private AvailabilityCallbackAdapter addAvailabilityChangedListenerToCollection(
+            @NonNull Executor executor, @NonNull OnAvailabilityChangedListener listener) {
+        AvailabilityCallbackAdapter adapter = new AvailabilityCallbackAdapter(executor, listener);
+        synchronized (mAvailabilityChangedCallbacks) {
+            mAvailabilityChangedCallbacks.put(listener, adapter);
+        }
+        return adapter;
+    }
+
+    /**
+     * Remove the existing {@link OnAvailabilityChangedListener} from the collection.
+     * @param listener The {@link OnAvailabilityChangedListener} to remove from the collection.
+     * @return The wrapper class {@link AvailabilityCallbackAdapter} associated with the
+     * {@link OnAvailabilityChangedListener}.
+     */
+    private AvailabilityCallbackAdapter removeAvailabilityChangedListenerFromCollection(
+            @NonNull OnAvailabilityChangedListener listener) {
+        synchronized (mAvailabilityChangedCallbacks) {
+            return mAvailabilityChangedCallbacks.remove(listener);
+        }
+    }
+
+    private IImsRcsController getIImsRcsController() {
+        IBinder binder = TelephonyFrameworkInitializer
+                .getTelephonyServiceManager()
+                .getTelephonyImsServiceRegisterer()
+                .get();
+        return IImsRcsController.Stub.asInterface(binder);
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsReasonInfo.java b/android-35/android/telephony/ims/ImsReasonInfo.java
new file mode 100644
index 0000000..67acda0
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsReasonInfo.java
@@ -0,0 +1,1440 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+/**
+ * Provides details on why an IMS call failed. Applications can use the methods in this class to
+ * get local or network fault behind an IMS services failure. For example, if the code is
+ * CODE_CALL_BARRED, then the call was blocked by network call barring configuration and it is not
+ * the device's bug and the user can retry the call when network lift the barring.
+ * Typical use case includes call backs when IMS call state changed with this class as a param
+ * containing details on why IMS call changed state/failed.
+ */
+public final class ImsReasonInfo implements Parcelable {
+
+    /**
+     * The Reason is unspecified.
+     */
+    public static final int CODE_UNSPECIFIED = 0;
+
+
+    // LOCAL
+
+    // IMS -> Telephony
+    /**
+     * The passed argument is invalid.
+     */
+    public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101;
+    /**
+     * The operation was invoked while in an invalid call state.
+     */
+    public static final int CODE_LOCAL_ILLEGAL_STATE = 102;
+    /**
+     * IMS service internal error
+     */
+    public static final int CODE_LOCAL_INTERNAL_ERROR = 103;
+    /**
+     * ImsService has crashed (service connection is lost).
+     */
+    public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106;
+    /**
+     * No pending incoming call exists
+     */
+    public static final int CODE_LOCAL_NO_PENDING_CALL = 107;
+    /**
+     * IMS Call ended during conference merge process
+     */
+    public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108;
+
+    // IMS -> Telephony
+    /**
+     * Service unavailable; radio power off
+     */
+    public static final int CODE_LOCAL_POWER_OFF = 111;
+    /**
+     * Service unavailable; low battery
+     */
+    public static final int CODE_LOCAL_LOW_BATTERY = 112;
+    /**
+     * Service unavailable; out of service (data service state)
+     */
+    public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121;
+    /**
+     * Service unavailable; no LTE coverage
+     * (VoLTE is not supported even though IMS is registered)
+     */
+    public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122;
+    /**
+     * Service unavailable; located in roaming area
+     */
+    public static final int CODE_LOCAL_NETWORK_ROAMING = 123;
+    /**
+     * Service unavailable; IP changed
+     */
+    public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124;
+    /**
+     * Service unavailable; for an unspecified reason
+     */
+    public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131;
+    /**
+     * Service unavailable; IMS is not registered
+     */
+    public static final int CODE_LOCAL_NOT_REGISTERED = 132;
+
+    // IMS <-> Telephony
+    /**
+     * Maximum number of simultaneous calls exceeded
+     */
+    public static final int CODE_LOCAL_CALL_EXCEEDED = 141;
+    // IMS <- Telephony
+    /**
+     * The call is busy.
+     */
+    public static final int CODE_LOCAL_CALL_BUSY = 142;
+    /**
+     * The Call has been declined locally on this device.
+     */
+    public static final int CODE_LOCAL_CALL_DECLINE = 143;
+    // IMS -> Telephony
+    /**
+     * Can not complete call; an SRVCC is in progress.
+     */
+    public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144;
+    /**
+     * Can not complete call; resource reservation is failed (QoS precondition)
+     */
+    public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145;
+    /**
+     * VoLTE service can't be provided by the network or remote end, retry the call.
+     * Resolve the extra code provided in (EXTRA_CODE_CALL_RETRY_*) if the below code is set
+     */
+    public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146;
+    /**
+     * VoLTE service can't be provided by the network temporarily, retry the call.
+     */
+    public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147;
+    /**
+     * IMS call is already terminated (in TERMINATED state).
+     */
+    public static final int CODE_LOCAL_CALL_TERMINATED = 148;
+    /**
+     * Call was disconnected because a handover is not feasible due to network conditions.
+     */
+    public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149;
+    /**
+     * This device does not support IMS.
+     * @hide
+     */
+    public static final int CODE_LOCAL_IMS_NOT_SUPPORTED_ON_DEVICE = 150;
+
+    /*
+     * TIMEOUT (IMS -> Telephony)
+     */
+    /**
+     * 1xx waiting timer is expired after sending INVITE request (MO calls only)
+     */
+    public static final int CODE_TIMEOUT_1XX_WAITING = 201;
+    /**
+     * User didn't answer during call setup operation (MO/MT)
+     * MO : 200 OK to INVITE request is not received,
+     * MT : No action from user after alerting the call
+     */
+    public static final int CODE_TIMEOUT_NO_ANSWER = 202;
+    /**
+     * User no answer during call update operation (MO/MT)
+     * MO : 200 OK to re-INVITE request is not received,
+     * MT : No action from user after alerting the call
+     */
+    public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203;
+
+    /**
+     * The call was blocked by call barring configuration.
+     */
+    public static final int CODE_CALL_BARRED = 240;
+
+    /**
+     * The operation is restricted to fixed dialing numbers only.
+     */
+    public static final int CODE_FDN_BLOCKED = 241;
+
+    /**
+     * Network rejected the emergency call request because IMEI was used as identification
+     * and this capability is not supported by the network.
+     */
+    public static final int CODE_IMEI_NOT_ACCEPTED = 243;
+
+    //STK CC errors
+    /**
+     * Stk Call Control modified DIAL request to USSD request.
+     */
+    public static final int CODE_DIAL_MODIFIED_TO_USSD = 244;
+    /**
+     * Stk Call Control modified DIAL request to SS request.
+     */
+    public static final int CODE_DIAL_MODIFIED_TO_SS = 245;
+    /**
+     * Stk Call Control modified DIAL request to DIAL with modified data.
+     */
+    public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246;
+    /**
+     * Stk Call Control modified DIAL request to Video DIAL request.
+     */
+    public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247;
+    /**
+     * Stk Call Control modified Video DIAL request to DIAL request.
+     */
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248;
+    /**
+     * Stk Call Control modified Video DIAL request to Video DIAL request.
+     */
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249;
+    /**
+     * Stk Call Control modified Video DIAL request to SS request.
+     */
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250;
+    /**
+     * Stk Call Control modified Video DIAL request to USSD request.
+     */
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251;
+
+    /*
+     * STATUSCODE (SIP response code) (IMS -> Telephony)
+     */
+    // 3xx responses
+    /**
+     * SIP 3xx response: SIP request is redirected
+     */
+    public static final int CODE_SIP_REDIRECTED = 321;
+    // 4xx responses
+    /**
+     * Sip 400 response : Bad Request
+     */
+    public static final int CODE_SIP_BAD_REQUEST = 331;
+    /**
+     * Sip 403 response : Forbidden
+     */
+    public static final int CODE_SIP_FORBIDDEN = 332;
+    /**
+     * Sip 404 response : Not Found
+     */
+    public static final int CODE_SIP_NOT_FOUND = 333;
+    /**
+     * Not supported, because of one of the following:
+     * SIP response 415 : Unsupported Media Type,
+     * SIP response 416 : Unsupported URI Scheme,
+     * SIP response 420 : Bad Extension
+     */
+    public static final int CODE_SIP_NOT_SUPPORTED = 334;
+    /**
+     * SIP response 408 : Request Timeout.
+     */
+    public static final int CODE_SIP_REQUEST_TIMEOUT = 335;
+    /**
+     * SIP response 480 : Temporarily Unavailable
+     */
+    public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336;
+    /**
+     * SIP response 484 : Address Incomplete
+     */
+    public static final int CODE_SIP_BAD_ADDRESS = 337;
+    /**
+     * Returned a busy response, may be one of the following:
+     * SIP response 486 : Busy Here,
+     * SIP response 600 : Busy Everywhere
+     */
+    public static final int CODE_SIP_BUSY = 338;
+    /**
+     * SIP response 487 : Request Terminated
+     */
+    public static final int CODE_SIP_REQUEST_CANCELLED = 339;
+    /**
+     * Received a not acceptable response, will be one of the following:
+     * SIP response 406 : Not Acceptable
+     * SIP response 488 : Not Acceptable Here
+     * SIP response 606 : Not Acceptable
+     */
+    public static final int CODE_SIP_NOT_ACCEPTABLE = 340;
+    /**
+     * Received a not acceptable response, will be one of the following:
+     * SIP response 410 : Gone
+     * SIP response 604 : Does Not Exist Anywhere
+     */
+    public static final int CODE_SIP_NOT_REACHABLE = 341;
+    /**
+     * Received another unspecified error SIP response from the client.
+     */
+    public static final int CODE_SIP_CLIENT_ERROR = 342;
+    /**
+     * SIP response 481: Transaction Does Not Exist
+     */
+    public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343;
+    // 5xx responses
+    /**
+     * SIP response 501 : Server Internal Error
+     */
+    public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351;
+    /**
+     * SIP response 503 : Service Unavailable
+     */
+    public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352;
+    /**
+     * SIP response 504 : Server Time-out
+     */
+    public static final int CODE_SIP_SERVER_TIMEOUT = 353;
+    /**
+     * Received an unspecified SIP server error response.
+     */
+    public static final int CODE_SIP_SERVER_ERROR = 354;
+    // 6xx responses
+    /**
+     * 603 : Decline
+     */
+    public static final int CODE_SIP_USER_REJECTED = 361;
+    /**
+     * Unspecified 6xx error.
+     */
+    public static final int CODE_SIP_GLOBAL_ERROR = 362;
+
+    /**
+     * Emergency call failed in the modem with a temporary fail cause and should be redialed on this
+     * slot.
+     */
+    public static final int CODE_EMERGENCY_TEMP_FAILURE = 363;
+    /**
+     * Emergency call failed in the modem with a permanent fail cause and should not be redialed on
+     * this slot. If there are any other slots available for emergency calling, try those.
+     */
+    public static final int CODE_EMERGENCY_PERM_FAILURE = 364;
+
+    /**
+     * Call failure code during hangup/reject if user marked the call as unwanted.
+     *
+     * Android Telephony will receive information whether ROBO call feature is supported by the
+     * network from modem and propagate the same to AOSP as new ImsCallProfile members. OEMs can
+     * check this information and provide an option to the user to mark the call as unwanted.
+     */
+    public static final int CODE_SIP_USER_MARKED_UNWANTED = 365;
+
+    /**
+     * SIP Response : 405
+     * Method not allowed for the address in the Request URI
+     */
+    public static final int CODE_SIP_METHOD_NOT_ALLOWED = 366;
+
+    /**
+     * SIP Response : 407
+     * The request requires user authentication
+     */
+    public static final int CODE_SIP_PROXY_AUTHENTICATION_REQUIRED = 367;
+
+    /**
+     * SIP Response : 413
+     * Request body too large
+     */
+    public static final int CODE_SIP_REQUEST_ENTITY_TOO_LARGE = 368;
+
+    /**
+     * SIP Response : 414
+     * Request-URI too large
+     */
+    public static final int CODE_SIP_REQUEST_URI_TOO_LARGE = 369;
+
+    /**
+     * SIP Response : 421
+     * Specific extension is required, which is not present in the HEADER
+     */
+    public static final int CODE_SIP_EXTENSION_REQUIRED = 370;
+
+    /**
+     * SIP Response : 422
+     * The session expiration field too small
+     */
+    public static final int CODE_SIP_INTERVAL_TOO_BRIEF = 371;
+
+    /**
+     * SIP Response : 481
+     * Request received by the server does not match any dialog or transaction
+     */
+    public static final int CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST = 372;
+
+    /**
+     * SIP Response : 482
+     * Server has detected a loop
+     */
+    public static final int CODE_SIP_LOOP_DETECTED = 373;
+
+    /**
+     * SIP Response : 483
+     * Max-Forwards value reached
+     */
+    public static final int CODE_SIP_TOO_MANY_HOPS = 374;
+
+    /**
+     * SIP Response : 485
+     * Request-URI is ambiguous
+     *
+     */
+    public static final int CODE_SIP_AMBIGUOUS = 376;
+
+    /**
+     * SIP Response : 491
+     * Server has pending request for same dialog
+     */
+    public static final int CODE_SIP_REQUEST_PENDING = 377;
+
+    /**
+     * SIP Response : 493
+     * The request cannot be decrypted by recipient
+     */
+    public static final int CODE_SIP_UNDECIPHERABLE = 378;
+
+    /**
+     * MEDIA (IMS -> Telephony)
+     */
+    /**
+     * Media resource initialization failed
+     */
+    public static final int CODE_MEDIA_INIT_FAILED = 401;
+    /**
+     * RTP timeout (no audio / video traffic in the session)
+     */
+    public static final int CODE_MEDIA_NO_DATA = 402;
+    /**
+     * Media is not supported; so dropped the call
+     */
+    public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403;
+    /**
+     * Unspecified media related error.
+     */
+    public static final int CODE_MEDIA_UNSPECIFIED = 404;
+
+    /*
+     * USER
+     */
+    // Telephony -> IMS
+    /**
+     * User triggers the call to be terminated.
+     */
+    public static final int CODE_USER_TERMINATED = 501;
+    /**
+     * No action was taken while an incoming call was ringing.
+     */
+    public static final int CODE_USER_NOANSWER = 502;
+    /**
+     * User ignored an incoming call.
+     */
+    public static final int CODE_USER_IGNORE = 503;
+    /**
+     * User declined an incoming call.
+     */
+    public static final int CODE_USER_DECLINE = 504;
+    /**
+     * Device declined/ended a call due to a low battery condition.
+     */
+    public static final int CODE_LOW_BATTERY = 505;
+    /**
+     * Device declined a call due to a denylisted caller ID.
+     */
+    public static final int CODE_BLACKLISTED_CALL_ID = 506;
+    // IMS -> Telephony
+    /**
+     * The call has been terminated by the network or remote user.
+     */
+    public static final int CODE_USER_TERMINATED_BY_REMOTE = 510;
+    /**
+    * Upgrade Downgrade request rejected by
+    * Remote user if the request is MO initiated
+    * Local user if the request is MT initiated
+    */
+    public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511;
+
+    /**
+    * Upgrade Downgrade request cancelled by the user who initiated it
+    */
+    public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512;
+
+    /**
+     * UPGRADE DOWNGRADE operation failed
+     * This can happen due to failure from SIP/RTP/SDP generation or a Call end is
+     * triggered/received while Reinvite is in progress.
+     */
+    public static final int CODE_SESSION_MODIFICATION_FAILED = 1517;
+
+    /*
+     * UT
+     */
+    /**
+     * UT is currently not supported on this device.
+     */
+    public static final int CODE_UT_NOT_SUPPORTED = 801;
+    /**
+     * UT services are currently not available on this device.
+     */
+    public static final int CODE_UT_SERVICE_UNAVAILABLE = 802;
+    /**
+     * The requested UT operation is not allowed.
+     */
+    public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803;
+    /**
+     * The UT request resulted in a network error.
+     */
+    public static final int CODE_UT_NETWORK_ERROR = 804;
+    /**
+     * The password entered for UT operations does not match the stored password.
+     */
+    public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821;
+    //STK CC errors
+    /**
+     * Sim Toolkit Call Control modified the UT operation to a dial command.
+     */
+    public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822;
+    /**
+     * Sim Toolkit Call Control modified the UT operation to a USSD command.
+     */
+    public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823;
+    /**
+     * Sim Toolkit Call Control modified the UT operation to another supplementary service command.
+     */
+    public static final int CODE_UT_SS_MODIFIED_TO_SS = 824;
+    /**
+     * Sim Toolkit Call Control modified the UT operation to a video call dial command.
+     */
+    public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825;
+
+    /**@hide*/
+    @IntDef(value = {
+            CODE_UT_NOT_SUPPORTED,
+            CODE_UT_SERVICE_UNAVAILABLE,
+            CODE_UT_OPERATION_NOT_ALLOWED,
+            CODE_UT_NETWORK_ERROR,
+            CODE_UT_CB_PASSWORD_MISMATCH,
+            CODE_UT_SS_MODIFIED_TO_DIAL,
+            CODE_UT_SS_MODIFIED_TO_USSD,
+            CODE_UT_SS_MODIFIED_TO_SS,
+            CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO
+    }, prefix = "CODE_UT_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UtReason {}
+
+    /**
+     * Emergency callback mode is not supported.
+     */
+    public static final int CODE_ECBM_NOT_SUPPORTED = 901;
+
+    /**
+     * Fail code used to indicate that Multi-endpoint is not supported by the IMS framework.
+     */
+    public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902;
+
+    /**
+     * IMS Registration error code
+     */
+    public static final int CODE_REGISTRATION_ERROR = 1000;
+
+    /*
+     * CALL DROP error codes (Call could drop because of many reasons like Network not available,
+     *  handover, failed, etc)
+     */
+    /**
+     * MT call has ended due to a release from the network because the call was answered elsewhere.
+     */
+    public static final int CODE_ANSWERED_ELSEWHERE = 1014;
+
+    /**
+     * For MultiEndpoint - Call Pull request has failed.
+     */
+    public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015;
+
+    /**
+     * For MultiEndpoint - Call has been pulled from primary to secondary.
+     */
+    public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016;
+
+    /**
+     * CALL DROP error code for the case when a device is ePDG capable and when the user is on an
+     * active wifi call and at the edge of coverage and there is no qualified LTE network available
+     * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
+     * code is received as part of the handover message.
+     */
+    public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100;
+
+    /**
+     * For MultiEndPoint - Call was rejected elsewhere
+     */
+    public static final int CODE_REJECTED_ELSEWHERE = 1017;
+
+    /**
+     * Supplementary services (HOLD/RESUME) failure error codes.
+     * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+     */
+
+    /**
+     * Supplementary Services (HOLD/RESUME) - the command failed.
+     */
+    public static final int CODE_SUPP_SVC_FAILED = 1201;
+    /**
+     * Supplementary Services (HOLD/RESUME) - the command was cancelled.
+     */
+    public static final int CODE_SUPP_SVC_CANCELLED = 1202;
+    /**
+     * Supplementary Services (HOLD/RESUME) - the command resulted in a re-invite collision.
+     */
+    public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203;
+
+    /**
+     * DPD Procedure received no response or send failed.
+     */
+    public static final int CODE_IWLAN_DPD_FAILURE = 1300;
+
+    /**
+     * Establishment of the ePDG Tunnel Failed.
+     */
+    public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400;
+
+    /**
+     * Re-keying of the ePDG Tunnel Failed; may not always result in teardown.
+     */
+    public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401;
+
+    /**
+     * Connection to the packet gateway is lost.
+     */
+    public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402;
+
+    /**
+     * The maximum number of calls allowed has been reached.  Used in a multi-endpoint scenario
+     * where the number of calls across all connected devices has reached the maximum.
+     */
+    public static final int CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED = 1403;
+
+    /**
+     * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
+     * declined the call.  Used in a multi-endpoint scenario where a remote device declined an
+     * incoming call.
+     */
+    public static final int CODE_REMOTE_CALL_DECLINE = 1404;
+
+    /**
+     * Indicates the call was disconnected due to the user reaching their data limit.
+     */
+    public static final int CODE_DATA_LIMIT_REACHED = 1405;
+
+    /**
+     * Indicates the call was disconnected due to the user disabling cellular data.
+     */
+    public static final int CODE_DATA_DISABLED = 1406;
+
+    /**
+     * Indicates a call was disconnected due to loss of wifi signal.
+     */
+    public static final int CODE_WIFI_LOST = 1407;
+
+    /**
+     * Indicates the registration attempt on IWLAN failed due to IKEv2 authetication failure
+     * during tunnel establishment.
+     */
+    public static final int CODE_IKEV2_AUTH_FAILURE = 1408;
+
+    /** The call cannot be established because RADIO is OFF */
+    public static final int CODE_RADIO_OFF = 1500;
+
+    /** The call cannot be established because of no valid SIM */
+    public static final int CODE_NO_VALID_SIM = 1501;
+
+    /** The failure is due internal error at modem */
+    public static final int CODE_RADIO_INTERNAL_ERROR = 1502;
+
+    /** The failure is due to UE timer expired while waiting for a response from network */
+    public static final int CODE_NETWORK_RESP_TIMEOUT = 1503;
+
+    /** The failure is due to explicit reject from network */
+    public static final int CODE_NETWORK_REJECT = 1504;
+
+    /** The failure is due to radio access failure. ex. RACH failure */
+    public static final int CODE_RADIO_ACCESS_FAILURE = 1505;
+
+    /** Call/IMS registration failed/dropped because of a RLF */
+    public static final int CODE_RADIO_LINK_FAILURE = 1506;
+
+    /** Call/IMS registration failed/dropped because of radio link lost */
+    public static final int CODE_RADIO_LINK_LOST = 1507;
+
+    /** The call Call/IMS registration failed because of a radio uplink issue */
+    public static final int CODE_RADIO_UPLINK_FAILURE = 1508;
+
+    /** Call failed because of a RRC connection setup failure */
+    public static final int CODE_RADIO_SETUP_FAILURE = 1509;
+
+    /** Call failed/dropped because of RRC connection release from NW */
+    public static final int CODE_RADIO_RELEASE_NORMAL = 1510;
+
+    /** Call failed/dropped because of RRC abnormally released by modem/network */
+    public static final int CODE_RADIO_RELEASE_ABNORMAL = 1511;
+
+    /** Call failed because of access class barring */
+    public static final int CODE_ACCESS_CLASS_BLOCKED = 1512;
+
+    /** Call/IMS registration is failed/dropped because of a network detach */
+    public static final int CODE_NETWORK_DETACH = 1513;
+
+    /**
+     * Call failed due to SIP code 380 (Alternative Service response) while dialing an "undetected
+     * emergency number".  This scenario is important in some regions where the carrier network will
+     * identify other non-emergency help numbers (e.g. mountain rescue) when attempting to dial.
+     */
+    public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514;
+
+    /**
+     * Call failed because of unobtainable number
+     * @hide
+     */
+    public static final int CODE_UNOBTAINABLE_NUMBER = 1515;
+
+    /**
+     * Call failed because WiFi call could not complete and circuit switch silent redial
+     * is not allowed while roaming on another network.
+     */
+    public static final int CODE_NO_CSFB_IN_CS_ROAM = 1516;
+
+    /**
+     * The rejection cause is not known.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_UNKNOWN = 1600;
+
+    /**
+     * Ongoing call, and call waiting is disabled.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_ONGOING_CALL_WAITING_DISABLED = 1601;
+
+    /**
+     * A call is ongoing on another sub.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_CALL_ON_OTHER_SUB = 1602;
+
+    /**
+     * CDMA call collision.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_1X_COLLISION = 1603;
+
+    /**
+     * IMS is not registered for service yet.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_SERVICE_NOT_REGISTERED = 1604;
+
+    /**
+     * The call type is not allowed on the current RAT.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_CALL_TYPE_NOT_ALLOWED = 1605;
+
+    /**
+     * And emergency call is ongoing.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_ONGOING_E911_CALL = 1606;
+
+    /**
+     * Another call is in the process of being establilshed.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_ONGOING_CALL_SETUP = 1607;
+
+    /**
+     * Maximum number of allowed calls are already in progress.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_MAX_CALL_LIMIT_REACHED = 1608;
+
+    /**
+     * Invalid/unsupported SIP headers received.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_UNSUPPORTED_SIP_HEADERS = 1609;
+
+    /**
+     * Invalid/unsupported SDP headers received.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_UNSUPPORTED_SDP_HEADERS = 1610;
+
+    /**
+     * A call transfer is in progress.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_ONGOING_CALL_TRANSFER = 1611;
+
+    /**
+     * An internal error occured while processing the call.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_INTERNAL_ERROR = 1612;
+
+    /**
+     * Call failure due to lack of dedicated bearer.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_QOS_FAILURE = 1613;
+
+    /**
+     * A call handover is in progress.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_ONGOING_HANDOVER = 1614;
+
+    /**
+     * Video calling not supported with TTY.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_VT_TTY_NOT_ALLOWED = 1615;
+
+    /**
+     * A call upgrade is in progress.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_ONGOING_CALL_UPGRADE = 1616;
+
+    /**
+     * Call from conference server, when TTY mode is ON.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED = 1617;
+
+    /**
+     * A conference call is ongoing.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_ONGOING_CONFERENCE_CALL = 1618;
+
+    /**
+     * A video call with AVPF is not supported.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_VT_AVPF_NOT_ALLOWED = 1619;
+
+    /**
+     * And encrypted call is ongoing; other calls not supported.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_ONGOING_ENCRYPTED_CALL = 1620;
+
+    /**
+     * A CS call is ongoing.
+     * <p>
+     * Used with implicit call rejection.
+     */
+    public static final int CODE_REJECT_ONGOING_CS_CALL = 1621;
+
+    /**
+     * An attempt was made to place an emergency call over WFC when emergency services is not
+     * currently available in the current location.
+     * @hide
+     */
+    public static final int CODE_EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE = 1622;
+
+    /**
+     * Indicates that WiFi calling service is not available in the current location.
+     * @hide
+     */
+    public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623;
+
+    /**
+     * Call failed because of network congestion, resource is not available,
+     * or no circuit or channel available, etc.
+     */
+    public static final int CODE_NETWORK_CONGESTION = 1624;
+
+    /**
+     * The dialed RTT call should be retried without RTT
+     * @hide
+     */
+    public static final int CODE_RETRY_ON_IMS_WITHOUT_RTT = 3001;
+
+    /*
+     * OEM specific error codes. To be used by OEMs when they don't want to reveal error code which
+     * would be replaced by ERROR_UNSPECIFIED.
+     */
+    public static final int CODE_OEM_CAUSE_1 = 0xf001;
+    public static final int CODE_OEM_CAUSE_2 = 0xf002;
+    public static final int CODE_OEM_CAUSE_3 = 0xf003;
+    public static final int CODE_OEM_CAUSE_4 = 0xf004;
+    public static final int CODE_OEM_CAUSE_5 = 0xf005;
+    public static final int CODE_OEM_CAUSE_6 = 0xf006;
+    public static final int CODE_OEM_CAUSE_7 = 0xf007;
+    public static final int CODE_OEM_CAUSE_8 = 0xf008;
+    public static final int CODE_OEM_CAUSE_9 = 0xf009;
+    public static final int CODE_OEM_CAUSE_10 = 0xf00a;
+    public static final int CODE_OEM_CAUSE_11 = 0xf00b;
+    public static final int CODE_OEM_CAUSE_12 = 0xf00c;
+    public static final int CODE_OEM_CAUSE_13 = 0xf00d;
+    public static final int CODE_OEM_CAUSE_14 = 0xf00e;
+    public static final int CODE_OEM_CAUSE_15 = 0xf00f;
+
+    /**
+     * @hide
+     */
+    @IntDef(value = {
+            CODE_UNSPECIFIED,
+            CODE_LOCAL_ILLEGAL_ARGUMENT,
+            CODE_LOCAL_ILLEGAL_STATE,
+            CODE_LOCAL_INTERNAL_ERROR,
+            CODE_LOCAL_IMS_SERVICE_DOWN,
+            CODE_LOCAL_NO_PENDING_CALL,
+            CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE,
+            CODE_LOCAL_POWER_OFF,
+            CODE_LOCAL_LOW_BATTERY,
+            CODE_LOCAL_NETWORK_NO_SERVICE,
+            CODE_LOCAL_NETWORK_NO_LTE_COVERAGE,
+            CODE_LOCAL_NETWORK_ROAMING,
+            CODE_LOCAL_NETWORK_IP_CHANGED,
+            CODE_LOCAL_SERVICE_UNAVAILABLE,
+            CODE_LOCAL_NOT_REGISTERED,
+            CODE_LOCAL_CALL_EXCEEDED,
+            CODE_LOCAL_CALL_BUSY,
+            CODE_LOCAL_CALL_DECLINE,
+            CODE_LOCAL_CALL_VCC_ON_PROGRESSING,
+            CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED,
+            CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
+            CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED,
+            CODE_LOCAL_CALL_TERMINATED,
+            CODE_LOCAL_HO_NOT_FEASIBLE,
+            CODE_TIMEOUT_1XX_WAITING,
+            CODE_TIMEOUT_NO_ANSWER,
+            CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE,
+            CODE_CALL_BARRED,
+            CODE_FDN_BLOCKED,
+            CODE_IMEI_NOT_ACCEPTED,
+            CODE_DIAL_MODIFIED_TO_USSD,
+            CODE_DIAL_MODIFIED_TO_SS,
+            CODE_DIAL_MODIFIED_TO_DIAL,
+            CODE_DIAL_MODIFIED_TO_DIAL_VIDEO,
+            CODE_DIAL_VIDEO_MODIFIED_TO_DIAL,
+            CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO,
+            CODE_DIAL_VIDEO_MODIFIED_TO_SS,
+            CODE_DIAL_VIDEO_MODIFIED_TO_USSD,
+            CODE_SIP_REDIRECTED,
+            CODE_SIP_BAD_REQUEST,
+            CODE_SIP_FORBIDDEN,
+            CODE_SIP_NOT_FOUND,
+            CODE_SIP_NOT_SUPPORTED,
+            CODE_SIP_REQUEST_TIMEOUT,
+            CODE_SIP_TEMPRARILY_UNAVAILABLE,
+            CODE_SIP_BAD_ADDRESS,
+            CODE_SIP_BUSY,
+            CODE_SIP_REQUEST_CANCELLED,
+            CODE_SIP_NOT_ACCEPTABLE,
+            CODE_SIP_NOT_REACHABLE,
+            CODE_SIP_CLIENT_ERROR,
+            CODE_SIP_TRANSACTION_DOES_NOT_EXIST,
+            CODE_SIP_SERVER_INTERNAL_ERROR,
+            CODE_SIP_SERVICE_UNAVAILABLE,
+            CODE_SIP_SERVER_TIMEOUT,
+            CODE_SIP_SERVER_ERROR,
+            CODE_SIP_USER_REJECTED,
+            CODE_SIP_GLOBAL_ERROR,
+            CODE_EMERGENCY_TEMP_FAILURE,
+            CODE_EMERGENCY_PERM_FAILURE,
+            CODE_SIP_USER_MARKED_UNWANTED,
+            CODE_SIP_METHOD_NOT_ALLOWED,
+            CODE_SIP_PROXY_AUTHENTICATION_REQUIRED,
+            CODE_SIP_REQUEST_ENTITY_TOO_LARGE,
+            CODE_SIP_REQUEST_URI_TOO_LARGE,
+            CODE_SIP_EXTENSION_REQUIRED,
+            CODE_SIP_INTERVAL_TOO_BRIEF,
+            CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST,
+            CODE_SIP_LOOP_DETECTED,
+            CODE_SIP_TOO_MANY_HOPS,
+            CODE_SIP_AMBIGUOUS,
+            CODE_SIP_REQUEST_PENDING,
+            CODE_SIP_UNDECIPHERABLE,
+            CODE_MEDIA_INIT_FAILED,
+            CODE_MEDIA_NO_DATA,
+            CODE_MEDIA_NOT_ACCEPTABLE,
+            CODE_MEDIA_UNSPECIFIED,
+            CODE_USER_TERMINATED,
+            CODE_USER_NOANSWER,
+            CODE_USER_IGNORE,
+            CODE_USER_DECLINE,
+            CODE_LOW_BATTERY,
+            CODE_BLACKLISTED_CALL_ID,
+            CODE_USER_TERMINATED_BY_REMOTE,
+            CODE_USER_REJECTED_SESSION_MODIFICATION,
+            CODE_USER_CANCELLED_SESSION_MODIFICATION,
+            CODE_SESSION_MODIFICATION_FAILED,
+            CODE_UT_NOT_SUPPORTED,
+            CODE_UT_SERVICE_UNAVAILABLE,
+            CODE_UT_OPERATION_NOT_ALLOWED,
+            CODE_UT_NETWORK_ERROR,
+            CODE_UT_CB_PASSWORD_MISMATCH,
+            CODE_UT_SS_MODIFIED_TO_DIAL,
+            CODE_UT_SS_MODIFIED_TO_USSD,
+            CODE_UT_SS_MODIFIED_TO_SS,
+            CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO,
+            CODE_ECBM_NOT_SUPPORTED,
+            CODE_MULTIENDPOINT_NOT_SUPPORTED,
+            CODE_REGISTRATION_ERROR,
+            CODE_ANSWERED_ELSEWHERE,
+            CODE_CALL_PULL_OUT_OF_SYNC,
+            CODE_CALL_END_CAUSE_CALL_PULL,
+            CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE,
+            CODE_REJECTED_ELSEWHERE,
+            CODE_SUPP_SVC_FAILED,
+            CODE_SUPP_SVC_CANCELLED,
+            CODE_SUPP_SVC_REINVITE_COLLISION,
+            CODE_IWLAN_DPD_FAILURE,
+            CODE_EPDG_TUNNEL_ESTABLISH_FAILURE,
+            CODE_EPDG_TUNNEL_REKEY_FAILURE,
+            CODE_EPDG_TUNNEL_LOST_CONNECTION,
+            CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED,
+            CODE_REMOTE_CALL_DECLINE,
+            CODE_DATA_LIMIT_REACHED,
+            CODE_DATA_DISABLED,
+            CODE_WIFI_LOST,
+            CODE_IKEV2_AUTH_FAILURE,
+            CODE_RADIO_OFF,
+            CODE_NO_VALID_SIM,
+            CODE_RADIO_INTERNAL_ERROR,
+            CODE_NETWORK_RESP_TIMEOUT,
+            CODE_NETWORK_REJECT,
+            CODE_RADIO_ACCESS_FAILURE,
+            CODE_RADIO_LINK_FAILURE,
+            CODE_RADIO_LINK_LOST,
+            CODE_RADIO_UPLINK_FAILURE,
+            CODE_RADIO_SETUP_FAILURE,
+            CODE_RADIO_RELEASE_NORMAL,
+            CODE_RADIO_RELEASE_ABNORMAL,
+            CODE_ACCESS_CLASS_BLOCKED,
+            CODE_NETWORK_DETACH,
+            CODE_SIP_ALTERNATE_EMERGENCY_CALL,
+            CODE_UNOBTAINABLE_NUMBER,
+            CODE_NO_CSFB_IN_CS_ROAM,
+            CODE_REJECT_UNKNOWN,
+            CODE_REJECT_ONGOING_CALL_WAITING_DISABLED,
+            CODE_REJECT_CALL_ON_OTHER_SUB,
+            CODE_REJECT_1X_COLLISION,
+            CODE_REJECT_SERVICE_NOT_REGISTERED,
+            CODE_REJECT_CALL_TYPE_NOT_ALLOWED,
+            CODE_REJECT_ONGOING_E911_CALL,
+            CODE_REJECT_ONGOING_CALL_SETUP,
+            CODE_REJECT_MAX_CALL_LIMIT_REACHED,
+            CODE_REJECT_UNSUPPORTED_SIP_HEADERS,
+            CODE_REJECT_UNSUPPORTED_SDP_HEADERS,
+            CODE_REJECT_ONGOING_CALL_TRANSFER,
+            CODE_REJECT_INTERNAL_ERROR,
+            CODE_REJECT_QOS_FAILURE,
+            CODE_REJECT_ONGOING_HANDOVER,
+            CODE_REJECT_VT_TTY_NOT_ALLOWED,
+            CODE_REJECT_ONGOING_CALL_UPGRADE,
+            CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED,
+            CODE_REJECT_ONGOING_CONFERENCE_CALL,
+            CODE_REJECT_VT_AVPF_NOT_ALLOWED,
+            CODE_REJECT_ONGOING_ENCRYPTED_CALL,
+            CODE_REJECT_ONGOING_CS_CALL,
+            CODE_NETWORK_CONGESTION,
+            CODE_RETRY_ON_IMS_WITHOUT_RTT,
+            CODE_OEM_CAUSE_1,
+            CODE_OEM_CAUSE_2,
+            CODE_OEM_CAUSE_3,
+            CODE_OEM_CAUSE_4,
+            CODE_OEM_CAUSE_5,
+            CODE_OEM_CAUSE_6,
+            CODE_OEM_CAUSE_7,
+            CODE_OEM_CAUSE_8,
+            CODE_OEM_CAUSE_9,
+            CODE_OEM_CAUSE_10,
+            CODE_OEM_CAUSE_11,
+            CODE_OEM_CAUSE_12,
+            CODE_OEM_CAUSE_13,
+            CODE_OEM_CAUSE_14,
+            CODE_OEM_CAUSE_15
+    }, prefix = "CODE_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsCode {}
+
+
+    private static final Map<Integer, String>   sImsCodeMap;
+    static {
+        sImsCodeMap = new HashMap<>();
+        sImsCodeMap.put(CODE_UNSPECIFIED, "CODE_UNSPECIFIED");
+        sImsCodeMap.put(CODE_LOCAL_ILLEGAL_ARGUMENT, "CODE_LOCAL_ILLEGAL_ARGUMENT");
+        sImsCodeMap.put(CODE_LOCAL_ILLEGAL_STATE, "CODE_LOCAL_ILLEGAL_STATE");
+        sImsCodeMap.put(CODE_LOCAL_INTERNAL_ERROR, "CODE_LOCAL_INTERNAL_ERROR");
+        sImsCodeMap.put(CODE_LOCAL_IMS_SERVICE_DOWN, "CODE_LOCAL_IMS_SERVICE_DOWN");
+        sImsCodeMap.put(CODE_LOCAL_NO_PENDING_CALL, "CODE_LOCAL_NO_PENDING_CALL");
+        sImsCodeMap.put(CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE,
+                "CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE");
+        sImsCodeMap.put(CODE_LOCAL_POWER_OFF, "CODE_LOCAL_POWER_OFF");
+        sImsCodeMap.put(CODE_LOCAL_LOW_BATTERY, "CODE_LOCAL_LOW_BATTERY");
+        sImsCodeMap.put(CODE_LOCAL_NETWORK_NO_SERVICE, "CODE_LOCAL_NETWORK_NO_SERVICE");
+        sImsCodeMap.put(CODE_LOCAL_NETWORK_NO_LTE_COVERAGE, "CODE_LOCAL_NETWORK_NO_LTE_COVERAGE");
+        sImsCodeMap.put(CODE_LOCAL_NETWORK_ROAMING, "CODE_LOCAL_NETWORK_ROAMING");
+        sImsCodeMap.put(CODE_LOCAL_NETWORK_IP_CHANGED, "CODE_LOCAL_NETWORK_IP_CHANGED");
+        sImsCodeMap.put(CODE_LOCAL_SERVICE_UNAVAILABLE, "CODE_LOCAL_SERVICE_UNAVAILABLE");
+        sImsCodeMap.put(CODE_LOCAL_NOT_REGISTERED, "CODE_LOCAL_NOT_REGISTERED");
+        sImsCodeMap.put(CODE_LOCAL_CALL_EXCEEDED, "CODE_LOCAL_CALL_EXCEEDED");
+        sImsCodeMap.put(CODE_LOCAL_CALL_BUSY, "CODE_LOCAL_CALL_BUSY");
+        sImsCodeMap.put(CODE_LOCAL_CALL_DECLINE, "CODE_LOCAL_CALL_DECLINE");
+        sImsCodeMap.put(CODE_LOCAL_CALL_VCC_ON_PROGRESSING, "CODE_LOCAL_CALL_VCC_ON_PROGRESSING");
+        sImsCodeMap.put(CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED,
+                "CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED");
+        sImsCodeMap.put(CODE_LOCAL_CALL_CS_RETRY_REQUIRED, "CODE_LOCAL_CALL_CS_RETRY_REQUIRED");
+        sImsCodeMap.put(CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED,
+                "CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED");
+        sImsCodeMap.put(CODE_LOCAL_CALL_TERMINATED, "CODE_LOCAL_CALL_TERMINATED");
+        sImsCodeMap.put(CODE_LOCAL_HO_NOT_FEASIBLE, "CODE_LOCAL_HO_NOT_FEASIBLE");
+        sImsCodeMap.put(CODE_TIMEOUT_1XX_WAITING, "CODE_TIMEOUT_1XX_WAITING");
+        sImsCodeMap.put(CODE_TIMEOUT_NO_ANSWER, "CODE_TIMEOUT_NO_ANSWER");
+        sImsCodeMap.put(CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE, "CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE");
+        sImsCodeMap.put(CODE_CALL_BARRED, "CODE_CALL_BARRED");
+        sImsCodeMap.put(CODE_FDN_BLOCKED, "CODE_FDN_BLOCKED");
+        sImsCodeMap.put(CODE_IMEI_NOT_ACCEPTED, "CODE_IMEI_NOT_ACCEPTED");
+        sImsCodeMap.put(CODE_DIAL_MODIFIED_TO_USSD, "CODE_DIAL_MODIFIED_TO_USSD");
+        sImsCodeMap.put(CODE_DIAL_MODIFIED_TO_SS, "CODE_DIAL_MODIFIED_TO_SS");
+        sImsCodeMap.put(CODE_DIAL_MODIFIED_TO_DIAL, "CODE_DIAL_MODIFIED_TO_DIAL");
+        sImsCodeMap.put(CODE_DIAL_MODIFIED_TO_DIAL_VIDEO, "CODE_DIAL_MODIFIED_TO_DIAL_VIDEO");
+        sImsCodeMap.put(CODE_DIAL_VIDEO_MODIFIED_TO_DIAL, "CODE_DIAL_VIDEO_MODIFIED_TO_DIAL");
+        sImsCodeMap.put(CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO,
+                "CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO");
+        sImsCodeMap.put(CODE_DIAL_VIDEO_MODIFIED_TO_SS, "CODE_DIAL_VIDEO_MODIFIED_TO_SS");
+        sImsCodeMap.put(CODE_DIAL_VIDEO_MODIFIED_TO_USSD, "CODE_DIAL_VIDEO_MODIFIED_TO_USSD");
+        sImsCodeMap.put(CODE_SIP_REDIRECTED, "CODE_SIP_REDIRECTED");
+        sImsCodeMap.put(CODE_SIP_BAD_REQUEST, "CODE_SIP_BAD_REQUEST");
+        sImsCodeMap.put(CODE_SIP_FORBIDDEN, "CODE_SIP_FORBIDDEN");
+        sImsCodeMap.put(CODE_SIP_NOT_FOUND, "CODE_SIP_NOT_FOUND");
+        sImsCodeMap.put(CODE_SIP_NOT_SUPPORTED, "CODE_SIP_NOT_SUPPORTED");
+        sImsCodeMap.put(CODE_SIP_REQUEST_TIMEOUT, "CODE_SIP_REQUEST_TIMEOUT");
+        sImsCodeMap.put(CODE_SIP_TEMPRARILY_UNAVAILABLE, "CODE_SIP_TEMPRARILY_UNAVAILABLE");
+        sImsCodeMap.put(CODE_SIP_BAD_ADDRESS, "CODE_SIP_BAD_ADDRESS");
+        sImsCodeMap.put(CODE_SIP_BUSY, "CODE_SIP_BUSY");
+        sImsCodeMap.put(CODE_SIP_REQUEST_CANCELLED, "CODE_SIP_REQUEST_CANCELLED");
+        sImsCodeMap.put(CODE_SIP_NOT_ACCEPTABLE, "CODE_SIP_NOT_ACCEPTABLE");
+        sImsCodeMap.put(CODE_SIP_NOT_REACHABLE, "CODE_SIP_NOT_REACHABLE");
+        sImsCodeMap.put(CODE_SIP_CLIENT_ERROR, "CODE_SIP_CLIENT_ERROR");
+        sImsCodeMap.put(CODE_SIP_TRANSACTION_DOES_NOT_EXIST, "CODE_SIP_TRANSACTION_DOES_NOT_EXIST");
+        sImsCodeMap.put(CODE_SIP_SERVER_INTERNAL_ERROR, "CODE_SIP_SERVER_INTERNAL_ERROR");
+        sImsCodeMap.put(CODE_SIP_SERVICE_UNAVAILABLE, "CODE_SIP_SERVICE_UNAVAILABLE");
+        sImsCodeMap.put(CODE_SIP_SERVER_TIMEOUT, "CODE_SIP_SERVER_TIMEOUT");
+        sImsCodeMap.put(CODE_SIP_SERVER_ERROR, "CODE_SIP_SERVER_ERROR");
+        sImsCodeMap.put(CODE_SIP_USER_REJECTED, "CODE_SIP_USER_REJECTED");
+        sImsCodeMap.put(CODE_SIP_GLOBAL_ERROR, "CODE_SIP_GLOBAL_ERROR");
+        sImsCodeMap.put(CODE_EMERGENCY_TEMP_FAILURE, "CODE_EMERGENCY_TEMP_FAILURE");
+        sImsCodeMap.put(CODE_EMERGENCY_PERM_FAILURE, "CODE_EMERGENCY_PERM_FAILURE");
+        sImsCodeMap.put(CODE_SIP_USER_MARKED_UNWANTED, "CODE_SIP_USER_MARKED_UNWANTED");
+        sImsCodeMap.put(CODE_SIP_METHOD_NOT_ALLOWED, "CODE_SIP_METHOD_NOT_ALLOWED");
+        sImsCodeMap.put(CODE_SIP_PROXY_AUTHENTICATION_REQUIRED,
+                "CODE_SIP_PROXY_AUTHENTICATION_REQUIRED");
+        sImsCodeMap.put(CODE_SIP_REQUEST_ENTITY_TOO_LARGE, "CODE_SIP_REQUEST_ENTITY_TOO_LARGE");
+        sImsCodeMap.put(CODE_SIP_REQUEST_URI_TOO_LARGE, "CODE_SIP_REQUEST_URI_TOO_LARGE");
+        sImsCodeMap.put(CODE_SIP_EXTENSION_REQUIRED, "CODE_SIP_EXTENSION_REQUIRED");
+        sImsCodeMap.put(CODE_SIP_INTERVAL_TOO_BRIEF, "CODE_SIP_INTERVAL_TOO_BRIEF");
+        sImsCodeMap.put(CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST,
+                "CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST");
+        sImsCodeMap.put(CODE_SIP_LOOP_DETECTED, "CODE_SIP_LOOP_DETECTED");
+        sImsCodeMap.put(CODE_SIP_TOO_MANY_HOPS, "CODE_SIP_TOO_MANY_HOPS");
+        sImsCodeMap.put(CODE_SIP_AMBIGUOUS, "CODE_SIP_AMBIGUOUS");
+        sImsCodeMap.put(CODE_SIP_REQUEST_PENDING, "CODE_SIP_REQUEST_PENDING");
+        sImsCodeMap.put(CODE_SIP_UNDECIPHERABLE, "CODE_SIP_UNDECIPHERABLE");
+        sImsCodeMap.put(CODE_MEDIA_INIT_FAILED, "CODE_MEDIA_INIT_FAILED");
+        sImsCodeMap.put(CODE_MEDIA_NO_DATA, "CODE_MEDIA_NO_DATA");
+        sImsCodeMap.put(CODE_MEDIA_NOT_ACCEPTABLE, "CODE_MEDIA_NOT_ACCEPTABLE");
+        sImsCodeMap.put(CODE_MEDIA_UNSPECIFIED, "CODE_MEDIA_UNSPECIFIED");
+        sImsCodeMap.put(CODE_USER_TERMINATED, "CODE_USER_TERMINATED");
+        sImsCodeMap.put(CODE_USER_NOANSWER, "CODE_USER_NOANSWER");
+        sImsCodeMap.put(CODE_USER_IGNORE, "CODE_USER_IGNORE");
+        sImsCodeMap.put(CODE_USER_DECLINE, "CODE_USER_DECLINE");
+        sImsCodeMap.put(CODE_LOW_BATTERY, "CODE_LOW_BATTERY");
+        sImsCodeMap.put(CODE_BLACKLISTED_CALL_ID, "CODE_BLACKLISTED_CALL_ID");
+        sImsCodeMap.put(CODE_USER_TERMINATED_BY_REMOTE, "CODE_USER_TERMINATED_BY_REMOTE");
+        sImsCodeMap.put(CODE_USER_REJECTED_SESSION_MODIFICATION,
+                "CODE_USER_REJECTED_SESSION_MODIFICATION");
+        sImsCodeMap.put(CODE_USER_CANCELLED_SESSION_MODIFICATION,
+                "CODE_USER_CANCELLED_SESSION_MODIFICATION");
+        sImsCodeMap.put(CODE_SESSION_MODIFICATION_FAILED, "CODE_SESSION_MODIFICATION_FAILED");
+        sImsCodeMap.put(CODE_UT_NOT_SUPPORTED, "CODE_UT_NOT_SUPPORTED");
+        sImsCodeMap.put(CODE_UT_SERVICE_UNAVAILABLE, "CODE_UT_SERVICE_UNAVAILABLE");
+        sImsCodeMap.put(CODE_UT_OPERATION_NOT_ALLOWED, "CODE_UT_OPERATION_NOT_ALLOWED");
+        sImsCodeMap.put(CODE_UT_NETWORK_ERROR, "CODE_UT_NETWORK_ERROR");
+        sImsCodeMap.put(CODE_UT_CB_PASSWORD_MISMATCH, "CODE_UT_CB_PASSWORD_MISMATCH");
+        sImsCodeMap.put(CODE_UT_SS_MODIFIED_TO_DIAL, "CODE_UT_SS_MODIFIED_TO_DIAL");
+        sImsCodeMap.put(CODE_UT_SS_MODIFIED_TO_USSD, "CODE_UT_SS_MODIFIED_TO_USSD");
+        sImsCodeMap.put(CODE_UT_SS_MODIFIED_TO_SS, "CODE_UT_SS_MODIFIED_TO_SS");
+        sImsCodeMap.put(CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO, "CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO");
+        sImsCodeMap.put(CODE_ECBM_NOT_SUPPORTED, "CODE_ECBM_NOT_SUPPORTED");
+        sImsCodeMap.put(CODE_MULTIENDPOINT_NOT_SUPPORTED, "CODE_MULTIENDPOINT_NOT_SUPPORTED");
+        sImsCodeMap.put(CODE_REGISTRATION_ERROR, "CODE_REGISTRATION_ERROR");
+        sImsCodeMap.put(CODE_ANSWERED_ELSEWHERE, "CODE_ANSWERED_ELSEWHERE");
+        sImsCodeMap.put(CODE_CALL_PULL_OUT_OF_SYNC, "CODE_CALL_PULL_OUT_OF_SYNC");
+        sImsCodeMap.put(CODE_CALL_END_CAUSE_CALL_PULL, "CODE_CALL_END_CAUSE_CALL_PULL");
+        sImsCodeMap.put(CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE,
+                "CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE");
+        sImsCodeMap.put(CODE_REJECTED_ELSEWHERE, "CODE_REJECTED_ELSEWHERE");
+        sImsCodeMap.put(CODE_SUPP_SVC_FAILED, "CODE_SUPP_SVC_FAILED");
+        sImsCodeMap.put(CODE_SUPP_SVC_CANCELLED, "CODE_SUPP_SVC_CANCELLED");
+        sImsCodeMap.put(CODE_SUPP_SVC_REINVITE_COLLISION, "CODE_SUPP_SVC_REINVITE_COLLISION");
+        sImsCodeMap.put(CODE_IWLAN_DPD_FAILURE, "CODE_IWLAN_DPD_FAILURE");
+        sImsCodeMap.put(CODE_EPDG_TUNNEL_ESTABLISH_FAILURE, "CODE_EPDG_TUNNEL_ESTABLISH_FAILURE");
+        sImsCodeMap.put(CODE_EPDG_TUNNEL_REKEY_FAILURE, "CODE_EPDG_TUNNEL_REKEY_FAILURE");
+        sImsCodeMap.put(CODE_EPDG_TUNNEL_LOST_CONNECTION, "CODE_EPDG_TUNNEL_LOST_CONNECTION");
+        sImsCodeMap.put(CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED,
+                "CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED");
+        sImsCodeMap.put(CODE_REMOTE_CALL_DECLINE, "CODE_REMOTE_CALL_DECLINE");
+        sImsCodeMap.put(CODE_DATA_LIMIT_REACHED, "CODE_DATA_LIMIT_REACHED");
+        sImsCodeMap.put(CODE_DATA_DISABLED, "CODE_DATA_DISABLED");
+        sImsCodeMap.put(CODE_WIFI_LOST, "CODE_WIFI_LOST");
+        sImsCodeMap.put(CODE_IKEV2_AUTH_FAILURE, "CODE_IKEV2_AUTH_FAILURE");
+        sImsCodeMap.put(CODE_RADIO_OFF, "CODE_RADIO_OFF");
+        sImsCodeMap.put(CODE_NO_VALID_SIM, "CODE_NO_VALID_SIM");
+        sImsCodeMap.put(CODE_RADIO_INTERNAL_ERROR, "CODE_RADIO_INTERNAL_ERROR");
+        sImsCodeMap.put(CODE_NETWORK_RESP_TIMEOUT, "CODE_NETWORK_RESP_TIMEOUT");
+        sImsCodeMap.put(CODE_NETWORK_REJECT, "CODE_NETWORK_REJECT");
+        sImsCodeMap.put(CODE_RADIO_ACCESS_FAILURE, "CODE_RADIO_ACCESS_FAILURE");
+        sImsCodeMap.put(CODE_RADIO_LINK_FAILURE, "CODE_RADIO_LINK_FAILURE");
+        sImsCodeMap.put(CODE_RADIO_LINK_LOST, "CODE_RADIO_LINK_LOST");
+        sImsCodeMap.put(CODE_RADIO_UPLINK_FAILURE, "CODE_RADIO_UPLINK_FAILURE");
+        sImsCodeMap.put(CODE_RADIO_SETUP_FAILURE, "CODE_RADIO_SETUP_FAILURE");
+        sImsCodeMap.put(CODE_RADIO_RELEASE_NORMAL, "CODE_RADIO_RELEASE_NORMAL");
+        sImsCodeMap.put(CODE_RADIO_RELEASE_ABNORMAL, "CODE_RADIO_RELEASE_ABNORMAL");
+        sImsCodeMap.put(CODE_ACCESS_CLASS_BLOCKED, "CODE_ACCESS_CLASS_BLOCKED");
+        sImsCodeMap.put(CODE_NETWORK_DETACH, "CODE_NETWORK_DETACH");
+        sImsCodeMap.put(CODE_SIP_ALTERNATE_EMERGENCY_CALL, "CODE_SIP_ALTERNATE_EMERGENCY_CALL");
+        sImsCodeMap.put(CODE_UNOBTAINABLE_NUMBER, "CODE_UNOBTAINABLE_NUMBER");
+        sImsCodeMap.put(CODE_NO_CSFB_IN_CS_ROAM, "CODE_NO_CSFB_IN_CS_ROAM");
+        sImsCodeMap.put(CODE_REJECT_UNKNOWN, "CODE_REJECT_UNKNOWN");
+        sImsCodeMap.put(CODE_REJECT_ONGOING_CALL_WAITING_DISABLED,
+                "CODE_REJECT_ONGOING_CALL_WAITING_DISABLED");
+        sImsCodeMap.put(CODE_REJECT_CALL_ON_OTHER_SUB, "CODE_REJECT_CALL_ON_OTHER_SUB");
+        sImsCodeMap.put(CODE_REJECT_1X_COLLISION, "CODE_REJECT_1X_COLLISION");
+        sImsCodeMap.put(CODE_REJECT_SERVICE_NOT_REGISTERED, "CODE_REJECT_SERVICE_NOT_REGISTERED");
+        sImsCodeMap.put(CODE_REJECT_CALL_TYPE_NOT_ALLOWED, "CODE_REJECT_CALL_TYPE_NOT_ALLOWED");
+        sImsCodeMap.put(CODE_REJECT_ONGOING_E911_CALL, "CODE_REJECT_ONGOING_E911_CALL");
+        sImsCodeMap.put(CODE_REJECT_ONGOING_CALL_SETUP, "CODE_REJECT_ONGOING_CALL_SETUP");
+        sImsCodeMap.put(CODE_REJECT_MAX_CALL_LIMIT_REACHED, "CODE_REJECT_MAX_CALL_LIMIT_REACHED");
+        sImsCodeMap.put(CODE_REJECT_UNSUPPORTED_SIP_HEADERS, "CODE_REJECT_UNSUPPORTED_SIP_HEADERS");
+        sImsCodeMap.put(CODE_REJECT_UNSUPPORTED_SDP_HEADERS, "CODE_REJECT_UNSUPPORTED_SDP_HEADERS");
+        sImsCodeMap.put(CODE_REJECT_ONGOING_CALL_TRANSFER, "CODE_REJECT_ONGOING_CALL_TRANSFER");
+        sImsCodeMap.put(CODE_REJECT_INTERNAL_ERROR, "CODE_REJECT_INTERNAL_ERROR");
+        sImsCodeMap.put(CODE_REJECT_QOS_FAILURE, "CODE_REJECT_QOS_FAILURE");
+        sImsCodeMap.put(CODE_REJECT_ONGOING_HANDOVER, "CODE_REJECT_ONGOING_HANDOVER");
+        sImsCodeMap.put(CODE_REJECT_VT_TTY_NOT_ALLOWED, "CODE_REJECT_VT_TTY_NOT_ALLOWED");
+        sImsCodeMap.put(CODE_REJECT_ONGOING_CALL_UPGRADE, "CODE_REJECT_ONGOING_CALL_UPGRADE");
+        sImsCodeMap.put(CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED,
+                "CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED");
+        sImsCodeMap.put(CODE_REJECT_ONGOING_CONFERENCE_CALL, "CODE_REJECT_ONGOING_CONFERENCE_CALL");
+        sImsCodeMap.put(CODE_REJECT_VT_AVPF_NOT_ALLOWED, "CODE_REJECT_VT_AVPF_NOT_ALLOWED");
+        sImsCodeMap.put(CODE_REJECT_ONGOING_ENCRYPTED_CALL, "CODE_REJECT_ONGOING_ENCRYPTED_CALL");
+        sImsCodeMap.put(CODE_REJECT_ONGOING_CS_CALL, "CODE_REJECT_ONGOING_CS_CALL");
+        sImsCodeMap.put(CODE_NETWORK_CONGESTION, "CODE_NETWORK_CONGESTION");
+        sImsCodeMap.put(CODE_RETRY_ON_IMS_WITHOUT_RTT, "CODE_RETRY_ON_IMS_WITHOUT_RTT");
+        sImsCodeMap.put(CODE_OEM_CAUSE_1, "CODE_OEM_CAUSE_1");
+        sImsCodeMap.put(CODE_OEM_CAUSE_2, "CODE_OEM_CAUSE_2");
+        sImsCodeMap.put(CODE_OEM_CAUSE_3, "CODE_OEM_CAUSE_3");
+        sImsCodeMap.put(CODE_OEM_CAUSE_4, "CODE_OEM_CAUSE_4");
+        sImsCodeMap.put(CODE_OEM_CAUSE_5, "CODE_OEM_CAUSE_5");
+        sImsCodeMap.put(CODE_OEM_CAUSE_6, "CODE_OEM_CAUSE_6");
+        sImsCodeMap.put(CODE_OEM_CAUSE_7, "CODE_OEM_CAUSE_7");
+        sImsCodeMap.put(CODE_OEM_CAUSE_8, "CODE_OEM_CAUSE_8");
+        sImsCodeMap.put(CODE_OEM_CAUSE_9, "CODE_OEM_CAUSE_9");
+        sImsCodeMap.put(CODE_OEM_CAUSE_10, "CODE_OEM_CAUSE_10");
+        sImsCodeMap.put(CODE_OEM_CAUSE_11, "CODE_OEM_CAUSE_11");
+        sImsCodeMap.put(CODE_OEM_CAUSE_12, "CODE_OEM_CAUSE_12");
+        sImsCodeMap.put(CODE_OEM_CAUSE_13, "CODE_OEM_CAUSE_13");
+        sImsCodeMap.put(CODE_OEM_CAUSE_14, "CODE_OEM_CAUSE_14");
+        sImsCodeMap.put(CODE_OEM_CAUSE_15, "CODE_OEM_CAUSE_15");
+    }
+
+    /**
+     * Network string error messages.
+     * mExtraMessage may have these values.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED =
+            "Forbidden. Not Authorized for Service";
+
+
+    /*
+     * Extra codes for the specific code value
+     * This value can be referred when the code is CODE_LOCAL_CALL_CS_RETRY_REQUIRED.
+     */
+    /**
+     * An extra that may be populated when the {@link #CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+     * been returned.
+     * <p>
+     * Try to connect the call using CS
+     */
+    public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1;
+    /**
+     * An extra that may be populated when the {@link #CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+     * been returned.
+     * <p>
+     * Try to connect the call using CS and do not notify the user.
+     */
+    public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2;
+    /**
+     * An extra that may be populated when the {@link #CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+     * been returned.
+     * <p>
+     * Try to connect the call using CS by using the settings.
+     */
+    public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3;
+
+    /**
+     * An extra that may be populated when the {@link #CODE_LOCAL_CALL_CS_RETRY_REQUIRED} result has
+     * been returned.
+     * <p>
+     * Try to connect the call using CS as emergency
+     */
+    public static final int EXTRA_CODE_CALL_RETRY_EMERGENCY = 4;
+
+    // For main reason code
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+            + "#getCode()}")
+    public int mCode;
+    // For the extra code value; it depends on the code value.
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+            + "#getExtraCode()}")
+    public int mExtraCode;
+    // For the additional message of the reason info.
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+            + "#getExtraMessage()}")
+    public String mExtraMessage;
+
+    /** @hide */
+    public ImsReasonInfo() {
+        mCode = CODE_UNSPECIFIED;
+        mExtraCode = CODE_UNSPECIFIED;
+        mExtraMessage = null;
+    }
+
+    private ImsReasonInfo(Parcel in) {
+        mCode = in.readInt();
+        mExtraCode = in.readInt();
+        mExtraMessage = in.readString();
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public ImsReasonInfo(int code, int extraCode) {
+        mCode = code;
+        mExtraCode = extraCode;
+        mExtraMessage = null;
+    }
+
+    public ImsReasonInfo(@ImsCode int code, int extraCode, @Nullable String extraMessage) {
+        mCode = code;
+        mExtraCode = extraCode;
+        mExtraMessage = extraMessage;
+    }
+
+    /**
+     * @return an integer representing more information about the completion of an operation.
+     */
+    public @ImsCode int getCode() {
+        return mCode;
+    }
+
+    /**
+     * @return an optional OEM specified code that provides extra information.
+     */
+    public int getExtraCode() {
+        return mExtraCode;
+    }
+
+    /**
+     * @return an optional OEM specified string that provides extra information about the operation
+     * result.
+     */
+    public @Nullable String getExtraMessage() {
+        return mExtraMessage;
+    }
+
+    /**
+     * @return the string format of {@link ImsReasonInfo}
+     */
+    @NonNull
+    @Override
+    public String toString() {
+        String imsCode = (sImsCodeMap.containsKey(mCode)) ? sImsCodeMap.get(mCode) : "UNKNOWN_CODE";
+        return "ImsReasonInfo :: {" + mCode + " : " + imsCode + ", "
+                + mExtraCode + ", " + mExtraMessage + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mCode);
+        out.writeInt(mExtraCode);
+        out.writeString(mExtraMessage);
+    }
+
+    public static final @NonNull Creator<ImsReasonInfo> CREATOR = new Creator<ImsReasonInfo>() {
+        @Override
+        public ImsReasonInfo createFromParcel(Parcel in) {
+            return new ImsReasonInfo(in);
+        }
+
+        @Override
+        public ImsReasonInfo[] newArray(int size) {
+            return new ImsReasonInfo[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/ims/ImsRegistrationAttributes.java b/android-35/android/telephony/ims/ImsRegistrationAttributes.java
new file mode 100644
index 0000000..f548dba
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsRegistrationAttributes.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2021 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.ims;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArraySet;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Contains the attributes associated with the current IMS registration.
+ */
+public final class ImsRegistrationAttributes implements Parcelable {
+
+    /**
+     * Attribute to specify if an EPDG tunnel is setup over the cellular internet APN.
+     * <p>
+     * If IMS is registered through an EPDG tunnel is setup over the cellular internet APN then this
+     * bit will be set. If IMS is registered through the IMS APN, then this bit will not be set.
+     *
+     */
+    public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1 << 0;
+    /**
+     * Attribute to specify if ims registration is of type normal or emergency.
+     * <p>
+     *     For emergency registration bit will be set.
+     *     For normal registration bit will not be set.
+     *     This flag is only applicable when listening to emergency IMS registration state updates
+     *     via the ImsMmTelManager#registerImsEmergencyRegistrationCallback API
+     * </p>
+     */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE)
+    public static final int ATTR_REGISTRATION_TYPE_EMERGENCY = 1 << 1;
+    /**
+     * Attribute to specify if virtual registration is required.
+     * <p>
+     *     If emergency registration is not required for making emergency call, in such cases
+     *     bit will be set and callback will represent virtual registration status update.
+     *     This flag is only applicable when listening to emergency IMS registration state updates
+     *     via the ImsMmTelManager#registerImsEmergencyRegistrationCallback API
+     * </p>
+     */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE)
+    public static final int ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL = 1 << 2;
+
+    /** @hide */
+    // Defines the underlying radio technology type that we have registered for IMS over.
+    @IntDef(prefix = "ATTR_",
+            value = {
+                    ATTR_EPDG_OVER_CELL_INTERNET,
+                    ATTR_REGISTRATION_TYPE_EMERGENCY,
+                    ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL,
+            },
+            flag = true)
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsAttributeFlag {}
+
+    /**
+     * Builder for creating {@link ImsRegistrationAttributes} instances.
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        private final int mRegistrationTech;
+        private Set<String> mFeatureTags = Collections.emptySet();
+        private @Nullable SipDetails mSipDetails;
+
+        private @ImsAttributeFlag int mAttributeFlags;
+
+        /**
+         * Build a new instance of {@link ImsRegistrationAttributes}.
+         *
+         * @param registrationTech The Radio Access Technology that IMS is registered on.
+         */
+        public Builder(@ImsRegistrationImplBase.ImsRegistrationTech int registrationTech) {
+            mRegistrationTech = registrationTech;
+            if (registrationTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
+                mAttributeFlags |= ATTR_EPDG_OVER_CELL_INTERNET;
+            }
+        }
+
+        /**
+         * Optional IMS feature tags included in this IMS registration.
+         * @param tags A set of Strings containing the MMTEL and RCS feature tags associated with
+         *         the IMS registration. This information is used for services such as the UCE
+         *         service to ascertain the complete IMS registration state to ensure the SIP
+         *         PUBLISH is accurate. The format of the set of feature tags must be one feature
+         *         tag key and value per entry. Each feature tag will contain the feature tag name
+         *         and string value (if applicable), even if they have the same feature tag name.
+         *         For example,
+         *         {@code +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg,
+         *         urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", +g.gsma.callcomposer} must
+         *         be split into three feature tag entries:
+         *         {@code {+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg",
+         *         +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session",
+         *         +g.gsma.callcomposer}}.
+         */
+        public @NonNull Builder setFeatureTags(@NonNull Set<String> tags) {
+            if (tags == null) {
+                throw new IllegalArgumentException("feature tag set must not be null");
+            }
+            mFeatureTags = new ArraySet<>(tags);
+            return this;
+        }
+
+        /**
+         * Set the SIP information.
+         * @param details The SIP information related to this IMS registration.
+         */
+        public @NonNull Builder setSipDetails(@NonNull SipDetails details) {
+            mSipDetails = details;
+            return this;
+        }
+
+        /**
+         * Set the attribute flag ATTR_REGISTRATION_TYPE_EMERGENCY.
+         */
+        @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE)
+        public @NonNull Builder setFlagRegistrationTypeEmergency() {
+            mAttributeFlags |= ATTR_REGISTRATION_TYPE_EMERGENCY;
+            return this;
+        }
+
+        /**
+         * Set the attribute flag ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL.
+         */
+        @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE)
+        public @NonNull Builder setFlagVirtualRegistrationForEmergencyCall() {
+            mAttributeFlags |= ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL;
+            return this;
+        }
+
+        /**
+         * @return A new instance created from this builder.
+         */
+        public @NonNull ImsRegistrationAttributes build() {
+            return new ImsRegistrationAttributes(mRegistrationTech,
+                    RegistrationManager.getAccessType(mRegistrationTech),
+                    mAttributeFlags,
+                    mFeatureTags, mSipDetails);
+        }
+    }
+
+    private final int mRegistrationTech;
+    private final int mTransportType;
+    private final int mImsAttributeFlags;
+    private final ArrayList<String> mFeatureTags;
+    private final @Nullable SipDetails mSipDetails;
+    /**
+     * Create a new {@link ImsRegistrationAttributes} instance.
+     * This is for backward compatibility.
+     *
+     * @param registrationTech The technology that IMS has been registered on.
+     * @param transportType The transport type that IMS has been registered on.
+     * @param imsAttributeFlags The attributes associated with the IMS registration.
+     * @param featureTags The feature tags included in the IMS registration.
+     * @hide
+     */
+    public ImsRegistrationAttributes(
+            @ImsRegistrationImplBase.ImsRegistrationTech int registrationTech,
+            @AccessNetworkConstants.TransportType int transportType,
+            @ImsAttributeFlag int imsAttributeFlags,
+            @Nullable Set<String> featureTags) {
+        mRegistrationTech = registrationTech;
+        mTransportType = transportType;
+        mImsAttributeFlags = imsAttributeFlags;
+        mFeatureTags = new ArrayList<>(featureTags);
+        mSipDetails = null;
+    }
+
+    /**
+     * Create a new {@link ImsRegistrationAttributes} instance.
+     *
+     * @param registrationTech The technology that IMS has been registered on.
+     * @param transportType The transport type that IMS has been registered on.
+     * @param imsAttributeFlags The attributes associated with the IMS registration.
+     * @param featureTags The feature tags included in the IMS registration.
+     * @param details The SIP information associated with the IMS registration.
+     * @see Builder
+     * @hide
+     */
+    public ImsRegistrationAttributes(
+            @ImsRegistrationImplBase.ImsRegistrationTech int registrationTech,
+            @AccessNetworkConstants.TransportType int transportType,
+            @ImsAttributeFlag int imsAttributeFlags,
+            @Nullable Set<String> featureTags,
+            @Nullable SipDetails details) {
+        mRegistrationTech = registrationTech;
+        mTransportType = transportType;
+        mImsAttributeFlags = imsAttributeFlags;
+        mFeatureTags = new ArrayList<>(featureTags);
+        mSipDetails = details;
+    }
+
+    /**@hide*/
+    public ImsRegistrationAttributes(Parcel source) {
+        mRegistrationTech = source.readInt();
+        mTransportType = source.readInt();
+        mImsAttributeFlags = source.readInt();
+        mFeatureTags = new ArrayList<>();
+        source.readList(mFeatureTags, null /*classloader*/, java.lang.String.class);
+        mSipDetails = source.readParcelable(null /*loader*/,
+                android.telephony.ims.SipDetails.class);
+    }
+
+    /**
+     * @return The Radio Access Technology that the IMS registration has been registered over.
+     * @hide
+     */
+    @SystemApi
+    public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTechnology() {
+        return mRegistrationTech;
+    }
+
+    /**
+     * @return The access network transport type that IMS has been registered over.
+     */
+    public @AccessNetworkConstants.TransportType int  getTransportType() {
+        return mTransportType;
+    }
+
+    /**
+     * @return A bit-mask containing attributes associated with the IMS registration.
+     */
+    public @ImsAttributeFlag int getAttributeFlags() {
+        return mImsAttributeFlags;
+    }
+
+    /**
+     * Gets the Set of feature tags associated with the current IMS registration, if the IMS
+     * service supports supplying this information.
+     * <p>
+     * The format of the set of feature tags will be one feature tag key and value per entry and
+     * will potentially contain MMTEL and RCS feature tags, depending the configuration of the IMS
+     * service associated with the registration indications. Each feature tag will contain the
+     * feature tag name and string value (if applicable), even if they have the same feature tag
+     * name. For example, {@code +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg,
+     * urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session", +g.gsma.callcomposer} will be split
+     * into three feature tag  entries:
+     * {@code {+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg",
+     * +g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session",
+     * +g.gsma.callcomposer}}.
+     * @return The Set of feature tags associated with the current IMS registration.
+     */
+    public @NonNull Set<String> getFeatureTags() {
+        if (mFeatureTags == null) {
+            return Collections.emptySet();
+        }
+        return new ArraySet<>(mFeatureTags);
+    }
+
+    /**
+     * @return The SIP information associated with the IMS registration.
+     */
+    public @Nullable SipDetails getSipDetails() {
+        return mSipDetails;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mRegistrationTech);
+        dest.writeInt(mTransportType);
+        dest.writeInt(mImsAttributeFlags);
+        dest.writeList(mFeatureTags);
+        dest.writeParcelable(mSipDetails, flags);
+    }
+
+    public static final @NonNull Creator<ImsRegistrationAttributes> CREATOR =
+            new Creator<ImsRegistrationAttributes>() {
+                @Override
+                public ImsRegistrationAttributes createFromParcel(Parcel source) {
+                    return new ImsRegistrationAttributes(source);
+                }
+
+                @Override
+                public ImsRegistrationAttributes[] newArray(int size) {
+                    return new ImsRegistrationAttributes[size];
+                }
+            };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ImsRegistrationAttributes that = (ImsRegistrationAttributes) o;
+        return mRegistrationTech == that.mRegistrationTech
+                && mTransportType == that.mTransportType
+                && mImsAttributeFlags == that.mImsAttributeFlags
+                && Objects.equals(mFeatureTags, that.mFeatureTags)
+                && Objects.equals(mSipDetails, that.mSipDetails);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRegistrationTech, mTransportType, mImsAttributeFlags, mFeatureTags,
+                mSipDetails);
+    }
+
+    @Override
+    public String toString() {
+        return "ImsRegistrationAttributes { transportType= " + mTransportType + ", attributeFlags="
+                + mImsAttributeFlags + ", featureTags=[" + mFeatureTags + "]"
+                + ",SipDetails=" + mSipDetails + "}";
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsService.java b/android-35/android/telephony/ims/ImsService.java
new file mode 100644
index 0000000..12e04c2
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsService.java
@@ -0,0 +1,994 @@
+/*
+ * Copyright (C) 2017 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.ims;
+
+import android.annotation.FlaggedApi;
+import android.annotation.LongDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsServiceController;
+import android.telephony.ims.aidl.IImsServiceControllerListener;
+import android.telephony.ims.aidl.ISipTransport;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.SipTransportImplBase;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
+/**
+ * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
+ * ImsService must register the service in their AndroidManifest to be detected by the framework.
+ * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
+ * permission. Then, the ImsService definition in the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgImsService"
+ *     android:permission="android.permission.BIND_IMS_SERVICE" >
+ *     ...
+ *     <intent-filter>
+ *         <action android:name="android.telephony.ims.ImsService" />
+ *     </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the ImsService you have defined in your manifest
+ * if you are either:
+ * 1) Defined as the default ImsService for the device in the device overlay using
+ *    "config_ims_mmtel_package" or "config_ims_rcs_package".
+ * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
+ *    {@link CarrierConfigManager#KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING} or
+ *    {@link CarrierConfigManager#KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING}.
+ *
+ * There are two ways to define to the platform which {@link ImsFeature}s this {@link ImsService}
+ * supports, dynamic or static definitions.
+ *
+ * In the static definition, the {@link ImsFeature}s that are supported are defined in the service
+ * definition of the AndroidManifest.xml file as metadata:
+ * <!-- Apps must declare which features they support as metadata. The different categories are
+ *      defined below. In this example, the MMTEL_FEATURE feature is supported. -->
+ * <meta-data android:name="android.telephony.ims.MMTEL_FEATURE" android:value="true" />
+ *
+ * The features that are currently supported in an ImsService are:
+ * - RCS_FEATURE: This ImsService implements the RcsFeature class.
+ * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class.
+ * - EMERGENCY_MMTEL_FEATURE: This ImsService supports Emergency Calling for MMTEL, must be
+ *   declared along with the MMTEL_FEATURE. If this is not specified, the framework will use
+ *   circuit switch for emergency calling.
+ *
+ * In the dynamic definition, the supported features are not specified in the service definition
+ * of the AndroidManifest. Instead, the framework binds to this service and calls
+ * {@link #querySupportedImsFeatures()}. The {@link ImsService} then returns an
+ * {@link ImsFeatureConfiguration}, which the framework uses to initialize the supported
+ * {@link ImsFeature}s. If at any time, the list of supported {@link ImsFeature}s changes,
+ * {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} can be called to tell the
+ * framework of the changes.
+ *
+ * @hide
+ */
+@SystemApi
+public class ImsService extends Service {
+
+    private static final String LOG_TAG = "ImsService";
+
+    /**
+     * This ImsService supports the capability to place emergency calls over MMTEL.
+     * <p>
+     * Note: This should never be set by {@link #getImsServiceCapabilities()}, as whether it is
+     * there or not depends on whether or not {@link ImsFeature#FEATURE_EMERGENCY_MMTEL} is defined
+     * for this ImsService. If it is set, it will be removed during sanitization before the final
+     * capabilities bitfield is sent back to the framework.
+     * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be
+     * adding other capabilities in a central location, so track this capability here as well.
+     */
+    public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0;
+
+    /**
+     * This ImsService supports the capability to create SIP delegates for other IMS applications
+     * to use to proxy SIP messaging traffic through it.
+     * <p>
+     * In order for the framework to report SipDelegate creation as being available for this
+     * ImsService implementation, this ImsService must report this capability flag in
+     * {@link #getImsServiceCapabilities()}, {@link #getSipTransport(int)} must not return null, and
+     * this ImsService MUST report the ability to create both {@link ImsFeature#FEATURE_MMTEL} and
+     * {@link ImsFeature#FEATURE_RCS} features.
+     */
+    public static final long CAPABILITY_SIP_DELEGATE_CREATION = 1 << 1;
+
+    /**
+     * This ImsService supports the terminal based call waiting service.
+     * <p>
+     * In order for the IMS service to support the service, IMS service shall
+     * override {@link MmTelFeature#setTerminalBasedCallWaitingStatus}.
+     * If ImsService has this capability, Android platform will handle the synchronization
+     * between the network based call waiting service over circuit-switched networks and the
+     * terminal based call waiting service of IMS service, and will handle the received
+     * circuit-switched waiting calls. Otherwise, this functionality of Android platform shall
+     * be disabled.
+     */
+    public static final long CAPABILITY_TERMINAL_BASED_CALL_WAITING = 1 << 2;
+
+    /**
+     * This ImsService supports the capability to manage calls on multiple subscriptions at the same
+     * time.
+     * <p>
+     * When set, this ImsService supports managing calls on multiple subscriptions at the same time
+     * for all WLAN network configurations. Telephony will allow new outgoing/incoming IMS calls to
+     * be set up on other subscriptions while there is an ongoing call. The ImsService must also
+     * support managing calls on WWAN + WWAN configurations whenever the modem also reports
+     * simultaneous calling availability, which can be listened to using the
+     * {@link android.telephony.TelephonyCallback.SimultaneousCellularCallingSupportListener} API.
+     * Telephony will only allow additional ongoing/incoming IMS calls on another subscription to be
+     * set up on WWAN + WWAN configurations when the modem reports that simultaneous cellular
+     * calling is allowed at the current time on both subscriptions where there are ongoing calls.
+     * <p>
+     * When unset (default), this ImsService can not support calls on multiple subscriptions at the
+     * same time for any WLAN or WWAN configurations, so pending outgoing call placed on another
+     * cellular subscription while there is an ongoing call will be cancelled by Telephony.
+     * Similarly, any incoming call notification on another cellular subscription while there is an
+     * ongoing call will be rejected.
+     * @hide TODO: move this to system API when we have a backing implementation + CTS testing
+     */
+    @FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS)
+    public static final long CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING = 1 << 3;
+
+    /**
+     * Used for internal correctness checks of capabilities set by the ImsService implementation and
+     * tracks the index of the largest defined flag in the capabilities long.
+     * @hide
+     */
+    public static final long CAPABILITY_MAX_INDEX =
+            Long.numberOfTrailingZeros(CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING);
+
+    /**
+     * @hide
+     */
+    @LongDef(flag = true,
+            prefix = "CAPABILITY_",
+            value = {
+                    // CAPABILITY_EMERGENCY_OVER_MMTEL is not included here because it is managed by
+                    // whether or not ImsFeature.FEATURE_EMERGENCY_MMTEL feature is set and should
+                    // not be set by users of ImsService.
+                    CAPABILITY_SIP_DELEGATE_CREATION,
+                    CAPABILITY_TERMINAL_BASED_CALL_WAITING,
+                    CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsServiceCapability {}
+
+    /**
+     * Used for logging purposes, see {@link #getCapabilitiesString(long)}
+     * @hide
+     */
+    private static final Map<Long, String> CAPABILITIES_LOG_MAP = Map.of(
+            CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL",
+            CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION",
+            CAPABILITY_TERMINAL_BASED_CALL_WAITING, "TERMINAL_BASED_CALL_WAITING",
+            CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING, "SIMULTANEOUS_CALLING");
+
+    /**
+     * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
+     * @hide
+     */
+    public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
+
+    // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
+    // slot.
+    // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
+    // call ImsFeature#onFeatureRemoved.
+    private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
+
+    // A map of slot id -> boolean array, where each entry in the boolean array corresponds to an
+    // ImsFeature that was created for a slot id and not a sub id for backwards compatibility
+    // purposes.
+    private final SparseArray<SparseBooleanArray> mCreateImsFeatureWithSlotIdFlagMap =
+            new SparseArray<>();
+
+    private IImsServiceControllerListener mListener;
+    private final Object mListenerLock = new Object();
+    private final Object mExecutorLock = new Object();
+    private Executor mExecutor;
+
+    /**
+     * Create a new ImsService.
+     * <p>
+     * Method stubs called from the framework will be called asynchronously. Vendor specifies the
+     * {@link Executor} that the methods stubs will be called. If mExecutor is set to null by
+     * vendor use Runnable::run.
+     */
+    public ImsService() {
+    }
+
+    /**
+     * Listener that notifies the framework of ImsService changes.
+     * @hide
+     */
+    public static class Listener extends IImsServiceControllerListener.Stub {
+        /**
+         * The IMS features that this ImsService supports has changed.
+         * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
+         *   that this ImsService supports. This may trigger the addition/removal of feature
+         *   in this service.
+         */
+        public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
+        }
+    }
+
+    /**
+     * @hide
+     */
+    protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
+        @Override
+        public void setListener(IImsServiceControllerListener l) {
+            synchronized (mListenerLock) {
+                if (mListener != null && mListener.asBinder().isBinderAlive()) {
+                    try {
+                        mListener.asBinder().unlinkToDeath(mDeathRecipient, 0);
+                    } catch (NoSuchElementException e) {
+                        Log.w(LOG_TAG, "IImsServiceControllerListener does not exist");
+                    }
+                }
+
+                mListener = l;
+                if (mListener == null) {
+                    executeMethodAsync(() -> releaseResource(), "releaseResource");
+                    return;
+                }
+
+                try {
+                    mListener.asBinder().linkToDeath(mDeathRecipient, 0);
+                    Log.i(LOG_TAG, "setListener: register linkToDeath");
+                } catch (RemoteException e) {
+                    // RemoteException means target binder process was crashed
+                    // release resource
+                    executeMethodAsync(() -> releaseResource(), "releaseResource");
+                }
+            }
+        }
+
+        @Override
+        public IImsMmTelFeature createMmTelFeature(int slotId, int subId) {
+            MmTelFeature f  = (MmTelFeature) getImsFeature(slotId, ImsFeature.FEATURE_MMTEL);
+            if (f == null) {
+                return executeMethodAsyncForResult(() -> createMmTelFeatureInternal(slotId, subId),
+                        "createMmTelFeature");
+            } else {
+                return f.getBinder();
+            }
+        }
+
+        @Override
+        public IImsMmTelFeature createEmergencyOnlyMmTelFeature(int slotId) {
+            MmTelFeature f  = (MmTelFeature) getImsFeature(slotId, ImsFeature.FEATURE_MMTEL);
+            if (f == null) {
+                return executeMethodAsyncForResult(() -> createEmergencyOnlyMmTelFeatureInternal(
+                        slotId), "createEmergencyOnlyMmTelFeature");
+            } else {
+                return f.getBinder();
+            }
+        }
+
+        @Override
+        public IImsRcsFeature createRcsFeature(int slotId, int subId) {
+            RcsFeature f  = (RcsFeature) getImsFeature(slotId, ImsFeature.FEATURE_RCS);
+            if (f == null) {
+                return executeMethodAsyncForResult(() ->
+                        createRcsFeatureInternal(slotId, subId), "createRcsFeature");
+            } else {
+                return f.getBinder();
+            }
+        }
+
+        @Override
+        public void addFeatureStatusCallback(int slotId, int featureType,
+                IImsFeatureStatusCallback c) {
+            executeMethodAsync(() -> ImsService.this.addImsFeatureStatusCallback(
+                    slotId, featureType, c), "addFeatureStatusCallback");
+        }
+
+        @Override
+        public void removeFeatureStatusCallback(int slotId, int featureType,
+                IImsFeatureStatusCallback c) {
+            executeMethodAsync(() -> ImsService.this.removeImsFeatureStatusCallback(
+                    slotId, featureType, c), "removeFeatureStatusCallback");
+        }
+
+        @Override
+        public void removeImsFeature(int slotId, int featureType, boolean changeSubId) {
+            if (changeSubId && isImsFeatureCreatedForSlot(slotId, featureType)) {
+                Log.w(LOG_TAG, "Do not remove Ims feature for compatibility");
+                return;
+            }
+            executeMethodAsync(() -> ImsService.this.removeImsFeature(slotId, featureType),
+                    "removeImsFeature");
+            setImsFeatureCreatedForSlot(slotId, featureType, false);
+        }
+
+        @Override
+        public ImsFeatureConfiguration querySupportedImsFeatures() {
+            return executeMethodAsyncForResult(() -> ImsService.this.querySupportedImsFeatures(),
+                    "ImsFeatureConfiguration");
+        }
+
+        @Override
+        public long getImsServiceCapabilities() {
+            return executeMethodAsyncForResult(() -> {
+                long caps = ImsService.this.getImsServiceCapabilities();
+                long sanitizedCaps = sanitizeCapabilities(caps);
+                if (caps != sanitizedCaps) {
+                    Log.w(LOG_TAG, "removing invalid bits from field: 0x"
+                            + Long.toHexString(caps ^ sanitizedCaps));
+                }
+                return sanitizedCaps;
+            }, "getImsServiceCapabilities");
+        }
+
+        @Override
+        public void notifyImsServiceReadyForFeatureCreation() {
+            executeMethodAsync(() -> ImsService.this.readyForFeatureCreation(),
+                    "notifyImsServiceReadyForFeatureCreation");
+        }
+
+        @Override
+        public IImsConfig getConfig(int slotId, int subId) {
+            return executeMethodAsyncForResult(() -> {
+                ImsConfigImplBase c =
+                        ImsService.this.getConfigForSubscription(slotId, subId);
+                if (c != null) {
+                    c.setDefaultExecutor(getCachedExecutor());
+                    return c.getIImsConfig();
+                } else {
+                    return null;
+                }
+            }, "getConfig");
+        }
+
+        @Override
+        public IImsRegistration getRegistration(int slotId, int subId) {
+            return executeMethodAsyncForResult(() -> {
+                ImsRegistrationImplBase r =
+                        ImsService.this.getRegistrationForSubscription(slotId, subId);
+                if (r != null) {
+                    r.setDefaultExecutor(getCachedExecutor());
+                    return r.getBinder();
+                } else {
+                    return null;
+                }
+            }, "getRegistration");
+        }
+
+        @Override
+        public ISipTransport getSipTransport(int slotId) {
+            return executeMethodAsyncForResult(() -> {
+                SipTransportImplBase s =  ImsService.this.getSipTransport(slotId);
+                if (s != null) {
+                    s.setDefaultExecutor(getCachedExecutor());
+                    return s.getBinder();
+                } else {
+                    return null;
+                }
+            }, "getSipTransport");
+        }
+
+        @Override
+        public void enableIms(int slotId, int subId) {
+            executeMethodAsync(() ->
+                    ImsService.this.enableImsForSubscription(slotId, subId), "enableIms");
+        }
+
+        @Override
+        public void disableIms(int slotId, int subId) {
+            executeMethodAsync(() ->
+                    ImsService.this.disableImsForSubscription(slotId, subId), "disableIms");
+        }
+
+        @Override
+        public void resetIms(int slotId, int subId) {
+            executeMethodAsync(() ->
+                    ImsService.this.resetImsInternal(slotId, subId), "resetIms");
+        }
+    };
+
+    private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            Log.w(LOG_TAG,
+                    "IImsServiceControllerListener binder to framework has died. Cleaning up");
+            executeMethodAsync(() -> releaseResource(), "releaseResource");
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        if(SERVICE_INTERFACE.equals(intent.getAction())) {
+            Log.i(LOG_TAG, "ImsService Bound.");
+            return mImsServiceController;
+        }
+        return null;
+    }
+
+    private Executor getCachedExecutor() {
+        synchronized (mExecutorLock) {
+            if (mExecutor == null) {
+                Executor e = ImsService.this.getExecutor();
+                mExecutor = (e != null) ? e : Runnable::run;
+            }
+            return mExecutor;
+        }
+    }
+
+    private IImsMmTelFeature createMmTelFeatureInternal(int slotId, int subscriptionId) {
+        MmTelFeature f = createMmTelFeatureForSubscription(slotId, subscriptionId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
+            f.setDefaultExecutor(getCachedExecutor());
+            return f.getBinder();
+        } else {
+            Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
+            return null;
+        }
+    }
+
+    private IImsMmTelFeature createEmergencyOnlyMmTelFeatureInternal(int slotId) {
+        MmTelFeature f = createEmergencyOnlyMmTelFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
+            f.setDefaultExecutor(getCachedExecutor());
+            return f.getBinder();
+        } else {
+            Log.e(LOG_TAG, "createEmergencyOnlyMmTelFeatureInternal: null feature returned.");
+            return null;
+        }
+    }
+
+    private IImsRcsFeature createRcsFeatureInternal(int slotId, int subId) {
+        RcsFeature f = createRcsFeatureForSubscription(slotId, subId);
+        if (f != null) {
+            f.setDefaultExecutor(getCachedExecutor());
+            setupFeature(f, slotId, ImsFeature.FEATURE_RCS);
+            return f.getBinder();
+        } else {
+            Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
+            return null;
+        }
+    }
+
+    private void setupFeature(ImsFeature f, int slotId, int featureType) {
+        f.initialize(this, slotId);
+        addImsFeature(slotId, featureType, f);
+    }
+
+    private void addImsFeatureStatusCallback(int slotId, int featureType,
+            IImsFeatureStatusCallback c) {
+        synchronized (mFeaturesBySlot) {
+            // get ImsFeature associated with the slot/feature
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback - no features on slot "
+                        + slotId);
+                return;
+            }
+            ImsFeature f = features.get(featureType);
+            if (f != null) {
+                f.addImsFeatureStatusCallback(c);
+            }
+        }
+    }
+
+    private void removeImsFeatureStatusCallback(int slotId, int featureType,
+            IImsFeatureStatusCallback c) {
+        synchronized (mFeaturesBySlot) {
+            // get ImsFeature associated with the slot/feature
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback - no features on slot "
+                        + slotId);
+                return;
+            }
+            ImsFeature f = features.get(featureType);
+            if (f != null) {
+                f.removeImsFeatureStatusCallback(c);
+            }
+        }
+    }
+
+    private void addImsFeature(int slotId, int featureType, ImsFeature f) {
+        synchronized (mFeaturesBySlot) {
+            // Get SparseArray for Features, by querying slot Id
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                // Populate new SparseArray of features if it doesn't exist for this slot yet.
+                features = new SparseArray<>();
+                mFeaturesBySlot.put(slotId, features);
+            }
+            features.put(featureType, f);
+        }
+    }
+
+    private void removeImsFeature(int slotId, int featureType) {
+        // clear cached data
+        notifySubscriptionRemoved(slotId);
+
+        synchronized (mFeaturesBySlot) {
+            // get ImsFeature associated with the slot/feature
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
+                        + slotId);
+                return;
+            }
+            ImsFeature f = features.get(featureType);
+            if (f == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
+                        + featureType + " exists on slot " + slotId);
+                return;
+            }
+            f.onFeatureRemoved();
+            features.remove(featureType);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public ImsFeature getImsFeature(int slotId, int featureType) {
+        synchronized (mFeaturesBySlot) {
+            // Get SparseArray for Features, by querying slot Id
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                return null;
+            }
+            return features.get(featureType);
+        }
+    }
+
+    private void setImsFeatureCreatedForSlot(int slotId,
+            @ImsFeature.FeatureType int featureType, boolean createdForSlot) {
+        synchronized (mCreateImsFeatureWithSlotIdFlagMap) {
+            getImsFeatureCreatedForSlot(slotId).put(featureType, createdForSlot);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public boolean isImsFeatureCreatedForSlot(int slotId,
+            @ImsFeature.FeatureType int featureType) {
+        synchronized (mCreateImsFeatureWithSlotIdFlagMap) {
+            return getImsFeatureCreatedForSlot(slotId).get(featureType);
+        }
+    }
+
+    private SparseBooleanArray getImsFeatureCreatedForSlot(int slotId) {
+        SparseBooleanArray createFlag = mCreateImsFeatureWithSlotIdFlagMap.get(slotId);
+        if (createFlag == null) {
+            createFlag = new SparseBooleanArray();
+            mCreateImsFeatureWithSlotIdFlagMap.put(slotId, createFlag);
+        }
+        return createFlag;
+    }
+
+    private void releaseResource() {
+        Log.w(LOG_TAG, "cleaning up features");
+        synchronized (mFeaturesBySlot) {
+            SparseArray<ImsFeature> features;
+            ImsFeature imsFeature;
+
+            for (int i = 0; i < mFeaturesBySlot.size(); i++) {
+                features = mFeaturesBySlot.valueAt(i);
+                if (features == null) {
+                    continue;
+                }
+
+                for (int index = 0; index < features.size(); index++) {
+                    imsFeature = features.valueAt(index);
+                    if (imsFeature != null) {
+                        imsFeature.onFeatureRemoved();
+                    }
+                }
+                features.clear();
+            }
+            mFeaturesBySlot.clear();
+        }
+    }
+
+    // Call the methods with a clean calling identity on the executor and wait indefinitely for
+    // the future to return.
+    private void executeMethodAsync(Runnable r, String errorLogName) {
+        try {
+            CompletableFuture.runAsync(
+                    () -> TelephonyUtils.runWithCleanCallingIdentity(r),
+                    getCachedExecutor()).join();
+        } catch (CancellationException | CompletionException e) {
+            Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
+                    + e.getMessage());
+        }
+    }
+
+    private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) {
+        CompletableFuture<T> future = CompletableFuture.supplyAsync(
+                () -> TelephonyUtils.runWithCleanCallingIdentity(r), getCachedExecutor());
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
+                    + e.getMessage());
+            return null;
+        }
+    }
+
+    private void resetImsInternal(int slotId, int subId) {
+        try {
+            resetIms(slotId);
+        } catch (UnsupportedOperationException e) {
+            disableImsForSubscription(slotId, subId);
+        }
+    }
+
+    /**
+     * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService}
+     * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that
+     * correspond to the {@link ImsFeature}s configured here.
+     *
+     * Use {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} to change the supported
+     * {@link ImsFeature}s.
+     *
+     * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports.
+     */
+    public ImsFeatureConfiguration querySupportedImsFeatures() {
+        // Return empty for base implementation
+        return new ImsFeatureConfiguration();
+    }
+
+    /**
+     * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
+     * features, that this {@link ImsService} supports. This may trigger the framework to add/remove
+     * new ImsFeatures, depending on the configuration.
+     */
+    public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
+            throws RemoteException {
+        IImsServiceControllerListener l;
+        synchronized (mListenerLock) {
+            if (mListener == null) {
+                throw new IllegalStateException("Framework is not ready");
+            }
+            l = mListener;
+        }
+        l.onUpdateSupportedImsFeatures(c);
+    }
+
+    /**
+     * The optional capabilities that this ImsService supports.
+     * <p>
+     * This should be a static configuration and should not change at runtime.
+     * @return The optional static capabilities of this ImsService implementation.
+     */
+    // ImsService follows a different convention, since it is a stub class. The on* methods are
+    // final and call back into the framework with a state update.
+    @SuppressLint("OnNameExpected")
+    public @ImsServiceCapability long getImsServiceCapabilities() {
+        // Stub implementation to be implemented by ImsService.
+        return 0L;
+    }
+
+    /**
+     * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
+     * the ImsService has registered for with the framework, either in the manifest or via
+     * {@link #querySupportedImsFeatures()}.
+     *
+     * The ImsService should use this signal instead of onCreate/onBind or similar to perform
+     * feature initialization because the framework may bind to this service multiple times to
+     * query the ImsService's {@link ImsFeatureConfiguration} via
+     * {@link #querySupportedImsFeatures()}before creating features.
+     */
+    public void readyForFeatureCreation() {
+    }
+
+    /**
+     * The framework has enabled IMS for the subscription specified, the ImsService should register
+     * for IMS and perform all appropriate initialization to bring up all ImsFeatures.
+     *
+     * @param slotId The slot ID that IMS will be enabled for.
+     * @param subscriptionId The subscription ID that IMS will be enabled for.
+     */
+    public void enableImsForSubscription(int slotId, int subscriptionId) {
+        enableIms(slotId);
+    }
+
+    /**
+     * The framework has disabled IMS for the subscription specified. The ImsService must deregister
+     * for IMS and set capability status to false for all ImsFeatures.
+     * @param slotId The slot ID that IMS will be disabled for.
+     * @param subscriptionId The subscription ID that IMS will be disabled for.
+     */
+    public void disableImsForSubscription(int slotId, int subscriptionId) {
+        disableIms(slotId);
+    }
+
+    /**
+     * The subscription has removed. The ImsService should notify ImsRegistrationImplBase and
+     * ImsConfigImplBase the SIM state was changed.
+     * @param slotId The slot ID which has removed.
+     */
+    private void notifySubscriptionRemoved(int slotId) {
+        ImsRegistrationImplBase registrationImplBase =
+                getRegistration(slotId);
+        if (registrationImplBase != null) {
+            registrationImplBase.clearRegistrationCache();
+        }
+
+        ImsConfigImplBase imsConfigImplBase = getConfig(slotId);
+        if (imsConfigImplBase != null) {
+            imsConfigImplBase.clearConfigurationCache();
+        }
+    }
+
+    /**
+     * The framework has enabled IMS for the slot specified, the ImsService should register for IMS
+     * and perform all appropriate initialization to bring up all ImsFeatures.
+     * @deprecated Use {@link #enableImsForSubscription} instead.
+     */
+    @Deprecated
+    public void enableIms(int slotId) {
+    }
+
+    /**
+     * The framework has disabled IMS for the slot specified. The ImsService must deregister for IMS
+     * and set capability status to false for all ImsFeatures.
+     * @deprecated Use {@link #disableImsForSubscription} instead.
+     */
+    @Deprecated
+    public void disableIms(int slotId) {
+    }
+
+    /**
+     * The framework has reset IMS for the slot specified. The ImsService must deregister
+     * and release all resources for IMS. After resetIms is called, either
+     * {@link #enableImsForSubscription(int, int)} or {@link #disableImsForSubscription(int, int)}
+     * will be called for the same slotId.
+     *
+     * @param slotId The slot ID that IMS will be reset for.
+     * @hide
+     */
+    public void resetIms(int slotId) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
+     * specified subscription.
+     *
+     * @param subscriptionId The subscription ID that the MMTEL Feature is being created for.
+     * @return The newly created {@link MmTelFeature} associated with the subscription or null if
+     * the feature is not supported.
+     */
+    public @Nullable MmTelFeature createMmTelFeatureForSubscription(int slotId,
+            int subscriptionId) {
+        setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_MMTEL, true);
+        return createMmTelFeature(slotId);
+    }
+
+    /**
+     * When called, the framework is requesting that a new {@link RcsFeature} is created for the
+     * specified subscription.
+     *
+     * @param subscriptionId The subscription ID that the RCS Feature is being created for.
+     * @return The newly created {@link RcsFeature} associated with the subscription or null if the
+     * feature is not supported.
+     */
+    public @Nullable RcsFeature createRcsFeatureForSubscription(int slotId, int subscriptionId) {
+        setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_RCS, true);
+        return createRcsFeature(slotId);
+    }
+
+    /**
+     * When called, the framework is requesting that a new emergency-only {@link MmTelFeature} is
+     * created for the specified slot. For emergency calls, there is no known Subscription Id.
+     *
+     * @param slotId The slot ID that the MMTEL Feature is being created for.
+     * @return An MmTelFeature instance to be used for the slot ID when there is not
+     * subscription inserted. Only requested when there is no subscription active on
+     * the specified slot.
+     */
+    public @Nullable MmTelFeature createEmergencyOnlyMmTelFeature(int slotId) {
+        setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_MMTEL, true);
+        return createMmTelFeature(slotId);
+    }
+
+    /**
+     * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
+     * specified slot.
+     * @deprecated Use {@link #createMmTelFeatureForSubscription} instead
+     *
+     * @param slotId The slot ID that the MMTEL Feature is being created for.
+     * @return The newly created {@link MmTelFeature} associated with the slot or null if the
+     * feature is not supported.
+     */
+    @Deprecated
+    public MmTelFeature createMmTelFeature(int slotId) {
+        return null;
+    }
+
+    /**
+     * When called, the framework is requesting that a new {@link RcsFeature} is created for the
+     * specified slot.
+     * @deprecated Use {@link #createRcsFeatureForSubscription} instead
+     *
+     * @param slotId The slot ID that the RCS Feature is being created for.
+     * @return The newly created {@link RcsFeature} associated with the slot or null if the feature
+     * is not supported.
+     */
+    @Deprecated
+    public RcsFeature createRcsFeature(int slotId) {
+        return null;
+    }
+
+    /**
+     * Return the {@link ImsConfigImplBase} implementation associated with the provided
+     * subscription. This will be used by the platform to get/set specific IMS related
+     * configurations.
+     *
+     * @param subscriptionId The subscription ID that the IMS configuration is associated with.
+     * @return ImsConfig implementation that is associated with the specified subscription.
+     */
+    public @NonNull ImsConfigImplBase getConfigForSubscription(int slotId, int subscriptionId) {
+        return getConfig(slotId);
+    }
+
+    /**
+     * Return the {@link ImsRegistrationImplBase} implementation associated with the provided
+     * subscription.
+     *
+     * @param subscriptionId The subscription ID that is associated with the IMS Registration.
+     * @return the ImsRegistration implementation associated with the subscription.
+     */
+    public @NonNull ImsRegistrationImplBase getRegistrationForSubscription(int slotId,
+            int subscriptionId) {
+        return getRegistration(slotId);
+    }
+
+    /**
+     * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This
+     * will be used by the platform to get/set specific IMS related configurations.
+     * @deprecated use {@link #getConfigForSubscription} instead.
+     *
+     * @param slotId The slot that the IMS configuration is associated with.
+     * @return ImsConfig implementation that is associated with the specified slot.
+     */
+    @Deprecated
+    public ImsConfigImplBase getConfig(int slotId) {
+        return new ImsConfigImplBase();
+    }
+
+    /**
+     * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot.
+     * @deprecated use  {@link #getRegistrationForSubscription} instead.
+     *
+     * @param slotId The slot that is associated with the IMS Registration.
+     * @return the ImsRegistration implementation associated with the slot.
+     */
+    @Deprecated
+    public ImsRegistrationImplBase getRegistration(int slotId) {
+        return new ImsRegistrationImplBase();
+    }
+
+    /**
+     * Return the {@link SipTransportImplBase} implementation associated with the provided slot.
+     * <p>
+     * This is an optional interface used for devices that must support IMS single registration and
+     * proxy SIP traffic to remote IMS applications. If this is not supported for this IMS service,
+     * this method should return {@code null}. If this feature is supported, then this method must
+     * never be {@code null} and the optional ImsService capability flag
+     * {@link #CAPABILITY_SIP_DELEGATE_CREATION} must be set in
+     * {@link #getImsServiceCapabilities()}. Otherwise the framework will assume this feature is not
+     * supported for this ImsService.
+     * @param slotId The slot that is associated with the SipTransport implementation.
+     * @return the SipTransport implementation for the specified slot.
+     */
+    // ImsService follows a different convention, since it is a stub class. The on* methods are
+    // final and call back into the framework with a state update.
+    @SuppressLint("OnNameExpected")
+    public @Nullable SipTransportImplBase getSipTransport(int slotId) {
+        // Stub implementation for ImsServices that do not support SipTransport.
+        return null;
+    }
+
+    private static long sanitizeCapabilities(@ImsServiceCapability long caps) {
+        long filter = 0xFFFFFFFFFFFFFFFFL;
+        // pad the "allowed" set with zeros
+        filter <<= CAPABILITY_MAX_INDEX + 1;
+        // remove values above the allowed set.
+        caps &= ~filter;
+        // CAPABILITY_EMERGENCY_OVER_MMTEL should also not be set here, will be set by telephony
+        // internally.
+        caps &= ~CAPABILITY_EMERGENCY_OVER_MMTEL;
+        return caps;
+    }
+
+    /**
+     * @return A string representation of the ImsService capabilities for logging.
+     * @hide
+     */
+    public static String getCapabilitiesString(@ImsServiceCapability long caps) {
+        StringBuffer result = new StringBuffer();
+        result.append("capabilities={ ");
+        // filter incrementally fills 0s from  left to right. This is used to keep filtering out
+        // more bits in the long until the remaining leftmost bits are all zero.
+        long filter = 0xFFFFFFFFFFFFFFFFL;
+        // position of iterator to potentially print capability.
+        long i = 0;
+        while ((caps & filter) != 0 && i <= 63) {
+            long bitToCheck = (1L << i);
+            if ((caps & bitToCheck) != 0) {
+                result.append(CAPABILITIES_LOG_MAP.getOrDefault(bitToCheck, bitToCheck + "?"));
+                result.append(" ");
+            }
+            // shift left by one and fill in another 1 on the leftmost bit.
+            filter <<= 1;
+            i++;
+        }
+        result.append("}");
+        return result.toString();
+    }
+
+    /**
+     * The ImsService will now be able to define an Executor that the ImsService can be used to
+     * execute the methods. By default all ImsService level method calls will use this Executor.
+     * The ImsService has set the default executor as Runnable::run,
+     * Should be override or default executor will be used.
+     *  @return an Executor used to execute methods called remotely by the framework.
+     */
+    public @NonNull Executor getExecutor() {
+        return Runnable::run;
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsSsData.java b/android-35/android/telephony/ims/ImsSsData.java
new file mode 100644
index 0000000..9f4b77e
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsSsData.java
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Provides STK Call Control Supplementary Service information.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class ImsSsData implements Parcelable {
+
+    private static final String TAG = ImsSsData.class.getCanonicalName();
+
+    // Supplementary Service Type
+    // Call Forwarding
+    public static final int SS_CFU = 0;
+    public static final int SS_CF_BUSY = 1;
+    public static final int SS_CF_NO_REPLY = 2;
+    public static final int SS_CF_NOT_REACHABLE = 3;
+    public static final int SS_CF_ALL = 4;
+    public static final int SS_CF_ALL_CONDITIONAL = 5;
+    public static final int SS_CFUT = 6;
+    // Called Line Presentation
+    public static final int SS_CLIP = 7;
+    public static final int SS_CLIR = 8;
+    public static final int SS_COLP = 9;
+    public static final int SS_COLR = 10;
+    // Calling Name Presentation
+    public static final int SS_CNAP = 11;
+    // Call Waiting
+    public static final int SS_WAIT = 12;
+    // Call Barring
+    public static final int SS_BAOC = 13;
+    public static final int SS_BAOIC = 14;
+    public static final int SS_BAOIC_EXC_HOME = 15;
+    public static final int SS_BAIC = 16;
+    public static final int SS_BAIC_ROAMING = 17;
+    public static final int SS_ALL_BARRING = 18;
+    public static final int SS_OUTGOING_BARRING = 19;
+    public static final int SS_INCOMING_BARRING = 20;
+    public static final int SS_INCOMING_BARRING_DN = 21;
+    public static final int SS_INCOMING_BARRING_ANONYMOUS = 22;
+
+
+    /**@hide*/
+    @IntDef(prefix = {"SS_"}, value = {
+            SS_ACTIVATION,
+            SS_DEACTIVATION,
+            SS_INTERROGATION,
+            SS_REGISTRATION,
+            SS_ERASURE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RequestType{}
+
+    //Supplementary Service Request Types
+    public static final int SS_ACTIVATION = 0;
+    public static final int SS_DEACTIVATION = 1;
+    public static final int SS_INTERROGATION = 2;
+    public static final int SS_REGISTRATION = 3;
+    public static final int SS_ERASURE = 4;
+
+    /**@hide*/
+    @IntDef(prefix = {"SS_"}, value = {
+            SS_ALL_TELE_AND_BEARER_SERVICES,
+            SS_ALL_TELESEVICES,
+            SS_TELEPHONY,
+            SS_ALL_DATA_TELESERVICES,
+            SS_SMS_SERVICES,
+            SS_ALL_TELESERVICES_EXCEPT_SMS})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TeleserviceType{}
+
+    // Supplementary Service Teleservice Type
+    public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0;
+    public static final int SS_ALL_TELESEVICES = 1;
+    public static final int SS_TELEPHONY = 2;
+    public static final int SS_ALL_DATA_TELESERVICES = 3;
+    public static final int SS_SMS_SERVICES = 4;
+    public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5;
+
+    /**
+     * No call forwarding service class defined.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_NONE = 0;
+
+    /**
+     * Service class flag for voice telephony.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_VOICE = 1;
+
+    /**
+     * Service class flag for all data bearers (including
+     * {@link #SERVICE_CLASS_DATA_CIRCUIT_SYNC,
+     * {@link #SERVICE_CLASS_DATA_CIRCUIT_ASYNC}, {@link #SERVICE_CLASS_PACKET_ACCESS},
+     * {@link #SERVICE_CLASS_PAD}}) if supported by the carrier.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_DATA = (1 << 1);
+    /**
+     * Service class flag for fax services.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_FAX = (1 << 2);
+    /**
+     * Service class flag for SMS services.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_SMS = (1 << 3);
+    /**
+     * Service class flag for the synchronous bearer service.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_DATA_CIRCUIT_SYNC = (1 << 4);
+
+    /**
+     * Service class flag for the asynchronous bearer service.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_DATA_CIRCUIT_ASYNC = (1 << 5);
+
+    /**
+     * Service class flag for the packet access bearer service.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_DATA_PACKET_ACCESS = (1 << 6);
+
+    /**
+     * Service class flag for the Packet Assembly/Disassembly bearer service.
+     *
+     * See TS 27.007 7.11 (+CCFC) and 7.4 (CLCK)
+     */
+    public static final int SERVICE_CLASS_DATA_PAD = (1 << 7);
+
+    /**@hide*/
+    @IntDef(flag = true, prefix = {"SERVICE_CLASS_"}, value = {
+            SERVICE_CLASS_NONE,
+            SERVICE_CLASS_VOICE,
+            SERVICE_CLASS_DATA,
+            SERVICE_CLASS_FAX,
+            SERVICE_CLASS_SMS,
+            SERVICE_CLASS_DATA_CIRCUIT_SYNC,
+            SERVICE_CLASS_DATA_CIRCUIT_ASYNC,
+            SERVICE_CLASS_DATA_PACKET_ACCESS,
+            SERVICE_CLASS_DATA_PAD})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ServiceClassFlags{}
+
+    /**
+     * Result code used if the operation was successful. See {@link #getResult()}.
+     */
+    public static final int RESULT_SUCCESS = 0;
+
+    /** @hide */
+    @IntDef(prefix = { "SS_" }, value = {
+            SS_CFU,
+            SS_CF_BUSY,
+            SS_CF_NO_REPLY,
+            SS_CF_NOT_REACHABLE,
+            SS_CF_ALL,
+            SS_CF_ALL_CONDITIONAL,
+            SS_CFUT,
+            SS_CLIP,
+            SS_CLIR,
+            SS_COLP,
+            SS_COLR,
+            SS_CNAP,
+            SS_WAIT,
+            SS_BAOC,
+            SS_BAOIC,
+            SS_BAOIC_EXC_HOME,
+            SS_BAIC,
+            SS_BAIC_ROAMING,
+            SS_ALL_BARRING,
+            SS_OUTGOING_BARRING,
+            SS_INCOMING_BARRING,
+            SS_INCOMING_BARRING_DN,
+            SS_INCOMING_BARRING_ANONYMOUS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ServiceType{}
+
+    /**
+     * The Service type of this Supplementary service.
+     * @hide
+     */
+    public final @ServiceType int serviceType;
+
+    /**
+     * Supplementary Service request Type:
+     *     {@link #SS_ACTIVATION),
+     *     {@link #SS_DEACTIVATION},
+     *     {@link #SS_INTERROGATION},
+     *     {@link #SS_REGISTRATION},
+     *     {@link #SS_ERASURE}
+     * @hide
+     */
+    public final @RequestType int requestType;
+
+    /**
+     * Supplementary Service teleservice type:
+     *     {@link #SS_ALL_TELE_AND_BEARER_SERVICES},
+     *     {@link #SS_ALL_TELESEVICES},
+     *     {@link #SS_TELEPHONY},
+     *     {@link #SS_ALL_DATA_TELESERVICES},
+     *     {@link #SS_SMS_SERVICES},
+     *     {@link #SS_ALL_TELESERVICES_EXCEPT_SMS}
+     *
+     * @hide
+     */
+    public final @TeleserviceType int teleserviceType;
+
+    /**
+     * Supplementary Service service class.
+     *
+     * @hide
+     */
+    public final @ServiceClassFlags int serviceClass;
+
+    /**
+     * Result of Supplementary Service operation. Valid values are:
+     *     {@link #RESULT_SUCCESS} if the result is success, or
+     *     ImsReasonInfo code if the result is a failure.
+     *
+     * @hide
+     */
+    public final int result;
+
+    private int[] mSsInfo;
+    private List<ImsCallForwardInfo> mCfInfo;
+    private List<ImsSsInfo> mImsSsInfo;
+
+    /**
+     * Builder for optional ImsSsData parameters.
+     */
+    public static final class Builder {
+        private ImsSsData mImsSsData;
+
+        /**
+         * Generate IMS Supplementary Service information.
+         * @param serviceType The Supplementary Service type.
+         * @param requestType Supplementary Service request Type:
+         *     {@link #SS_ACTIVATION},
+         *     {@link #SS_DEACTIVATION},
+         *     {@link #SS_INTERROGATION},
+         *     {@link #SS_REGISTRATION},
+         *     {@link #SS_ERASURE}
+         * @param teleserviceType Supplementary Service teleservice type:
+         *     {@link #SS_ALL_TELE_AND_BEARER_SERVICES},
+         *     {@link #SS_ALL_TELESEVICES},
+         *     {@link #SS_TELEPHONY},
+         *     {@link #SS_ALL_DATA_TELESERVICES},
+         *     {@link #SS_SMS_SERVICES},
+         *     {@link #SS_ALL_TELESERVICES_EXCEPT_SMS}
+         * @param serviceClass Supplementary Service service class. See See 27.007 +CCFC or +CLCK.
+         * @param result Result of Supplementary Service operation. Valid values are 0 if the result
+         *               is success, or {@link ImsReasonInfo} code if the result is a failure.
+         * @return this Builder instance for further constructing.
+         * @see #build()
+         */
+        public Builder(@ServiceType int serviceType, int requestType, int teleserviceType,
+                @ServiceClassFlags int serviceClass, int result) {
+            mImsSsData = new ImsSsData(serviceType, requestType, teleserviceType, serviceClass,
+                    result);
+        }
+
+        /**
+         * Set the array of {@link ImsSsInfo}s that are associated with this supplementary service
+         * data.
+         */
+        public @NonNull Builder setSuppServiceInfo(@NonNull List<ImsSsInfo> imsSsInfos) {
+            mImsSsData.mImsSsInfo = imsSsInfos;
+            return this;
+        }
+
+        /**
+         * Set the array of {@link ImsCallForwardInfo}s that are associated with this supplementary
+         * service data.
+         */
+        public @NonNull Builder setCallForwardingInfo(
+                @NonNull List<ImsCallForwardInfo> imsCallForwardInfos) {
+            mImsSsData.mCfInfo = imsCallForwardInfos;
+            return this;
+        }
+
+        /**
+         * @return an {@link ImsSsData} containing optional parameters.
+         */
+        public @NonNull ImsSsData build() {
+            return mImsSsData;
+        }
+    }
+
+    /**
+     * Generate IMS Supplementary Service information.
+     * @param serviceType The Supplementary Service type.
+     * @param requestType Supplementary Service request Type. Valid values are:
+     *     {@link #SS_ACTIVATION},
+     *     {@link #SS_DEACTIVATION},
+     *     {@link #SS_INTERROGATION},
+     *     {@link #SS_REGISTRATION},
+     *     {@link #SS_ERASURE}
+     * @param teleserviceType Supplementary Service teleservice type:
+     *     {@link #SS_ALL_TELE_AND_BEARER_SERVICES},
+     *     {@link #SS_ALL_TELESEVICES},
+     *     {@link #SS_TELEPHONY},
+     *     {@link #SS_ALL_DATA_TELESERVICES},
+     *     {@link #SS_SMS_SERVICES},
+     *     {@link #SS_ALL_TELESERVICES_EXCEPT_SMS}
+     * @param serviceClass Supplementary Service service class. See See 27.007 +CCFC or +CLCK.
+     * @param result Result of Supplementary Service operation. Valid values are 0 if the result is
+     *               success, or ImsReasonInfo code if the result is a failure.
+     */
+    public ImsSsData(@ServiceType int serviceType, int requestType, int teleserviceType,
+            @ServiceClassFlags int serviceClass, int result) {
+        this.serviceType = serviceType;
+        this.requestType = requestType;
+        this.teleserviceType = teleserviceType;
+        this.serviceClass = serviceClass;
+        this.result = result;
+    }
+
+    private ImsSsData(Parcel in) {
+        serviceType = in.readInt();
+        requestType = in.readInt();
+        teleserviceType = in.readInt();
+        serviceClass = in.readInt();
+        result = in.readInt();
+        mSsInfo = in.createIntArray();
+        mCfInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader(), android.telephony.ims.ImsCallForwardInfo.class);
+        mImsSsInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader(), android.telephony.ims.ImsSsInfo.class);
+    }
+
+    public static final @android.annotation.NonNull Creator<ImsSsData> CREATOR = new Creator<ImsSsData>() {
+        @Override
+        public ImsSsData createFromParcel(Parcel in) {
+            return new ImsSsData(in);
+        }
+
+        @Override
+        public ImsSsData[] newArray(int size) {
+            return new ImsSsData[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(getServiceType());
+        out.writeInt(getRequestType());
+        out.writeInt(getTeleserviceType());
+        out.writeInt(getServiceClass());
+        out.writeInt(getResult());
+        out.writeIntArray(mSsInfo);
+        out.writeParcelableList(mCfInfo, 0);
+        out.writeParcelableList(mImsSsInfo, 0);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Old method, kept for compatibility. See {@link #isTypeCf()}
+     * @hide
+     */
+    public boolean isTypeCF() {
+        return (getServiceType() == SS_CFU || getServiceType() == SS_CF_BUSY
+                || getServiceType() == SS_CF_NO_REPLY || getServiceType() == SS_CF_NOT_REACHABLE
+                || getServiceType() == SS_CF_ALL || getServiceType() == SS_CF_ALL_CONDITIONAL);
+    }
+
+    public boolean isTypeCf() {
+        return isTypeCF();
+    }
+
+    public boolean isTypeUnConditional() {
+        return (getServiceType() == SS_CFU || getServiceType() == SS_CF_ALL);
+    }
+
+    /**
+     * Old method, kept for compatibility. See {@link #isTypeCf()}
+     * @hide
+     */
+    public boolean isTypeCW() {
+        return (getServiceType() == SS_WAIT);
+    }
+
+    public boolean isTypeCw() {
+        return isTypeCW();
+    }
+
+    public boolean isTypeClip() {
+        return (getServiceType() == SS_CLIP);
+    }
+
+    public boolean isTypeColr() {
+        return (getServiceType() == SS_COLR);
+    }
+
+    public boolean isTypeColp() {
+        return (getServiceType() == SS_COLP);
+    }
+
+    public boolean isTypeClir() {
+        return (getServiceType() == SS_CLIR);
+    }
+
+    public boolean isTypeIcb() {
+        return (getServiceType() == SS_INCOMING_BARRING_DN
+                || getServiceType() == SS_INCOMING_BARRING_ANONYMOUS);
+    }
+
+    public boolean isTypeBarring() {
+        return (getServiceType() == SS_BAOC || getServiceType() == SS_BAOIC
+                || getServiceType() == SS_BAOIC_EXC_HOME || getServiceType() == SS_BAIC
+                || getServiceType() == SS_BAIC_ROAMING || getServiceType() == SS_ALL_BARRING
+                || getServiceType() == SS_OUTGOING_BARRING
+                || getServiceType() == SS_INCOMING_BARRING);
+    }
+
+    public boolean isTypeInterrogation() {
+        return (getRequestType() == SS_INTERROGATION);
+    }
+
+    /**
+     * Supplementary Service request Type.
+     */
+    public @RequestType int getRequestType() {
+        return requestType;
+    }
+
+    /**
+     * The Service type of this Supplementary service.
+     */
+    public @ServiceType int getServiceType() {
+        return serviceType;
+    }
+
+    /**
+     * Supplementary Service teleservice type.
+     */
+    public @TeleserviceType int getTeleserviceType() {
+        return teleserviceType;
+    }
+
+    /**
+     * Supplementary Service service class.
+     */
+    public @ServiceClassFlags int getServiceClass() {
+        return serviceClass;
+    }
+
+    /**
+     * Result of Supplementary Service operation. Valid values are:
+     *     {@link #RESULT_SUCCESS} if the result is success, or
+     *     {@link ImsReasonInfo.UtReason} code if the result is a failure.
+     */
+    public @ImsReasonInfo.UtReason int getResult() {
+        return result;
+    }
+
+    /** @hide */
+    public void setSuppServiceInfo(int[] ssInfo) {
+        mSsInfo = ssInfo;
+    }
+
+    /** @hide */
+    public void setImsSpecificSuppServiceInfo(ImsSsInfo[] imsSsInfo) {
+        mImsSsInfo = Arrays.asList(imsSsInfo);
+    }
+
+    /** @hide */
+    public void setCallForwardingInfo(ImsCallForwardInfo[] cfInfo) {
+        mCfInfo = Arrays.asList(cfInfo);
+    }
+
+    /**
+     * This is a compatibility function to transform the public API to a form that can be processed
+     * by telephony.
+     *
+     * @hide
+     */
+    //TODO: Refactor Telephony to use well defined classes instead of an int[] to process SS.
+    public int[] getSuppServiceInfoCompat() {
+        if (mSsInfo != null) {
+            // Something has set the ssInfo using hidden APIs, so for compatibility just return that
+            // structure directly.
+            return mSsInfo;
+        }
+
+
+        int[] result = new int[2];
+        if (mImsSsInfo == null || mImsSsInfo.size() == 0) {
+            Rlog.e(TAG, "getSuppServiceInfoCompat: Could not parse mImsSsInfo, returning empty "
+                    + "int[]");
+            return result;
+        }
+
+        // Convert ImsSsInfo into a form that telephony can read (as per 3GPP 27.007)
+        // CLIR (section 7.7)
+        if (isTypeClir()) {
+            // Assume there will only be one ImsSsInfo.
+            // contains {"n","m"} parameters
+            result[0] = mImsSsInfo.get(0).getClirOutgoingState();
+            result[1] = mImsSsInfo.get(0).getClirInterrogationStatus();
+            return result;
+        }
+        // COLR 7.31
+        if (isTypeColr()) {
+            result[0] = mImsSsInfo.get(0).getProvisionStatus();
+        }
+        // Facility Lock CLCK 7.4 (for call barring), CLIP 7.6, COLP 7.8, as well as any
+        // other result, just return the status for the "n" parameter and provisioning status for
+        // "m" as the default.
+        result[0] = mImsSsInfo.get(0).getStatus();
+        result[1] = mImsSsInfo.get(0).getProvisionStatus();
+        return result;
+    }
+
+    /**
+     * @return an array of {@link ImsSsInfo}s associated with this supplementary service data.
+     */
+    public @NonNull List<ImsSsInfo> getSuppServiceInfo() {
+        return mImsSsInfo;
+    }
+
+    /**
+     * @return an array of {@link ImsCallForwardInfo}s associated with this supplementary service
+     * data.
+     **/
+    public @Nullable List<ImsCallForwardInfo> getCallForwardInfo() {
+        return mCfInfo;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "[ImsSsData] " + "ServiceType: " + getServiceType()
+            + " RequestType: " + getRequestType()
+            + " TeleserviceType: " + getTeleserviceType()
+            + " ServiceClass: " + getServiceClass()
+            + " Result: " + getResult();
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsSsInfo.java b/android-35/android/telephony/ims/ImsSsInfo.java
new file mode 100644
index 0000000..bc53b62
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsSsInfo.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides the result to the update operation for the supplementary service configuration.
+ *
+ * Also supports IMS specific Incoming Communication Barring (ICB) as well as Anonymous
+ * Communication Rejection (ACR), as per 3GPP 24.611.
+ *
+ * @see Builder
+ * @hide
+ */
+@SystemApi
+public final class ImsSsInfo implements Parcelable {
+
+    /**@hide*/
+    @IntDef(value = {
+            NOT_REGISTERED,
+            DISABLED,
+            ENABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ServiceStatus {}
+
+    /**
+     * For the status of service registration or activation/deactivation.
+     */
+    public static final int NOT_REGISTERED = (-1);
+    public static final int DISABLED = 0;
+    public static final int ENABLED = 1;
+
+    /**
+     * Provision status of service.
+     * @hide
+     */
+    @IntDef(value = {
+            SERVICE_PROVISIONING_UNKNOWN,
+            SERVICE_NOT_PROVISIONED,
+            SERVICE_PROVISIONED
+    }, prefix = "SERVICE_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ServiceProvisionStatus {}
+
+    /**
+     * Unknown provision status for the service.
+     */
+    public static final int SERVICE_PROVISIONING_UNKNOWN = (-1);
+
+    /**
+     * Service is not provisioned.
+     */
+    public static final int SERVICE_NOT_PROVISIONED = 0;
+
+    /**
+     * Service is provisioned.
+     */
+    public static final int SERVICE_PROVISIONED = 1;
+
+    /**@hide*/
+    @IntDef(value = {
+            CLIR_OUTGOING_DEFAULT,
+            CLIR_OUTGOING_INVOCATION,
+            CLIR_OUTGOING_SUPPRESSION
+    }, prefix = "CLIR_OUTGOING_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ClirOutgoingState {}
+
+    /**
+     * Calling line identification restriction (CLIR) is set to the default according to the
+     * subscription of the CLIR service.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_OUTGOING_DEFAULT = 0;
+    /**
+     * Activate Calling line identification restriction for outgoing calls.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_OUTGOING_INVOCATION = 1;
+    /**
+     * Deactivate Calling line identification restriction for outgoing calls.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_OUTGOING_SUPPRESSION = 2;
+
+    /**
+     * Calling line identification restriction is currently not provisioned.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_STATUS_NOT_PROVISIONED = 0;
+    /**
+     * Calling line identification restriction is currently provisioned in permanent mode.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_STATUS_PROVISIONED_PERMANENT = 1;
+    /**
+     * Calling line identification restriction is currently unknown, e.g. no network, etc.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_STATUS_UNKNOWN = 2;
+    /**
+     * Calling line identification restriction temporary mode, temporarily restricted.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_STATUS_TEMPORARILY_RESTRICTED = 3;
+    /**
+     * Calling line identification restriction temporary mode, temporarily allowed.
+     *
+     * See TS 27.007, section 7.7 for more information.
+     */
+    public static final int CLIR_STATUS_TEMPORARILY_ALLOWED = 4;
+
+    /**@hide*/
+    @IntDef(value = {
+            CLIR_STATUS_NOT_PROVISIONED,
+            CLIR_STATUS_PROVISIONED_PERMANENT,
+            CLIR_STATUS_UNKNOWN,
+            CLIR_STATUS_TEMPORARILY_RESTRICTED,
+            CLIR_STATUS_TEMPORARILY_ALLOWED
+    }, prefix = "CLIR_STATUS_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ClirInterrogationStatus {}
+
+    // 0: disabled, 1: enabled
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int mStatus;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public String mIcbNum;
+    /** @hide */
+    public int mProvisionStatus = SERVICE_PROVISIONING_UNKNOWN;
+    private int mClirInterrogationStatus = CLIR_STATUS_UNKNOWN;
+    private int mClirOutgoingState = CLIR_OUTGOING_DEFAULT;
+
+    /**@hide*/
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public ImsSsInfo() {
+    }
+
+    /**
+     * Builds {@link ImsSsInfo} instances, which may include optional parameters.
+     */
+    public static final class Builder {
+
+        private final ImsSsInfo mImsSsInfo;
+
+        public Builder(@ServiceStatus int status) {
+            mImsSsInfo = new ImsSsInfo();
+            mImsSsInfo.mStatus = status;
+        }
+
+        /**
+         * Set the ICB number for IMS call barring.
+         * @param number The number in E.164 international format.
+         */
+        public @NonNull Builder setIncomingCommunicationBarringNumber(@NonNull String number) {
+            mImsSsInfo.mIcbNum = number;
+            return this;
+        }
+
+        /**
+         * Set the provisioning status for a Supplementary Service interrogation response.
+         */
+        public @NonNull Builder setProvisionStatus(@ServiceProvisionStatus int provisionStatus) {
+            mImsSsInfo.mProvisionStatus = provisionStatus;
+            return this;
+        }
+
+        /**
+         * Set the Calling Line Identification Restriction (CLIR) status for a supplementary service
+         * interrogation response.
+         */
+        public @NonNull Builder setClirInterrogationStatus(@ClirInterrogationStatus int status) {
+            mImsSsInfo.mClirInterrogationStatus = status;
+            return this;
+        }
+
+        /**
+         * Set the Calling line identification Restriction (CLIR) state for outgoing calls.
+         */
+        public @NonNull Builder setClirOutgoingState(@ClirOutgoingState int state) {
+            mImsSsInfo.mClirOutgoingState = state;
+            return this;
+        }
+
+        /**
+         * @return a built {@link ImsSsInfo} containing optional the parameters that were set.
+         */
+        public @NonNull ImsSsInfo build() {
+            return mImsSsInfo;
+        }
+    }
+
+    /**
+     *
+     * @param status The status of the service registration of activation/deactiviation.
+     * @param icbNum The Incoming barring number.
+     * @deprecated use {@link ImsSsInfo.Builder} instead.
+     */
+    @Deprecated
+    public ImsSsInfo(@ServiceStatus int status, @Nullable String icbNum) {
+        mStatus = status;
+        mIcbNum = icbNum;
+    }
+
+    private ImsSsInfo(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mStatus);
+        out.writeString(mIcbNum);
+        out.writeInt(mProvisionStatus);
+        out.writeInt(mClirInterrogationStatus);
+        out.writeInt(mClirOutgoingState);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return super.toString() + ", Status: " + ((mStatus == 0) ? "disabled" : "enabled")
+                + ", ProvisionStatus: " + provisionStatusToString(mProvisionStatus);
+    }
+
+    private static String provisionStatusToString(int pStatus) {
+        switch (pStatus) {
+            case SERVICE_NOT_PROVISIONED:
+                return "Service not provisioned";
+             case SERVICE_PROVISIONED:
+                return "Service provisioned";
+             default:
+                return "Service provisioning unknown";
+        }
+    }
+
+    private void readFromParcel(Parcel in) {
+        mStatus = in.readInt();
+        mIcbNum = in.readString();
+        mProvisionStatus = in.readInt();
+        mClirInterrogationStatus = in.readInt();
+        mClirOutgoingState = in.readInt();
+    }
+
+    public static final @android.annotation.NonNull Creator<ImsSsInfo> CREATOR =
+            new Creator<ImsSsInfo>() {
+        @Override
+        public ImsSsInfo createFromParcel(Parcel in) {
+            return new ImsSsInfo(in);
+        }
+
+        @Override
+        public ImsSsInfo[] newArray(int size) {
+            return new ImsSsInfo[size];
+        }
+    };
+
+    /**
+     * @return Supplementary Service Configuration status.
+     */
+    public @ServiceStatus int getStatus() {
+        return mStatus;
+    }
+
+    /** @deprecated Use {@link #getIncomingCommunicationBarringNumber()} instead.*/
+    @Deprecated
+    public String getIcbNum() {
+        return mIcbNum;
+    }
+
+    /**
+     * @return The Incoming Communication Barring (ICB) number.
+     */
+    public @Nullable String getIncomingCommunicationBarringNumber() {
+        return mIcbNum;
+    }
+
+    /**
+     * @return Supplementary Service Provision status.
+     */
+    public @ServiceProvisionStatus int getProvisionStatus() {
+        return mProvisionStatus;
+    }
+
+    /**
+     * @return the Calling Line Identification Restriction State for outgoing calls with respect to
+     * this subscription. Will be {@link #CLIR_OUTGOING_DEFAULT} if not applicable to this SS info.
+     */
+    public @ClirOutgoingState int getClirOutgoingState() {
+        return mClirOutgoingState;
+    }
+
+    /**
+     * @return the calling line identification restriction provisioning status upon interrogation of
+     * the service for this subscription. Will be {@link #CLIR_STATUS_UNKNOWN} if not applicable to
+     * this SS info.
+     */
+    public @ClirInterrogationStatus int getClirInterrogationStatus() {
+        return mClirInterrogationStatus;
+    }
+
+    /**
+     * Parts of telephony still use the old {m,n} 3GPP definition, so convert to that format.
+     * @hide
+     */
+    public int[] getCompatArray(@ImsSsData.ServiceType int type) {
+        int[] result = new int[2];
+        // Convert ImsSsInfo into a form that telephony can read (as per 3GPP 27.007)
+        // CLIR (section 7.7)
+        if (type == ImsSsData.SS_CLIR) {
+            // Assume there will only be one ImsSsInfo.
+            // contains {"n","m"} parameters
+            result[0] = getClirOutgoingState();
+            result[1] = getClirInterrogationStatus();
+            return result;
+        }
+        // COLR 7.31
+        if (type == ImsSsData.SS_COLR) {
+            result[0] = getProvisionStatus();
+        }
+        // Facility Lock CLCK 7.4 (for call barring), CLIP 7.6, COLP 7.8, as well as any
+        // other result, just return the status for the "n" parameter and provisioning status for
+        // "m" as the default.
+        result[0] = getStatus();
+        result[1] = getProvisionStatus();
+        return result;
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsStateCallback.java b/android-35/android/telephony/ims/ImsStateCallback.java
new file mode 100644
index 0000000..b9ba93f
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsStateCallback.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 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.ims;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Binder;
+
+import com.android.internal.telephony.IImsStateCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Executor;
+
+/**
+ * A callback class used for monitoring changes in IMS service connection states
+ * for a specific subscription.
+ * <p>
+ * @see ImsMmTelManager#registerImsStateCallback(Executor, ImsStateCallback)
+ * @see ImsRcsManager#registerImsStateCallback(Executor, ImsStateCallback)
+ */
+public abstract class ImsStateCallback {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "REASON_", value = {
+            REASON_UNKNOWN_TEMPORARY_ERROR,
+            REASON_UNKNOWN_PERMANENT_ERROR,
+            REASON_IMS_SERVICE_DISCONNECTED,
+            REASON_NO_IMS_SERVICE_CONFIGURED,
+            REASON_SUBSCRIPTION_INACTIVE,
+            REASON_IMS_SERVICE_NOT_READY
+    })
+    public @interface DisconnectedReason {}
+
+    /**
+     * The underlying IMS service is temporarily unavailable for the
+     * associated subscription.
+     * {@link #onAvailable} will be called when the IMS service becomes
+     * available again.
+     */
+    public static final int REASON_UNKNOWN_TEMPORARY_ERROR     = 1;
+
+    /**
+     * The underlying IMS service is permanently unavailable for the
+     * associated subscription and there will be no Manager available for
+     * this subscription.
+     */
+    public static final int REASON_UNKNOWN_PERMANENT_ERROR     = 2;
+
+    /**
+     * The underlying IMS service has died, is reconfiguring, or has never
+     * come up yet and as a result is currently unavailable.
+     * {@link #onAvailable} will be called when the IMS service becomes
+     * available. All callbacks should be unregistered now and registered again
+     * if the IMS service moves back to available.
+     */
+    public static final int REASON_IMS_SERVICE_DISCONNECTED    = 3;
+
+    /**
+     * There is no IMS service configured for the subscription ID specified.
+     * This is a permanent error and there will be no Manager available for
+     * this subscription.
+     */
+    public static final int REASON_NO_IMS_SERVICE_CONFIGURED   = 4;
+
+    /**
+     * The subscription associated with this Manager has moved to an inactive
+     * state (e.g. SIM removed) and the IMS service has torn down the resources
+     * related to this subscription. This has caused this callback
+     * to be deregistered. The callback must be re-registered when this subscription
+     * becomes active in order to continue listening to the IMS service state.
+     */
+    public static final int REASON_SUBSCRIPTION_INACTIVE       = 5;
+
+    /**
+     * The IMS service is connected, but in a NOT_READY state. Once the
+     * service moves to ready, {@link #onAvailable} will be called.
+     */
+    public static final int REASON_IMS_SERVICE_NOT_READY       = 6;
+
+    private IImsStateCallbackStub mCallback;
+
+    /**
+     * @hide
+     */
+    public void init(@NonNull @CallbackExecutor Executor executor) {
+        if (executor == null) {
+            throw new IllegalArgumentException("ImsStateCallback Executor must be non-null");
+        }
+        mCallback = new IImsStateCallbackStub(this, executor);
+    }
+
+    /**
+     * Using a static class and weak reference here to avoid memory leak caused by the
+     * IImsStateCallback.Stub callback retaining references to the outside ImsStateCallback.
+     */
+    private static class IImsStateCallbackStub extends IImsStateCallback.Stub {
+        private WeakReference<ImsStateCallback> mImsStateCallbackWeakRef;
+        private Executor mExecutor;
+
+        IImsStateCallbackStub(ImsStateCallback imsStateCallback, Executor executor) {
+            mImsStateCallbackWeakRef = new WeakReference<ImsStateCallback>(imsStateCallback);
+            mExecutor = executor;
+        }
+
+        Executor getExecutor() {
+            return mExecutor;
+        }
+
+        public void onAvailable() {
+            ImsStateCallback callback = mImsStateCallbackWeakRef.get();
+            if (callback == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> callback.onAvailable()));
+        }
+
+        public void onUnavailable(int reason) {
+            ImsStateCallback callback = mImsStateCallbackWeakRef.get();
+            if (callback == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> callback.onUnavailable(reason)));
+        }
+    }
+
+    /**
+     * The IMS service has disconnected or is reporting NOT_READY and is no longer
+     * available to users. The user should clean up all related state and
+     * unregister callbacks. If it is a temporary error, {@link #onAvailable} will
+     * be called when the IMS service becomes available again.
+     *
+     * @param reason the specified reason
+     */
+    public abstract void onUnavailable(@DisconnectedReason int reason);
+
+    /**
+     * The IMS service is connected and is ready for communication over the
+     * provided Manager.
+     */
+    public abstract void onAvailable();
+
+    /**
+     * An unexpected error has occurred and the Telephony process has crashed. This
+     * has caused this callback to be deregistered. The callback must be
+     * re-registered in order to continue listening to the IMS service state.
+     */
+    public abstract void onError();
+
+    /**
+     * The callback to notify the death of telephony process
+     * @hide
+     */
+    public final void binderDied() {
+        if (mCallback != null) {
+            mCallback.getExecutor().execute(() -> onError());
+        }
+    }
+
+    /**
+     * Return the callback binder
+     * @hide
+     */
+    public IImsStateCallbackStub getCallbackBinder() {
+        return mCallback;
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsStreamMediaProfile.java b/android-35/android/telephony/ims/ImsStreamMediaProfile.java
new file mode 100644
index 0000000..d924bae
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsStreamMediaProfile.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Parcelable object to handle IMS stream media profile.
+ * It provides the media direction, quality of audio and/or video.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsStreamMediaProfile implements Parcelable {
+    private static final String TAG = "ImsStreamMediaProfile";
+
+    /**
+     * Media directions
+     */
+    public static final int DIRECTION_INVALID = (-1);
+    public static final int DIRECTION_INACTIVE = 0;
+    public static final int DIRECTION_RECEIVE = 1;
+    public static final int DIRECTION_SEND = 2;
+    public static final int DIRECTION_SEND_RECEIVE = 3;
+
+    /**
+     * Audio information
+     */
+    public static final int AUDIO_QUALITY_NONE = 0;
+    public static final int AUDIO_QUALITY_AMR = 1;
+    public static final int AUDIO_QUALITY_AMR_WB = 2;
+    public static final int AUDIO_QUALITY_QCELP13K = 3;
+    public static final int AUDIO_QUALITY_EVRC = 4;
+    public static final int AUDIO_QUALITY_EVRC_B = 5;
+    public static final int AUDIO_QUALITY_EVRC_WB = 6;
+    public static final int AUDIO_QUALITY_EVRC_NW = 7;
+    public static final int AUDIO_QUALITY_GSM_EFR = 8;
+    public static final int AUDIO_QUALITY_GSM_FR = 9;
+    public static final int AUDIO_QUALITY_GSM_HR = 10;
+    public static final int AUDIO_QUALITY_G711U = 11;
+    public static final int AUDIO_QUALITY_G723 = 12;
+    public static final int AUDIO_QUALITY_G711A = 13;
+    public static final int AUDIO_QUALITY_G722 = 14;
+    public static final int AUDIO_QUALITY_G711AB = 15;
+    public static final int AUDIO_QUALITY_G729 = 16;
+    public static final int AUDIO_QUALITY_EVS_NB = 17;
+    public static final int AUDIO_QUALITY_EVS_WB = 18;
+    public static final int AUDIO_QUALITY_EVS_SWB = 19;
+    public static final int AUDIO_QUALITY_EVS_FB = 20;
+
+   /**
+     * Video information
+     */
+    public static final int VIDEO_QUALITY_NONE = 0;
+    public static final int VIDEO_QUALITY_QCIF = (1 << 0);
+    public static final int VIDEO_QUALITY_QVGA_LANDSCAPE = (1 << 1);
+    public static final int VIDEO_QUALITY_QVGA_PORTRAIT = (1 << 2);
+    public static final int VIDEO_QUALITY_VGA_LANDSCAPE = (1 << 3);
+    public static final int VIDEO_QUALITY_VGA_PORTRAIT = (1 << 4);
+
+    /**
+     * RTT Modes
+     */
+    public static final int RTT_MODE_DISABLED = 0;
+    public static final int RTT_MODE_FULL = 1;
+
+    // Audio related information
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int mAudioQuality;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int mAudioDirection;
+    // Audio codec attributes
+    private AudioCodecAttributes mAudioCodecAttributes;
+
+    // Video related information
+    /** @hide */
+    public int mVideoQuality;
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int mVideoDirection;
+    // Rtt related information
+    /** @hide */
+    public int mRttMode;
+    // RTT Audio Speech Indicator
+    /** @hide */
+    public boolean mIsReceivingRttAudio = false;
+
+    /** @hide */
+    public ImsStreamMediaProfile(Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param audioQuality The audio quality. Can be one of the following:
+     *                     {@link #AUDIO_QUALITY_AMR},
+     *                     {@link #AUDIO_QUALITY_AMR_WB},
+     *                     {@link #AUDIO_QUALITY_QCELP13K},
+     *                     {@link #AUDIO_QUALITY_EVRC},
+     *                     {@link #AUDIO_QUALITY_EVRC_B},
+     *                     {@link #AUDIO_QUALITY_EVRC_WB},
+     *                     {@link #AUDIO_QUALITY_EVRC_NW},
+     *                     {@link #AUDIO_QUALITY_GSM_EFR},
+     *                     {@link #AUDIO_QUALITY_GSM_FR},
+     *                     {@link #AUDIO_QUALITY_GSM_HR},
+     *                     {@link #AUDIO_QUALITY_G711U},
+     *                     {@link #AUDIO_QUALITY_G723},
+     *                     {@link #AUDIO_QUALITY_G711A},
+     *                     {@link #AUDIO_QUALITY_G722},
+     *                     {@link #AUDIO_QUALITY_G711AB},
+     *                     {@link #AUDIO_QUALITY_G729},
+     *                     {@link #AUDIO_QUALITY_EVS_NB},
+     *                     {@link #AUDIO_QUALITY_EVS_WB},
+     *                     {@link #AUDIO_QUALITY_EVS_SWB},
+     *                     {@link #AUDIO_QUALITY_EVS_FB},
+     * @param audioDirection The audio direction. Can be one of the following:
+     *                       {@link #DIRECTION_INVALID},
+     *                       {@link #DIRECTION_INACTIVE},
+     *                       {@link #DIRECTION_RECEIVE},
+     *                       {@link #DIRECTION_SEND},
+     *                       {@link #DIRECTION_SEND_RECEIVE},
+     * @param videoQuality The video quality. Can be one of the following:
+     *                     {@link #VIDEO_QUALITY_NONE},
+     *                     {@link #VIDEO_QUALITY_QCIF},
+     *                     {@link #VIDEO_QUALITY_QVGA_LANDSCAPE},
+     *                     {@link #VIDEO_QUALITY_QVGA_PORTRAIT},
+     *                     {@link #VIDEO_QUALITY_VGA_LANDSCAPE},
+     *                     {@link #VIDEO_QUALITY_VGA_PORTRAIT},
+     * @param videoDirection The video direction. Can be one of the following:
+     *                       {@link #DIRECTION_INVALID},
+     *                       {@link #DIRECTION_INACTIVE},
+     *                       {@link #DIRECTION_RECEIVE},
+     *                       {@link #DIRECTION_SEND},
+     *                       {@link #DIRECTION_SEND_RECEIVE},
+     * @param rttMode The rtt mode. Can be one of the following:
+     *                {@link #RTT_MODE_DISABLED},
+     *                {@link #RTT_MODE_FULL}
+     */
+    public ImsStreamMediaProfile(int audioQuality, int audioDirection,
+            int videoQuality, int videoDirection, int rttMode) {
+        mAudioQuality = audioQuality;
+        mAudioDirection = audioDirection;
+        mVideoQuality = videoQuality;
+        mVideoDirection = videoDirection;
+        mRttMode = rttMode;
+    }
+
+    /** @hide */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public ImsStreamMediaProfile() {
+        mAudioQuality = AUDIO_QUALITY_NONE;
+        mAudioDirection = DIRECTION_SEND_RECEIVE;
+        mVideoQuality = VIDEO_QUALITY_NONE;
+        mVideoDirection = DIRECTION_INVALID;
+        mRttMode = RTT_MODE_DISABLED;
+    }
+
+    /** @hide */
+    public ImsStreamMediaProfile(int audioQuality, int audioDirection,
+            int videoQuality, int videoDirection) {
+        mAudioQuality = audioQuality;
+        mAudioDirection = audioDirection;
+        mVideoQuality = videoQuality;
+        mVideoDirection = videoDirection;
+    }
+
+    /** @hide */
+    public ImsStreamMediaProfile(int rttMode) {
+        mRttMode = rttMode;
+    }
+
+    public void copyFrom(ImsStreamMediaProfile profile) {
+        mAudioQuality = profile.mAudioQuality;
+        mAudioDirection = profile.mAudioDirection;
+        mAudioCodecAttributes = profile.mAudioCodecAttributes;
+        mVideoQuality = profile.mVideoQuality;
+        mVideoDirection = profile.mVideoDirection;
+        mRttMode = profile.mRttMode;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "{ audioQuality=" + mAudioQuality
+                + ", audioDirection=" + mAudioDirection
+                + ", audioCodecAttribute=" + mAudioCodecAttributes
+                + ", videoQuality=" + mVideoQuality
+                + ", videoDirection=" + mVideoDirection
+                + ", rttMode=" + mRttMode
+                + ", hasRttAudioSpeech=" + mIsReceivingRttAudio + " }";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mAudioQuality);
+        out.writeInt(mAudioDirection);
+        out.writeTypedObject(mAudioCodecAttributes, flags);
+        out.writeInt(mVideoQuality);
+        out.writeInt(mVideoDirection);
+        out.writeInt(mRttMode);
+        out.writeBoolean(mIsReceivingRttAudio);
+    }
+
+    private void readFromParcel(Parcel in) {
+        mAudioQuality = in.readInt();
+        mAudioDirection = in.readInt();
+        mAudioCodecAttributes = in.readTypedObject(AudioCodecAttributes.CREATOR);
+        mVideoQuality = in.readInt();
+        mVideoDirection = in.readInt();
+        mRttMode = in.readInt();
+        mIsReceivingRttAudio = in.readBoolean();
+    }
+
+    public static final @android.annotation.NonNull Creator<ImsStreamMediaProfile> CREATOR =
+            new Creator<ImsStreamMediaProfile>() {
+        @Override
+        public ImsStreamMediaProfile createFromParcel(Parcel in) {
+            return new ImsStreamMediaProfile(in);
+        }
+
+        @Override
+        public ImsStreamMediaProfile[] newArray(int size) {
+            return new ImsStreamMediaProfile[size];
+        }
+    };
+
+    /**
+     * Determines if it's RTT call
+     * @return true if RTT call, false otherwise.
+     */
+    public boolean isRttCall() {
+        return (mRttMode == RTT_MODE_FULL);
+    }
+
+    /**
+     * Updates the RttCall attribute
+     */
+    public void setRttMode(int rttMode) {
+        mRttMode = rttMode;
+    }
+
+    /**
+     * Sets whether the remote party is transmitting audio over the RTT call.
+     * @param audioOn true if audio is being received, false otherwise.
+     */
+    public void setReceivingRttAudio(boolean audioOn) {
+        mIsReceivingRttAudio = audioOn;
+    }
+
+    public int getAudioQuality() {
+        return mAudioQuality;
+    }
+
+    public int getAudioDirection() {
+        return mAudioDirection;
+    }
+
+    /**
+     * Get the audio codec attributes {@link AudioCodecAttributes} which may be {@code null} if
+     * ImsService doesn't support this information.
+     * @return audio codec attributes
+     */
+    public @Nullable AudioCodecAttributes getAudioCodecAttributes() {
+        return mAudioCodecAttributes;
+    }
+
+    /**
+     * Set the audio codec attributes {@link AudioCodecAttributes} which includes bitrate and
+     * bandwidth information.
+     */
+    public void setAudioCodecAttributes(@NonNull AudioCodecAttributes audioCodecAttributes) {
+        mAudioCodecAttributes = audioCodecAttributes;
+    }
+
+    public int getVideoQuality() {
+        return mVideoQuality;
+    }
+
+    public int getVideoDirection() {
+        return mVideoDirection;
+    }
+
+    public int getRttMode() {
+        return mRttMode;
+    }
+
+    /**
+     * @return true if remote party is transmitting audio, false otherwise.
+     */
+    public boolean isReceivingRttAudio() {
+        return mIsReceivingRttAudio;
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsSuppServiceNotification.java b/android-35/android/telephony/ims/ImsSuppServiceNotification.java
new file mode 100644
index 0000000..1630368
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsSuppServiceNotification.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+
+/**
+ * Parcelable object to handle IMS supplementary service notifications.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsSuppServiceNotification implements Parcelable {
+    private static final String TAG = "ImsSuppServiceNotification";
+
+    /** Type of notification: 0 = MO; 1 = MT */
+    public final int notificationType;
+    /** TS 27.007 7.17 "code1" or "code2" */
+    public final int code;
+    /** TS 27.007 7.17 "index" - Not used currently*/
+    public final int index;
+    /** TS 27.007 7.17 "type" (MT only) - Not used currently */
+    public final int type;
+    /** TS 27.007 7.17 "number" (MT only) */
+    public final String number;
+    /** List of forwarded numbers, if any */
+    public final String[] history;
+
+
+    public ImsSuppServiceNotification(int notificationType, int code, int index, int type,
+            String number, String[] history) {
+        this.notificationType = notificationType;
+        this.code = code;
+        this.index = index;
+        this.type = type;
+        this.number = number;
+        this.history = history;
+    }
+
+    /** @hide */
+    public ImsSuppServiceNotification(Parcel in) {
+        notificationType = in.readInt();
+        code = in.readInt();
+        index = in.readInt();
+        type = in.readInt();
+        number = in.readString();
+        history = in.createStringArray();
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "{ notificationType=" + notificationType +
+                ", code=" + code +
+                ", index=" + index +
+                ", type=" + type +
+                ", number=" + number +
+                ", history=" + Arrays.toString(history) +
+                " }";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(notificationType);
+        out.writeInt(code);
+        out.writeInt(index);
+        out.writeInt(type);
+        out.writeString(number);
+        out.writeStringArray(history);
+    }
+
+    public static final @android.annotation.NonNull Creator<ImsSuppServiceNotification> CREATOR =
+            new Creator<ImsSuppServiceNotification>() {
+        @Override
+        public ImsSuppServiceNotification createFromParcel(Parcel in) {
+            return new ImsSuppServiceNotification(in);
+        }
+
+        @Override
+        public ImsSuppServiceNotification[] newArray(int size) {
+            return new ImsSuppServiceNotification[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/ims/ImsUtListener.java b/android-35/android/telephony/ims/ImsUtListener.java
new file mode 100644
index 0000000..754814f
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsUtListener.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.telephony.ims.stub.ImsUtImplBase;
+import android.util.Log;
+
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Listener interface used to receive network responses back from UT supplementary service queries
+ * made by the framework.
+ * @hide
+ */
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsUt maintained by other ImsServices.
+@SystemApi
+public class ImsUtListener {
+
+    /**
+     * The {@link Bundle} key for a Calling Line Identification Restriction (CLIR) response. The
+     * value will be an int[] with two values:
+     * int[0] contains the 'n' parameter from TS 27.007 7.7, which is the
+     * outgoing CLIR state. See {@link ImsSsInfo#CLIR_OUTGOING_DEFAULT},
+     * {@link ImsSsInfo#CLIR_OUTGOING_INVOCATION}, and {@link ImsSsInfo#CLIR_OUTGOING_SUPPRESSION};
+     * int[1] contains the 'm' parameter from TS 27.007 7.7, which is the CLIR interrogation status.
+     * See {@link ImsSsInfo#CLIR_STATUS_NOT_PROVISIONED},
+     * {@link ImsSsInfo#CLIR_STATUS_PROVISIONED_PERMANENT}, {@link ImsSsInfo#CLIR_STATUS_UNKNOWN},
+     * {@link ImsSsInfo#CLIR_STATUS_TEMPORARILY_RESTRICTED}, and
+     * {@link ImsSsInfo#CLIR_STATUS_TEMPORARILY_ALLOWED}.
+     * @deprecated Use {@link #onLineIdentificationSupplementaryServiceResponse(int, ImsSsInfo)}
+     * instead, this key has been added for backwards compatibility with older proprietary
+     * implementations only and is being phased out.
+     */
+    @Deprecated
+    public static final String BUNDLE_KEY_CLIR = "queryClir";
+
+    /**
+     * The {@link Bundle} key for a Calling Line Identification Presentation (CLIP), Connected Line
+     * Identification Presentation (COLP), or Connected Line Identification Restriction (COLR)
+     * response. The value will be an instance of {@link ImsSsInfo}, which contains the response to
+     * the query.
+     * @deprecated Use {@link #onLineIdentificationSupplementaryServiceResponse(int, ImsSsInfo)}
+     * instead, this key has been added for backwards compatibility with older proprietary
+     * implementations only and is being phased out.
+     */
+    @Deprecated
+    public static final String BUNDLE_KEY_SSINFO = "imsSsInfo";
+
+    private IImsUtListener mServiceInterface;
+    private static final String LOG_TAG = "ImsUtListener";
+
+    public void onUtConfigurationUpdated(int id) {
+        try {
+            mServiceInterface.utConfigurationUpdated(null, id);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationUpdated: remote exception");
+        }
+    }
+
+    public void onUtConfigurationUpdateFailed(int id, ImsReasonInfo error) {
+        try {
+            mServiceInterface.utConfigurationUpdateFailed(null, id, error);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationUpdateFailed: remote exception");
+        }
+    }
+
+    /**
+     * Notify the framework of a UT configuration response to a {@link ImsUtImplBase#queryClir()},
+     * {@link ImsUtImplBase#queryClip()}, {@link ImsUtImplBase#queryColp()}, or
+     * {@link ImsUtImplBase#queryColr()} query for the transaction ID specified. If the query fails,
+     * {@link #onUtConfigurationQueryFailed(int, ImsReasonInfo)} should be called.
+     * @param id The ID associated with this UT configuration transaction from the framework.
+     * @param configuration A {@link Bundle} containing the result of querying the UT configuration.
+     *                      Must contain {@link #BUNDLE_KEY_CLIR} if it is a response to
+     *                      {@link ImsUtImplBase#queryClir()} or
+     *                      {@link #BUNDLE_KEY_SSINFO} if it is a response to
+     *                      {@link ImsUtImplBase#queryClip()}, {@link ImsUtImplBase#queryColp()}, or
+     *                      {@link ImsUtImplBase#queryColr()}.
+     * @deprecated Use {@link #onLineIdentificationSupplementaryServiceResponse(int, ImsSsInfo)}
+     * instead.
+     */
+    @Deprecated
+    public void onUtConfigurationQueried(int id, Bundle configuration) {
+        try {
+            mServiceInterface.utConfigurationQueried(null, id, configuration);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationQueried: remote exception");
+        }
+    }
+
+    /**
+     * Notify the framework of a UT configuration response to a {@link ImsUtImplBase#queryClir()},
+     * {@link ImsUtImplBase#queryClip()}, {@link ImsUtImplBase#queryColp()}, or
+     * {@link ImsUtImplBase#queryColr()} query for the transaction ID specified. If the query fails,
+     * the framework should be notified via
+     * {@link #onUtConfigurationQueryFailed(int, ImsReasonInfo)}.
+     * @param id The ID associated with this UT configuration transaction from the framework.
+     * @param configuration An {@link ImsSsInfo} instance containing the configuration for the
+     *                      line identification supplementary service queried.
+     */
+    public void onLineIdentificationSupplementaryServiceResponse(int id,
+            @NonNull ImsSsInfo configuration) {
+        try {
+            mServiceInterface.lineIdentificationSupplementaryServiceResponse(id, configuration);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notify the Framework of the line identification query failure.
+     * @param id The ID associated with the UT query transaction.
+     * @param error The query failure reason.
+     */
+    public void onUtConfigurationQueryFailed(int id, ImsReasonInfo error) {
+        try {
+            mServiceInterface.utConfigurationQueryFailed(null, id, error);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationQueryFailed: remote exception");
+        }
+    }
+
+    public void onUtConfigurationCallBarringQueried(int id, ImsSsInfo[] cbInfo) {
+        try {
+            mServiceInterface.utConfigurationCallBarringQueried(null, id, cbInfo);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationCallBarringQueried: remote exception");
+        }
+    }
+
+    public void onUtConfigurationCallForwardQueried(int id, ImsCallForwardInfo[] cfInfo) {
+        try {
+            mServiceInterface.utConfigurationCallForwardQueried(null, id, cfInfo);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationCallForwardQueried: remote exception");
+        }
+    }
+
+    public void onUtConfigurationCallWaitingQueried(int id, ImsSsInfo[] cwInfo) {
+        try {
+            mServiceInterface.utConfigurationCallWaitingQueried(null, id, cwInfo);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationCallWaitingQueried: remote exception");
+        }
+    }
+
+    public void onSupplementaryServiceIndication(ImsSsData ssData) {
+        try {
+            mServiceInterface.onSupplementaryServiceIndication(ssData);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "onSupplementaryServiceIndication: remote exception");
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public ImsUtListener(IImsUtListener serviceInterface) {
+        mServiceInterface = serviceInterface;
+    }
+
+    /**
+     * @hide
+     */
+    public IImsUtListener getListenerInterface() {
+        return mServiceInterface;
+    }
+}
diff --git a/android-35/android/telephony/ims/ImsVideoCallProvider.java b/android-35/android/telephony/ims/ImsVideoCallProvider.java
new file mode 100644
index 0000000..64bdcbb
--- /dev/null
+++ b/android-35/android/telephony/ims/ImsVideoCallProvider.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.VideoProfile;
+import android.telecom.VideoProfile.CameraCapabilities;
+import android.view.Surface;
+
+import com.android.ims.internal.IImsVideoCallCallback;
+import com.android.ims.internal.IImsVideoCallProvider;
+import com.android.internal.os.SomeArgs;
+
+/**
+ * @hide
+ */
+@SystemApi
+public abstract class ImsVideoCallProvider {
+    private static final int MSG_SET_CALLBACK = 1;
+    private static final int MSG_SET_CAMERA = 2;
+    private static final int MSG_SET_PREVIEW_SURFACE = 3;
+    private static final int MSG_SET_DISPLAY_SURFACE = 4;
+    private static final int MSG_SET_DEVICE_ORIENTATION = 5;
+    private static final int MSG_SET_ZOOM = 6;
+    private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
+    private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
+    private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
+    private static final int MSG_REQUEST_CALL_DATA_USAGE = 10;
+    private static final int MSG_SET_PAUSE_IMAGE = 11;
+
+    private final ImsVideoCallProviderBinder mBinder;
+
+    private IImsVideoCallCallback mCallback;
+
+    /**
+     * Default handler used to consolidate binder method calls onto a single thread.
+     */
+    private final Handler mProviderHandler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SET_CALLBACK:
+                    mCallback = (IImsVideoCallCallback) msg.obj;
+                    break;
+                case MSG_SET_CAMERA:
+                {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        onSetCamera((String) args.arg1);
+                        onSetCamera((String) args.arg1, args.argi1);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_SET_PREVIEW_SURFACE:
+                    onSetPreviewSurface((Surface) msg.obj);
+                    break;
+                case MSG_SET_DISPLAY_SURFACE:
+                    onSetDisplaySurface((Surface) msg.obj);
+                    break;
+                case MSG_SET_DEVICE_ORIENTATION:
+                    onSetDeviceOrientation(msg.arg1);
+                    break;
+                case MSG_SET_ZOOM:
+                    onSetZoom((Float) msg.obj);
+                    break;
+                case MSG_SEND_SESSION_MODIFY_REQUEST: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        VideoProfile fromProfile = (VideoProfile) args.arg1;
+                        VideoProfile toProfile = (VideoProfile) args.arg2;
+
+                        onSendSessionModifyRequest(fromProfile, toProfile);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_SEND_SESSION_MODIFY_RESPONSE:
+                    onSendSessionModifyResponse((VideoProfile) msg.obj);
+                    break;
+                case MSG_REQUEST_CAMERA_CAPABILITIES:
+                    onRequestCameraCapabilities();
+                    break;
+                case MSG_REQUEST_CALL_DATA_USAGE:
+                    onRequestCallDataUsage();
+                    break;
+                case MSG_SET_PAUSE_IMAGE:
+                    onSetPauseImage((Uri) msg.obj);
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    /**
+     * IImsVideoCallProvider stub implementation.
+     */
+    private final class ImsVideoCallProviderBinder extends IImsVideoCallProvider.Stub {
+        public void setCallback(IImsVideoCallCallback callback) {
+            mProviderHandler.obtainMessage(MSG_SET_CALLBACK, callback).sendToTarget();
+        }
+
+        public void setCamera(String cameraId, int uid) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = cameraId;
+            args.argi1 = uid;
+            mProviderHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget();
+        }
+
+        public void setPreviewSurface(Surface surface) {
+            mProviderHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
+        }
+
+        public void setDisplaySurface(Surface surface) {
+            mProviderHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
+        }
+
+        public void setDeviceOrientation(int rotation) {
+            mProviderHandler.obtainMessage(MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget();
+        }
+
+        public void setZoom(float value) {
+            mProviderHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
+        }
+
+        public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = fromProfile;
+            args.arg2 = toProfile;
+            mProviderHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST, args).sendToTarget();
+        }
+
+        public void sendSessionModifyResponse(VideoProfile responseProfile) {
+            mProviderHandler.obtainMessage(
+                    MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
+        }
+
+        public void requestCameraCapabilities() {
+            mProviderHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
+        }
+
+        public void requestCallDataUsage() {
+            mProviderHandler.obtainMessage(MSG_REQUEST_CALL_DATA_USAGE).sendToTarget();
+        }
+
+        public void setPauseImage(Uri uri) {
+            mProviderHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
+        }
+    }
+
+    public ImsVideoCallProvider() {
+        mBinder = new ImsVideoCallProviderBinder();
+    }
+
+    /**
+     * Returns binder object which can be used across IPC methods.
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public final IImsVideoCallProvider getInterface() {
+        return mBinder;
+    }
+
+    /** @see Connection.VideoProvider#onSetCamera */
+    public abstract void onSetCamera(String cameraId);
+
+    /**
+     * Similar to {@link #onSetCamera(String)}, except includes the UID of the calling process which
+     * the IMS service uses when opening the camera.  This ensures camera permissions are verified
+     * by the camera service.
+     *
+     * @param cameraId The id of the camera to be opened.
+     * @param uid The uid of the caller, used when opening the camera for permission verification.
+     * @see Connection.VideoProvider#onSetCamera
+     */
+    public void onSetCamera(String cameraId, int uid) {
+    }
+
+    /** @see Connection.VideoProvider#onSetPreviewSurface */
+    public abstract void onSetPreviewSurface(Surface surface);
+
+    /** @see Connection.VideoProvider#onSetDisplaySurface */
+    public abstract void onSetDisplaySurface(Surface surface);
+
+    /** @see Connection.VideoProvider#onSetDeviceOrientation */
+    public abstract void onSetDeviceOrientation(int rotation);
+
+    /** @see Connection.VideoProvider#onSetZoom */
+    public abstract void onSetZoom(float value);
+
+    /** @see Connection.VideoProvider#onSendSessionModifyRequest */
+    public abstract void onSendSessionModifyRequest(VideoProfile fromProfile,
+            VideoProfile toProfile);
+
+    /** @see Connection.VideoProvider#onSendSessionModifyResponse */
+    public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
+
+    /** @see Connection.VideoProvider#onRequestCameraCapabilities */
+    public abstract void onRequestCameraCapabilities();
+
+    /** @see Connection.VideoProvider#onRequestCallDataUsage */
+    public abstract void onRequestCallDataUsage();
+
+    /** @see Connection.VideoProvider#onSetPauseImage */
+    public abstract void onSetPauseImage(Uri uri);
+
+    /** @see Connection.VideoProvider#receiveSessionModifyRequest */
+    public void receiveSessionModifyRequest(VideoProfile VideoProfile) {
+        if (mCallback != null) {
+            try {
+                mCallback.receiveSessionModifyRequest(VideoProfile);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#receiveSessionModifyResponse */
+    public void receiveSessionModifyResponse(
+            int status, VideoProfile requestedProfile, VideoProfile responseProfile) {
+        if (mCallback != null) {
+            try {
+                mCallback.receiveSessionModifyResponse(status, requestedProfile, responseProfile);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#handleCallSessionEvent */
+    public void handleCallSessionEvent(int event) {
+        if (mCallback != null) {
+            try {
+                mCallback.handleCallSessionEvent(event);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#changePeerDimensions */
+    public void changePeerDimensions(int width, int height) {
+        if (mCallback != null) {
+            try {
+                mCallback.changePeerDimensions(width, height);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#changeCallDataUsage */
+    public void changeCallDataUsage(long dataUsage) {
+        if (mCallback != null) {
+            try {
+                mCallback.changeCallDataUsage(dataUsage);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#changeCameraCapabilities */
+    public void changeCameraCapabilities(CameraCapabilities CameraCapabilities) {
+        if (mCallback != null) {
+            try {
+                mCallback.changeCameraCapabilities(CameraCapabilities);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#changeVideoQuality */
+    public void changeVideoQuality(int videoQuality) {
+        if (mCallback != null) {
+            try {
+                mCallback.changeVideoQuality(videoQuality);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/MediaQualityStatus.java b/android-35/android/telephony/ims/MediaQualityStatus.java
new file mode 100644
index 0000000..e2df0d4
--- /dev/null
+++ b/android-35/android/telephony/ims/MediaQualityStatus.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2022 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.ims;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.TransportType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A representation of Media quality status.
+ *
+ * @hide
+ */
+@SystemApi
+public final class MediaQualityStatus implements Parcelable {
+    public static final int MEDIA_SESSION_TYPE_AUDIO =  1;
+    public static final int MEDIA_SESSION_TYPE_VIDEO =  2;
+
+    private final String mImsCallSessionId;
+    private final int mMediaSessionType;
+    private final int mTransportType;
+    private final int mRtpPacketLossRate;
+    private final int mRtpJitterMillis;
+    private final long mRtpInactivityTimeMillis;
+
+    /** @hide */
+    @IntDef(
+            value = {
+                    MEDIA_SESSION_TYPE_AUDIO,
+                    MEDIA_SESSION_TYPE_VIDEO,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaSessionType {}
+
+    /**
+     * The constructor for MediaQualityStatus, which represents the media quality for each session
+     * type ({@link #MEDIA_SESSION_TYPE_AUDIO} or {@link #MEDIA_SESSION_TYPE_VIDEO}) of the IMS call
+     *
+     * @param imsCallSessionId IMS call session id of this quality status
+     * @param mediaSessionType media session type of this quality status
+     * @param transportType transport type of this quality status
+     * @param rtpPacketLossRate measured RTP packet loss rate in percentage
+     * @param rtpJitterMillis measured RTP jitter(RFC3550) in milliseconds
+     * @param rptInactivityTimeMillis measured RTP inactivity time in milliseconds
+     */
+    public MediaQualityStatus(@NonNull String imsCallSessionId,
+            @MediaSessionType int mediaSessionType, @TransportType int transportType,
+            @IntRange(from = 0, to = 100) int rtpPacketLossRate,
+            @IntRange(from = 0) int rtpJitterMillis,
+            @IntRange(from = 0) long rptInactivityTimeMillis) {
+        mImsCallSessionId = imsCallSessionId;
+        mMediaSessionType = mediaSessionType;
+        mTransportType = transportType;
+        mRtpPacketLossRate = rtpPacketLossRate;
+        mRtpJitterMillis = rtpJitterMillis;
+        mRtpInactivityTimeMillis = rptInactivityTimeMillis;
+    }
+
+    /**
+     * Retrieves call session ID for this quality status
+     */
+    @NonNull
+    public String getCallSessionId() {
+        return mImsCallSessionId;
+    }
+
+    /**
+     * Retrieves media session type of this quality status
+     */
+    public @MediaSessionType int getMediaSessionType() {
+        return mMediaSessionType;
+    }
+
+
+    /**
+     * Retrieves Transport type for which this media quality was measured.
+     */
+    public @TransportType int getTransportType() {
+        return mTransportType;
+    }
+
+    /**
+     * Retrieves measured RTP packet loss rate in percentage.
+     */
+    @IntRange(from = 0, to = 100)
+    public int getRtpPacketLossRate() {
+        return mRtpPacketLossRate;
+    }
+
+    /**
+     * Retrieves measured RTP jitter(RFC3550) value in milliseconds
+     */
+    @IntRange(from = 0)
+    public int getRtpJitterMillis() {
+        return mRtpJitterMillis;
+    }
+
+    /**
+     * Retrieves measured RTP inactivity time in milliseconds
+     */
+    @IntRange(from = 0)
+    public long getRtpInactivityMillis() {
+        return mRtpInactivityTimeMillis;
+    }
+
+    /**
+     * Creates a new instance of {@link MediaQualityStatus} from a parcel.
+     * @param in The parceled data to read.
+     */
+    private MediaQualityStatus(@NonNull Parcel in) {
+        mImsCallSessionId = in.readString();
+        mMediaSessionType = in.readInt();
+        mTransportType = in.readInt();
+        mRtpPacketLossRate = in.readInt();
+        mRtpJitterMillis = in.readInt();
+        mRtpInactivityTimeMillis = in.readLong();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mImsCallSessionId);
+        dest.writeInt(mMediaSessionType);
+        dest.writeInt(mTransportType);
+        dest.writeInt(mRtpPacketLossRate);
+        dest.writeInt(mRtpJitterMillis);
+        dest.writeLong(mRtpInactivityTimeMillis);
+    }
+
+    public static final @NonNull Creator<MediaQualityStatus> CREATOR =
+            new Creator<MediaQualityStatus>() {
+                @Override
+                public MediaQualityStatus createFromParcel(@NonNull Parcel in) {
+                    return new MediaQualityStatus(in);
+                }
+
+                @Override
+                public MediaQualityStatus[] newArray(int size) {
+                    return new MediaQualityStatus[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        MediaQualityStatus that = (MediaQualityStatus) o;
+        return mImsCallSessionId != null && mImsCallSessionId.equals(that.mImsCallSessionId)
+                && mMediaSessionType == that.mMediaSessionType
+                && mTransportType == that.mTransportType
+                && mRtpPacketLossRate == that.mRtpPacketLossRate
+                && mRtpJitterMillis == that.mRtpJitterMillis
+                && mRtpInactivityTimeMillis == that.mRtpInactivityTimeMillis;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mImsCallSessionId, mMediaSessionType, mTransportType,
+                mRtpPacketLossRate, mRtpJitterMillis, mRtpInactivityTimeMillis);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("MediaThreshold{mImsCallSessionId=");
+        sb.append(mImsCallSessionId);
+        sb.append(", mMediaSessionType=");
+        sb.append(mMediaSessionType);
+        sb.append(", mTransportType=");
+        sb.append(mTransportType);
+        sb.append(", mRtpPacketLossRate=");
+        sb.append(mRtpPacketLossRate);
+        sb.append(", mRtpJitterMillis=");
+        sb.append(mRtpJitterMillis);
+        sb.append(", mRtpInactivityTimeMillis=");
+        sb.append(mRtpInactivityTimeMillis);
+        sb.append("}");
+        return sb.toString();
+    }
+}
diff --git a/android-35/android/telephony/ims/MediaThreshold.java b/android-35/android/telephony/ims/MediaThreshold.java
new file mode 100644
index 0000000..343aa4a
--- /dev/null
+++ b/android-35/android/telephony/ims/MediaThreshold.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2022 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.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.feature.MmTelFeature;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.TreeSet;
+
+/**
+ * A MediaThreshold represents a series of packet loss rate, jitter and rtp inactivity time
+ * thresholds which when crossed should result in a {@link MediaQualityStatus} report being
+ * generated by the {@link ImsService} via {@link MmTelFeature#notifyMediaQualityStatusChanged(
+ * MediaQualityStatus)}
+ *
+ * <p/>
+ * A {@link MediaQualityStatus} should be triggered when any of various
+ * attributes pass one of the thresholds defined here.
+ *
+ *  @hide
+ */
+@SystemApi
+public final class MediaThreshold implements Parcelable {
+    private final int[] mRtpPacketLossRate;
+    private final int[] mRtpJitter;
+    private final long[] mRtpInactivityTimeMillis;
+
+    /**
+     * Retrieves threshold values for RTP packet loss rate in percentage.
+     *
+     * @return int array including threshold values for packet loss rate
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    public int[] getThresholdsRtpPacketLossRate() {
+        return mRtpPacketLossRate;
+    }
+
+    /**
+     * Retrieves threshold values for jitter(RFC3550) in milliseconds.
+     *
+     * @return int array including threshold values for RTP jitter.
+     */
+    @NonNull
+    public int[] getThresholdsRtpJitterMillis() {
+        return mRtpJitter;
+    }
+
+    /**
+     * Retrieves threshold values for RTP inactivity time in milliseconds.
+     *
+     * @return int array including threshold values for RTP inactivity time.
+     */
+    @NonNull
+    public long[] getThresholdsRtpInactivityTimeMillis() {
+        return mRtpInactivityTimeMillis;
+    }
+
+    private MediaThreshold(
+            int[] packetLossRateThresholds,
+            int[] jitterThresholds,
+            long[] inactivityTimeThresholds) {
+        mRtpPacketLossRate = packetLossRateThresholds;
+        mRtpJitter = jitterThresholds;
+        mRtpInactivityTimeMillis = inactivityTimeThresholds;
+    }
+
+    /**
+     * Creates a new instance of {@link MediaThreshold} from a parcel.
+     * @param in The parceled data to read.
+     */
+    private MediaThreshold(@NonNull Parcel in) {
+        mRtpPacketLossRate = in.createIntArray();
+        mRtpJitter = in.createIntArray();
+        mRtpInactivityTimeMillis = in.createLongArray();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeIntArray(mRtpPacketLossRate);
+        dest.writeIntArray(mRtpJitter);
+        dest.writeLongArray(mRtpInactivityTimeMillis);
+    }
+
+    public static final @NonNull Creator<MediaThreshold> CREATOR =
+            new Creator<MediaThreshold>() {
+                @Override
+                public MediaThreshold createFromParcel(@NonNull Parcel in) {
+                    return new MediaThreshold(in);
+                }
+
+                @Override
+                public MediaThreshold[] newArray(int size) {
+                    return new MediaThreshold[size];
+                }
+            };
+
+    /**
+     * Returns whether the RTP packet loss rate threshold is valid or not.
+     *
+     * @param packetLossRate packet loss rate
+     * @return the threshold is valid or not.
+     * @hide
+     */
+    public static boolean isValidRtpPacketLossRate(int packetLossRate) {
+        return (packetLossRate >= 0 && packetLossRate <= 100);
+    }
+
+    /**
+     * Returns whether the RTP jitter threshold is valid or not.
+     *
+     * @param jitter jitter value in milliseconds
+     * @return the threshold is valid or not.
+     * @hide
+     */
+    public static boolean isValidJitterMillis(int jitter) {
+        return (jitter >= 0 && jitter <= 10000);
+    }
+
+    /**
+     * Returns whether the RTP packet loss rate threshold is valid or not.
+     *
+     * @param inactivityTime packet loss rate
+     * @return the threshold is valid or not.
+     * @hide
+     */
+    public static boolean isValidRtpInactivityTimeMillis(long inactivityTime) {
+        return (inactivityTime >= 0 && inactivityTime <= 60000);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        MediaThreshold that = (MediaThreshold) o;
+        return Arrays.equals(mRtpPacketLossRate, that.mRtpPacketLossRate)
+                && Arrays.equals(mRtpJitter, that.mRtpJitter)
+                && Arrays.equals(mRtpInactivityTimeMillis, that.mRtpInactivityTimeMillis);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(Arrays.hashCode(mRtpPacketLossRate), Arrays.hashCode(mRtpJitter),
+                Arrays.hashCode(mRtpInactivityTimeMillis));
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("MediaThreshold{mRtpPacketLossRate=");
+        for (int i : mRtpPacketLossRate) {
+            sb.append(" ").append(i);
+        }
+        sb.append(", mRtpJitter=");
+        for (int b : mRtpJitter) {
+            sb.append(" ").append(b);
+        }
+        sb.append(", mRtpInactivityTimeMillis=");
+        for (long i : mRtpInactivityTimeMillis) {
+            sb.append(" ").append(i);
+        }
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
+     * Provides a convenient way to set the fields of an {@link MediaThreshold} when creating a
+     * new instance.
+     *
+     * <p>The example below shows how you might create a new {@code RtpThreshold}:
+     *
+     * <pre><code>
+     *
+     * RtpThreshold = new RtpThreshold.Builder()
+     *     .setRtpSessionType({@link MediaQualityStatus#MEDIA_SESSION_TYPE_AUDIO} or
+     *                          {@link MediaQualityStatus#MEDIA_SESSION_TYPE_VIDEO})
+     *     .setThresholdsRtpPacketLossRate(int[] packetLossRateThresholds)
+     *     .setThresholdsRtpJitterMillis(int[] jitterThresholds)
+     *     .setThresholdsRtpInactivityTimeMillis(int[] inactivityTimeThresholds)
+     *     .build();
+     * </code></pre>
+     *
+     * @hide
+     */
+    public static final class Builder {
+        private int[] mRtpPacketLossRate = null;
+        private int[] mRtpJitter = null;
+        private long[] mRtpInactivityTimeMillis = null;
+
+        /**
+         * Default constructor for the Builder.
+         *
+         * @hide
+         */
+        public Builder() {
+        }
+
+        /**
+         * Set threshold values for RTP packet loss rate in percentage.
+         * <p/>
+         * The packet loss calculation should be done at least once per
+         * second. It should be calculated with at least the last 3 seconds
+         * of data.
+         *
+         * @param packetLossRateThresholds int array for threshold values.
+         * @return The same instance of the builder.
+         *
+         * @hide
+         */
+        @NonNull
+        public Builder setThresholdsRtpPacketLossRate(int[] packetLossRateThresholds) {
+            if (packetLossRateThresholds.length > 0) {
+                TreeSet<Integer> thresholds = new TreeSet<>();
+                for (Integer value : packetLossRateThresholds) {
+                    if (isValidRtpPacketLossRate(value)) {
+                        thresholds.add(value);
+                    }
+                }
+                int[] targetArray = new int[thresholds.size()];
+                int i = 0;
+                for (int element : thresholds) {
+                    targetArray[i++] = element;
+                }
+                this.mRtpPacketLossRate = targetArray;
+            } else {
+                this.mRtpPacketLossRate = packetLossRateThresholds;
+            }
+            return this;
+        }
+
+
+        /**
+         * Set threshold values for RTP jitter in Milliseconds.
+         *
+         * @param jitterThresholds int array including threshold values for Jitter.
+         * @return The same instance of the builder.
+         *
+         * @hide
+         */
+        @NonNull
+        public Builder setThresholdsRtpJitterMillis(int[] jitterThresholds) {
+            if (jitterThresholds.length > 0) {
+                TreeSet<Integer> thresholds = new TreeSet<>();
+                for (Integer value : jitterThresholds) {
+                    if (isValidJitterMillis(value)) {
+                        thresholds.add(value);
+                    }
+                }
+                int[] targetArray = new int[thresholds.size()];
+                int i = 0;
+                for (int element : thresholds) {
+                    targetArray[i++] = element;
+                }
+                this.mRtpJitter = targetArray;
+            } else {
+                this.mRtpJitter = jitterThresholds;
+            }
+            return this;
+        }
+
+        /**
+         * Set threshold values for RTP inactivity time.
+         *
+         * @param inactivityTimeThresholds int array including threshold
+         *                              values for RTP inactivity time.
+         * @return The same instance of the builder.
+         *
+         * @hide
+         */
+        @NonNull
+        public Builder setThresholdsRtpInactivityTimeMillis(long[] inactivityTimeThresholds) {
+            if (inactivityTimeThresholds.length > 0) {
+                TreeSet<Long> thresholds = new TreeSet<>();
+                for (Long value : inactivityTimeThresholds) {
+                    if (isValidRtpInactivityTimeMillis(value)) {
+                        thresholds.add(value);
+                    }
+                }
+                long[] targetArray = new long[thresholds.size()];
+                int i = 0;
+                for (long element : thresholds) {
+                    targetArray[i++] = element;
+                }
+                this.mRtpInactivityTimeMillis = targetArray;
+            } else {
+                this.mRtpInactivityTimeMillis = inactivityTimeThresholds;
+            }
+            return this;
+        }
+
+        /**
+         * Build the {@link MediaThreshold}
+         *
+         * @return the {@link MediaThreshold} object
+         *
+         * @hide
+         */
+        @NonNull
+        public MediaThreshold build() {
+            mRtpPacketLossRate = mRtpPacketLossRate != null ? mRtpPacketLossRate : new int[0];
+            mRtpJitter = mRtpJitter != null ? mRtpJitter : new int[0];
+            mRtpInactivityTimeMillis =
+                    mRtpInactivityTimeMillis != null ? mRtpInactivityTimeMillis : new long[0];
+            return new MediaThreshold(mRtpPacketLossRate, mRtpJitter, mRtpInactivityTimeMillis);
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/ProvisioningManager.java b/android-35/android/telephony/ims/ProvisioningManager.java
new file mode 100644
index 0000000..62b8420
--- /dev/null
+++ b/android-35/android/telephony/ims/ProvisioningManager.java
@@ -0,0 +1,2015 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.aidl.IFeatureProvisioningCallback;
+import android.telephony.ims.aidl.IImsConfigCallback;
+import android.telephony.ims.aidl.IRcsConfigCallback;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+
+import com.android.internal.telephony.ITelephony;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages IMS provisioning and configuration parameters, as well as callbacks for apps to listen
+ * to changes in these configurations.
+ *
+ * IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
+ * applications and may vary. It is up to the carrier and OEM applications to ensure that the
+ * correct provisioning keys are being used when integrating with a vendor's ImsService.
+ *
+ * Use {@link android.telephony.ims.ImsManager#getProvisioningManager(int)} to get an instance of
+ * this manager.
+ */
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
+public class ProvisioningManager {
+
+    private static final String TAG = "ProvisioningManager";
+
+    /**@hide*/
+    @StringDef(prefix = "STRING_QUERY_RESULT_ERROR_", value = {
+            STRING_QUERY_RESULT_ERROR_GENERIC,
+            STRING_QUERY_RESULT_ERROR_NOT_READY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StringResultError {}
+
+    /**
+     * The query from {@link #getProvisioningStringValue(int)} has resulted in an unspecified error.
+     * @hide
+     */
+    @SystemApi
+    public static final String STRING_QUERY_RESULT_ERROR_GENERIC =
+            "STRING_QUERY_RESULT_ERROR_GENERIC";
+
+    /**
+     * The query from {@link #getProvisioningStringValue(int)} has resulted in an error because the
+     * ImsService implementation was not ready for provisioning queries.
+     * @hide
+     */
+    @SystemApi
+    public static final String STRING_QUERY_RESULT_ERROR_NOT_READY =
+            "STRING_QUERY_RESULT_ERROR_NOT_READY";
+
+    /**
+     * There is no existing configuration for the queried provisioning key.
+     * @hide
+     */
+    public static final int PROVISIONING_RESULT_UNKNOWN = -1;
+
+    /**
+     * The integer result of provisioning for the queried key is disabled.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROVISIONING_VALUE_DISABLED = 0;
+
+    /**
+     * The integer result of provisioning for the queried key is enabled.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROVISIONING_VALUE_ENABLED = 1;
+
+
+    // Inheriting values from ImsConfig for backwards compatibility.
+    /**
+     * AMR CODEC Mode Value set, 0-7 in comma separated sequence.
+     * <p>
+     * This corresponds to the {@code mode-set} parameter for the AMR codec.
+     * See 3GPP TS 26.101 Table 1A for more information.
+     * <p>
+     * <UL>
+     *     <LI>0 - AMR 4.75 kbit/s</LI>
+     *     <LI>1 - AMR 5.15 kbit/s</LI>
+     *     <LI>2 - AMR 5.90 kbit/s</LI>
+     *     <LI>3 - AMR 6.70 kbit/s (PDC-EFR)</LI>
+     *     <LI>4 - AMR 7.40 kbit/s (TDMA-EFR)</LI>
+     *     <LI>5 - AMR 7.95 kbit/s</LI>
+     *     <LI>6 - AMR 10.2 kbit/s</LI>
+     *     <LI>7 - AMR 12.2 kbit/s (GSM-EFR)</LI>
+     * </UL>
+     * <p>
+     * Value is in String format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0;
+
+    /**
+     * Wide Band AMR CODEC Mode Value set,0-7 in comma separated sequence.
+     * <p>
+     * This corresponds to the {@code mode-set} parameter for the AMR wideband codec.
+     * See 3GPP TS 26.101 Table 1A for more information.
+     * <p>
+     * <UL>
+     *     <LI>0 - AMR 4.75 kbit/s</LI>
+     *     <LI>1 - AMR 5.15 kbit/s</LI>
+     *     <LI>2 - AMR 5.90 kbit/s</LI>
+     *     <LI>3 - AMR 6.70 kbit/s (PDC-EFR)</LI>
+     *     <LI>4 - AMR 7.40 kbit/s (TDMA-EFR)</LI>
+     *     <LI>5 - AMR 7.95 kbit/s</LI>
+     *     <LI>6 - AMR 10.2 kbit/s</LI>
+     *     <LI>7 - AMR 12.2 kbit/s (GSM-EFR)</LI>
+     * </UL>
+     * <p>
+     * Value is in String format.
+     * @see #setProvisioningStringValue(int, String)
+     * @see #getProvisioningStringValue(int)
+     * @hide
+     */
+    public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1;
+
+    /**
+     * SIP Session Timer value (seconds).
+     * <p>
+     * See RFC4028 for more information.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SIP_SESSION_TIMER_SEC = 2;
+
+    /**
+     * Minimum SIP Session Expiration Timer in (seconds).
+     * <p>
+     * See RFC4028 for more information.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3;
+
+    /**
+     * SIP_INVITE cancellation time out value (in milliseconds). Integer format.
+     * <p>
+     * See RFC4028 for more information.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4;
+
+    /**
+     * Delay time when an iRAT transitions from eHRPD/HRPD/1xRTT to LTE.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5;
+
+    /**
+     * Silent redial status of Enabled (True), or Disabled (False).
+     * Value is in boolean format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_ENABLE_SILENT_REDIAL = 6;
+
+    /**
+     * An integer key representing the SIP T1 timer value in milliseconds for the associated
+     * subscription.
+     * <p>
+     * The SIP T1 timer is an estimate of the round-trip time and will retransmit
+     * INVITE transactions that are longer than T1 milliseconds over unreliable transports, doubling
+     * the time before retransmission every time there is no response. See RFC3261, section 17.1.1.1
+     * for more details.
+     * <p>
+     * The value is an integer.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_T1_TIMER_VALUE_MS = 7;
+
+    /**
+     * SIP T2 timer value in milliseconds.  See RFC 3261 for information.
+     * <p>
+     * The T2 timer is the maximum retransmit interval for non-INVITE requests and INVITE responses.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_T2_TIMER_VALUE_MS = 8;
+
+    /**
+     * SIP TF timer value in milliseconds.  See RFC 3261 for information.
+     * <p>
+     * The TF timer is the non-INVITE transaction timeout timer.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_TF_TIMER_VALUE_MS = 9;
+
+    /**
+     * An integer key representing the voice over LTE (VoLTE) provisioning status for the
+     * associated subscription. Determines whether the user can register for voice services over
+     * LTE.
+     * <p>
+     * Use {@link #PROVISIONING_VALUE_ENABLED} to enable VoLTE provisioning and
+     * {@link #PROVISIONING_VALUE_DISABLED} to disable VoLTE provisioning.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_VOLTE_PROVISIONING_STATUS = 10;
+
+    /**
+     * An integer key representing the video telephony (VT) provisioning status for the
+     * associated subscription. Determines whether the user can register for video services over
+     * LTE.
+     * <p>
+     * Use {@link #PROVISIONING_VALUE_ENABLED} to enable VT provisioning and
+     * {@link #PROVISIONING_VALUE_DISABLED} to disable VT provisioning.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_VT_PROVISIONING_STATUS = 11;
+
+    /**
+     * Domain Name for the device to populate the request URI for REGISTRATION.
+     * Value is in String format.
+     * @see #setProvisioningStringValue(int, String)
+     * @see #getProvisioningStringValue(int)
+     * @hide
+     */
+    public static final int KEY_REGISTRATION_DOMAIN_NAME = 12;
+
+    /**
+     * Device Outgoing SMS based on either 3GPP or 3GPP2 standards.
+     * Value is in Integer format.
+     * Valid values are {@link #SMS_FORMAT_3GPP} and {@link #SMS_FORMAT_3GPP2}.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SMS_FORMAT = 13;
+
+    /**
+     * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP2 SMS format is used.
+     * See {@link android.telephony.SmsMessage#FORMAT_3GPP2} for more information.
+     * @hide
+     */
+    public static final int SMS_FORMAT_3GPP2 = 0;
+
+    /**
+     * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP SMS format is used.
+     * See {@link android.telephony.SmsMessage#FORMAT_3GPP} for more information.
+     * @hide
+     */
+    public static final int SMS_FORMAT_3GPP = 1;
+
+    /**
+     * Turns SMS over IMS ON/OFF on the device.
+     * Value is in Integer format. ON (1), OFF(0).
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SMS_OVER_IP_ENABLED = 14;
+
+    /**
+     * An integer key associated with the carrier configured SIP PUBLISH timer, which dictates the
+     * expiration time in seconds for published online availability in RCS presence.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15;
+
+    /**
+     * An integer key associated with the carrier configured expiration time in seconds for
+     * published offline availability in RCS presence provided, which is provided to the network.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16;
+
+    /**
+     * An integer key associated with whether or not capability discovery is provisioned for this
+     * subscription. Any capability requests will be ignored by the RCS service.
+     * <p>
+     * The value is an integer, either {@link #PROVISIONING_VALUE_DISABLED} if capability
+     * discovery is disabled or {@link #PROVISIONING_VALUE_ENABLED} if capability discovery is
+     * enabled.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17;
+
+    /**
+     * An integer key associated with the period of time in seconds the capability information of
+     * each contact is cached on the device.
+     * <p>
+     * Seconds are used because this is usually measured in the span of days.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18;
+
+    /**
+     * An integer key associated with the period of time in seconds that the availability
+     * information of a contact is cached on the device, which is based on the carrier provisioning
+     * configuration from the network.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19;
+
+    /**
+     * An integer key associated with the carrier configured interval in seconds expected between
+     * successive capability polling attempts, which is based on the carrier provisioning
+     * configuration from the network.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20;
+
+    /**
+     * An integer key representing the minimum time allowed between two consecutive presence publish
+     * messages from the device in milliseconds.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21;
+
+    /**
+     * An integer key associated with the maximum number of MDNs contained in one SIP Request
+     * Contained List (RCS) used to retrieve the RCS capabilities of the contacts book.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22;
+
+    /**
+     * An integer associated with the expiration timer used during the SIP subscription of a
+     * Request Contained List (RCL), which is used to retrieve the RCS capabilities of the contact
+     * book. This timer value is sent in seconds to the network.
+     * <p>
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23;
+
+    /**
+     * Applies compression to LIST Subscription.
+     * Value is in Integer format. Enable (1), Disable(0).
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24;
+
+    /**
+     * An integer key representing the RCS enhanced address book (EAB) provisioning status for the
+     * associated subscription. Determines whether or not SIP OPTIONS or presence will be used to
+     * retrieve RCS capabilities for the user's contacts.
+     * <p>
+     * Use {@link #PROVISIONING_VALUE_ENABLED} to enable EAB provisioning and
+     * {@link #PROVISIONING_VALUE_DISABLED} to disable EAB provisioning.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_EAB_PROVISIONING_STATUS = 25;
+
+    /**
+     * Override the user-defined WiFi Roaming enabled setting for this subscription, defined in
+     * {@link android.telephony.SubscriptionManager#WFC_ROAMING_ENABLED_CONTENT_URI},
+     * for the purposes of provisioning the subscription for WiFi Calling.
+     *
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    @SystemApi
+    public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26;
+
+    /**
+     * Override the user-defined WiFi mode for this subscription, defined in
+     * {@link android.telephony.SubscriptionManager#WFC_MODE_CONTENT_URI},
+     * for the purposes of provisioning this subscription for WiFi Calling.
+     *
+     * Valid values for this key are:
+     * {@link ImsMmTelManager#WIFI_MODE_WIFI_ONLY},
+     * {@link ImsMmTelManager#WIFI_MODE_CELLULAR_PREFERRED}, or
+     * {@link ImsMmTelManager#WIFI_MODE_WIFI_PREFERRED}.
+     *
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    @SystemApi
+    public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27;
+
+    /**
+     * Enable voice over wifi.  Enabled (1), or Disabled (0).
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28;
+
+    /**
+     * Mobile data enabled.
+     * Value is in Integer format. On (1), OFF(0).
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_MOBILE_DATA_ENABLED = 29;
+
+    /**
+     * VoLTE user opted in status.
+     * Value is in Integer format. Opted-in (1) Opted-out (0).
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30;
+
+    /**
+     * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
+     * Value is in String format.
+     * @hide
+     */
+    public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31;
+
+    /**
+     * Keep Alive Enabled for SIP.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32;
+
+    /**
+     * Registration retry Base Time value in seconds, which is based off of the carrier
+     * configuration.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33;
+
+    /**
+     * Registration retry Max Time value in seconds, which is based off of the carrier
+     * configuration.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34;
+
+    /**
+     * Smallest RTP port for speech codec.
+     * Value is in integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+
+    public static final int KEY_RTP_SPEECH_START_PORT = 35;
+
+    /**
+     * Largest RTP port for speech code.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_RTP_SPEECH_END_PORT = 36;
+
+    /**
+     * SIP Timer A's value in milliseconds. Timer A is the INVITE request retransmit interval (in
+     * milliseconds), for UDP only.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37;
+
+    /**
+     * SIP Timer B's value in milliseconds. Timer B is the wait time for INVITE message to be,
+     * in milliseconds.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38;
+
+    /**
+     * SIP Timer D's value in milliseconds. Timer D is the wait time for response retransmits of
+     * the invite client transactions, in milliseconds.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39;
+
+    /**
+     * SIP Timer E's value in milliseconds. Timer E is the value Non-INVITE request retransmit
+     * interval (in milliseconds), for UDP only.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40;
+
+    /**
+     * SIP Timer F's value in milliseconds. Timer F is the Non-INVITE transaction timeout timer,
+     * in milliseconds.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41;
+
+    /**
+     * SIP Timer G's value in milliseconds. Timer G is the value of INVITE response
+     * retransmit interval.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42;
+
+    /**
+     * SIP Timer H's value in milliseconds. Timer H is the value of wait time for
+     * ACK receipt.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43;
+
+    /**
+     * SIP Timer I's value in milliseconds. Timer I is the value of wait time for
+     * ACK retransmits.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44;
+
+    /**
+     * SIP Timer J's value in milliseconds. Timer J is the value of wait time for
+     * non-invite request retransmission.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45;
+
+    /**
+     * SIP Timer K's value in milliseconds. Timer K is the value of wait time for
+     * non-invite response retransmits.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46;
+
+    /**
+     * AMR WB octet aligned dynamic payload type.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47;
+
+    /**
+     * AMR WB bandwidth efficient payload type.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48;
+
+    /**
+     * AMR octet aligned dynamic payload type.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49;
+
+    /**
+     * AMR bandwidth efficient payload type.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50;
+
+    /**
+     * DTMF WB payload type.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51;
+
+    /**
+     * DTMF NB payload type.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52;
+
+    /**
+     * AMR Default encoding mode.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53;
+
+    /**
+     * SMS Public Service Identity.
+     * Value is in String format.
+     * @hide
+     */
+    public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54;
+
+    /**
+     * Video Quality - VideoQualityFeatureValuesConstants.
+     * Valid values are: {@link #VIDEO_QUALITY_HIGH} and {@link #VIDEO_QUALITY_LOW}.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_VIDEO_QUALITY = 55;
+
+    /**
+     * Used with {@link #KEY_VIDEO_QUALITY} to indicate low video quality.
+     * @hide
+     */
+    public static final int VIDEO_QUALITY_LOW = 0;
+
+    /**
+     * Used with {@link #KEY_VIDEO_QUALITY} to indicate high video quality.
+     * @hide
+     */
+    public static final int VIDEO_QUALITY_HIGH = 1;
+
+    /**
+     * LTE to WIFI handover threshold.
+     * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= {@link #KEY_WIFI_THRESHOLD_A}.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_LTE_THRESHOLD_1 = 56;
+
+    /**
+     * WIFI to LTE handover threshold.
+     * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi < {@link
+     * #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}).
+     * Value is in Integer format.
+     *
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_LTE_THRESHOLD_2 = 57;
+
+    /**
+     * LTE to WIFI handover threshold.
+     * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi < {@link
+     * #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}).
+     * Value is in Integer format.
+     *
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_LTE_THRESHOLD_3 = 58;
+
+    /**
+     * 1x to WIFI handover threshold.
+     * Handover from 1x to WiFi if 1x < {@link #KEY_1X_THRESHOLD}.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_1X_THRESHOLD = 59;
+
+    /**
+     * LTE to WIFI threshold A.
+     * Handover from LTE to WiFi if LTE < {@link #KEY_LTE_THRESHOLD_1} and WiFi >= {@link
+     * #KEY_WIFI_THRESHOLD_A}.
+     * Value is in Integer format.
+     *
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_WIFI_THRESHOLD_A = 60;
+
+    /**
+     * WiFi to LTRE handover threshold B.
+     * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi <
+     * {@link #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}).
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_WIFI_THRESHOLD_B = 61;
+
+    /**
+     * LTE ePDG timer (in seconds).
+     * Device shall not handover back to LTE until the T_ePDG_LTE timer expires.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_LTE_EPDG_TIMER_SEC = 62;
+
+    /**
+     * WiFi ePDG timer (in seconds).
+     * Device shall not handover back to WiFi until the T_ePDG_WiFi timer expires.
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_WIFI_EPDG_TIMER_SEC = 63;
+
+    /**
+     * 1x ePDG timer (in seconds).
+     * Device shall not re-register on 1x until the T_ePDG_1x timer expires.
+     * @hide
+     */
+    public static final int KEY_1X_EPDG_TIMER_SEC = 64;
+
+    /**
+     * MultiEndpoint status: Enabled (1), or Disabled (0).
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_MULTIENDPOINT_ENABLED = 65;
+
+    /**
+     * RTT status: Enabled (1), or Disabled (0).
+     * Value is in Integer format.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_RTT_ENABLED = 66;
+
+    /**
+     * An obfuscated string defined by the carrier to indicate VoWiFi entitlement status.
+     *
+     * <p>Implementation note: how to generate the value and how it affects VoWiFi service
+     * should follow carrier requirements. For example, set an empty string could result in
+     * VoWiFi being disabled by IMS service, and set to a specific string could enable.
+     *
+     * <p>Value is in String format.
+     * @see #setProvisioningStringValue(int, String)
+     * @see #getProvisioningStringValue(int)
+     * @hide
+     */
+    @SystemApi
+    public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67;
+
+    /**
+     * An integer key representing the voice over IMS opt-in provisioning status for the
+     * associated subscription. Determines whether the user can see for voice services over
+     * IMS.
+     *
+     * <p> The flag will force to show the VoLTE option in settings irrespective of others VoLTE
+     * carrier config which hide the VoLTE option (e.g.
+     * {@link CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL}).
+     *
+     * <p>Use {@link #PROVISIONING_VALUE_ENABLED} to enable VoIMS provisioning and
+     * {@link #PROVISIONING_VALUE_DISABLED} to disable VoIMS  provisioning.
+     * @see #setProvisioningIntValue(int, int)
+     * @see #getProvisioningIntValue(int)
+     * @hide
+     */
+    public static final int KEY_VOIMS_OPT_IN_STATUS = 68;
+
+    /**
+     * Callback for IMS provisioning changes.
+     * @hide
+     */
+    @SystemApi
+    public static class Callback {
+
+        private static class CallbackBinder extends IImsConfigCallback.Stub {
+
+            private final Callback mLocalConfigurationCallback;
+            private Executor mExecutor;
+
+            private CallbackBinder(Callback localConfigurationCallback) {
+                mLocalConfigurationCallback = localConfigurationCallback;
+            }
+
+            @Override
+            public final void onIntConfigChanged(int item, int value) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() ->
+                            mLocalConfigurationCallback.onProvisioningIntChanged(item, value));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            @Override
+            public final void onStringConfigChanged(int item, String value) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() ->
+                            mLocalConfigurationCallback.onProvisioningStringChanged(item, value));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final CallbackBinder mBinder = new CallbackBinder(this);
+
+        /**
+         * Called when a provisioning item has changed.
+         * @param item the IMS provisioning key constant, as defined by the OEM.
+         * @param value the new integer value of the IMS provisioning key.
+         */
+        public void onProvisioningIntChanged(int item, int value) {
+            // Base Implementation
+        }
+
+        /**
+         * Called when a provisioning item has changed.
+         * @param item the IMS provisioning key constant, as defined by the OEM.
+         * @param value the new String value of the IMS configuration constant.
+         */
+        public void onProvisioningStringChanged(int item, @NonNull String value) {
+            // Base Implementation
+        }
+
+        /**@hide*/
+        public final IImsConfigCallback getBinder() {
+            return mBinder;
+        }
+
+        /**@hide*/
+        public void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+    }
+
+    /**
+     * Callback for IMS provisioning feature changes.
+     */
+    public abstract static class FeatureProvisioningCallback {
+
+        private static class CallbackBinder extends IFeatureProvisioningCallback.Stub {
+
+            private final FeatureProvisioningCallback mFeatureProvisioningCallback;
+            private Executor mExecutor;
+
+            private CallbackBinder(FeatureProvisioningCallback featureProvisioningCallback) {
+                mFeatureProvisioningCallback = featureProvisioningCallback;
+            }
+
+            @Override
+            public final void onFeatureProvisioningChanged(
+                    int capability, int tech, boolean isProvisioned) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() ->
+                            mFeatureProvisioningCallback.onFeatureProvisioningChanged(
+                                    capability, tech, isProvisioned));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            @Override
+            public final void onRcsFeatureProvisioningChanged(
+                    int capability, int tech, boolean isProvisioned) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() ->
+                            mFeatureProvisioningCallback.onRcsFeatureProvisioningChanged(
+                                    capability, tech, isProvisioned));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final CallbackBinder mBinder = new CallbackBinder(this);
+
+        /**
+         * The IMS MMTEL provisioning has changed for a specific capability and IMS
+         * registration technology.
+         * @param capability The MMTEL capability that provisioning has changed for.
+         * @param tech The IMS registration technology associated with the MMTEL capability that
+         * provisioning has changed for.
+         * @param isProvisioned {@code true} if the capability is provisioned for the technology
+         * specified, or {@code false} if the capability is not provisioned for the technology
+         * specified.
+         */
+        public abstract void onFeatureProvisioningChanged(
+                @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+                @ImsRegistrationImplBase.ImsRegistrationTech int tech,
+                boolean isProvisioned);
+
+        /**
+         * The IMS RCS provisioning has changed for a specific capability and IMS
+         * registration technology.
+         * @param capability The RCS capability that provisioning has changed for.
+         * @param tech The IMS registration technology associated with the RCS capability that
+         * provisioning has changed for.
+         * @param isProvisioned {@code true} if the capability is provisioned for the technology
+         * specified, or {@code false} if the capability is not provisioned for the technology
+         * specified.
+         */
+        public abstract void onRcsFeatureProvisioningChanged(
+                @ImsRcsManager.RcsImsCapabilityFlag int capability,
+                @ImsRegistrationImplBase.ImsRegistrationTech int tech,
+                boolean isProvisioned);
+
+        /**@hide*/
+        public final IFeatureProvisioningCallback getBinder() {
+            return mBinder;
+        }
+
+        /**@hide*/
+        public void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+    }
+
+    private int mSubId;
+
+    /**
+     * The callback for RCS provisioning changes.
+     * @hide
+     */
+    @SystemApi
+    public static class RcsProvisioningCallback {
+        private static class CallbackBinder extends IRcsConfigCallback.Stub {
+
+            private final RcsProvisioningCallback mLocalCallback;
+            private Executor mExecutor;
+
+            private CallbackBinder(RcsProvisioningCallback localCallback) {
+                mLocalCallback = localCallback;
+            }
+
+            @Override
+            public void onConfigurationChanged(byte[] configXml) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onConfigurationChanged(configXml));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+
+            @Override
+            public void onAutoConfigurationErrorReceived(int errorCode, String errorString) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onAutoConfigurationErrorReceived(
+                            errorCode, errorString));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+
+            @Override
+            public void onConfigurationReset() {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onConfigurationReset());
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+
+            @Override
+            public void onRemoved() {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onRemoved());
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+
+            @Override
+            public void onPreProvisioningReceived(byte[] configXml) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onPreProvisioningReceived(configXml));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final CallbackBinder mBinder = new CallbackBinder(this);
+
+        /**
+         * RCS configuration received via OTA provisioning. Configuration may change
+         * due to various triggers defined in GSMA RCC.14 for ACS(auto configuration
+         * server) or other operator defined triggers. If RCS provisioning is already
+         * completed at the time of callback registration, then this method shall be
+         * invoked with the current configuration.
+         * @param configXml The RCS configuration XML received by OTA. It is defined
+         * by GSMA RCC.07.
+         */
+        public void onConfigurationChanged(@NonNull byte[] configXml) {}
+
+        /**
+         * Errors during autoconfiguration connection setup are notified by the
+         * ACS(auto configuration server) client using this interface.
+         * @param errorCode HTTP error received during connection setup defined in
+         * GSMA RCC.14 2.4.3, like {@link java.net.HttpURLConnection#HTTP_UNAUTHORIZED},
+         * {@link java.net.HttpURLConnection#HTTP_FORBIDDEN}, etc.
+         * @param errorString reason phrase received with the error
+         */
+        public void onAutoConfigurationErrorReceived(int errorCode,
+                @NonNull String errorString) {}
+
+        /**
+         * When the previously valid RCS configuration is cleaned up by telephony for
+         * any case like SIM removed, default messaging application changed, etc.,
+         * this method will be invoked to notify the application regarding this change.
+         */
+        public void onConfigurationReset() {}
+
+        /**
+         * When the RCS application is no longer the Default messaging application,
+         * or when the subscription associated with this callback is removed (SIM
+         * removed, ESIM swap,etc...), callback will automatically be removed and
+         * the below method is invoked. There is a possibility that the method is
+         * invoked after the subscription has become inactive
+         */
+        public void onRemoved() {}
+
+        /**
+         * Some carriers using ACS (auto configuration server) may send a carrier-specific
+         * pre-provisioning configuration XML if the user has not been provisioned for RCS
+         * services yet. When this provisioning XML is received, the framework will move
+         * into a "not provisioned" state for RCS. In order for provisioning to proceed,
+         * the application must parse this configuration XML and perform the carrier specific
+         * opt-in flow for RCS services. If the user accepts, {@link #triggerRcsReconfiguration}
+         * must be called in order for the device to move out of this state and try to fetch
+         * the RCS provisioning information.
+         *
+         * @param configXml the pre-provisioning config in carrier specified format.
+         */
+        public void onPreProvisioningReceived(@NonNull byte[] configXml) {}
+
+        /**@hide*/
+        public final IRcsConfigCallback getBinder() {
+            return mBinder;
+        }
+
+        /**@hide*/
+        public void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+    }
+
+    /**
+     * Create a new {@link ProvisioningManager} for the subscription specified.
+     *
+     * @param subId The ID of the subscription that this ProvisioningManager will use.
+     * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+     * @throws IllegalArgumentException if the subscription is invalid.
+     * @hide
+     */
+    @SystemApi
+    public static @NonNull ProvisioningManager createForSubscriptionId(int subId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid subscription ID");
+        }
+
+        return new ProvisioningManager(subId);
+    }
+
+    /**@hide*/
+    //@SystemApi
+    public ProvisioningManager(int subId) {
+        mSubId = subId;
+    }
+
+    /**
+     * Register a new {@link Callback} to listen to changes to changes in IMS provisioning.
+     *
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be removed.
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+     * </ul>
+     *
+     * @param executor The {@link Executor} to call the callback methods on
+     * @param callback The provisioning callbackto be registered.
+     * @see #unregisterProvisioningChangedCallback(Callback)
+     * @see SubscriptionManager.OnSubscriptionsChangedListener
+     * @throws IllegalArgumentException if the subscription associated with this callback is not
+     * active (SIM is not inserted, ESIM inactive) or the subscription is invalid.
+     * @throws ImsException if the subscription associated with this callback is valid, but
+     * the {@link ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+     * reason.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void registerProvisioningChangedCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull Callback callback) throws ImsException {
+        callback.setExecutor(executor);
+        try {
+            getITelephony().registerImsProvisioningChangedCallback(mSubId, callback.getBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException | IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Unregister an existing {@link Callback}. When the subscription associated with this
+     * callback is removed (SIM removed, ESIM swap, etc...), this callback will automatically be
+     * removed. If this method is called for an inactive subscription, it will result in a no-op.
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+     * </ul>
+     *
+     * @param callback The existing {@link Callback} to be removed.
+     * @see #registerProvisioningChangedCallback(Executor, Callback)
+     *
+     * @throws IllegalArgumentException if the subscription associated with this callback is
+     * invalid.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void unregisterProvisioningChangedCallback(@NonNull Callback callback) {
+        try {
+            getITelephony().unregisterImsProvisioningChangedCallback(mSubId, callback.getBinder());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Register a new {@link FeatureProvisioningCallback}, which is used to listen for
+     * IMS feature provisioning updates.
+     * <p>
+     * When the subscription associated with this callback is removed (SIM removed,
+     * ESIM swap,etc...), this callback will automatically be removed.
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li> android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+     *     <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+     *     <li>or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @param executor The executor that the callback methods will be called on.
+     * @param callback The callback instance being registered.
+     * @throws ImsException if the subscription associated with this callback is
+     * valid, but the service crashed, for example. See
+     * {@link ImsException#getCode()} for a more detailed reason.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public void registerFeatureProvisioningChangedCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull FeatureProvisioningCallback callback) throws ImsException {
+        callback.setExecutor(executor);
+        try {
+            getITelephony().registerFeatureProvisioningChangedCallback(mSubId,
+                    callback.getBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException | IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Unregisters a previously registered {@link FeatureProvisioningCallback}
+     * instance.  When the subscription associated with this
+     * callback is removed (SIM removed, ESIM swap, etc...), this callback will
+     * automatically be removed. If this method is called for an inactive
+     * subscription, it will result in a no-op.
+     *
+     * @param callback The existing {@link FeatureProvisioningCallback} to be removed.
+     * @see #registerFeatureProvisioningChangedCallback(Executor, FeatureProvisioningCallback)
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    public void unregisterFeatureProvisioningChangedCallback(
+            @NonNull FeatureProvisioningCallback callback) {
+        try {
+            getITelephony().unregisterFeatureProvisioningChangedCallback(mSubId,
+                    callback.getBinder());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query for the integer value associated with the provided key.
+     *
+     * This operation is blocking and should not be performed on the UI thread.
+     *
+     * @param key An integer that represents the provisioning key, which is defined by the OEM.
+     * @return an integer value for the provided key, or
+     * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN} if the key doesn't exist.
+     * @throws IllegalArgumentException if the key provided was invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public int getProvisioningIntValue(int key) {
+        try {
+            return getITelephony().getImsProvisioningInt(mSubId, key);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query for the String value associated with the provided key.
+     *
+     * This operation is blocking and should not be performed on the UI thread.
+     *
+     * @param key A String that represents the provisioning key, which is defined by the OEM.
+     * @return a String value for the provided key, {@code null} if the key doesn't exist, or
+     * {@link StringResultError} if there was an error getting the value for the provided key.
+     * @throws IllegalArgumentException if the key provided was invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @Nullable @StringResultError String getProvisioningStringValue(int key) {
+        try {
+            return getITelephony().getImsProvisioningString(mSubId, key);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the integer value associated with the provided key.
+     *
+     * This operation is blocking and should not be performed on the UI thread.
+     *
+     * Use {@link #setProvisioningStringValue(int, String)} with proper namespacing (to be defined
+     * per OEM or carrier) when possible instead to avoid key collision if needed.
+     * @param key An integer that represents the provisioning key, which is defined by the OEM.
+     * @param value a integer value for the provided key.
+     * @return the result of setting the configuration value.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     *
+     * Note: For compatibility purposes, the integer values [0 - 99] used in
+     * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
+     * previously defined in the Android framework. Please do not redefine new provisioning keys
+     * in this range or it may generate collisions with existing keys. Some common constants have
+     * also been defined in this class to make integrating with other system apps easier.
+     */
+    @SystemApi
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public @ImsConfigImplBase.SetConfigResult int setProvisioningIntValue(int key, int value) {
+        try {
+            return getITelephony().setImsProvisioningInt(mSubId, key, value);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the String value associated with the provided key.
+     *
+     * This operation is blocking and should not be performed on the UI thread.
+     *
+     * @param key A String that represents the provisioning key, which is defined by the OEM and
+     *     should be appropriately namespaced to avoid collision.
+     * @param value a String value for the provided key.
+     * @return the result of setting the configuration value.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public @ImsConfigImplBase.SetConfigResult int setProvisioningStringValue(int key,
+            @NonNull String value) {
+        try {
+            return getITelephony().setImsProvisioningString(mSubId, key, value);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the provisioning status for the IMS MmTel capability using the specified subscription.
+     *
+     * Provisioning may or may not be required, depending on the carrier configuration. If
+     * provisioning is not required for the carrier associated with this subscription or the device
+     * does not support the capability/technology combination specified, this operation will be a
+     * no-op.
+     *
+     * <p>Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE},</li>
+     *     <li>or that the calling app has carrier privileges (see</li>
+     *     <li>{@link TelephonyManager#hasCarrierPrivileges}).</li>
+     * </ul>
+     *
+     * @see CarrierConfigManager.Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
+     * @param isProvisioned true if the device is provisioned for UT over IMS, false otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setProvisioningStatusForCapability(
+            @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int tech,  boolean isProvisioned) {
+
+        try {
+            getITelephony().setImsProvisioningStatusForCapability(mSubId, capability, tech,
+                        isProvisioned);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Get the provisioning status for the IMS MmTel capability specified.
+     *
+     * If provisioning is not required for the queried {@code capability} and
+     * {@code tech} combination specified, this method will
+     * always return {@code true}.
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+     *     <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+     *     <li>or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @see CarrierConfigManager.Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
+     * @return true if the device is provisioned for the capability or does not require
+     * provisioning, false if the capability does require provisioning and has not been
+     * provisioned yet.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public boolean getProvisioningStatusForCapability(
+            @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+        try {
+            return getITelephony().getImsProvisioningStatusForCapability(mSubId, capability, tech);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Get the provisioning status for the IMS RCS capability specified.
+     *
+     * If provisioning is not required for the queried
+     * {@code capability} or if the device does not support IMS
+     * this method will always return {@code true}.
+     *
+     * @see CarrierConfigManager.Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+     * @return true if the device is provisioned for the capability or does not require
+     * provisioning, false if the capability does require provisioning and has not been
+     * provisioned yet.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     *
+     * @deprecated Use {@link #getRcsProvisioningStatusForCapability(int, int)} instead,
+     * as this only retrieves provisioning information for
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean getRcsProvisioningStatusForCapability(
+            @ImsRcsManager.RcsImsCapabilityFlag int capability) {
+        try {
+            return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability,
+            ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Get the provisioning status for the IMS RCS capability specified.
+     *
+     * If provisioning is not required for the queried
+     * {@code capability} or if the device does not support IMS
+     * this method will always return {@code true}.
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+     *     <li>or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @see CarrierConfigManager.Ims#KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
+     * @return true if the device is provisioned for the capability or does not require
+     * provisioning, false if the capability does require provisioning and has not been
+     * provisioned yet.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public boolean getRcsProvisioningStatusForCapability(
+            @ImsRcsManager.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+        try {
+            return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability, tech);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the provisioning status for the IMS RCS capability using the specified subscription.
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE}</li>
+     *     <li>or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+
+     * Provisioning may or may not be required, depending on the carrier configuration. If
+     * provisioning is not required for the carrier associated with this subscription or the device
+     * does not support the capability/technology combination specified, this operation will be a
+     * no-op.
+     *
+     * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+     * @param isProvisioned true if the device is provisioned for the RCS capability specified,
+     *                      false otherwise.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     *
+     * @deprecated Use {@link #setRcsProvisioningStatusForCapability(int, int, boolean)} instead,
+     * as this method only sets provisioning information for
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setRcsProvisioningStatusForCapability(
+            @ImsRcsManager.RcsImsCapabilityFlag int capability,
+            boolean isProvisioned) {
+        try {
+            getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE, isProvisioned);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the provisioning status for the IMS RCS capability using the specified subscription.
+     *
+     * Provisioning may or may not be required, depending on the carrier configuration. If
+     * provisioning is not required for the carrier associated with this subscription or the device
+     * does not support the capability/technology combination specified, this operation will be a
+     * no-op.
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE},</li>
+     *     <li>or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @see CarrierConfigManager.Ims#KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
+     * @param isProvisioned true if the device is provisioned for the RCS capability specified,
+     *                      false otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @WorkerThread
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setRcsProvisioningStatusForCapability(
+            @ImsRcsManager.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned) {
+        try {
+            getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
+                    tech, isProvisioned);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Indicates whether provisioning for the MMTEL capability and IMS registration technology
+     * specified is required or not
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li> android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+     *     <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+     *     <li> or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @return true if provisioning is required for the MMTEL capability and IMS
+     * registration technology specified, false if it is not required or if the device does not
+     * support IMS.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public boolean isProvisioningRequiredForCapability(
+            @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+        try {
+            return getITelephony().isProvisioningRequiredForCapability(mSubId, capability, tech);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+
+    /**
+     * Indicates whether provisioning for the RCS capability and IMS registration technology
+     * specified is required or not
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li> android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+     *     <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+     *     <li> or that the caller has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @return true if provisioning is required for the RCS capability and IMS
+     * registration technology specified, false if it is not required or if the device does not
+     * support IMS.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public boolean isRcsProvisioningRequiredForCapability(
+            @ImsRcsManager.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+        try {
+            return getITelephony().isRcsProvisioningRequiredForCapability(mSubId, capability, tech);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+
+    /**
+     * Notify the framework that an RCS autoconfiguration XML file has been received for
+     * provisioning. This API is only valid if the device supports IMS, which can be checked using
+     * {@link PackageManager#hasSystemFeature}.
+     *
+     * <p>Requires Permission:
+     * <ul>
+     *     <li>{@link Manifest.permission#MODIFY_PHONE_STATE},</li>
+     *     <li>or that the calling app has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed.
+     * @param isCompressed The XML file is compressed in gzip format and must be decompressed
+     *         before being read.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
+    public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
+        if (config == null) {
+            throw new IllegalArgumentException("Must include a non-null config XML file.");
+        }
+
+        try {
+            getITelephony().notifyRcsAutoConfigurationReceived(mSubId, config, isCompressed);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Provides the single registration capability of the device and the carrier.
+     *
+     * <p>This intent only provides the capability and not the current provisioning status of
+     * the RCS VoLTE single registration feature. Only default messaging application may receive
+     * the intent.
+     *
+     * <p>Contains {@link #EXTRA_SUBSCRIPTION_ID} to specify the subscription index for which
+     * the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration
+     * status.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE =
+            "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE";
+
+    /**
+     * Integer extra to specify subscription index.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_SUBSCRIPTION_ID =
+            "android.telephony.ims.extra.SUBSCRIPTION_ID";
+
+    /**
+     * Integer extra to specify RCS single registration status
+     *
+     * <p>The value can be {@link #STATUS_CAPABLE}, {@link #STATUS_DEVICE_NOT_CAPABLE},
+     * {@link #STATUS_CARRIER_NOT_CAPABLE}, or bitwise OR of
+     * {@link #STATUS_DEVICE_NOT_CAPABLE} and {@link #STATUS_CARRIER_NOT_CAPABLE}.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_STATUS = "android.telephony.ims.extra.STATUS";
+
+    /**
+     * RCS VoLTE single registration is supported by the device and carrier.
+     * @hide
+     */
+    @SystemApi
+    public static final int STATUS_CAPABLE                       = 0;
+
+    /**
+     * RCS VoLTE single registration is not supported by the device.
+     * @hide
+     */
+    @SystemApi
+    public static final int STATUS_DEVICE_NOT_CAPABLE            = 0x01;
+
+    /**
+     * RCS VoLTE single registration is not supported by the carrier
+     * @hide
+     */
+    @SystemApi
+    public static final int STATUS_CARRIER_NOT_CAPABLE           = 0x01 << 1;
+
+    /**
+     * Provide the client configuration parameters of the RCS application.
+     *
+     * <p>When this application is also the default messaging application, and RCS
+     * provisioning is done using autoconfiguration, then these parameters shall be
+     * sent in the HTTP get request to fetch the RCS provisioning. RCS client
+     * configuration must be provided by the application before registering for the
+     * provisioning status events
+     * {@link #registerRcsProvisioningCallback(Executor, RcsProvisioningCallback)}
+     * When the IMS/RCS service receives the RCS client configuration, it will detect
+     * the change in the configuration, and trigger the auto-configuration as needed.
+     * @param rcc RCS client configuration {@link RcsClientConfiguration}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
+    public void setRcsClientConfiguration(
+            @NonNull RcsClientConfiguration rcc) throws ImsException {
+        try {
+            getITelephony().setRcsClientConfiguration(mSubId, rcc);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException | IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Returns a flag to indicate whether or not the device supports IMS single registration for
+     * MMTEL and RCS features as well as if the carrier has provisioned the feature.
+     *
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+     *     <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
+     *     <li>or that the calling app has carrier privileges (see
+     *         {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+     * </ul>
+     *
+     * @return true if IMS single registration is capable at this time, or false otherwise
+     * @throws ImsException If the remote ImsService is not available for any reason or
+     * the subscription associated with this instance is no longer active.
+     * See {@link ImsException#getCode()} for more information.
+     * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION for whether or not this
+     * device supports IMS single registration.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
+    public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
+        try {
+            return getITelephony().isRcsVolteSingleRegistrationCapable(mSubId);
+        } catch (RemoteException | IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        }
+    }
+
+   /**
+    * Registers a new {@link RcsProvisioningCallback} to listen to changes to
+    * RCS provisioning xml.
+    *
+    * <p>RCS application must be the default messaging application and must
+    * have already registered its {@link RcsClientConfiguration} by using
+    * {@link #setRcsClientConfiguration} before it registers the provisioning
+    * callback. If ProvisioningManager has a valid RCS configuration at the
+    * time of callback registration and a reconfiguration is not required
+    * due to RCS client parameters change, then the callback shall be invoked
+    * immediately with the xml.
+    * When the subscription associated with this callback is removed (SIM removed,
+    * ESIM swap,etc...), this callback will automatically be removed.
+    * <p> Requires Permission:
+    * <ul>
+    *     <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+    *     <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
+    * </ul>
+    *
+    * @param executor The {@link Executor} to call the callback methods on
+    * @param callback The rcs provisioning callback to be registered.
+    * @see #unregisterRcsProvisioningCallback(RcsProvisioningCallback)
+    * @see SubscriptionManager.OnSubscriptionsChangedListener
+    * @throws IllegalArgumentException if the subscription associated with this
+    * callback is not active (SIM is not inserted, ESIM inactive) or the
+    * subscription is invalid.
+    * @throws ImsException if the subscription associated with this callback is
+    * valid, but the {@link ImsService} associated with the subscription is not
+    * available. This can happen if the service crashed, for example.
+    * It shall also throw this exception when the RCS client parameters for the
+    * application are not valid. In that case application must set the client
+    * params (See {@link #setRcsClientConfiguration}) and re register the
+    * callback.
+    * See {@link ImsException#getCode()} for a more detailed reason.
+    * @throws UnsupportedOperationException If the device does not have
+    *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
+    * @hide
+    */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
+    public void registerRcsProvisioningCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull RcsProvisioningCallback callback) throws ImsException {
+        callback.setExecutor(executor);
+        try {
+            getITelephony().registerRcsProvisioningCallback(mSubId, callback.getBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException | IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Unregister an existing {@link RcsProvisioningCallback}. Application can
+     * unregister when its no longer interested in the provisioning updates
+     * like when a user disables RCS from the UI/settings.
+     * When the subscription associated with this callback is removed (SIM
+     * removed, ESIM swap, etc...), this callback will automatically be
+     * removed. If this method is called for an inactive subscription, it
+     * will result in a no-op.
+     * <p> Requires Permission:
+     * <ul>
+     *     <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+     *     <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
+     * </ul>
+     *
+     * @param callback The existing {@link RcsProvisioningCallback} to be
+     * removed.
+     * @see #registerRcsProvisioningCallback(Executor, RcsProvisioningCallback)
+     * @throws IllegalArgumentException if the subscription associated with
+     * this callback is invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
+    public void unregisterRcsProvisioningCallback(
+            @NonNull RcsProvisioningCallback callback) {
+        try {
+            getITelephony().unregisterRcsProvisioningCallback(
+                    mSubId, callback.getBinder());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Reconfiguration triggered by the RCS application. Most likely cause
+     * is the 403 forbidden to a HTTP request. This API is only valid if the device supports IMS,
+     * which can be checked using {@link PackageManager#hasSystemFeature}
+     *
+     * <p>When this api is called, the RCS configuration for the associated
+     * subscription will be removed, and the application which has registered
+     * {@link RcsProvisioningCallback} may expect to receive
+     * {@link RcsProvisioningCallback#onConfigurationReset}, then
+     * {@link RcsProvisioningCallback#onConfigurationChanged} when the new
+     * RCS configuration is received and notified by {@link #notifyRcsAutoConfigurationReceived}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
+    public void triggerRcsReconfiguration() {
+        try {
+            getITelephony().triggerRcsReconfiguration(mSubId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    private static ITelephony getITelephony() {
+        ITelephony binder = ITelephony.Stub.asInterface(
+                TelephonyFrameworkInitializer
+                        .getTelephonyServiceManager()
+                        .getTelephonyServiceRegisterer()
+                        .get());
+        if (binder == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+        return binder;
+    }
+}
diff --git a/android-35/android/telephony/ims/PublishAttributes.java b/android-35/android/telephony/ims/PublishAttributes.java
new file mode 100644
index 0000000..b987f1c
--- /dev/null
+++ b/android-35/android/telephony/ims/PublishAttributes.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2022 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.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.RcsUceAdapter.PublishState;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * This class provides detailed information related to publish state, SIP information and
+ * presence tuples in publication.
+ * This allows the application can check the detailed information of publication.
+ * @hide
+ */
+@SystemApi
+public final class PublishAttributes implements Parcelable {
+
+    private final @PublishState int mPublishState;
+    private List<RcsContactPresenceTuple> mPresenceTuples;
+    private @Nullable SipDetails mSipDetails;
+
+    /**
+     * Builder for creating {@link Builder} instances.
+     * @hide
+     */
+    public static final class Builder {
+        private PublishAttributes mAttributes;
+        /**
+         * Build a new instance of {@link PublishAttributes}.
+         *
+         * @param publishState The current publication state {@link RcsUceAdapter.PublishState}.
+         */
+        public Builder(@PublishState int publishState) {
+            mAttributes = new PublishAttributes(publishState);
+        }
+
+        /**
+         * Sets the SIP information in received response to a publish operation.
+         * @param details The {@link SipDetails} in received response.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setSipDetails(@Nullable SipDetails details) {
+            mAttributes.mSipDetails = details;
+            return this;
+        }
+
+        /**
+         * The tuple elements associated with the presence element portion of the PIDF document
+         * successfully sent to the network.
+         * @param tuples The list of the {@link RcsContactPresenceTuple} sent to the server.
+         *               The contact URI should not be included in this tuples.
+         * @return this The same instance of the builder.
+         */
+        public @NonNull Builder setPresenceTuples(@NonNull List<RcsContactPresenceTuple> tuples) {
+            mAttributes.mPresenceTuples = tuples;
+            return this;
+        }
+
+        /**
+         * @return a new PublishAttributes from this Builder.
+         */
+        public @NonNull PublishAttributes build() {
+            return mAttributes;
+        }
+    }
+
+    /**
+     * Generate the attributes related to the publication.
+     *
+     * @param publishState The current publication state.
+     *                     See {@link RcsUceAdapter.PublishState}.
+     */
+    private PublishAttributes(@PublishState int publishState) {
+        mPublishState = publishState;
+    }
+
+    /**
+     * Get the current publication state when the publishing state has changed or
+     * the publishing operation has done.
+     * @return The current publication state. See {@link RcsUceAdapter.PublishState}.
+     */
+    public @PublishState int getPublishState() {
+        return mPublishState;
+    }
+
+    /**
+     * Get the presence tuples from the PIDF on which the publishing was successful.
+     * @return The list of the {@link RcsContactPresenceTuple} sent to the server. If publish is
+     *          not successful yet, the value is empty.
+     */
+    public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() {
+        if (mPresenceTuples == null) {
+            return Collections.emptyList();
+        }
+        return mPresenceTuples;
+    }
+
+    /**
+     * Get the SipDetails set in ImsService.
+     * @return The {@link SipDetails} received in response. This value may be null if
+     *          the device doesn't support the collection of this information.
+     */
+    public @Nullable SipDetails getSipDetails() {
+        return mSipDetails;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mPublishState);
+        dest.writeList(mPresenceTuples);
+        dest.writeParcelable(mSipDetails, 0);
+    }
+
+    public static final @NonNull Creator<PublishAttributes> CREATOR =
+            new Creator<PublishAttributes>() {
+                @Override
+                public PublishAttributes createFromParcel(Parcel source) {
+                    return new PublishAttributes(source);
+                }
+
+                @Override
+                public PublishAttributes[] newArray(int size) {
+                    return new PublishAttributes[size];
+                }
+            };
+
+    /**
+     * Construct a PublishAttributes object from the given parcel.
+     */
+    private PublishAttributes(Parcel in) {
+        mPublishState = in.readInt();
+        mPresenceTuples = new ArrayList<>();
+        in.readList(mPresenceTuples, null, RcsContactPresenceTuple.class);
+        mSipDetails = in.readParcelable(SipDetails.class.getClassLoader(),
+                android.telephony.ims.SipDetails.class);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PublishAttributes that = (PublishAttributes) o;
+        return mPublishState == that.mPublishState
+                && Objects.equals(mPresenceTuples, that.mPresenceTuples)
+                && Objects.equals(mSipDetails, that.mSipDetails);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPublishState, mPresenceTuples, mSipDetails);
+    }
+
+    @Override
+    public String toString() {
+        return "PublishAttributes { publishState= " + mPublishState
+                + ", presenceTuples=[" + mPresenceTuples + "]" + "SipDetails=" + mSipDetails + "}";
+    }
+}
+
diff --git a/android-35/android/telephony/ims/RcsClientConfiguration.java b/android-35/android/telephony/ims/RcsClientConfiguration.java
new file mode 100644
index 0000000..39c9d8b
--- /dev/null
+++ b/android-35/android/telephony/ims/RcsClientConfiguration.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The container of RCS application related configs.
+ *
+ * @hide
+ */
+@SystemApi
+public final class RcsClientConfiguration implements Parcelable {
+
+    /**@hide*/
+    @StringDef(prefix = "RCS_PROFILE_",
+            value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3, RCS_PROFILE_2_4})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StringRcsProfile {}
+
+    /**
+     * RCS profile UP 1.0
+     */
+    public static final String RCS_PROFILE_1_0 = "UP_1.0";
+    /**
+     * RCS profile UP 2.3
+     */
+    public static final String RCS_PROFILE_2_3 = "UP_2.3";
+    /**
+     * RCS profile UP 2.4
+     */
+    public static final String RCS_PROFILE_2_4 = "UP_2.4";
+
+    private String mRcsVersion;
+    private String mRcsProfile;
+    private String mClientVendor;
+    private String mClientVersion;
+    private boolean mRcsEnabledByUser;
+
+    /**
+     * Create a RcsClientConfiguration object.
+     * Default messaging application must pass a valid configuration object
+     * @param rcsVersion The parameter identifies the RCS version supported
+     * by the client. Refer to GSMA RCC.07 "rcs_version" parameter.
+     * @param rcsProfile Identifies a fixed set of RCS services that are
+     * supported by the client. See {@link #RCS_PROFILE_1_0 },
+     * {@link #RCS_PROFILE_2_3 } or {@link #RCS_PROFILE_2_4 }
+     * @param clientVendor Identifies the vendor providing the RCS client.
+     * @param clientVersion Identifies the RCS client version. Refer to GSMA
+     * RCC.07 "client_version" parameter.
+     * Example:client_version=RCSAndrd-1.0
+     * @deprecated Use {@link #RcsClientConfiguration(String, String, String, String, boolean)}
+     * instead. Deprecated prototype assumes that the user setting controlling RCS is enabled.
+     */
+    @Deprecated
+    public RcsClientConfiguration(@NonNull String rcsVersion,
+            @NonNull @StringRcsProfile String rcsProfile,
+            @NonNull String clientVendor, @NonNull String clientVersion) {
+        this(rcsVersion, rcsProfile, clientVendor, clientVersion, true);
+    }
+
+    /**
+     * Create a RcsClientConfiguration object.
+     * Default messaging application must pass a valid configuration object
+     * @param rcsVersion The parameter identifies the RCS version supported
+     * by the client. Refer to GSMA RCC.07 "rcs_version" parameter.
+     * @param rcsProfile Identifies a fixed set of RCS services that are
+     * supported by the client. See {@link #RCS_PROFILE_1_0 },
+     * {@link #RCS_PROFILE_2_3 } or {@link #RCS_PROFILE_2_4 }
+     * @param clientVendor Identifies the vendor providing the RCS client.
+     * @param clientVersion Identifies the RCS client version. Refer to GSMA
+     * RCC.07 "client_version" parameter.
+     * Example:client_version=RCSAndrd-1.0
+     * @param isRcsEnabledByUser The current user setting for whether or not the user has
+     * enabled or disabled RCS. Please refer to GSMA RCC.07 "rcs_state" parameter for how this
+     * can affect provisioning.
+     */
+    public RcsClientConfiguration(@NonNull String rcsVersion,
+            @NonNull @StringRcsProfile String rcsProfile,
+            @NonNull String clientVendor, @NonNull String clientVersion,
+            boolean isRcsEnabledByUser) {
+        mRcsVersion = rcsVersion;
+        mRcsProfile = rcsProfile;
+        mClientVendor = clientVendor;
+        mClientVersion = clientVersion;
+        mRcsEnabledByUser = isRcsEnabledByUser;
+    }
+
+    /**
+     * Returns RCS version supported.
+     */
+    public @NonNull String getRcsVersion() {
+        return mRcsVersion;
+    }
+
+    /**
+     * Returns RCS profile supported.
+     */
+    public @NonNull @StringRcsProfile String getRcsProfile() {
+        return mRcsProfile;
+    }
+
+    /**
+     * Returns the name of the vendor providing the RCS client.
+     */
+    public @NonNull String getClientVendor() {
+        return mClientVendor;
+    }
+
+    /**
+     * Returns the RCS client version.
+     */
+    public @NonNull String getClientVersion() {
+        return mClientVersion;
+    }
+
+    /**
+     * The current user setting provided by the RCS messaging application that determines
+     * whether or not the user has enabled RCS.
+     * <p>
+     * See GSMA RCC.07 "rcs_state" parameter for more information about how this setting
+     * affects provisioning.
+     * @return true if RCS is enabled by the user, false if RCS is disabled by the user.
+     */
+    public boolean isRcsEnabledByUser() {
+        return mRcsEnabledByUser;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeString(mRcsVersion);
+        out.writeString(mRcsProfile);
+        out.writeString(mClientVendor);
+        out.writeString(mClientVersion);
+        out.writeBoolean(mRcsEnabledByUser);
+    }
+
+    /**
+     * {@link Parcelable.Creator}
+     *
+     */
+    public static final @android.annotation.NonNull Parcelable.Creator<
+            RcsClientConfiguration> CREATOR = new Creator<RcsClientConfiguration>() {
+                @Override
+                public RcsClientConfiguration createFromParcel(Parcel in) {
+                    String rcsVersion = in.readString();
+                    String rcsProfile = in.readString();
+                    String clientVendor = in.readString();
+                    String clientVersion = in.readString();
+                    Boolean rcsEnabledByUser = in.readBoolean();
+                    return new RcsClientConfiguration(rcsVersion, rcsProfile,
+                            clientVendor, clientVersion, rcsEnabledByUser);
+                }
+
+                @Override
+                public RcsClientConfiguration[] newArray(int size) {
+                    return new RcsClientConfiguration[size];
+                }
+            };
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof RcsClientConfiguration)) {
+            return false;
+        }
+
+        RcsClientConfiguration other = (RcsClientConfiguration) obj;
+
+        return mRcsVersion.equals(other.mRcsVersion) && mRcsProfile.equals(other.mRcsProfile)
+                && mClientVendor.equals(other.mClientVendor)
+                && mClientVersion.equals(other.mClientVersion)
+                && (mRcsEnabledByUser == other.mRcsEnabledByUser);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRcsVersion, mRcsProfile, mClientVendor, mClientVersion,
+                mRcsEnabledByUser);
+    }
+}
diff --git a/android-35/android/telephony/ims/RcsConfig.java b/android-35/android/telephony/ims/RcsConfig.java
new file mode 100644
index 0000000..32d686d
--- /dev/null
+++ b/android-35/android/telephony/ims/RcsConfig.java
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Build;
+import android.provider.Telephony.SimInfo;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.telephony.Rlog;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * RCS config data and methods to process the config
+ * @hide
+ */
+public final class RcsConfig {
+    private static final String LOG_TAG = "RcsConfig";
+    private static final boolean DBG = Build.IS_ENG;
+
+    // Tag and attribute defined in RCC.07 A.2
+    private static final String TAG_CHARACTERISTIC = "characteristic";
+    private static final String TAG_PARM = "parm";
+    private static final String ATTRIBUTE_TYPE = "type";
+    private static final String ATTRIBUTE_NAME = "name";
+    private static final String ATTRIBUTE_VALUE = "value";
+    // Keyword for Rcs Volte single registration defined in RCC.07 A.1.6.2
+    private static final String PARM_SINGLE_REGISTRATION = "rcsVolteSingleRegistration";
+
+    /**
+     * Characteristic of the RCS provisioning config
+     */
+    public static class Characteristic {
+        private String mType;
+        private final Map<String, String> mParms = new ArrayMap<>();
+        private final Set<Characteristic> mSubs = new ArraySet<>();
+        private final Characteristic mParent;
+
+        private Characteristic(String type, Characteristic parent) {
+            mType = type;
+            mParent = parent;
+        }
+
+        private String getType() {
+            return mType;
+        }
+
+        private Map<String, String> getParms() {
+            return mParms;
+        }
+
+        private Set<Characteristic> getSubs() {
+            return mSubs;
+        }
+
+        private Characteristic getParent() {
+            return mParent;
+        }
+
+        private Characteristic getSubByType(String type) {
+            if (TextUtils.equals(mType, type)) {
+                return this;
+            }
+            Characteristic result = null;
+            for (Characteristic sub : mSubs) {
+                result = sub.getSubByType(type);
+                if (result != null) {
+                    break;
+                }
+            }
+            return result;
+        }
+
+        private boolean hasSubByType(String type) {
+            return getSubByType(type) != null;
+        }
+
+        private String getParmValue(String name) {
+            String value = mParms.get(name);
+            if (value == null) {
+                for (Characteristic sub : mSubs) {
+                    value = sub.getParmValue(name);
+                    if (value != null) {
+                        break;
+                    }
+                }
+            }
+            return value;
+        }
+
+        boolean hasParm(String name) {
+            if (mParms.containsKey(name)) {
+                return true;
+            }
+
+            for (Characteristic sub : mSubs) {
+                if (sub.hasParm(name)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("[" + mType + "]: ");
+            if (DBG) {
+                sb.append(mParms);
+            }
+            for (Characteristic sub : mSubs) {
+                sb.append("\n");
+                sb.append(sub.toString().replace("\n", "\n\t"));
+            }
+            return sb.toString();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof Characteristic)) {
+                return false;
+            }
+
+            Characteristic o = (Characteristic) obj;
+
+            return TextUtils.equals(mType, o.mType) && mParms.equals(o.mParms)
+                    && mSubs.equals(o.mSubs);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mType, mParms, mSubs);
+        }
+    }
+
+    private final Characteristic mRoot;
+    private Characteristic mCurrent;
+    private final byte[] mData;
+
+    public RcsConfig(byte[] data) throws IllegalArgumentException {
+        if (data == null || data.length == 0) {
+            throw new IllegalArgumentException("Empty data");
+        }
+        mRoot = new Characteristic(null, null);
+        mCurrent = mRoot;
+        mData = data;
+        Characteristic current = mRoot;
+        ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+        try {
+            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
+            factory.setNamespaceAware(true);
+            XmlPullParser xpp = factory.newPullParser();
+            xpp.setInput(inputStream, null);
+            int eventType = xpp.getEventType();
+            String tag = null;
+            while (eventType != XmlPullParser.END_DOCUMENT && current != null) {
+                if (eventType == XmlPullParser.START_TAG) {
+                    tag = xpp.getName().trim().toLowerCase(Locale.ROOT);
+                    if (TAG_CHARACTERISTIC.equals(tag)) {
+                        int count = xpp.getAttributeCount();
+                        String type = null;
+                        if (count > 0) {
+                            for (int i = 0; i < count; i++) {
+                                String name = xpp.getAttributeName(i).trim()
+                                        .toLowerCase(Locale.ROOT);
+                                if (ATTRIBUTE_TYPE.equals(name)) {
+                                    type = xpp.getAttributeValue(xpp.getAttributeNamespace(i),
+                                            name).trim().toLowerCase(Locale.ROOT);
+                                    break;
+                                }
+                            }
+                        }
+                        Characteristic next = new Characteristic(type, current);
+                        current.getSubs().add(next);
+                        current = next;
+                    } else if (TAG_PARM.equals(tag)) {
+                        int count = xpp.getAttributeCount();
+                        String key = null;
+                        String value = null;
+                        if (count > 1) {
+                            for (int i = 0; i < count; i++) {
+                                String name = xpp.getAttributeName(i).trim()
+                                        .toLowerCase(Locale.ROOT);
+                                if (ATTRIBUTE_NAME.equals(name)) {
+                                    key = xpp.getAttributeValue(xpp.getAttributeNamespace(i),
+                                            name).trim().toLowerCase(Locale.ROOT);
+                                } else if (ATTRIBUTE_VALUE.equals(name)) {
+                                    value = xpp.getAttributeValue(xpp.getAttributeNamespace(i),
+                                            name).trim();
+                                }
+                            }
+                        }
+                        if (key != null && value != null) {
+                            current.getParms().put(key, value);
+                        }
+                    }
+                } else if (eventType == XmlPullParser.END_TAG) {
+                    tag = xpp.getName().trim().toLowerCase(Locale.ROOT);
+                    if (TAG_CHARACTERISTIC.equals(tag)) {
+                        current = current.getParent();
+                    }
+                    tag = null;
+                }
+                eventType = xpp.next();
+            }
+        } catch (IOException | XmlPullParserException e) {
+            throw new IllegalArgumentException(e);
+        } finally {
+            try {
+                inputStream.close();
+            } catch (IOException e) {
+                loge("error to close input stream, skip.");
+            }
+        }
+    }
+
+    /**
+     * Retrieve a String value of the config item with the tag
+     *
+     * @param tag The name of the config to retrieve.
+     * @param defaultVal Value to return if the config does not exist.
+     *
+     * @return Returns the config value if it exists, or defaultVal.
+     */
+    public @Nullable String getString(@NonNull String tag, @Nullable String defaultVal) {
+        String value = mCurrent.getParmValue(tag.trim().toLowerCase(Locale.ROOT));
+        return value != null ?  value : defaultVal;
+    }
+
+    /**
+     * Retrieve a int value of the config item with the tag
+     *
+     * @param tag The name of the config to retrieve.
+     * @param defaultVal Value to return if the config does not exist or not valid.
+     *
+     * @return Returns the config value if it exists and is a valid int, or defaultVal.
+     */
+    public int getInteger(@NonNull String tag, int defaultVal) {
+        try {
+            return Integer.parseInt(getString(tag, null));
+        } catch (NumberFormatException e) {
+            logd("error to getInteger for " + tag + " due to " + e);
+        }
+        return defaultVal;
+    }
+
+    /**
+     * Retrieve a boolean value of the config item with the tag
+     *
+     * @param tag The name of the config to retrieve.
+     * @param defaultVal Value to return if the config does not exist.
+     *
+     * @return Returns the config value if it exists, or defaultVal.
+     */
+    public boolean getBoolean(@NonNull String tag, boolean defaultVal) {
+        String value = getString(tag, null);
+        return value != null ? Boolean.parseBoolean(value) : defaultVal;
+    }
+
+    /**
+     * Check whether the config item exists
+     *
+     * @param tag The name of the config to retrieve.
+     *
+     * @return Returns true if it exists, or false.
+     */
+    public boolean hasConfig(@NonNull String tag) {
+        return mCurrent.hasParm(tag.trim().toLowerCase(Locale.ROOT));
+    }
+
+    /**
+     * Return the Characteristic with the given type
+     */
+    public @Nullable Characteristic getCharacteristic(@NonNull String type) {
+        return mCurrent.getSubByType(type.trim().toLowerCase(Locale.ROOT));
+    }
+
+    /**
+     * Check whether the Characteristic with the given type exists
+     */
+    public boolean hasCharacteristic(@NonNull String type) {
+        return mCurrent.getSubByType(type.trim().toLowerCase(Locale.ROOT)) != null;
+    }
+
+    /**
+     * Set current Characteristic to given Characteristic
+     */
+    public void setCurrentCharacteristic(@NonNull Characteristic current) {
+        if (current != null) {
+            mCurrent = current;
+        }
+    }
+
+    /**
+     * Move current Characteristic to parent layer
+     */
+    public boolean moveToParent() {
+        if (mCurrent.getParent() == null) {
+            return false;
+        }
+        mCurrent = mCurrent.getParent();
+        return true;
+    }
+
+    /**
+     * Move current Characteristic to the root
+     */
+    public void moveToRoot() {
+        mCurrent = mRoot;
+    }
+
+    /**
+     * Return root Characteristic
+     */
+    public @NonNull Characteristic getRoot() {
+        return mRoot;
+    }
+
+    /**
+     * Return current Characteristic
+     */
+    public @NonNull Characteristic getCurrentCharacteristic() {
+        return mCurrent;
+    }
+
+    /**
+     * Check whether Rcs Volte single registration is supported by the config.
+     */
+    public boolean isRcsVolteSingleRegistrationSupported(boolean isRoaming) {
+        int val = getInteger(PARM_SINGLE_REGISTRATION, 1);
+        return isRoaming ? val == 1 : val > 0;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("[RCS Config]");
+        if (DBG) {
+            sb.append("=== Root ===\n");
+            sb.append(mRoot);
+            sb.append("=== Current ===\n");
+            sb.append(mCurrent);
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof RcsConfig)) {
+            return false;
+        }
+
+        RcsConfig other = (RcsConfig) obj;
+
+        return mRoot.equals(other.mRoot) && mCurrent.equals(other.mCurrent);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRoot, mCurrent);
+    }
+
+    /**
+     * compress the gzip format data
+     */
+    public static @Nullable byte[] compressGzip(@NonNull byte[] data) {
+        if (data == null || data.length == 0) {
+            return data;
+        }
+        byte[] out = null;
+        try {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
+            GZIPOutputStream gzipCompressingStream =
+                    new GZIPOutputStream(outputStream);
+            gzipCompressingStream.write(data);
+            gzipCompressingStream.close();
+            out = outputStream.toByteArray();
+            outputStream.close();
+        } catch (IOException e) {
+            loge("Error to compressGzip due to " + e);
+        }
+        return out;
+    }
+
+    /**
+     * decompress the gzip format data
+     */
+    public static @Nullable byte[] decompressGzip(@NonNull byte[] data) {
+        if (data == null || data.length == 0) {
+            return data;
+        }
+        byte[] out = null;
+        try {
+            ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            GZIPInputStream gzipDecompressingStream =
+                    new GZIPInputStream(inputStream);
+            byte[] buf = new byte[1024];
+            int size = gzipDecompressingStream.read(buf);
+            while (size >= 0) {
+                outputStream.write(buf, 0, size);
+                size = gzipDecompressingStream.read(buf);
+            }
+            gzipDecompressingStream.close();
+            inputStream.close();
+            out = outputStream.toByteArray();
+            outputStream.close();
+        } catch (IOException e) {
+            loge("Error to decompressGzip due to " + e);
+        }
+        return out;
+    }
+
+    /**
+     * save the config to siminfo db. It is only used internally.
+     */
+    public static void updateConfigForSub(@NonNull Context cxt, int subId,
+            @NonNull byte[] config, boolean isCompressed) {
+        //always store gzip compressed data
+        byte[] data = isCompressed ? config : compressGzip(config);
+        ContentValues values = new ContentValues();
+        values.put(SimInfo.COLUMN_RCS_CONFIG, data);
+        cxt.getContentResolver().update(SimInfo.CONTENT_URI, values,
+                SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null);
+    }
+
+    /**
+     * load the config from siminfo db. It is only used internally.
+     */
+    public static @Nullable byte[] loadRcsConfigForSub(@NonNull Context cxt,
+            int subId, boolean isCompressed) {
+
+        byte[] data = null;
+
+        Cursor cursor = cxt.getContentResolver().query(SimInfo.CONTENT_URI, null,
+                SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID + "=" + subId, null, null);
+        try {
+            if (cursor != null && cursor.moveToFirst()) {
+                data = cursor.getBlob(cursor.getColumnIndexOrThrow(SimInfo.COLUMN_RCS_CONFIG));
+            }
+        } catch (Exception e) {
+            loge("error to load rcs config for sub:" + subId + " due to " + e);
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return isCompressed ? data : decompressGzip(data);
+    }
+
+    private static void logd(String msg) {
+        Rlog.d(LOG_TAG, msg);
+    }
+
+    private static void loge(String msg) {
+        Rlog.e(LOG_TAG, msg);
+    }
+}
diff --git a/android-35/android/telephony/ims/RcsContactPresenceTuple.java b/android-35/android/telephony/ims/RcsContactPresenceTuple.java
new file mode 100644
index 0000000..74bac22
--- /dev/null
+++ b/android-35/android/telephony/ims/RcsContactPresenceTuple.java
@@ -0,0 +1,575 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Represents a PIDF tuple element that is part of the presence element returned from the carrier
+ * network during a SUBSCRIBE request. See RFC3863 for more information.
+ * @hide
+ */
+@SystemApi
+public final class RcsContactPresenceTuple implements Parcelable {
+
+    private static final String LOG_TAG = "RcsContactPresenceTuple";
+
+    /**
+     * The service ID used to indicate that service discovery via presence is available.
+     * <p>
+     * See RCC.07 v5.0 specification for more information.
+     * @hide
+     */
+    public static final String SERVICE_ID_PRESENCE =
+            "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcse.dp";
+
+    /**
+     * The service ID used to indicate that MMTEL service is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
+
+    /**
+     * The service ID used to indicate that the chat(v1.0) is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session";
+
+    /**
+     * The service ID used to indicate that the chat(v2.0) is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession";
+
+    /**
+     * The service ID used to indicate that the File Transfer is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP";
+
+    /**
+     * The service ID used to indicate that the File Transfer over SMS is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_FT_OVER_SMS =
+            "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms";
+
+    /**
+     * The service ID used to indicate that the Geolocation Push is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_GEO_PUSH =
+            "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush";
+
+    /**
+     * The service ID used to indicate that the Geolocation Push via SMS is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_GEO_PUSH_VIA_SMS =
+            "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms";
+
+    /**
+     * The service ID used to indicate that the Call Composer is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_CALL_COMPOSER =
+            "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer";
+
+    /**
+     * The service ID used to indicate that the Post Call is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_POST_CALL =
+            "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered";
+
+    /**
+     * The service ID used to indicate that the Shared Map is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_SHARED_MAP =
+            "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap";
+
+    /**
+     * The service ID used to indicate that the Shared Sketch is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_SHARED_SKETCH =
+            "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch";
+
+    /**
+     * The service ID used to indicate that the Chatbot using Session is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_CHATBOT =
+            "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot";
+
+    /**
+     * The service ID used to indicate that the Chatbot using Standalone Messaging is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_CHATBOT_STANDALONE =
+            " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa";
+
+    /**
+     * The service ID used to indicate that the Chatbot Role is available.
+     * <p>
+     * See the GSMA RCC.07 specification for more information.
+     */
+    public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot";
+
+    /**
+     * The service ID used to indicate that the Standalone Messaging is available.
+     * <p>
+     * See the GSMA RCC.07 RCS5_1_advanced_communications_specification_v4.0 specification
+     * for more information.
+     */
+    public static final String SERVICE_ID_SLM = "org.openmobilealliance:StandaloneMsg";
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef(prefix = "SERVICE_ID_", value = {
+            SERVICE_ID_MMTEL,
+            SERVICE_ID_CHAT_V1,
+            SERVICE_ID_CHAT_V2,
+            SERVICE_ID_FT,
+            SERVICE_ID_FT_OVER_SMS,
+            SERVICE_ID_GEO_PUSH,
+            SERVICE_ID_GEO_PUSH_VIA_SMS,
+            SERVICE_ID_CALL_COMPOSER,
+            SERVICE_ID_POST_CALL,
+            SERVICE_ID_SHARED_MAP,
+            SERVICE_ID_SHARED_SKETCH,
+            SERVICE_ID_CHATBOT,
+            SERVICE_ID_CHATBOT_STANDALONE,
+            SERVICE_ID_CHATBOT_ROLE,
+            SERVICE_ID_SLM
+    })
+    public @interface ServiceId {}
+
+    /** The service capabilities is available. */
+    public static final String TUPLE_BASIC_STATUS_OPEN = "open";
+
+    /** The service capabilities is unavailable. */
+    public static final String TUPLE_BASIC_STATUS_CLOSED = "closed";
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef(prefix = "TUPLE_BASIC_STATUS_", value = {
+        TUPLE_BASIC_STATUS_OPEN,
+        TUPLE_BASIC_STATUS_CLOSED
+    })
+    public @interface BasicStatus {}
+
+    /**
+     * An optional addition to the PIDF Presence Tuple containing service capabilities, which is
+     * defined in the servcaps element. See RFC5196, section 3.2.1.
+     */
+    public static final class ServiceCapabilities implements Parcelable {
+
+        /** The service can simultaneously send and receive data. */
+        public static final String DUPLEX_MODE_FULL = "full";
+
+        /** The service can alternate between sending and receiving data.*/
+        public static final String DUPLEX_MODE_HALF = "half";
+
+        /** The service can only receive data. */
+        public static final String DUPLEX_MODE_RECEIVE_ONLY = "receive-only";
+
+        /** The service can only send data. */
+        public static final String DUPLEX_MODE_SEND_ONLY = "send-only";
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @StringDef(prefix = "DUPLEX_MODE_", value = {
+            DUPLEX_MODE_FULL,
+            DUPLEX_MODE_HALF,
+            DUPLEX_MODE_RECEIVE_ONLY,
+            DUPLEX_MODE_SEND_ONLY
+        })
+        public @interface DuplexMode {}
+
+        /**
+         * Builder to help construct {@link ServiceCapabilities} instances.
+         */
+        public static final class Builder {
+
+            private ServiceCapabilities mCapabilities;
+
+            /**
+             * Create the ServiceCapabilities builder, which can be used to set service capabilities
+             * as well as custom capability extensions.
+             * @param isAudioCapable Whether the audio is capable or not.
+             * @param isVideoCapable Whether the video is capable or not.
+             */
+            public Builder(boolean isAudioCapable, boolean isVideoCapable) {
+                mCapabilities = new ServiceCapabilities(isAudioCapable, isVideoCapable);
+            }
+
+            /**
+             * Add the supported duplex mode.
+             * @param mode The supported duplex mode
+             */
+            public @NonNull Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) {
+                mCapabilities.mSupportedDuplexModeList.add(mode);
+                return this;
+            }
+
+            /**
+             * Add the unsupported duplex mode.
+             * @param mode The unsupported duplex mode
+             */
+            public @NonNull Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) {
+                mCapabilities.mUnsupportedDuplexModeList.add(mode);
+                return this;
+            }
+
+            /**
+             * @return the ServiceCapabilities instance.
+             */
+            public @NonNull ServiceCapabilities build() {
+                return mCapabilities;
+            }
+        }
+
+        private final boolean mIsAudioCapable;
+        private final boolean mIsVideoCapable;
+        private final @DuplexMode List<String> mSupportedDuplexModeList = new ArrayList<>();
+        private final @DuplexMode List<String> mUnsupportedDuplexModeList = new ArrayList<>();
+
+        /**
+         * Use {@link Builder} to build an instance of this interface.
+         * @param isAudioCapable Whether the audio is capable.
+         * @param isVideoCapable Whether the video is capable.
+         */
+        ServiceCapabilities(boolean isAudioCapable, boolean isVideoCapable) {
+            mIsAudioCapable = isAudioCapable;
+            mIsVideoCapable = isVideoCapable;
+        }
+
+        private ServiceCapabilities(Parcel in) {
+            mIsAudioCapable = in.readBoolean();
+            mIsVideoCapable = in.readBoolean();
+            in.readStringList(mSupportedDuplexModeList);
+            in.readStringList(mUnsupportedDuplexModeList);
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel out, int flags) {
+            out.writeBoolean(mIsAudioCapable);
+            out.writeBoolean(mIsVideoCapable);
+            out.writeStringList(mSupportedDuplexModeList);
+            out.writeStringList(mUnsupportedDuplexModeList);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final @NonNull Creator<ServiceCapabilities> CREATOR =
+                new Creator<ServiceCapabilities>() {
+                    @Override
+                    public ServiceCapabilities createFromParcel(Parcel in) {
+                        return new ServiceCapabilities(in);
+                    }
+
+                    @Override
+                    public ServiceCapabilities[] newArray(int size) {
+                        return new ServiceCapabilities[size];
+                    }
+                };
+
+        /**
+         * Query the audio capable.
+         * @return true if the audio is capable, false otherwise.
+         */
+        public boolean isAudioCapable() {
+            return mIsAudioCapable;
+        }
+
+        /**
+         * Query the video capable.
+         * @return true if the video is capable, false otherwise.
+         */
+        public boolean isVideoCapable() {
+            return mIsVideoCapable;
+        }
+
+        /**
+         * Get the supported duplex mode list.
+         * @return The list of supported duplex mode
+         */
+        public @NonNull @DuplexMode List<String> getSupportedDuplexModes() {
+            return Collections.unmodifiableList(mSupportedDuplexModeList);
+        }
+
+        /**
+         * Get the unsupported duplex mode list.
+         * @return The list of unsupported duplex mode
+         */
+        public @NonNull @DuplexMode List<String> getUnsupportedDuplexModes() {
+            return Collections.unmodifiableList(mUnsupportedDuplexModeList);
+        }
+
+        @Override
+        public String toString() {
+            return "servCaps{" + "a=" + mIsAudioCapable + ", v=" + mIsVideoCapable
+                    + ", supported=" + mSupportedDuplexModeList + ", unsupported="
+                    + mUnsupportedDuplexModeList + '}';
+        }
+    }
+
+    /**
+     * Builder to help construct {@link RcsContactPresenceTuple} instances.
+     */
+    public static final class Builder {
+
+        private final RcsContactPresenceTuple mPresenceTuple;
+
+        /**
+         * Builds a RcsContactPresenceTuple instance.
+         * @param status The status associated with the service capability. See RFC3865 for more
+         * information.
+         * @param serviceId The OMA Presence service-id associated with this capability. See the
+         * OMA Presence SIMPLE specification v1.1, section 10.5.1.
+         * @param serviceVersion The OMA Presence version associated with the service capability.
+         * See the OMA Presence SIMPLE specification v1.1, section 10.5.1.
+         */
+        public Builder(@NonNull @BasicStatus String status, @NonNull @ServiceId String serviceId,
+                @NonNull String serviceVersion) {
+            mPresenceTuple = new RcsContactPresenceTuple(status, serviceId, serviceVersion);
+        }
+
+        /**
+         * The optional SIP Contact URI associated with the PIDF tuple element if the network
+         * expects the user to use the URI instead of the contact URI to contact it.
+         */
+        public @NonNull Builder setContactUri(@NonNull Uri contactUri) {
+            mPresenceTuple.mContactUri = contactUri;
+            return this;
+        }
+
+        /**
+         * The optional timestamp indicating the data and time of the status change of this tuple.
+         * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format
+         * string per RFC3339.
+         */
+        public @NonNull Builder setTime(@NonNull Instant timestamp) {
+            mPresenceTuple.mTimestamp = timestamp;
+            return this;
+        }
+
+        /**
+         * An optional parameter containing the description element of the service-description. See
+         * OMA Presence SIMPLE specification v1.1
+         */
+        public @NonNull Builder setServiceDescription(@NonNull String description) {
+            mPresenceTuple.mServiceDescription = description;
+            return this;
+        }
+
+        /**
+         * An optional parameter containing the service capabilities of the presence tuple if they
+         * are present in the servcaps element.
+         */
+        public @NonNull Builder setServiceCapabilities(@NonNull ServiceCapabilities caps) {
+            mPresenceTuple.mServiceCapabilities = caps;
+            return this;
+        }
+
+        /**
+         * @return the constructed instance.
+         */
+        public @NonNull RcsContactPresenceTuple build() {
+            return mPresenceTuple;
+        }
+    }
+
+    private Uri mContactUri;
+    private Instant mTimestamp;
+    private @BasicStatus String mStatus;
+
+    // The service information in the service-description element.
+    private String mServiceId;
+    private String mServiceVersion;
+    private String mServiceDescription;
+
+    private ServiceCapabilities mServiceCapabilities;
+
+    private RcsContactPresenceTuple(@NonNull @BasicStatus String status, @NonNull String serviceId,
+            @NonNull String serviceVersion) {
+        mStatus = status;
+        mServiceId = serviceId;
+        mServiceVersion = serviceVersion;
+    }
+
+    private RcsContactPresenceTuple(Parcel in) {
+        mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
+        mTimestamp = convertStringFormatTimeToInstant(in.readString());
+        mStatus = in.readString();
+        mServiceId = in.readString();
+        mServiceVersion = in.readString();
+        mServiceDescription = in.readString();
+        mServiceCapabilities = in.readParcelable(ServiceCapabilities.class.getClassLoader(), android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.class);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeParcelable(mContactUri, flags);
+        out.writeString(convertInstantToStringFormat(mTimestamp));
+        out.writeString(mStatus);
+        out.writeString(mServiceId);
+        out.writeString(mServiceVersion);
+        out.writeString(mServiceDescription);
+        out.writeParcelable(mServiceCapabilities, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Creator<RcsContactPresenceTuple> CREATOR =
+            new Creator<RcsContactPresenceTuple>() {
+                @Override
+                public RcsContactPresenceTuple createFromParcel(Parcel in) {
+                    return new RcsContactPresenceTuple(in);
+                }
+
+                @Override
+                public RcsContactPresenceTuple[] newArray(int size) {
+                    return new RcsContactPresenceTuple[size];
+                }
+            };
+
+    // Convert the Instant to the string format
+    private String convertInstantToStringFormat(Instant instant) {
+        if (instant == null) {
+            return "";
+        }
+        return instant.toString();
+    }
+
+    // Convert the time string format to Instant
+    private @Nullable Instant convertStringFormatTimeToInstant(String timestamp) {
+        if (TextUtils.isEmpty(timestamp)) {
+            return null;
+        }
+        try {
+            return DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp, Instant::from);
+        } catch (DateTimeParseException e) {
+            return null;
+        }
+    }
+
+    /** @return the status of the tuple element. */
+    public @NonNull @BasicStatus String getStatus() {
+        return mStatus;
+    }
+
+    /** @return the service-id element of the service-description */
+    public @NonNull String getServiceId() {
+        return mServiceId;
+    }
+
+    /** @return the version element of the service-description */
+    public @NonNull String getServiceVersion() {
+        return mServiceVersion;
+    }
+
+    /** @return the SIP URI contained in the contact element of the tuple if it exists. */
+    public @Nullable Uri getContactUri() {
+        return mContactUri;
+    }
+
+    /** @return the timestamp element contained in the tuple if it exists */
+    public @Nullable Instant getTime() {
+        return mTimestamp;
+    }
+
+    /** @return the description element contained in the service-description if it exists */
+    public @Nullable String getServiceDescription() {
+        return mServiceDescription;
+    }
+
+    /** @return the {@link ServiceCapabilities} of the tuple if it exists. */
+    public @Nullable ServiceCapabilities getServiceCapabilities() {
+        return mServiceCapabilities;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("{");
+        if (Build.IS_ENG) {
+            builder.append("u=");
+            builder.append(mContactUri);
+        } else {
+            builder.append("u=");
+            builder.append(mContactUri != null ? "XXX" : "null");
+        }
+        builder.append(", id=");
+        builder.append(mServiceId);
+        builder.append(", v=");
+        builder.append(mServiceVersion);
+        builder.append(", s=");
+        builder.append(mStatus);
+        if (mTimestamp != null) {
+            builder.append(", timestamp=");
+            builder.append(mTimestamp);
+        }
+        if (mServiceDescription != null) {
+            builder.append(", servDesc=");
+            builder.append(mServiceDescription);
+        }
+        if (mServiceCapabilities != null) {
+            builder.append(", servCaps=");
+            builder.append(mServiceCapabilities);
+        }
+        builder.append("}");
+        return builder.toString();
+    }
+}
diff --git a/android-35/android/telephony/ims/RcsContactTerminatedReason.java b/android-35/android/telephony/ims/RcsContactTerminatedReason.java
new file mode 100644
index 0000000..ea022de
--- /dev/null
+++ b/android-35/android/telephony/ims/RcsContactTerminatedReason.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * When the resource for the presence subscribe event has been terminated, the method
+ * SubscribeResponseCallback#onResourceTerminated wil be called with a list of
+ * RcsContactTerminatedReason.
+ * @hide
+ */
+public final class RcsContactTerminatedReason implements Parcelable {
+    private final Uri mContactUri;
+    private final String mReason;
+
+    public RcsContactTerminatedReason(Uri contact, String reason) {
+        mContactUri = contact;
+        mReason = reason;
+    }
+
+    private RcsContactTerminatedReason(Parcel in) {
+        mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
+        mReason = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeParcelable(mContactUri, flags);
+        out.writeString(mReason);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Creator<RcsContactTerminatedReason> CREATOR =
+            new Creator<RcsContactTerminatedReason>() {
+                @Override
+                public RcsContactTerminatedReason createFromParcel(Parcel in) {
+                    return new RcsContactTerminatedReason(in);
+                }
+
+                @Override
+                public RcsContactTerminatedReason[] newArray(int size) {
+                    return new RcsContactTerminatedReason[size];
+                }
+            };
+
+    public Uri getContactUri() {
+        return mContactUri;
+    }
+
+    public String getReason() {
+        return mReason;
+    }
+}
diff --git a/android-35/android/telephony/ims/RcsContactUceCapability.java b/android-35/android/telephony/ims/RcsContactUceCapability.java
new file mode 100644
index 0000000..51a3d72
--- /dev/null
+++ b/android-35/android/telephony/ims/RcsContactUceCapability.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Contains the User Capability Exchange capabilities corresponding to a contact's URI.
+ * @hide
+ */
+@SystemApi
+public final class RcsContactUceCapability implements Parcelable {
+
+    /** Contains presence information associated with the contact */
+    public static final int CAPABILITY_MECHANISM_PRESENCE = 1;
+
+    /** Contains OPTIONS information associated with the contact */
+    public static final int CAPABILITY_MECHANISM_OPTIONS = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CAPABILITY_MECHANISM_", value = {
+        CAPABILITY_MECHANISM_PRESENCE,
+        CAPABILITY_MECHANISM_OPTIONS
+    })
+    public @interface CapabilityMechanism {}
+
+    /**
+     * The capabilities of this contact were requested recently enough to still be considered in
+     * the availability window.
+     */
+    public static final int SOURCE_TYPE_NETWORK = 0;
+
+    /**
+     * The capabilities of this contact were retrieved from the cached information in the Enhanced
+     * Address Book.
+     */
+    public static final int SOURCE_TYPE_CACHED = 1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "SOURCE_TYPE_", value = {
+        SOURCE_TYPE_NETWORK,
+        SOURCE_TYPE_CACHED
+    })
+    public @interface SourceType {}
+
+    /**
+     * Capability information for the requested contact has expired and can not be refreshed due to
+     * a temporary network error. This is a temporary error and the capabilities of the contact
+     * should be queried again at a later time.
+     */
+    public static final int REQUEST_RESULT_UNKNOWN = 0;
+
+    /**
+     * The requested contact was found to be offline when queried. This is only applicable to
+     * contact capabilities that were queried via OPTIONS requests and the network returned a
+     * 408/480 response.
+     */
+    public static final int REQUEST_RESULT_NOT_ONLINE = 1;
+
+    /**
+     * Capability information for the requested contact was not found. The contact should not be
+     * considered an RCS user.
+     */
+    public static final int REQUEST_RESULT_NOT_FOUND = 2;
+
+    /**
+     * Capability information for the requested contact was found successfully.
+     */
+    public static final int REQUEST_RESULT_FOUND = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "REQUEST_RESULT_", value = {
+        REQUEST_RESULT_UNKNOWN,
+        REQUEST_RESULT_NOT_ONLINE,
+        REQUEST_RESULT_NOT_FOUND,
+        REQUEST_RESULT_FOUND
+    })
+    public @interface RequestResult {}
+
+    /**
+     * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
+     * queried through SIP OPTIONS.
+     */
+    public static final class OptionsBuilder {
+
+        private final RcsContactUceCapability mCapabilities;
+
+        /**
+         * Create the Builder, which can be used to set UCE capabilities as well as custom
+         * capability extensions.
+         * @param contact The contact URI that the capabilities are attached to.
+         */
+        public OptionsBuilder(@NonNull Uri contact) {
+            mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_OPTIONS,
+                    SOURCE_TYPE_NETWORK);
+        }
+
+        /**
+         * Create the Builder, which can be used to set UCE capabilities as well as custom
+         * capability extensions.
+         * @param contact The contact URI that the capabilities are attached to.
+         * @param sourceType The type where the capabilities of this contact were retrieved from.
+         * @hide
+         */
+        public OptionsBuilder(@NonNull Uri contact, @SourceType int sourceType) {
+            mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_OPTIONS,
+                    sourceType);
+        }
+
+        /**
+         * Set the result of the capabilities request.
+         * @param requestResult the request result
+         * @return this OptionBuilder
+         */
+        public @NonNull OptionsBuilder setRequestResult(@RequestResult int requestResult) {
+            mCapabilities.mRequestResult = requestResult;
+            return this;
+        }
+
+        /**
+         * Add the feature tag into the capabilities instance.
+         * @param tag the supported feature tag
+         * @return this OptionBuilder
+         */
+        public @NonNull OptionsBuilder addFeatureTag(@NonNull String tag) {
+            mCapabilities.mFeatureTags.add(tag);
+            return this;
+        }
+
+        /**
+         * Add the list of feature tag into the capabilities instance.
+         * @param tags the list of the supported feature tags
+         * @return this OptionBuilder
+         */
+        public @NonNull OptionsBuilder addFeatureTags(@NonNull Set<String> tags) {
+            mCapabilities.mFeatureTags.addAll(tags);
+            return this;
+        }
+
+        /**
+         * @return the constructed instance.
+         */
+        public @NonNull RcsContactUceCapability build() {
+            return mCapabilities;
+        }
+    }
+
+    /**
+     * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
+     * queried through a presence server.
+     */
+    public static final class PresenceBuilder {
+
+        private final RcsContactUceCapability mCapabilities;
+
+        /**
+         * Create the builder, which can be used to set UCE capabilities as well as custom
+         * capability extensions.
+         * @param contact The contact URI that the capabilities are attached to.
+         * @param sourceType The type where the capabilities of this contact were retrieved from.
+         * @param requestResult the request result
+         */
+        public PresenceBuilder(@NonNull Uri contact, @SourceType int sourceType,
+                @RequestResult int requestResult) {
+            mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_PRESENCE,
+                sourceType);
+            mCapabilities.mRequestResult = requestResult;
+        }
+
+        /**
+         * Add the {@link RcsContactPresenceTuple} into the capabilities instance.
+         * @param tuple The {@link RcsContactPresenceTuple} to be added into.
+         * @return this PresenceBuilder
+         */
+        public @NonNull PresenceBuilder addCapabilityTuple(@NonNull RcsContactPresenceTuple tuple) {
+            mCapabilities.mPresenceTuples.add(tuple);
+            return this;
+        }
+
+        /**
+         * Add the list of {@link RcsContactPresenceTuple} into the capabilities instance.
+         * @param tuples The list of the {@link RcsContactPresenceTuple} to be added into.
+         * @return this PresenceBuilder
+         */
+        public @NonNull PresenceBuilder addCapabilityTuples(
+                @NonNull List<RcsContactPresenceTuple> tuples) {
+            mCapabilities.mPresenceTuples.addAll(tuples);
+            return this;
+        }
+
+        /**
+         * Set the entity URI related to the contact whose capabilities were requested.
+         * @param entityUri the 'pres' URL of the PRESENTITY publishing presence document.
+         */
+        public @NonNull PresenceBuilder setEntityUri(@NonNull Uri entityUri) {
+            mCapabilities.mEntityUri = entityUri;
+            return this;
+        }
+
+        /**
+         * @return the RcsContactUceCapability instance.
+         */
+        public @NonNull RcsContactUceCapability build() {
+            return mCapabilities;
+        }
+    }
+
+    private final Uri mContactUri;
+    private @SourceType int mSourceType;
+    private @CapabilityMechanism int mCapabilityMechanism;
+    private @RequestResult int mRequestResult;
+    private Uri mEntityUri;
+
+    private final Set<String> mFeatureTags = new HashSet<>();
+    private final List<RcsContactPresenceTuple> mPresenceTuples = new ArrayList<>();
+
+    private RcsContactUceCapability(@NonNull Uri contactUri, @CapabilityMechanism int mechanism,
+            @SourceType int sourceType) {
+        mContactUri = contactUri;
+        mCapabilityMechanism = mechanism;
+        mSourceType = sourceType;
+    }
+
+    private RcsContactUceCapability(Parcel in) {
+        mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
+        mCapabilityMechanism = in.readInt();
+        mSourceType = in.readInt();
+        mRequestResult = in.readInt();
+        mEntityUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
+        List<String> featureTagList = new ArrayList<>();
+        in.readStringList(featureTagList);
+        mFeatureTags.addAll(featureTagList);
+        in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader(), android.telephony.ims.RcsContactPresenceTuple.class);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeParcelable(mContactUri, flags);
+        out.writeInt(mCapabilityMechanism);
+        out.writeInt(mSourceType);
+        out.writeInt(mRequestResult);
+        out.writeParcelable(mEntityUri, flags);
+        out.writeStringList(new ArrayList<>(mFeatureTags));
+        out.writeParcelableList(mPresenceTuples, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Creator<RcsContactUceCapability> CREATOR =
+            new Creator<RcsContactUceCapability>() {
+                @Override
+                public RcsContactUceCapability createFromParcel(Parcel in) {
+                    return new RcsContactUceCapability(in);
+                }
+
+                @Override
+                public RcsContactUceCapability[] newArray(int size) {
+                    return new RcsContactUceCapability[size];
+                }
+            };
+
+    /**
+     * @return The mechanism used to get the capabilities.
+     */
+    public @CapabilityMechanism int getCapabilityMechanism() {
+        return mCapabilityMechanism;
+    }
+
+    /**
+     * @return The feature tags present in the OPTIONS response from the network.
+     * <p>
+     * Note: this is only populated if {@link #getCapabilityMechanism} is
+     * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS}
+     */
+    public @NonNull Set<String> getFeatureTags() {
+        if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
+            return Collections.emptySet();
+        }
+        return Collections.unmodifiableSet(mFeatureTags);
+    }
+
+    /**
+     * @return The tuple elements associated with the presence element portion of the PIDF document
+     * contained in the NOTIFY response from the network.
+     * <p>
+     * Note: this is only populated if {@link #getCapabilityMechanism} is
+     * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
+     */
+    public @NonNull List<RcsContactPresenceTuple> getCapabilityTuples() {
+        if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
+            return Collections.emptyList();
+        }
+        return Collections.unmodifiableList(mPresenceTuples);
+    }
+
+    /**
+     * Get the RcsContactPresenceTuple associated with the given service id.
+     * @param serviceId The service id to get the presence tuple.
+     * @return The RcsContactPresenceTuple which has the given service id or {@code null} if the
+     * service id does not exist in the list of presence tuples returned from the network.
+     *
+     * <p>
+     * Note: this is only populated if {@link #getCapabilityMechanism} is
+     * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
+     */
+    public @Nullable RcsContactPresenceTuple getCapabilityTuple(@NonNull String serviceId) {
+        if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
+            return null;
+        }
+        for (RcsContactPresenceTuple tuple : mPresenceTuples) {
+            if (tuple.getServiceId() != null && tuple.getServiceId().equals(serviceId)) {
+                return tuple;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return the source of the data that was used to populate the capabilities of the requested
+     * contact.
+     */
+    public @SourceType int getSourceType() {
+        return mSourceType;
+    }
+
+    /**
+     * @return the result of querying the capabilities of the requested contact.
+     */
+    public @RequestResult int getRequestResult() {
+        return mRequestResult;
+    }
+
+    /**
+     * Retrieve the contact URI requested by the applications.
+     * @return the URI representing the contact associated with the capabilities.
+     */
+    public @NonNull Uri getContactUri() {
+        return mContactUri;
+    }
+
+    /**
+     * Retrieve the entity URI of the contact whose presence information is being requested for.
+     * @return the URI representing the 'pres' URL of the PRESENTITY publishing presence document
+     * or {@code null} if the entity uri does not exist in the presence document.
+     */
+    public @Nullable Uri getEntityUri() {
+        return mEntityUri;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("RcsContactUceCapability");
+        if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) {
+            builder.append("(presence) {");
+        } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) {
+            builder.append("(options) {");
+        } else {
+            builder.append("(?) {");
+        }
+        if (Build.IS_ENG) {
+            builder.append("uri=");
+            builder.append(mContactUri);
+        } else {
+            builder.append("uri (isNull)=");
+            builder.append(mContactUri != null ? "XXX" : "null");
+        }
+        builder.append(", sourceType=");
+        builder.append(mSourceType);
+        builder.append(", requestResult=");
+        builder.append(mRequestResult);
+        if (Build.IS_ENG) {
+            builder.append("entity uri=");
+            builder.append(mEntityUri != null ? mEntityUri : "null");
+        } else {
+            builder.append("entity uri (isNull)=");
+            builder.append(mEntityUri != null ? "XXX" : "null");
+        }
+
+        if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) {
+            builder.append(", presenceTuples={");
+            builder.append(mPresenceTuples);
+            builder.append("}");
+        } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) {
+            builder.append(", featureTags={");
+            builder.append(mFeatureTags);
+            builder.append("}");
+        }
+
+        return builder.toString();
+    }
+}
diff --git a/android-35/android/telephony/ims/RcsUceAdapter.java b/android-35/android/telephony/ims/RcsUceAdapter.java
new file mode 100644
index 0000000..8925a9e
--- /dev/null
+++ b/android-35/android/telephony/ims/RcsUceAdapter.java
@@ -0,0 +1,1032 @@
+/*
+ * Copyright (C) 2018 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.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.ims.aidl.IImsRcsController;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages RCS User Capability Exchange for the subscription specified.
+ *
+ * @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class.
+ */
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
+public class RcsUceAdapter {
+    private static final String TAG = "RcsUceAdapter";
+
+    /**
+     * This carrier supports User Capability Exchange as, defined by the framework using
+     * SIP OPTIONS. If set, the RcsFeature should support capability exchange. If not set, this
+     * RcsFeature should not publish capabilities or service capability requests.
+     * @deprecated Use {@link ImsRcsManager#CAPABILITY_TYPE_OPTIONS_UCE} instead.
+     * @hide
+     */
+    @Deprecated
+    public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
+
+    /**
+     * This carrier supports User Capability Exchange as, defined by the framework using a
+     * presence server. If set, the RcsFeature should support capability exchange. If not set, this
+     * RcsFeature should not publish capabilities or service capability requests.
+     * @deprecated Use {@link ImsRcsManager#CAPABILITY_TYPE_PRESENCE_UCE} instead.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
+
+    /**
+     * @deprecated Use {@link ImsRcsManager.RcsImsCapabilityFlag} instead.
+     * @hide
+     */
+    @Deprecated
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CAPABILITY_TYPE_", value = {
+            CAPABILITY_TYPE_OPTIONS_UCE,
+            CAPABILITY_TYPE_PRESENCE_UCE
+    })
+    public @interface RcsImsCapabilityFlag {}
+
+    /**
+     * An unknown error has caused the request to fail.
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_GENERIC_FAILURE = 1;
+
+    /**
+     * The carrier network does not have UCE support enabled for this subscriber.
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_NOT_ENABLED = 2;
+
+    /**
+     * The data network that the device is connected to does not support UCE currently (e.g. it is
+     * 1x only currently).
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_NOT_AVAILABLE = 3;
+
+    /**
+     * The network has responded with SIP 403 error and a reason "User not registered."
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_NOT_REGISTERED = 4;
+
+    /**
+     * The network has responded to this request with a SIP 403 error and reason "not authorized for
+     * presence" for this subscriber.
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_NOT_AUTHORIZED = 5;
+
+    /**
+     * The network has responded to this request with a SIP 403 error and no reason.
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_FORBIDDEN = 6;
+
+    /**
+     * The contact URI requested is not provisioned for voice or it is not known as an IMS
+     * subscriber to the carrier network.
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_NOT_FOUND = 7;
+
+    /**
+     * The capabilities request contained too many URIs for the carrier network to handle. Retry
+     * with a lower number of contact numbers. The number varies per carrier.
+     * @hide
+     */
+    @SystemApi
+    // TODO: Try to integrate this into the API so that the service will split based on carrier.
+    public static final int ERROR_REQUEST_TOO_LARGE = 8;
+
+    /**
+     * The network did not respond to the capabilities request before the request timed out.
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_REQUEST_TIMEOUT = 9;
+
+    /**
+     * The request failed due to the service having insufficient memory.
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_INSUFFICIENT_MEMORY = 10;
+
+    /**
+     * The network was lost while trying to complete the request.
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_LOST_NETWORK = 11;
+
+    /**
+     * The network is temporarily unavailable or busy. Retries should only be done after the retry
+     * time returned in {@link CapabilitiesCallback#onError} has elapsed.
+     * @hide
+     */
+    @SystemApi
+    public static final int ERROR_SERVER_UNAVAILABLE = 12;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "ERROR_", value = {
+            ERROR_GENERIC_FAILURE,
+            ERROR_NOT_ENABLED,
+            ERROR_NOT_AVAILABLE,
+            ERROR_NOT_REGISTERED,
+            ERROR_NOT_AUTHORIZED,
+            ERROR_FORBIDDEN,
+            ERROR_NOT_FOUND,
+            ERROR_REQUEST_TOO_LARGE,
+            ERROR_REQUEST_TIMEOUT,
+            ERROR_INSUFFICIENT_MEMORY,
+            ERROR_LOST_NETWORK,
+            ERROR_SERVER_UNAVAILABLE
+    })
+    public @interface ErrorCode {}
+
+    /**
+     * A capability update has been requested but the reason is unknown.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0;
+
+    /**
+     * A capability update has been requested due to the Entity Tag (ETag) expiring.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1;
+
+    /**
+     * A capability update has been requested due to moving to LTE with VoPS disabled.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 2;
+
+    /**
+     * A capability update has been requested due to moving to LTE with VoPS enabled.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 3;
+
+    /**
+     * A capability update has been requested due to moving to eHRPD.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4;
+
+    /**
+     * A capability update has been requested due to moving to HSPA+.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5;
+
+    /**
+     * A capability update has been requested due to moving to 3G.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6;
+
+    /**
+     * A capability update has been requested due to moving to 2G.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7;
+
+    /**
+     * A capability update has been requested due to moving to WLAN
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8;
+
+    /**
+     * A capability update has been requested due to moving to IWLAN
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9;
+
+    /**
+     * A capability update has been requested due to moving to 5G NR with VoPS disabled.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10;
+
+    /**
+     * A capability update has been requested due to moving to 5G NR with VoPS enabled.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11;
+
+    /**
+     * A capability update has been requested due to IMS being registered over INTERNET PDN.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_INTERNET_PDN = 12;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "ERROR_", value = {
+            CAPABILITY_UPDATE_TRIGGER_UNKNOWN,
+            CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED,
+            CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED,
+            CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED,
+            CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD,
+            CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS,
+            CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G,
+            CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G,
+            CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN,
+            CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN,
+            CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED,
+            CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED,
+            CAPABILITY_UPDATE_TRIGGER_MOVE_TO_INTERNET_PDN
+    })
+    public @interface StackPublishTriggerType {}
+
+    /**
+     * The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for
+     * UCE.
+     * @hide
+     */
+    @SystemApi
+    public static final int PUBLISH_STATE_OK = 1;
+
+    /**
+     * The hasn't published its capabilities since boot or hasn't gotten any publish response yet.
+     * @hide
+     */
+    @SystemApi
+    public static final int PUBLISH_STATE_NOT_PUBLISHED = 2;
+
+    /**
+     * The device has tried to publish its capabilities, which has resulted in an error. This error
+     * is related to the fact that the device is not provisioned for voice.
+     * @hide
+     */
+    @SystemApi
+    public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3;
+
+    /**
+     * The device has tried to publish its capabilities, which has resulted in an error. This error
+     * is related to the fact that the device is not RCS or UCE provisioned.
+     * @hide
+     */
+    @SystemApi
+    public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4;
+
+    /**
+     * The last publish resulted in a "408 Request Timeout" response.
+     * @hide
+     */
+    @SystemApi
+    public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5;
+
+    /**
+     * The last publish resulted in another unknown error, such as SIP 503 - "Service Unavailable"
+     * or SIP 423 - "Interval too short".
+     * <p>
+     * Device shall retry with exponential back-off.
+     * @hide
+     */
+    @SystemApi
+    public static final int PUBLISH_STATE_OTHER_ERROR = 6;
+
+    /**
+     * The device is currently trying to publish its capabilities to the network.
+     * @hide
+     */
+    @SystemApi
+    public static final int PUBLISH_STATE_PUBLISHING = 7;
+
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "PUBLISH_STATE_", value = {
+            PUBLISH_STATE_OK,
+            PUBLISH_STATE_NOT_PUBLISHED,
+            PUBLISH_STATE_VOICE_PROVISION_ERROR,
+            PUBLISH_STATE_RCS_PROVISION_ERROR,
+            PUBLISH_STATE_REQUEST_TIMEOUT,
+            PUBLISH_STATE_OTHER_ERROR,
+            PUBLISH_STATE_PUBLISHING
+    })
+    public @interface PublishState {}
+
+    /**
+     * An application can use {@link #addOnPublishStateChangedListener} to register a
+     * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to
+     * the network changes.
+     * @hide
+     */
+    @SystemApi
+    public interface OnPublishStateChangedListener {
+        /**
+         * Notifies the callback when the publish state has changed.
+         * @param publishState The latest update to the publish state.
+         *
+         * @deprecated Replaced by {@link #onPublishStateChange}, deprecated for
+         * sip information.
+         */
+        @Deprecated
+        void onPublishStateChange(@PublishState int publishState);
+
+        /**
+         * Notifies the callback when the publish state has changed or the publish operation is
+         * done.
+         * @param attributes The latest information related to the publish.
+         */
+        default void onPublishStateChange(@NonNull PublishAttributes attributes) {
+            onPublishStateChange(attributes.getPublishState());
+        };
+    }
+
+    /**
+     * An application can use {@link #addOnPublishStateChangedListener} to register a
+     * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to
+     * the network changes.
+     * @hide
+     */
+    public static class PublishStateCallbackAdapter {
+
+        private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub {
+            private final OnPublishStateChangedListener mPublishStateChangeListener;
+            private final Executor mExecutor;
+
+            PublishStateBinder(Executor executor, OnPublishStateChangedListener listener) {
+                mExecutor = executor;
+                mPublishStateChangeListener = listener;
+            }
+
+            @Override
+            public void onPublishUpdated(@NonNull PublishAttributes attributes) {
+                if (mPublishStateChangeListener == null) return;
+
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() ->
+                            mPublishStateChangeListener.onPublishStateChange(attributes));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+        }
+
+        private final PublishStateBinder mBinder;
+
+        public PublishStateCallbackAdapter(@NonNull Executor executor,
+                @NonNull OnPublishStateChangedListener listener) {
+            mBinder = new PublishStateBinder(executor, listener);
+        }
+
+        /**@hide*/
+        public final IRcsUcePublishStateCallback getBinder() {
+            return mBinder;
+        }
+    }
+
+    /**
+     * A callback for the response to a UCE request. The method
+     * {@link CapabilitiesCallback#onCapabilitiesReceived} will be called zero or more times as the
+     * capabilities are fetched from multiple sources, both cached on the device and on the network.
+     * <p>
+     * This request will take a varying amount of time depending on if the contacts requested are
+     * cached or if it requires a network query. The timeout time of these requests can vary
+     * depending on the network, however in poor cases it could take up to a minute for a request
+     * to timeout. In that time, only a subset of capabilities may have been retrieved.
+     * <p>
+     * After {@link CapabilitiesCallback#onComplete} or {@link CapabilitiesCallback#onError} has
+     * been called, the reference to this callback will be discarded on the service side.
+     * @see #requestCapabilities(Collection, Executor, CapabilitiesCallback)
+     * @hide
+     */
+    @SystemApi
+    public interface CapabilitiesCallback {
+
+        /**
+         * The pending capability request has completed successfully for one or more of the
+         * requested contacts.
+         * This may be called one or more times before the request is fully completed, as
+         * capabilities may need to be fetched from multiple sources both on device and on the
+         * network. Once the capabilities of all the requested contacts have been received,
+         * {@link #onComplete()} will be called. If there was an error during the capability
+         * exchange process, {@link #onError(int, long)} will be called instead.
+         * @param contactCapabilities List of capabilities associated with each contact requested.
+         */
+        void onCapabilitiesReceived(@NonNull List<RcsContactUceCapability> contactCapabilities);
+
+        /**
+         * Called when the pending request has completed successfully due to all requested contacts
+         * information being delivered. The callback {@link #onCapabilitiesReceived(List)} will be
+         * called one or more times and will contain the contacts in the request that the device has
+         * received capabilities for.
+         *
+         * @see #onComplete(SipDetails) onComplete(SipDetails) provides more information related to
+         * the underlying SIP transaction used to perform the capabilities exchange. Either this
+         * method or the alternate method should be implemented to determine when the request has
+         * completed successfully.
+         */
+        default void onComplete() {}
+
+        /**
+         * The pending request has resulted in an error and may need to be retried, depending on the
+         * error code.
+         * @param errorCode The reason for the framework being unable to process the request.
+         * @param retryIntervalMillis The time in milliseconds the requesting application should
+         * wait before retrying, if non-zero.
+         *
+         * @see #onError(int, long, SipDetails) onError(int, long, SipDetails) provides more
+         * information related to the underlying SIP transaction that resulted in an error. Either
+         * this method or the alternative method should be implemented to determine when the
+         * request has completed with an error.
+         */
+        default void onError(@ErrorCode int errorCode, long retryIntervalMillis) {}
+
+        /**
+         * Called when the pending request has completed successfully due to all requested contacts
+         * information being delivered. The callback {@link #onCapabilitiesReceived(List)} will be
+         * called one or more times and will contain the contacts in the request that the device has
+         * received capabilities for.
+         *
+         * This method contains more information about the underlying SIP transaction if it exists.
+         * If this information is not needed, {@link #onComplete()} can be implemented
+         * instead.
+         *
+         * @param details The SIP information related to this request if the device supports
+         *                supplying this information. This parameter will be {@code null} if this
+         *                information is not available.
+         */
+        default void onComplete(@Nullable SipDetails details) {
+            onComplete();
+        };
+
+        /**
+         * The pending request has resulted in an error and may need to be retried, depending on the
+         * error code.
+         *
+         * This method contains more information about the underlying SIP transaction if it exists.
+         * If this information is not needed, {@link #onError(int, long)} can be implemented
+         * instead.
+         * @param errorCode The reason for the framework being unable to process the request.
+         * @param retryIntervalMillis The time in milliseconds the requesting application should
+         * wait before retrying, if non-zero.
+         * @param details The SIP information related to this request if the device supports
+         *                supplying this information. This parameter will be {@code null} if this
+         *                information is not available.
+         */
+        default void onError(@ErrorCode int errorCode, long retryIntervalMillis,
+                @Nullable SipDetails details) {
+            onError(errorCode, retryIntervalMillis);
+        };
+    }
+
+    private final Context mContext;
+    private final int mSubId;
+    private final Map<OnPublishStateChangedListener, PublishStateCallbackAdapter>
+            mPublishStateCallbacks;
+
+    /**
+     * Not to be instantiated directly, use {@link ImsRcsManager#getUceAdapter()} to instantiate
+     * this manager class.
+     * @hide
+     */
+    RcsUceAdapter(Context context, int subId) {
+        mContext = context;
+        mSubId = subId;
+        mPublishStateCallbacks = new HashMap<>();
+    }
+
+    /**
+     * Request the RCS capabilities for one or more contacts using RCS User Capability Exchange.
+     * <p>
+     * This API will first check a local cache for the requested numbers and return the cached
+     * RCS capabilities of each number if the cache exists and is not stale. If the cache for a
+     * number is stale or there is no cached information about the requested number, the device will
+     * then perform a query to the carrier's network to request the RCS capabilities of the
+     * requested numbers.
+     * <p>
+     * Depending on the number of requests being sent, this API may throttled internally as the
+     * operations are queued to be executed by the carrier's network.
+     * <p>
+     * Be sure to check the availability of this feature using
+     * {@link ImsRcsManager#isAvailable(int, int)} and ensuring
+     * {@link
+     * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
+     * {@link
+     * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is
+     * enabled or else this operation will fail with {@link #ERROR_NOT_AVAILABLE} or
+     * {@link #ERROR_NOT_ENABLED}.
+     *
+     * @param contactNumbers A list of numbers that the capabilities are being requested for.
+     * @param executor The executor that will be used when the request is completed and the
+     *         {@link CapabilitiesCallback} is called.
+     * @param c A one-time callback for when the request for capabilities completes or there is an
+     *         error processing the request.
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
+            Manifest.permission.READ_CONTACTS})
+    public void requestCapabilities(@NonNull Collection<Uri> contactNumbers,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull CapabilitiesCallback c) throws ImsException {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        if (contactNumbers == null) {
+            throw new IllegalArgumentException("Must include non-null contact number list.");
+        }
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "requestCapabilities: IImsRcsController is null");
+            throw new ImsException("Can not find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+            @Override
+            public void onComplete(@Nullable SipDetails details) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> c.onComplete(details));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+            @Override
+            public void onError(int errorCode, long retryAfterMilliseconds,
+                    @Nullable SipDetails details) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds, details));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+        };
+
+        try {
+            imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(),
+                    mContext.getAttributionTag(), new ArrayList(contactNumbers), internalCallback);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.toString(), e.errorCode);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e);
+            throw new ImsException("Remote IMS Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Request the RCS capabilities for a phone number using User Capability Exchange.
+     * <p>
+     * Unlike {@link #requestCapabilities(Collection, Executor, CapabilitiesCallback)}, which caches
+     * the result received from the network for a certain amount of time and uses that cached result
+     * for subsequent requests for RCS capabilities of the same phone number, this API will always
+     * request the RCS capabilities of a contact from the carrier's network.
+     * <p>
+     * Depending on the number of requests, this API may throttled internally as the operations are
+     * queued to be executed by the carrier's network.
+     * <p>
+     * Be sure to check the availability of this feature using
+     * {@link ImsRcsManager#isAvailable(int, int)} and ensuring
+     * {@link
+     * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
+     * {@link
+     * android.telephony.ims.feature.RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is
+     * enabled or else this operation will fail with
+     * {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
+     *
+     * @param contactNumber The contact of the capabilities is being requested for.
+     * @param executor The executor that will be used when the request is completed and the
+     * {@link CapabilitiesCallback} is called.
+     * @param c A one-time callback for when the request for capabilities completes or there is
+     * an error processing the request.
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE,
+            Manifest.permission.READ_CONTACTS})
+    public void requestAvailability(@NonNull Uri contactNumber,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull CapabilitiesCallback c) throws ImsException {
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        if (contactNumber == null) {
+            throw new IllegalArgumentException("Must include non-null contact number.");
+        }
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback.");
+        }
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "requestAvailability: IImsRcsController is null");
+            throw new ImsException("Cannot find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+            @Override
+            public void onComplete(@Nullable SipDetails details) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> c.onComplete(details));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+            @Override
+            public void onError(int errorCode, long retryAfterMilliseconds,
+                    @Nullable SipDetails details) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds, details));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+        };
+
+        try {
+            imsRcsController.requestAvailability(mSubId, mContext.getOpPackageName(),
+                    mContext.getAttributionTag(), contactNumber, internalCallback);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.toString(), e.errorCode);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling IImsRcsController#requestAvailability", e);
+            throw new ImsException("Remote IMS Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Gets the last publish result from the UCE service if the device is using an RCS presence
+     * server.
+     * @return The last publish result from the UCE service. If the device is using SIP OPTIONS,
+     * this method will return {@link #PUBLISH_STATE_OK} as well.
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @PublishState int getUcePublishState() throws ImsException {
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "getUcePublishState: IImsRcsController is null");
+            throw new ImsException("Can not find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            return imsRcsController.getUcePublishState(mSubId);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling IImsRcsController#getUcePublishState", e);
+            throw new ImsException("Remote IMS Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Registers a {@link OnPublishStateChangedListener} with the system, which will provide publish
+     * state updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}.
+     * <p>
+     * Use {@link android.telephony.SubscriptionManager.OnSubscriptionsChangedListener} to listen
+     * to subscription
+     * changed events and call
+     * {@link #removeOnPublishStateChangedListener(OnPublishStateChangedListener)} to clean up.
+     * <p>
+     * The registered {@link OnPublishStateChangedListener} will also receive a callback when it is
+     * registered with the current publish state.
+     *
+     * @param executor The executor the listener callback events should be run on.
+     * @param listener The {@link OnPublishStateChangedListener} to be added.
+     * @throws ImsException if the subscription associated with this callback is valid, but
+     * the {@link ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+     * reason.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void addOnPublishStateChangedListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OnPublishStateChangedListener listener) throws ImsException {
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        if (listener == null) {
+            throw new IllegalArgumentException(
+                    "Must include a non-null OnPublishStateChangedListener.");
+        }
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "addOnPublishStateChangedListener : IImsRcsController is null");
+            throw new ImsException("Cannot find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        PublishStateCallbackAdapter stateCallback = addPublishStateCallback(executor, listener);
+        try {
+            imsRcsController.registerUcePublishStateCallback(mSubId, stateCallback.getBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e);
+            throw new ImsException("Remote IMS Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Removes an existing {@link OnPublishStateChangedListener}.
+     * <p>
+     * When the subscription associated with this callback is removed
+     * (SIM removed, ESIM swap,etc...), this callback will automatically be removed. If this method
+     * is called for an inactive subscription, it will result in a no-op.
+     *
+     * @param listener The callback to be unregistered.
+     * @throws ImsException if the subscription associated with this callback is valid, but
+     * the {@link ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+     * reason.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void removeOnPublishStateChangedListener(
+            @NonNull OnPublishStateChangedListener listener) throws ImsException {
+        if (listener == null) {
+            throw new IllegalArgumentException(
+                    "Must include a non-null OnPublishStateChangedListener.");
+        }
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "removeOnPublishStateChangedListener: IImsRcsController is null");
+            throw new ImsException("Cannot find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        PublishStateCallbackAdapter callback = removePublishStateCallback(listener);
+        if (callback == null) {
+            return;
+        }
+
+        try {
+            imsRcsController.unregisterUcePublishStateCallback(mSubId, callback.getBinder());
+        } catch (android.os.ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling IImsRcsController#unregisterUcePublishStateCallback", e);
+            throw new ImsException("Remote IMS Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * The setting for whether or not the user has opted in to the automatic refresh of the RCS
+     * capabilities associated with the contacts in the user's contact address book. By default,
+     * this setting is disabled and must be enabled after the user has seen the opt-in dialog shown
+     * by {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}.
+     * <p>
+     * If this feature is enabled, the device will periodically share the phone numbers of all of
+     * the contacts in the user's address book with the carrier to refresh the RCS capabilities
+     * cache associated with those contacts as the local cache becomes stale.
+     * <p>
+     * This setting will only enable this feature if
+     * {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is
+     * also enabled.
+     * <p>
+     * Note: This setting does not affect whether or not the device publishes its service
+     * capabilities if the subscription supports presence publication.
+     *
+     * @return true if the user has opted in for automatic refresh of the RCS capabilities of their
+     * contacts, false otherwise.
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public boolean isUceSettingEnabled() throws ImsException {
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "isUceSettingEnabled: IImsRcsController is null");
+            throw new ImsException("Can not find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+        try {
+            // Telephony.SimInfo#IMS_RCS_UCE_ENABLED can also be used to listen to changes to this.
+            return imsRcsController.isUceSettingEnabled(mSubId, mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling IImsRcsController#isUceSettingEnabled", e);
+            throw new ImsException("Remote IMS Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Change the user’s setting for whether or not the user has opted in to the automatic
+     * refresh of the RCS capabilities associated with the contacts in the user's contact address
+     * book. By default, this setting is disabled and must be enabled using this method after the
+     * user has seen the opt-in dialog shown by
+     * {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}.
+     * <p>
+     * If an application wishes to request that the user enable this feature, they must launch an
+     * Activity using the Intent {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN},
+     * which will ask the user if they wish to enable this feature. This setting must only be
+     * enabled after the user has opted-in to this feature.
+     * <p>
+     * This must not affect the
+     * {@link #requestCapabilities(Collection, Executor, CapabilitiesCallback)} or
+     * {@link #requestAvailability(Uri, Executor, CapabilitiesCallback)} API,
+     * as those APIs are still required for per-contact RCS capability queries of phone numbers
+     * required for operations such as placing a Video Telephony call or starting an RCS chat
+     * session.
+     * <p>
+     * This setting will only enable this feature if
+     * {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is
+     * also enabled.
+     * <p>
+     * Note: This setting does not affect whether or not the device publishes its service
+     * capabilities if the subscription supports presence publication.
+     *
+     * @param isEnabled true if the user has opted in for automatic refresh of the RCS capabilities
+     *                  of their contacts, or false if they have chosen to opt-out. By default this
+     *                  setting is disabled.
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setUceSettingEnabled(boolean isEnabled) throws ImsException {
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "setUceSettingEnabled: IImsRcsController is null");
+            throw new ImsException("Can not find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            imsRcsController.setUceSettingEnabled(mSubId, isEnabled);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling IImsRcsController#setUceSettingEnabled", e);
+            throw new ImsException("Remote IMS Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Add the {@link OnPublishStateChangedListener} to collection for tracking.
+     * @param executor The executor that will be used when the publish state is changed and the
+     * {@link OnPublishStateChangedListener} is called.
+     * @param listener The {@link OnPublishStateChangedListener} to call the publish state changed.
+     * @return The {@link PublishStateCallbackAdapter} to wrapper the
+     * {@link OnPublishStateChangedListener}
+     */
+    private PublishStateCallbackAdapter addPublishStateCallback(@NonNull Executor executor,
+            @NonNull OnPublishStateChangedListener listener) {
+        PublishStateCallbackAdapter adapter = new PublishStateCallbackAdapter(executor, listener);
+        synchronized (mPublishStateCallbacks) {
+            mPublishStateCallbacks.put(listener, adapter);
+        }
+        return adapter;
+    }
+
+    /**
+     * Remove the existing {@link OnPublishStateChangedListener}.
+     * @param listener The {@link OnPublishStateChangedListener} to remove from the collection.
+     * @return The wrapper class {@link PublishStateCallbackAdapter} associated with the
+     * {@link OnPublishStateChangedListener}.
+     */
+    private PublishStateCallbackAdapter removePublishStateCallback(
+            @NonNull OnPublishStateChangedListener listener) {
+        synchronized (mPublishStateCallbacks) {
+            return mPublishStateCallbacks.remove(listener);
+        }
+    }
+
+    private IImsRcsController getIImsRcsController() {
+        IBinder binder = TelephonyFrameworkInitializer
+                .getTelephonyServiceManager()
+                .getTelephonyImsServiceRegisterer()
+                .get();
+        return IImsRcsController.Stub.asInterface(binder);
+    }
+}
diff --git a/android-35/android/telephony/ims/RegistrationManager.java b/android-35/android/telephony/ims/RegistrationManager.java
new file mode 100644
index 0000000..9f83da9
--- /dev/null
+++ b/android-35/android/telephony/ims/RegistrationManager.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2019 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.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Manages IMS Service registration state for associated {@code ImsFeature}s.
+ */
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
+public interface RegistrationManager {
+
+    /**
+     * @hide
+     */
+    // Defines the underlying radio technology type that we have registered for IMS over.
+    @IntDef(prefix = "REGISTRATION_STATE_",
+            value = {
+                    REGISTRATION_STATE_NOT_REGISTERED,
+                    REGISTRATION_STATE_REGISTERING,
+                    REGISTRATION_STATE_REGISTERED
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsRegistrationState {}
+
+    /**
+     * The IMS service is currently not registered to the carrier network.
+     */
+    int REGISTRATION_STATE_NOT_REGISTERED = 0;
+
+    /**
+     * The IMS service is currently in the process of registering to the carrier network.
+     */
+    int REGISTRATION_STATE_REGISTERING = 1;
+
+    /**
+     * The IMS service is currently registered to the carrier network.
+     */
+    int REGISTRATION_STATE_REGISTERED = 2;
+
+    /** @hide */
+    @IntDef(prefix = {"SUGGESTED_ACTION_"},
+            value = {
+                SUGGESTED_ACTION_NONE,
+                SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK,
+                SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT,
+                SUGGESTED_ACTION_TRIGGER_RAT_BLOCK,
+                SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SuggestedAction {}
+
+    /**
+     * Default value. No action is suggested when IMS registration fails.
+     * @hide
+     */
+    @SystemApi
+    public static final int SUGGESTED_ACTION_NONE = 0;
+
+    /**
+     * Indicates that the IMS registration is failed with fatal error such as 403 or 404
+     * on all P-CSCF addresses. The radio shall block the current PLMN or disable
+     * the RAT as per the carrier requirements.
+     * @hide
+     */
+    @SystemApi
+    public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK = 1;
+
+    /**
+     * Indicates that the IMS registration on current PLMN failed multiple times.
+     * The radio shall block the current PLMN or disable the RAT during EPS or 5GS mobility
+     * management timer value as per the carrier requirements.
+     * @hide
+     */
+    @SystemApi
+    public static final int SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT = 2;
+
+    /**
+     * Indicates that the IMS registration on current RAT failed multiple times.
+     * The radio shall block the {@link ImsRegistrationImplBase.ImsRegistrationTech}
+     * included with this and search for other available RATs in the background.
+     * If no other RAT is available that meets the carrier requirements, the
+     * radio may remain on the blocked RAT for internet service. The radio clears all
+     * RATs marked as unavailable if the IMS service is registered to the carrier network.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ADD_RAT_RELATED_SUGGESTED_ACTION_TO_IMS_REGISTRATION)
+    int SUGGESTED_ACTION_TRIGGER_RAT_BLOCK = 3;
+
+    /**
+     * Indicates that the radio clears all RATs marked as unavailable and tries to find
+     * an available RAT that meets the carrier requirements.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ADD_RAT_RELATED_SUGGESTED_ACTION_TO_IMS_REGISTRATION)
+    int SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS = 4;
+
+    /**@hide*/
+    // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
+    // and WWAN are more accurate constants.
+    Map<Integer, Integer> IMS_REG_TO_ACCESS_TYPE_MAP = Map.of(
+            // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE
+            // case, since it is defined.
+            ImsRegistrationImplBase.REGISTRATION_TECH_NONE,
+                    AccessNetworkConstants.TRANSPORT_TYPE_INVALID,
+            ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                    AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+            ImsRegistrationImplBase.REGISTRATION_TECH_NR,
+                    AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+            ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+                    AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
+            /* As the cross sim will be using ePDG tunnel over internet, it behaves
+               like IWLAN in most cases. Hence setting the access type as IWLAN
+             */
+            ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM,
+                    AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+
+    /** @hide */
+    @NonNull
+    static String registrationStateToString(
+            final @NetworkRegistrationInfo.RegistrationState int value) {
+        switch (value) {
+            case REGISTRATION_STATE_NOT_REGISTERED:
+                return "REGISTRATION_STATE_NOT_REGISTERED";
+            case REGISTRATION_STATE_REGISTERING:
+                return "REGISTRATION_STATE_REGISTERING";
+            case REGISTRATION_STATE_REGISTERED:
+                return "REGISTRATION_STATE_REGISTERED";
+            default:
+                return Integer.toString(value);
+        }
+    }
+
+    /**
+     * @param regtech The registration technology.
+     * @return The Access Network type from registration technology.
+     * @hide
+     */
+    static int getAccessType(int regtech) {
+        if (!RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regtech)) {
+            Log.w("RegistrationManager", "getAccessType - invalid regType returned: "
+                    + regtech);
+            return AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+        }
+        return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regtech);
+    }
+
+    /**
+     * Callback class for receiving IMS network Registration callback events.
+     * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
+     * @see #unregisterImsRegistrationCallback(RegistrationCallback)
+     */
+    class RegistrationCallback {
+
+        private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
+
+            private final RegistrationCallback mLocalCallback;
+            private Executor mExecutor;
+            private Bundle mBundle = new Bundle();
+
+            RegistrationBinder(RegistrationCallback localCallback) {
+                mLocalCallback = localCallback;
+            }
+
+            @Override
+            public void onRegistered(ImsRegistrationAttributes attr) {
+                if (mLocalCallback == null) return;
+
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onRegistered(attr));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            @Override
+            public void onRegistering(ImsRegistrationAttributes attr) {
+                if (mLocalCallback == null) return;
+
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onRegistering(attr));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            @Override
+            public void onDeregistered(ImsReasonInfo info,
+                    @SuggestedAction int suggestedAction,
+                    @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+                if (mLocalCallback == null) return;
+
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onUnregistered(info,
+                            suggestedAction, imsRadioTech));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            @Override
+            public void onDeregisteredWithDetails(ImsReasonInfo info,
+                    @SuggestedAction int suggestedAction,
+                    @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech,
+                    @NonNull SipDetails details) {
+                if (mLocalCallback == null) return;
+
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onUnregistered(info, suggestedAction,
+                            imsRadioTech));
+                    mExecutor.execute(() -> mLocalCallback.onUnregistered(info, details));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            @Override
+            public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
+                if (mLocalCallback == null) return;
+
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed(
+                            getAccessType(imsRadioTech), info));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+                if (mLocalCallback == null) return;
+
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onSubscriberAssociatedUriChanged(uris));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final RegistrationBinder mBinder = new RegistrationBinder(this);
+
+        /**
+         * Notifies the framework when the IMS Provider is registered to the IMS network.
+         *
+         * @param imsTransportType the radio access technology.
+         * @deprecated Use {@link #onRegistered(ImsRegistrationAttributes)} instead.
+         */
+        @Deprecated
+        public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is registered to the IMS network
+         * with corresponding attributes.
+         *
+         * @param attributes The attributes associated with this IMS registration.
+         */
+        public void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
+            // Default impl to keep backwards compatibility with old implementations
+            onRegistered(attributes.getTransportType());
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is trying to register the IMS network.
+         *
+         * @param imsTransportType the radio access technology.
+         * @deprecated Use {@link #onRegistering(ImsRegistrationAttributes)} instead.
+         */
+        public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is trying to register the IMS network.
+         *
+         * @param attributes The attributes associated with this IMS registration.
+         */
+        public void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
+            // Default impl to keep backwards compatibility with old implementations
+            onRegistering(attributes.getTransportType());
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is unregistered from the IMS network.
+         *
+         * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+         */
+        public void onUnregistered(@NonNull ImsReasonInfo info) {
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is unregistered from the IMS network.
+         *
+         * Since this callback is only required for the communication between telephony framework
+         * and ImsService, it is made hidden.
+         *
+         * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+         * @param suggestedAction the expected behavior of radio protocol stack.
+         * @param imsRadioTech the network type on which IMS registration has failed.
+         * @hide
+         */
+        public void onUnregistered(@NonNull ImsReasonInfo info,
+                @SuggestedAction int suggestedAction,
+                @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+            // Default impl to keep backwards compatibility with old implementations
+            onUnregistered(info);
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is unregistered from the IMS network.
+         *
+         * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+         * @param details the {@link SipDetails} related to disconnected Ims registration.
+         *
+         * @hide
+         */
+        @SystemApi
+        public void onUnregistered(@NonNull ImsReasonInfo info,
+                @NonNull SipDetails details) {
+        }
+
+        /**
+         * A failure has occurred when trying to handover registration to another technology type.
+         *
+         * @param imsTransportType The transport type that has failed to handover registration to.
+         * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
+         */
+        public void onTechnologyChangeFailed(
+                @AccessNetworkConstants.TransportType int imsTransportType,
+                @NonNull ImsReasonInfo info) {
+        }
+
+        /**
+         * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
+         * it changes. Per RFC3455, an associated URI is a URI that the service provider has
+         * allocated to a user for their own usage. A user's phone number is typically one of the
+         * associated URIs.
+         * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
+         *         subscription.
+         * @hide
+         */
+        public void onSubscriberAssociatedUriChanged(@Nullable Uri[] uris) {
+        }
+
+        /**@hide*/
+        public final IImsRegistrationCallback getBinder() {
+            return mBinder;
+        }
+
+        /**@hide*/
+        //Only exposed as public for compatibility with deprecated ImsManager APIs.
+        public void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+    }
+
+    /**
+     * Registers a {@link RegistrationCallback} with the system. Use
+     * @param executor The {@link Executor} that will be used to call the IMS registration state
+     *                 callback.
+     * @param c A callback called on the supplied {@link Executor} that will contain the
+     *                      registration state of the IMS service, which will be one of the
+     * {@see  SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
+     * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
+     *
+     * When the callback is registered, it will initiate the callback c to be called with the
+     * current registration state.
+     *
+     * @param executor The executor the callback events should be run on.
+     * @param c The {@link RegistrationCallback} to be added.
+     * @see #unregisterImsRegistrationCallback(RegistrationCallback)
+     * @throws ImsException if the subscription associated with this callback is valid, but
+     * the {@code ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+     * reason.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull RegistrationCallback c) throws ImsException;
+
+    /**
+     * Removes an existing {@link RegistrationCallback}.
+     *
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be removed. If this method is called for an
+     * inactive subscription, it will result in a no-op.
+     *
+     * @param c The {@link RegistrationCallback} to be removed.
+     * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener
+     * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c);
+
+    /**
+     * Gets the registration state of the IMS service.
+     * @param executor The {@link Executor} that will be used to call the IMS registration state
+     *                 callback.
+     * @param stateCallback A callback called on the supplied {@link Executor} that will contain the
+ *                      registration state of the IMS service, which will be one of the
+ *                      following: {@link #REGISTRATION_STATE_NOT_REGISTERED},
+ *                      {@link #REGISTRATION_STATE_REGISTERING}, or
+ *                      {@link #REGISTRATION_STATE_REGISTERED}.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
+            @NonNull @ImsRegistrationState Consumer<Integer> stateCallback);
+
+    /**
+     * Gets the Transport Type associated with the current IMS registration.
+     * @param executor The {@link Executor} that will be used to call the transportTypeCallback.
+     * @param transportTypeCallback The transport type associated with the current IMS registration,
+     * which will be one of following:
+     * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN},
+     * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or
+     * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    void getRegistrationTransportType(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback);
+}
diff --git a/android-35/android/telephony/ims/RtpHeaderExtension.java b/android-35/android/telephony/ims/RtpHeaderExtension.java
new file mode 100644
index 0000000..8815cef
--- /dev/null
+++ b/android-35/android/telephony/ims/RtpHeaderExtension.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * A representation of an RTP header extension.
+ * <p>
+ * Per RFC8285, an RTP header extension consists of both a local identifier in the range 1-14, an
+ * 8-bit length indicator and a number of extension data bytes equivalent to the stated length.
+ * @hide
+ */
+@SystemApi
+public final class RtpHeaderExtension implements Parcelable {
+    private int mLocalIdentifier;
+    private byte[] mExtensionData;
+
+    /**
+     * Creates a new {@link RtpHeaderExtension}.
+     * @param localIdentifier The local identifier for this RTP header extension.
+     * @param extensionData The data for this RTP header extension.
+     * @throws IllegalArgumentException if {@code extensionId} is not in the range 1-14.
+     * @throws NullPointerException if {@code extensionData} is null.
+     */
+    public RtpHeaderExtension(@IntRange(from = 1, to = 14) int localIdentifier,
+            @NonNull byte[] extensionData) {
+        if (localIdentifier < 1 || localIdentifier > 13) {
+            throw new IllegalArgumentException("localIdentifier must be in range 1-14");
+        }
+        if (extensionData == null) {
+            throw new NullPointerException("extensionDa is required.");
+        }
+        mLocalIdentifier = localIdentifier;
+        mExtensionData = extensionData;
+    }
+
+    /**
+     * Creates a new instance of {@link RtpHeaderExtension} from a parcel.
+     * @param in The parceled data to read.
+     */
+    private RtpHeaderExtension(@NonNull Parcel in) {
+        mLocalIdentifier = in.readInt();
+        mExtensionData = in.createByteArray();
+    }
+
+    /**
+     * The local identifier for the RTP header extension.
+     * <p>
+     * Per RFC8285, the extension ID is a value in the range 1-14 (0 is reserved for padding and
+     * 15 is reserved for the one-byte header form.
+     * <p>
+     * Within the current call, this extension ID will match one of the
+     * {@link RtpHeaderExtensionType#getLocalIdentifier()}s.
+     *
+     * @return The local identifier for this RTP header extension.
+     */
+    @IntRange(from = 1, to = 14)
+    public int getLocalIdentifier() {
+        return mLocalIdentifier;
+    }
+
+    /**
+     * The data payload for the RTP header extension.
+     * <p>
+     * Per RFC8285 Sec 4.3, an RTP header extension includes an 8-bit length field which indicate
+     * how many bytes of data are present in the RTP header extension.  The extension includes this
+     * many bytes of actual data.
+     * <p>
+     * We represent this as a byte array who's length is equivalent to the 8-bit length field.
+     * @return RTP header extension data payload.  The payload may be up to 255 bytes in length.
+     */
+    public @NonNull byte[] getExtensionData() {
+        return mExtensionData;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mLocalIdentifier);
+        dest.writeByteArray(mExtensionData);
+    }
+
+    public static final @NonNull Creator<RtpHeaderExtension> CREATOR =
+            new Creator<RtpHeaderExtension>() {
+                @Override
+                public RtpHeaderExtension createFromParcel(@NonNull Parcel in) {
+                    return new RtpHeaderExtension(in);
+                }
+
+                @Override
+                public RtpHeaderExtension[] newArray(int size) {
+                    return new RtpHeaderExtension[size];
+                }
+            };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        RtpHeaderExtension that = (RtpHeaderExtension) o;
+        return mLocalIdentifier == that.mLocalIdentifier
+                && Arrays.equals(mExtensionData, that.mExtensionData);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mLocalIdentifier);
+        result = 31 * result + Arrays.hashCode(mExtensionData);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("RtpHeaderExtension{mLocalIdentifier=");
+        sb.append(mLocalIdentifier);
+        sb.append(", mData=");
+        for (byte b : mExtensionData) {
+            sb.append(Integer.toBinaryString(b));
+            sb.append("b_");
+        }
+        sb.append("}");
+
+        return sb.toString();
+    }
+}
diff --git a/android-35/android/telephony/ims/RtpHeaderExtensionType.java b/android-35/android/telephony/ims/RtpHeaderExtensionType.java
new file mode 100644
index 0000000..b9ffd24
--- /dev/null
+++ b/android-35/android/telephony/ims/RtpHeaderExtensionType.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Defines a mapping between a local identifier and a {@link Uri} which identifies an RTP header
+ * extension.
+ * <p>
+ * According to RFC8285, SDP (Session Description Protocol) signalling for a call provides a means
+ * for the supported RTP header extensions for a call to be negotiated at call initiation time.
+ * The types of RTP header extensions potentially usable in a session are identified by a local
+ * identifier ({@link #getLocalIdentifier()}) when RTP header extensions are present on RTP packets.
+ * A {@link Uri} ({@link #getUri()}) provides a unique identifier for the RTP header extension
+ * format which parties in a call can use to identify supported RTP header extensions.
+ * @hide
+ */
+@SystemApi
+public final class RtpHeaderExtensionType implements Parcelable {
+    private int mLocalIdentifier;
+    private Uri mUri;
+
+    /**
+     * Create a new RTP header extension type.
+     * @param localIdentifier the local identifier.
+     * @param uri the {@link Uri} identifying the RTP header extension type.
+     * @throws IllegalArgumentException if {@code localIdentifier} is out of the expected range.
+     * @throws NullPointerException if {@code uri} is null.
+     */
+    public RtpHeaderExtensionType(@IntRange(from = 1, to = 14) int localIdentifier,
+            @NonNull Uri uri) {
+        if (localIdentifier < 1 || localIdentifier > 13) {
+            throw new IllegalArgumentException("localIdentifier must be in range 1-14");
+        }
+        if (uri == null) {
+            throw new NullPointerException("uri is required.");
+        }
+        mLocalIdentifier = localIdentifier;
+        mUri = uri;
+    }
+
+    private RtpHeaderExtensionType(Parcel in) {
+        mLocalIdentifier = in.readInt();
+        mUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
+    }
+
+    public static final @NonNull Creator<RtpHeaderExtensionType> CREATOR =
+            new Creator<RtpHeaderExtensionType>() {
+                @Override
+                public RtpHeaderExtensionType createFromParcel(@NonNull Parcel in) {
+                    return new RtpHeaderExtensionType(in);
+                }
+
+                @Override
+                public @NonNull RtpHeaderExtensionType[] newArray(int size) {
+                    return new RtpHeaderExtensionType[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mLocalIdentifier);
+        dest.writeParcelable(mUri, flags);
+    }
+
+    /**
+     * The local identifier for this RTP header extension type.
+     * <p>
+     * {@link RtpHeaderExtension}s which indicate a {@link RtpHeaderExtension#getLocalIdentifier()}
+     * matching this local identifier will have the format specified by {@link #getUri()}.
+     * <p>
+     * Per RFC8285, the extension ID is a value in the range 1-14 (0 is reserved for padding and
+     * 15 is reserved for the one-byte header form.
+     *
+     * @return The local identifier associated with this {@link #getUri()}.
+     */
+    public @IntRange(from = 1, to = 14) int getLocalIdentifier() {
+        return mLocalIdentifier;
+    }
+
+    /**
+     * A {@link Uri} which identifies the format of the RTP extension header.
+     * <p>
+     * According to RFC8285 section 5, URIs MUST be absolute and SHOULD contain a month/date pair
+     * in the form mmyyyy to indicate versioning of the extension.  Extension headers defined in an
+     * RFC are typically defined using URNs starting with {@code urn:ietf:params:rtp-hdrext:}.
+     * For example, RFC6464 defines {@code urn:ietf:params:rtp-hdrext:ssrc-audio-level} which is an
+     * RTP header extension for communicating client to mixer audio level indications.
+     *
+     * @return A unique {@link Uri} identifying the format of the RTP extension header.
+     */
+    public @NonNull Uri getUri() {
+        return mUri;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        RtpHeaderExtensionType that = (RtpHeaderExtensionType) o;
+        return mLocalIdentifier == that.mLocalIdentifier
+                && mUri.equals(that.mUri);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLocalIdentifier, mUri);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("RtpHeaderExtensionType{mLocalIdentifier=");
+        sb.append(mLocalIdentifier);
+        sb.append(", mUri=");
+        sb.append(mUri);
+        sb.append("}");
+
+        return sb.toString();
+    }
+}
diff --git a/android-35/android/telephony/ims/SipDelegateConfiguration.java b/android-35/android/telephony/ims/SipDelegateConfiguration.java
new file mode 100644
index 0000000..db0ae03
--- /dev/null
+++ b/android-35/android/telephony/ims/SipDelegateConfiguration.java
@@ -0,0 +1,932 @@
+/*
+ * Copyright (C) 2021 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.ims;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.InetAddresses;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.Objects;
+
+/**
+ * The IMS registration and other attributes that the {@link SipDelegateConnection} used by the
+ * IMS application will need to be aware of to correctly generate outgoing {@link SipMessage}s.
+ * <p>
+ * The IMS service must generate new instances of this configuration as the IMS configuration
+ * managed by the IMS service changes. Along with each {@link SipDelegateConfiguration} instance
+ * containing the configuration is the "version", which should be incremented every time a new
+ * {@link SipDelegateConfiguration} instance is created. The {@link SipDelegateConnection} will
+ * include the version of the {@link SipDelegateConfiguration} instance that it used in order for
+ * the {@link SipDelegate} to easily identify if the IMS application used a now stale configuration
+ * to generate the {@link SipMessage} and return
+ * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION} in
+ * {@link DelegateMessageCallback#onMessageSendFailure(String, int)} so that the IMS application can
+ * regenerate that {@link SipMessage} using the correct {@link SipDelegateConfiguration}
+ * instance.
+ * <p>
+ * Every time the IMS configuration state changes in the IMS service, a full configuration should
+ * be generated. The new  {@link SipDelegateConfiguration} instance should not be an incremental
+ * update.
+ * @see Builder
+ * @hide
+ */
+@SystemApi
+public final class SipDelegateConfiguration implements Parcelable {
+
+    /**
+     * The SIP transport uses UDP.
+     */
+    public static final int SIP_TRANSPORT_UDP = 0;
+
+    /**
+     * The SIP transport uses TCP.
+     */
+    public static final int SIP_TRANSPORT_TCP = 1;
+
+    /**@hide*/
+    @IntDef(prefix = "SIP_TRANSPORT_", value = {
+            SIP_TRANSPORT_UDP,
+            SIP_TRANSPORT_TCP
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TransportType {}
+
+    /**
+     * The value returned by {@link #getMaxUdpPayloadSizeBytes()} when it is not defined.
+     */
+    public static final int UDP_PAYLOAD_SIZE_UNDEFINED = -1;
+
+    /**
+     * SIP over IPSec configuration
+     */
+    public static final class IpSecConfiguration {
+        private final int mLocalTxPort;
+        private final int mLocalRxPort;
+        private final int mLastLocalTxPort;
+        private final int mRemoteTxPort;
+        private final int mRemoteRxPort;
+        private final int mLastRemoteTxPort;
+        private final String mSecurityHeader;
+
+        /**
+         * Describes the SIP over IPSec configuration the SipDelegate will need to use.
+         *
+         * @param localTxPort  Local SIP port number used to send traffic.
+         * @param localRxPort Local SIP port number used to receive traffic.
+         * @param lastLocalTxPort Local SIP port number used for the previous IPsec security
+         *                        association.
+         * @param remoteTxPort Remote port number used by the SIP server to send SIP traffic.
+         * @param remoteRxPort Remote port number used by the SIP server to receive incoming SIP
+         *                     traffic.
+         * @param lastRemoteTxPort Remote port number used by the SIP server to send SIP traffic on
+         *                         the previous IPSec security association.
+         * @param securityHeader The value of the SIP security verify header.
+         */
+        public IpSecConfiguration(int localTxPort, int localRxPort, int lastLocalTxPort,
+                int remoteTxPort, int remoteRxPort, int lastRemoteTxPort,
+                @NonNull String securityHeader) {
+            mLocalTxPort = localTxPort;
+            mLocalRxPort = localRxPort;
+            mLastLocalTxPort = lastLocalTxPort;
+            mRemoteTxPort = remoteTxPort;
+            mRemoteRxPort = remoteRxPort;
+            mLastRemoteTxPort = lastRemoteTxPort;
+            mSecurityHeader = securityHeader;
+        }
+
+        /**
+         * @return The local SIP port number used to send traffic.
+         */
+        public int getLocalTxPort() {
+            return mLocalTxPort;
+        }
+
+        /**
+         * @return The Local SIP port number used to receive traffic.
+         */
+        public int getLocalRxPort() {
+            return mLocalRxPort;
+        }
+
+        /**
+         * @return The last local SIP port number used for the previous IPsec security association.
+         */
+        public int getLastLocalTxPort() {
+            return mLastLocalTxPort;
+        }
+
+        /**
+         * @return The remote port number used by the SIP server to send SIP traffic.
+         */
+        public int getRemoteTxPort() {
+            return mRemoteTxPort;
+        }
+
+        /**
+         * @return the remote port number used by the SIP server to receive incoming SIP traffic.
+         */
+        public int getRemoteRxPort() {
+            return mRemoteRxPort;
+        }
+
+        /**
+         * @return the remote port number used by the SIP server to send SIP traffic on the previous
+         * IPSec security association.
+         */
+        public int getLastRemoteTxPort() {
+            return mLastRemoteTxPort;
+        }
+
+        /**
+         * @return The value of the SIP security verify header.
+         */
+        public @NonNull String getSipSecurityVerifyHeader() {
+            return mSecurityHeader;
+        }
+
+        /**
+         * Helper for parcelling this object.
+         * @hide
+         */
+        public void addToParcel(Parcel dest) {
+            dest.writeInt(mLocalTxPort);
+            dest.writeInt(mLocalRxPort);
+            dest.writeInt(mLastLocalTxPort);
+            dest.writeInt(mRemoteTxPort);
+            dest.writeInt(mRemoteRxPort);
+            dest.writeInt(mLastRemoteTxPort);
+            dest.writeString(mSecurityHeader);
+        }
+
+        /**
+         * Helper for unparcelling this object.
+         * @hide
+         */
+        public static IpSecConfiguration fromParcel(Parcel source) {
+            return new IpSecConfiguration(source.readInt(), source.readInt(), source.readInt(),
+                    source.readInt(), source.readInt(), source.readInt(), source.readString());
+        }
+
+        @Override
+        public String toString() {
+            return "IpSecConfiguration{" + "localTx=" + mLocalTxPort + ", localRx=" + mLocalRxPort
+                    + ", lastLocalTx=" + mLastLocalTxPort + ", remoteTx=" + mRemoteTxPort
+                    + ", remoteRx=" + mRemoteRxPort + ", lastRemoteTx=" + mLastRemoteTxPort
+                    + ", securityHeader=" + mSecurityHeader + '}';
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            IpSecConfiguration that = (IpSecConfiguration) o;
+            return mLocalTxPort == that.mLocalTxPort
+                    && mLocalRxPort == that.mLocalRxPort
+                    && mLastLocalTxPort == that.mLastLocalTxPort
+                    && mRemoteTxPort == that.mRemoteTxPort
+                    && mRemoteRxPort == that.mRemoteRxPort
+                    && mLastRemoteTxPort == that.mLastRemoteTxPort
+                    && Objects.equals(mSecurityHeader, that.mSecurityHeader);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mLocalTxPort, mLocalRxPort, mLastLocalTxPort, mRemoteTxPort,
+                    mRemoteRxPort, mLastRemoteTxPort, mSecurityHeader);
+        }
+    }
+
+    /**
+     *  Creates a new instance of {@link SipDelegateConfiguration} composed from optional
+     *  configuration items.
+     */
+    public static final class Builder {
+        private final SipDelegateConfiguration mConfig;
+
+        /**
+         *
+         * @param version The version associated with the {@link SipDelegateConfiguration} instance
+         *                being built. See {@link #getVersion()} for more information.
+         * @param transportType The transport type to use for SIP signalling.
+         * @param localAddr The local socket address used for SIP traffic.
+         * @param serverAddr The SIP server or P-CSCF default IP address for sip traffic.
+         * @see InetAddresses#parseNumericAddress(String) for how to create an
+         * {@link InetAddress} without requiring a DNS lookup.
+         */
+        public Builder(@IntRange(from = 0) long version, @TransportType int transportType,
+                @NonNull InetSocketAddress localAddr, @NonNull InetSocketAddress serverAddr) {
+            mConfig = new SipDelegateConfiguration(version, transportType, localAddr,
+                    serverAddr);
+        }
+
+        /**
+         * Create a new {@link SipDelegateConfiguration} instance with the same exact configuration
+         * as the passed in instance, except for the version parameter, which will be incremented
+         * by 1.
+         * <p>
+         * This method is useful for cases where only a small subset of configurations have changed
+         * and the new configuration is based off of the old configuration.
+         * @param c The older {@link SipDelegateConfiguration} instance to base this instance's
+         *          configuration off of.
+         */
+        public Builder(@NonNull SipDelegateConfiguration c) {
+            mConfig = c.copyAndIncrementVersion();
+        }
+
+        /**
+         * Sets whether or not SIP compact form is enabled for the associated SIP delegate.
+         * <p>
+         * If unset, this configuration defaults to {@code false}.
+         * @param isEnabled {@code true} if SIP compact form is enabled for the associated SIP
+         *     Delegate, {@code false} if it is not.
+         * @return this Builder instance with the compact form configuration set.
+         */
+        public @NonNull Builder setSipCompactFormEnabled(boolean isEnabled) {
+            mConfig.mIsSipCompactFormEnabled = isEnabled;
+            return this;
+        }
+
+        /**
+         * Sets whether or not underlying SIP keepalives are enabled for the associated SIP
+         * delegate.
+         * <p>
+         * If unset, this configuration defaults to {@code false}.
+         * @param isEnabled {@code true} if SIP keepalives are enabled for the associated SIP
+         *     Delegate, {@code false} if it is not.
+         * @return this Builder instance with the new configuration set.
+         */
+        public @NonNull Builder setSipKeepaliveEnabled(boolean isEnabled) {
+            mConfig.mIsSipKeepaliveEnabled = isEnabled;
+            return this;
+        }
+
+        /**
+         * Sets the max SIP payload size in bytes to be sent on UDP. If the SIP message payload is
+         * greater than the max UDP payload size, then TCP must be used.
+         * <p>
+         * If unset, this configuration defaults to {@link #UDP_PAYLOAD_SIZE_UNDEFINED}, or no
+         * size specified.
+         * @param size The maximum SIP payload size in bytes for UDP.
+         * @return this Builder instance with the new configuration set.
+         */
+        public @NonNull Builder setMaxUdpPayloadSizeBytes(@IntRange(from = 1) int size) {
+            mConfig.mMaxUdpPayloadSize = size;
+            return this;
+        }
+
+        /**
+         * Sets the IMS public user identifier.
+         * <p>
+         * If unset, this configuration defaults to {@code null}, or no identifier specified.
+         * @param id The IMS public user identifier.
+         * @return this Builder instance with the new configuration set.
+         */
+        public @NonNull Builder setPublicUserIdentifier(@Nullable String id) {
+            mConfig.mPublicUserIdentifier = id;
+            return this;
+        }
+
+        /**
+         * Sets the IMS private user identifier.
+         * <p>
+         * If unset, this configuration defaults to {@code null}, or no identifier specified.
+         * @param id The IMS private user identifier.
+         * @return this Builder instance with the new configuration set.
+         */
+        public @NonNull Builder setPrivateUserIdentifier(@Nullable String id) {
+            mConfig.mPrivateUserIdentifier = id;
+            return this;
+        }
+
+        /**
+         * Sets the IMS home domain.
+         * <p>
+         * If unset, this configuration defaults to {@code null}, or no domain specified.
+         * @param domain The IMS home domain.
+         * @return this Builder instance with the new configuration set.
+         */
+        public @NonNull Builder setHomeDomain(@Nullable String domain) {
+            mConfig.mHomeDomain = domain;
+            return this;
+        }
+
+        /**
+         * Sets the IMEI of the associated device.
+         * <p>
+         * Application can include the Instance-ID feature tag {@code "+sip.instance"} in the
+         * Contact header with a value of the device IMEI in the form
+         * {@code "urn:gsma:imei:<device IMEI>"}.
+         * <p>
+         * If unset, this configuration defaults to {@code null}, or no IMEI string specified.
+         * @param imei The IMEI of the device.
+         * @return this Builder instance with the new configuration set.
+         */
+        public @NonNull Builder setImei(@Nullable String imei) {
+            mConfig.mImei = imei;
+            return this;
+        }
+
+        /**
+         * Set the optional {@link IpSecConfiguration} instance used if the associated SipDelegate
+         * is communicating over IPSec.
+         * <p>
+         * If unset, this configuration defaults to {@code null}
+         * @param c The IpSecConfiguration instance to set.
+         * @return this Builder instance with IPSec configuration set.
+         */
+        public @NonNull Builder setIpSecConfiguration(@Nullable IpSecConfiguration c) {
+            mConfig.mIpSecConfiguration = c;
+            return this;
+        }
+
+        /**
+         * Describes the Device's Public IP Address and port that is set when Network Address
+         * Translation is enabled and the device is behind a NAT.
+         * <p>
+         * If unset, this configuration defaults to {@code null}
+         * @param addr The {@link InetAddress} representing the device's public IP address and port
+         *          when behind a NAT.
+         * @return this Builder instance with the new configuration set.
+         * @see InetAddresses#parseNumericAddress(String) For an example of how to create an
+         * instance of {@link InetAddress} without causing a DNS lookup.
+         */
+        public @NonNull Builder setNatSocketAddress(@Nullable InetSocketAddress addr) {
+            mConfig.mNatAddress = addr;
+            return this;
+        }
+
+        /**
+         * Sets the optional URI of the device's Globally routable user-agent URI (GRUU) if this
+         * feature is enabled for the SIP delegate.
+         * <p>
+         * If unset, this configuration defaults to {@code null}
+         * @param uri The GRUU to set.
+         * @return this builder instance with the new configuration set.
+         */
+        public @NonNull Builder setPublicGruuUri(@Nullable Uri uri) {
+            mConfig.mGruu = uri;
+            return this;
+        }
+
+        /**
+         * Sets the SIP authentication header value.
+         * <p>
+         * If unset, this configuration defaults to {@code null}.
+         * @param header The SIP authentication header's value.
+         * @return this builder instance with the new configuration set.
+         */
+        public @NonNull Builder setSipAuthenticationHeader(@Nullable String header) {
+            mConfig.mSipAuthHeader = header;
+            return this;
+        }
+
+        /**
+         * Sets the SIP authentication nonce.
+         * <p>
+         * If unset, this configuration defaults to {@code null}.
+         * @param nonce The SIP authentication nonce.
+         * @return this builder instance with the new configuration set.
+         */
+        public @NonNull Builder setSipAuthenticationNonce(@Nullable String nonce) {
+            mConfig.mSipAuthNonce = nonce;
+            return this;
+        }
+
+        /**
+         * Sets the SIP service route header value.
+         * <p>
+         * If unset, this configuration defaults to {@code null}.
+         * @param header The SIP service route header value.
+         * @return this builder instance with the new configuration set.
+         */
+        public @NonNull Builder setSipServiceRouteHeader(@Nullable String header) {
+            mConfig.mServiceRouteHeader = header;
+            return this;
+        }
+
+        /**
+         * Sets the SIP path header value.
+         * <p>
+         * If unset, this configuration defaults to {@code null}.
+         * @param header The SIP path header value.
+         * @return this builder instance with the new configuration set.
+         */
+        public @NonNull Builder setSipPathHeader(@Nullable String header) {
+            mConfig.mPathHeader = header;
+            return this;
+        }
+
+        /**
+         * Sets the SIP User-Agent header value.
+         * <p>
+         * If unset, this configuration defaults to {@code null}.
+         * @param header The SIP User-Agent header value.
+         * @return this builder instance with the new configuration set.
+         */
+        public @NonNull Builder setSipUserAgentHeader(@Nullable String header) {
+            mConfig.mUserAgentHeader = header;
+            return this;
+        }
+
+        /**
+         * Sets the SIP Contact header's User parameter value.
+         * <p>
+         * If unset, this configuration defaults to {@code null}.
+         * @param param The SIP Contact header's User parameter value.
+         * @return this builder instance with the new configuration set.
+         */
+        public @NonNull Builder setSipContactUserParameter(@Nullable String param) {
+            mConfig.mContactUserParam = param;
+            return this;
+        }
+
+        /**
+         * Sets the SIP P-Access-Network-Info (P-ANI) header value. Populated for networks that
+         * require this information to be provided as part of outgoing SIP messages.
+         * <p>
+         * If unset, this configuration defaults to {@code null}.
+         * @param header The SIP P-ANI header value.
+         * @return this builder instance with the new configuration set.
+         */
+        public @NonNull Builder setSipPaniHeader(@Nullable String header) {
+            mConfig.mPaniHeader = header;
+            return this;
+        }
+
+        /**
+         * Sets the SIP P-Last-Access-Network-Info (P-LANI) header value. Populated for
+         * networks that require this information to be provided as part of outgoing SIP messages.
+         * <p>
+         * If unset, this configuration defaults to {@code null}.
+         * @param header The SIP P-LANI header value.
+         * @return this builder instance with the new configuration set.
+         */
+        public @NonNull Builder setSipPlaniHeader(@Nullable String header) {
+            mConfig.mPlaniHeader = header;
+            return this;
+        }
+
+        /**
+         * Sets the SIP Cellular-Network-Info (CNI) header value (See 3GPP 24.229, section 7.2.15),
+         * populated for networks that require this information to be provided as part of outgoing
+         * SIP messages.
+         * <p>
+         * If unset, this configuration defaults to {@code null}.
+         * @param header The SIP P-LANI header value.
+         * @return this builder instance with the new configuration set.
+         */
+        public @NonNull Builder setSipCniHeader(@Nullable String header) {
+            mConfig.mCniHeader = header;
+            return this;
+        }
+
+        /**
+         * Sets the SIP P-associated-uri header value.
+         * <p>
+         * If unset, this configuration defaults to {@code null}.
+         * @param header The SIP P-associated-uri header value.
+         * @return this builder instance with the new configuration set.
+         */
+        public @NonNull Builder setSipAssociatedUriHeader(@Nullable String header) {
+            mConfig.mAssociatedUriHeader = header;
+            return this;
+        }
+
+        /**
+         * @return A {@link SipDelegateConfiguration} instance with the optional configurations set.
+         */
+        public @NonNull SipDelegateConfiguration build() {
+            return mConfig;
+        }
+    }
+
+    private final long mVersion;
+    private final int mTransportType;
+    private final InetSocketAddress mLocalAddress;
+    private final InetSocketAddress mSipServerAddress;
+    private boolean mIsSipCompactFormEnabled = false;
+    private boolean mIsSipKeepaliveEnabled = false;
+    private int mMaxUdpPayloadSize = -1;
+    private String mPublicUserIdentifier = null;
+    private String mPrivateUserIdentifier = null;
+    private String mHomeDomain = null;
+    private String mImei = null;
+    private Uri mGruu = null;
+    private String mSipAuthHeader = null;
+    private String mSipAuthNonce = null;
+    private String mServiceRouteHeader = null;
+    private String mPathHeader = null;
+    private String mUserAgentHeader = null;
+    private String mContactUserParam = null;
+    private String mPaniHeader = null;
+    private String mPlaniHeader = null;
+    private String mCniHeader = null;
+    private String mAssociatedUriHeader = null;
+    private IpSecConfiguration mIpSecConfiguration = null;
+    private InetSocketAddress mNatAddress = null;
+
+
+    private SipDelegateConfiguration(long version, int transportType,
+            InetSocketAddress localAddress, InetSocketAddress sipServerAddress) {
+        mVersion = version;
+        mTransportType = transportType;
+        mLocalAddress = localAddress;
+        mSipServerAddress = sipServerAddress;
+    }
+
+    private SipDelegateConfiguration(Parcel source) {
+        mVersion = source.readLong();
+        mTransportType = source.readInt();
+        mLocalAddress = readAddressFromParcel(source);
+        mSipServerAddress = readAddressFromParcel(source);
+        mIsSipCompactFormEnabled = source.readBoolean();
+        mIsSipKeepaliveEnabled = source.readBoolean();
+        mMaxUdpPayloadSize = source.readInt();
+        mPublicUserIdentifier = source.readString();
+        mPrivateUserIdentifier = source.readString();
+        mHomeDomain = source.readString();
+        mImei = source.readString();
+        mGruu = source.readParcelable(null, android.net.Uri.class);
+        mSipAuthHeader = source.readString();
+        mSipAuthNonce = source.readString();
+        mServiceRouteHeader = source.readString();
+        mPathHeader = source.readString();
+        mUserAgentHeader = source.readString();
+        mContactUserParam = source.readString();
+        mPaniHeader = source.readString();
+        mPlaniHeader = source.readString();
+        mCniHeader = source.readString();
+        mAssociatedUriHeader = source.readString();
+        boolean isIpsecConfigAvailable = source.readBoolean();
+        if (isIpsecConfigAvailable) {
+            mIpSecConfiguration = IpSecConfiguration.fromParcel(source);
+        }
+        boolean isNatConfigAvailable = source.readBoolean();
+        if (isNatConfigAvailable) {
+            mNatAddress = readAddressFromParcel(source);
+        }
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeLong(mVersion);
+        dest.writeInt(mTransportType);
+        writeAddressToParcel(mLocalAddress, dest);
+        writeAddressToParcel(mSipServerAddress, dest);
+        dest.writeBoolean(mIsSipCompactFormEnabled);
+        dest.writeBoolean(mIsSipKeepaliveEnabled);
+        dest.writeInt(mMaxUdpPayloadSize);
+        dest.writeString(mPublicUserIdentifier);
+        dest.writeString(mPrivateUserIdentifier);
+        dest.writeString(mHomeDomain);
+        dest.writeString(mImei);
+        dest.writeParcelable(mGruu, flags);
+        dest.writeString(mSipAuthHeader);
+        dest.writeString(mSipAuthNonce);
+        dest.writeString(mServiceRouteHeader);
+        dest.writeString(mPathHeader);
+        dest.writeString(mUserAgentHeader);
+        dest.writeString(mContactUserParam);
+        dest.writeString(mPaniHeader);
+        dest.writeString(mPlaniHeader);
+        dest.writeString(mCniHeader);
+        dest.writeString(mAssociatedUriHeader);
+        dest.writeBoolean(mIpSecConfiguration != null);
+        if (mIpSecConfiguration != null) {
+            mIpSecConfiguration.addToParcel(dest);
+        }
+        dest.writeBoolean(mNatAddress != null);
+        if (mNatAddress != null) {
+            writeAddressToParcel(mNatAddress, dest);
+        }
+    }
+
+    /**
+     * @return A copy of this instance with an incremented version.
+     * @hide
+     */
+    public SipDelegateConfiguration copyAndIncrementVersion() {
+        SipDelegateConfiguration c = new SipDelegateConfiguration(getVersion() + 1, mTransportType,
+                mLocalAddress, mSipServerAddress);
+        c.mIsSipCompactFormEnabled = mIsSipCompactFormEnabled;
+        c.mIsSipKeepaliveEnabled = mIsSipKeepaliveEnabled;
+        c.mMaxUdpPayloadSize = mMaxUdpPayloadSize;
+        c.mIpSecConfiguration = mIpSecConfiguration;
+        c.mNatAddress = mNatAddress;
+        c.mPublicUserIdentifier = mPublicUserIdentifier;
+        c.mPrivateUserIdentifier = mPrivateUserIdentifier;
+        c.mHomeDomain = mHomeDomain;
+        c.mImei = mImei;
+        c.mGruu = mGruu;
+        c.mSipAuthHeader = mSipAuthHeader;
+        c.mSipAuthNonce = mSipAuthNonce;
+        c.mServiceRouteHeader = mServiceRouteHeader;
+        c.mPathHeader = mPathHeader;
+        c.mUserAgentHeader = mUserAgentHeader;
+        c.mContactUserParam = mContactUserParam;
+        c.mPaniHeader = mPaniHeader;
+        c.mPlaniHeader = mPlaniHeader;
+        c.mCniHeader = mCniHeader;
+        c.mAssociatedUriHeader = mAssociatedUriHeader;
+        return c;
+    }
+
+    /**
+     * An integer representing the version number of this SipDelegateImsConfiguration.
+     * {@link SipMessage}s that are created using this configuration will also have a this
+     * version number associated with them, which will allow the IMS service to validate that the
+     * {@link SipMessage} was using the latest configuration during creation and not a stale
+     * configuration due to race conditions between the configuration being updated and the RCS
+     * application not receiving the updated configuration before generating a new message.
+     * <p>
+     * The version number should be a positive number that starts at 0 and increments sequentially
+     * as new {@link SipDelegateConfiguration} instances are created to update the IMS
+     * configuration state.
+     *
+     * @return the version number associated with this {@link SipDelegateConfiguration}.
+     */
+    public @IntRange(from = 0) long getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * @return The Transport type of the SIP delegate.
+     */
+    public @TransportType int getTransportType() {
+        return mTransportType;
+    }
+
+    /**
+     * @return The local IP address and port used for SIP traffic.
+     */
+    public @NonNull InetSocketAddress getLocalAddress() {
+        return mLocalAddress;
+    }
+
+    /**
+     * @return The default IP Address and port of the SIP server or P-CSCF used for SIP traffic.
+     */
+    public @NonNull InetSocketAddress getSipServerAddress() {
+        return mSipServerAddress;
+    }
+
+    /**
+     * @return {@code true} if SIP compact form is enabled for the associated SIP Delegate,
+     * {@code false} if it is not.
+     */
+    public boolean isSipCompactFormEnabled() {
+        return mIsSipCompactFormEnabled;
+    }
+
+    /**
+     * @return {@code true} if SIP keepalives are enabled for the associated SIP Delegate,
+     * {@code false} if it is not.
+     */
+    public boolean isSipKeepaliveEnabled() {
+        return mIsSipKeepaliveEnabled;
+    }
+
+    /**
+     * @return The maximum SIP payload size in bytes for UDP or {code -1} if no value was set.
+     */
+    public int getMaxUdpPayloadSizeBytes() {
+        return mMaxUdpPayloadSize;
+    }
+
+    /**
+     * @return The IMS public user identifier or {@code null} if it was not set.
+     */
+    public @Nullable String getPublicUserIdentifier() {
+        return mPublicUserIdentifier;
+    }
+
+    /**
+     * @return The IMS private user identifier or {@code null} if it was not set.
+     */
+    public @Nullable String getPrivateUserIdentifier() {
+        return mPrivateUserIdentifier;
+    }
+
+    /**
+     * @return The IMS home domain or {@code null} if it was not set.
+     */
+    public @Nullable String getHomeDomain() {
+        return mHomeDomain;
+    }
+
+    /**
+     * get the IMEI of the associated device.
+     * <p>
+     * Application can include the Instance-ID feature tag {@code "+sip.instance"} in the Contact
+     * header with a value of the device IMEI in the form {@code "urn:gsma:imei:<device IMEI>"}.
+     * @return The IMEI of the device or {@code null} if it was not set.
+     */
+    public @Nullable String getImei() {
+        return mImei;
+    }
+
+    /**
+     * @return The IPSec configuration that must be used because SIP is communicating over IPSec.
+     * This returns {@code null} SIP is not communicating over IPSec.
+     */
+    public @Nullable IpSecConfiguration getIpSecConfiguration() {
+        return mIpSecConfiguration;
+    }
+
+    /**
+     * @return The public IP address and port of the device due to it being behind a NAT.
+     * This returns {@code null} if the device is not behind a NAT.
+     */
+    public @Nullable InetSocketAddress getNatSocketAddress() {
+        return mNatAddress;
+    }
+
+    /**
+     * @return The device's Globally routable user-agent URI (GRUU) or {@code null} if this feature
+     * is not enabled for the SIP delegate.
+     */
+    public @Nullable Uri getPublicGruuUri() {
+        return mGruu;
+    }
+
+    /**
+     * @return The value of the SIP authentication header or {@code null} if there is none set.
+     */
+    public @Nullable String getSipAuthenticationHeader() {
+        return mSipAuthHeader;
+    }
+
+    /**
+     * @return The value of the SIP authentication nonce or {@code null} if there is none set.
+     */
+    public @Nullable String getSipAuthenticationNonce() {
+        return mSipAuthNonce;
+    }
+
+    /**
+     * @return The value of the SIP service route header or {@code null} if there is none set.
+     */
+    public @Nullable String getSipServiceRouteHeader() {
+        return mServiceRouteHeader;
+    }
+
+    /**
+     * @return The value of the SIP path header or {@code null} if there is none set.
+     */
+    public @Nullable String getSipPathHeader() {
+        return mPathHeader;
+    }
+
+    /**
+     * @return The value of the SIP User-Agent header or {@code null} if there is none set.
+     */
+    public @Nullable String getSipUserAgentHeader() {
+        return mUserAgentHeader;
+    }
+    /**
+     * @return The value of the SIP Contact header's User parameter or {@code null} if there is
+     * none set.
+     */
+    public @Nullable String getSipContactUserParameter() {
+        return mContactUserParam;
+    }
+
+    /**
+     * @return The value of the SIP P-Access-Network-Info (P-ANI) header or {@code null} if there is
+     * none set.
+     */
+    public @Nullable String getSipPaniHeader() {
+        return mPaniHeader;
+    }
+    /**
+     * @return The value of the SIP P-Last-Access-Network-Info (P-LANI) header or {@code null} if
+     * there is none set.
+     */
+    public @Nullable String getSipPlaniHeader() {
+        return mPlaniHeader;
+    }
+
+    /**
+     * @return The value of the SIP Cellular-Network-Info (CNI) header or {@code null} if there is
+     * none set.
+     */
+    public @Nullable String getSipCniHeader() {
+        return mCniHeader;
+    }
+
+    /**
+     * @return The value of the SIP P-associated-uri header or {@code null} if there is none set.
+     */
+    public @Nullable String getSipAssociatedUriHeader() {
+        return mAssociatedUriHeader;
+    }
+
+    private void writeAddressToParcel(InetSocketAddress addr, Parcel dest) {
+        dest.writeByteArray(addr.getAddress().getAddress());
+        dest.writeInt(addr.getPort());
+    }
+
+    private InetSocketAddress readAddressFromParcel(Parcel source) {
+        final byte[] addressBytes = source.createByteArray();
+        final int port = source.readInt();
+        try {
+            return new InetSocketAddress(InetAddress.getByAddress(addressBytes), port);
+        } catch (UnknownHostException e) {
+            // Should not happen, as length of array was verified before parcelling.
+            Log.e("SipDelegateConfiguration", "exception reading address, returning null");
+            return null;
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Creator<SipDelegateConfiguration> CREATOR =
+            new Creator<SipDelegateConfiguration>() {
+        @Override
+        public SipDelegateConfiguration createFromParcel(Parcel source) {
+            return new SipDelegateConfiguration(source);
+        }
+
+        @Override
+        public SipDelegateConfiguration[] newArray(int size) {
+            return new SipDelegateConfiguration[size];
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SipDelegateConfiguration that = (SipDelegateConfiguration) o;
+        return mVersion == that.mVersion
+                && mTransportType == that.mTransportType
+                && mIsSipCompactFormEnabled == that.mIsSipCompactFormEnabled
+                && mIsSipKeepaliveEnabled == that.mIsSipKeepaliveEnabled
+                && mMaxUdpPayloadSize == that.mMaxUdpPayloadSize
+                && Objects.equals(mLocalAddress, that.mLocalAddress)
+                && Objects.equals(mSipServerAddress, that.mSipServerAddress)
+                && Objects.equals(mPublicUserIdentifier, that.mPublicUserIdentifier)
+                && Objects.equals(mPrivateUserIdentifier, that.mPrivateUserIdentifier)
+                && Objects.equals(mHomeDomain, that.mHomeDomain)
+                && Objects.equals(mImei, that.mImei)
+                && Objects.equals(mGruu, that.mGruu)
+                && Objects.equals(mSipAuthHeader, that.mSipAuthHeader)
+                && Objects.equals(mSipAuthNonce, that.mSipAuthNonce)
+                && Objects.equals(mServiceRouteHeader, that.mServiceRouteHeader)
+                && Objects.equals(mPathHeader, that.mPathHeader)
+                && Objects.equals(mUserAgentHeader, that.mUserAgentHeader)
+                && Objects.equals(mContactUserParam, that.mContactUserParam)
+                && Objects.equals(mPaniHeader, that.mPaniHeader)
+                && Objects.equals(mPlaniHeader, that.mPlaniHeader)
+                && Objects.equals(mCniHeader, that.mCniHeader)
+                && Objects.equals(mAssociatedUriHeader, that.mAssociatedUriHeader)
+                && Objects.equals(mIpSecConfiguration, that.mIpSecConfiguration)
+                && Objects.equals(mNatAddress, that.mNatAddress);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mVersion, mTransportType, mLocalAddress, mSipServerAddress,
+                mIsSipCompactFormEnabled, mIsSipKeepaliveEnabled, mMaxUdpPayloadSize,
+                mPublicUserIdentifier, mPrivateUserIdentifier, mHomeDomain, mImei, mGruu,
+                mSipAuthHeader, mSipAuthNonce, mServiceRouteHeader, mPathHeader, mUserAgentHeader,
+                mContactUserParam, mPaniHeader, mPlaniHeader, mCniHeader, mAssociatedUriHeader,
+                mIpSecConfiguration, mNatAddress);
+    }
+
+    @Override
+    public String toString() {
+        return "SipDelegateConfiguration{ mVersion=" + mVersion + ", mTransportType="
+                + mTransportType + '}';
+    }
+}
diff --git a/android-35/android/telephony/ims/SipDelegateConnection.java b/android-35/android/telephony/ims/SipDelegateConnection.java
new file mode 100644
index 0000000..498b408
--- /dev/null
+++ b/android-35/android/telephony/ims/SipDelegateConnection.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.telephony.ims.stub.SipDelegate;
+
+/**
+ * Represents a connection to the remote {@link SipDelegate} that is managed by the
+ * {@link ImsService} implementing IMS for the subscription that is associated with it.
+ * <p>
+ * The remote delegate will handle messages sent by this {@link SipDelegateConnection}, notifying
+ * the associated {@link DelegateMessageCallback} when the message was either sent successfully or
+ * failed to be sent.
+ * <p>
+ * It is also the responsibility of this {@link SipDelegateConnection} to acknowledge when incoming
+ * SIP messages have been received successfully via
+ * {@link DelegateMessageCallback#onMessageReceived(SipMessage)} or when there was an error
+ * receiving the message using {@link #notifyMessageReceived(String)} and
+ * {@link #notifyMessageReceiveError(String, int)}.
+ *
+ * @see SipDelegateManager#createSipDelegate
+ * @hide
+ */
+@SystemApi
+public interface SipDelegateConnection {
+
+    /**
+     * Send a SIP message to the SIP delegate to be sent over the carrier’s network. The
+     * {@link SipMessage} will either be acknowledged with
+     * {@link DelegateMessageCallback#onMessageSent(String)} upon successful sending of this message
+     * or {@link DelegateMessageCallback#onMessageSendFailure(String, int)} if there was an error
+     * sending the message.
+     * @param sipMessage The SipMessage to be sent.
+     * @param configVersion The SipDelegateImsConfiguration version used to construct the
+     *                      SipMessage. See {@link SipDelegateConfiguration#getVersion} for more
+     */
+    void sendMessage(@NonNull SipMessage sipMessage, long configVersion);
+
+    /**
+     * Notify the {@link SipDelegate} that a SIP message received from
+     * {@link DelegateMessageCallback#onMessageReceived(SipMessage)} has been received successfully
+     * and is being processed.
+     * @param viaTransactionId Per RFC3261 Sec 8.1.1.7 the transaction ID associated with the Via
+     *         branch parameter.
+     */
+    void notifyMessageReceived(@NonNull String viaTransactionId);
+
+    /**
+     * The SIP session associated with the provided Call-ID is being closed and routing resources
+     * associated with the session are free to be released. Each SIP session may contain multiple
+     * dialogs due to SIP INVITE forking, so this method must be called after all SIP dialogs
+     * associated with the session has closed.
+     * <p>
+     * Calling this method is also mandatory for situations where the framework IMS stack is waiting
+     * for pending SIP sessions to be closed before it can perform a handover or apply a
+     * provisioning change. See {@link DelegateRegistrationState} for more information about
+     * the scenarios where this can occur.
+     * <p>
+     * This method will need to be called for each SIP session managed by this application when it
+     * is closed.
+     * @param callId The call-ID header value associated with the ongoing SIP Dialog that is
+     *         closing.
+     */
+    void cleanupSession(@NonNull String callId);
+
+    /**
+     * Notify the SIP delegate that the SIP message has been received from
+     * {@link DelegateMessageCallback#onMessageReceived(SipMessage)}, however there was an error
+     * processing it.
+     * @param viaTransactionId Per RFC3261 Sec 8.1.1.7 the transaction ID associated with the Via
+     *         branch parameter.
+     * @param reason The reason why the error occurred.
+     */
+    void notifyMessageReceiveError(@NonNull String viaTransactionId,
+            @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/android-35/android/telephony/ims/SipDelegateImsConfiguration.java b/android-35/android/telephony/ims/SipDelegateImsConfiguration.java
new file mode 100644
index 0000000..fe14dd1
--- /dev/null
+++ b/android-35/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -0,0 +1,594 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.net.InetAddresses;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetSocketAddress;
+
+/**
+ * @hide
+ * @removed Use {@link SipDelegateConfiguration} instead.
+ */
+@Deprecated
+@SystemApi
+public final class SipDelegateImsConfiguration implements Parcelable {
+
+    /**
+     * IPV4 Address type.
+     * <p>
+     * Used as a potential value for {@link #KEY_SIP_CONFIG_IPTYPE_STRING}.
+     */
+    public static final String IPTYPE_IPV4 = "IPV4";
+
+    /**
+     * IPV6 Address type.
+     * <p>
+     * Used as a potential value for {@link #KEY_SIP_CONFIG_IPTYPE_STRING}.
+     */
+    public static final String IPTYPE_IPV6 = "IPV6";
+
+    /**
+     * The SIP transport uses UDP.
+     * <p>
+     * Used as a potential value for {@link #KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING}.
+     */
+    public static final String SIP_TRANSPORT_UDP = "UDP";
+
+    /**
+     * The SIP transport uses TCP.
+     * <p>
+     * Used as a potential value for {@link #KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING}.
+     */
+    public static final String SIP_TRANSPORT_TCP = "TCP";
+
+    /**
+     * Flag specifying if SIP compact form is enabled
+     */
+    public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL =
+            "sip_config_is_compact_form_enabled_bool";
+
+    /**
+     * Flag specifying if SIP keepalives are enabled
+     */
+    public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL =
+            "sip_config_is_keepalive_enabled_bool";
+
+    /**
+     * Maximum SIP payload to be sent on UDP. If the SIP message payload is greater than max udp
+     * payload size, then TCP must be used
+     */
+    public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT =
+            "sip_config_udp_max_payload_size_int";
+
+    /**
+     * Transport protocol used for SIP signaling.
+     * Available options are: {@link #SIP_TRANSPORT_UDP }, {@link #SIP_TRANSPORT_TCP }
+     */
+    public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING =
+            "sip_config_protocol_type_string";
+
+    /**
+     * IMS public user identifier string
+     */
+    public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING =
+            "sip_config_ue_public_user_id_string";
+
+    /**
+     * IMS private user identifier string
+     */
+    public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING =
+            "sip_config_ue_private_user_id_string";
+
+    /**
+     * IMS home domain string
+     */
+    public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
+
+    /**
+     * IMEI string. Application can include the Instance-ID feature tag " +sip.instance" in the
+     * Contact header with a value of the device IMEI in the form "urn:gsma:imei:<device IMEI>".
+     */
+    public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
+
+    /**
+     * IP address type for SIP signaling.
+     * Available options are: {@link #IPTYPE_IPV6}, {@link #IPTYPE_IPV4}
+     */
+    public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
+
+    /**
+     * Local IPaddress used for SIP signaling.
+     */
+    public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING =
+            "sip_config_ue_default_ipaddress_string";
+
+    /**
+     * Local port used for sending SIP traffic
+     */
+    public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT =
+            "sip_config_ue_default_port_int";
+
+    /**
+     * SIP server / PCSCF default ip address
+     */
+    public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING =
+            "sip_config_server_default_ipaddress_string";
+
+    /**
+     * SIP server / PCSCF port used for sending SIP traffic
+     */
+    public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT =
+            "sip_config_server_default_port_int";
+
+    /**
+     * Flag specifying if Network Address Translation is enabled and UE is behind a NAT.
+     */
+    public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL =
+            "sip_config_is_nat_enabled_bool";
+
+    /**
+     * UE's public IPaddress when UE is behind a NAT.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING =
+            "sip_config_ue_public_ipaddress_with_nat_string";
+
+    /**
+     * UE's public SIP port when UE is behind a NAT.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT =
+            "sip_config_ue_public_port_with_nat_int";
+
+    /**
+     * Flag specifying if Globally routable user-agent uri (GRUU) is enabled as per TS 23.808
+     */
+    public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL =
+            "sip_config_is_gruu_enabled_bool";
+
+    /**
+     * UE's Globally routable user-agent uri if this feature is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING =
+            "sip_config_ue_public_gruu_string";
+
+    /**
+     * Flag specifying if SIP over IPSec is enabled.
+     */
+    public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL =
+            "sip_config_is_ipsec_enabled_bool";
+    /**
+     * UE's SIP port used to send traffic when IPSec is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT =
+            "sip_config_ue_ipsec_client_port_int";
+
+    /**
+     * UE's SIP port used to receive traffic when IPSec is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT =
+            "sip_config_ue_ipsec_server_port_int";
+
+    /**
+     * UE's SIP port used for the previous IPsec security association if IPSec is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT =
+            "sip_config_ue_ipsec_old_client_port_int";
+
+    /**
+     * Port number used by the SIP server to send SIP traffic when IPSec is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT =
+            "sip_config_server_ipsec_client_port_int";
+
+    /**
+     * Port number used by the SIP server to receive incoming SIP traffic when IPSec is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT =
+            "sip_config_server_ipsec_server_port_int";
+
+    /**
+     * Port number used by the SIP server to send SIP traffic on the previous IPSec security
+     * association when IPSec is enabled.
+     * <p>
+     * This key will not exist if {@link #KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL} is {@code false}.
+     */
+    public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT =
+            "sip_config_server_ipsec_old_client_port_int";
+    /**
+     * SIP Authentication header string
+     */
+    public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING =
+            "sip_config_auhentication_header_string";
+
+    /**
+     * SIP Authentication nonce string
+     */
+    public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING =
+            "sip_config_authentication_nonce_string";
+
+    /**
+     * SIP service route header string
+     */
+    public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING =
+            "sip_config_service_route_header_string";
+
+    /**
+     * SIP security verify header string
+     */
+    public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING =
+            "sip_config_security_verify_header_string";
+
+    /**
+     * SIP Path header string
+     */
+    public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING =
+            "sip_config_path_header_string";
+
+    /**
+     * The SIP User-Agent header value used by the IMS stack during IMS registration.
+     */
+    public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING =
+            "sip_config_sip_user_agent_header_string";
+
+    /**
+     * SIP User part string in contact header
+     */
+    public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING =
+            "sip_config_uri_user_part_string";
+
+    /**
+     * SIP P-access-network-info header string
+     */
+    public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING =
+            "sip_config_p_access_network_info_header_string";
+
+    /**
+     * The SIP P-last-access-network-info header value, populated for networks that require this
+     * information to be provided in outgoing SIP messages.
+     */
+    public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING =
+            "sip_config_p_last_access_network_info_header_string";
+
+    /**
+     * The Cellular-Network-Info header value (See 3GPP 24.229, section 7.2.15), populated for
+     * networks that require this information to be provided as part of outgoing SIP messages.
+     */
+    public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING =
+            "sip_config_cellular_network_info_header_string";
+
+    /**
+     * SIP P-associated-uri header string
+     */
+    public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING =
+            "sip_config_p_associated_uri_header_string";
+
+    /**@hide*/
+    @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_STRING", value = {
+            KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING,
+            KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING,
+            KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING,
+            KEY_SIP_CONFIG_HOME_DOMAIN_STRING,
+            KEY_SIP_CONFIG_IMEI_STRING,
+            KEY_SIP_CONFIG_IPTYPE_STRING,
+            KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING,
+            KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING,
+            KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING,
+            KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING,
+            KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING,
+            KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING,
+            KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING,
+            KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING,
+            KEY_SIP_CONFIG_PATH_HEADER_STRING,
+            KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING,
+            KEY_SIP_CONFIG_URI_USER_PART_STRING,
+            KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING,
+            KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING,
+            KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING,
+            KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StringConfigKey {}
+
+    /**@hide*/
+    @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_INT", value = {
+            KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT,
+            KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT,
+            KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT,
+            KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT,
+            KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT,
+            KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT,
+            KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT,
+            KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT,
+            KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT,
+            KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IntConfigKey {}
+
+    /**@hide*/
+    @StringDef(prefix = "KEY_SIP_CONFIG", suffix = "_BOOL", value = {
+            KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL,
+            KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL,
+            KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL,
+            KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL,
+            KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BooleanConfigKey {}
+
+    /**
+     * Builder class to be used when constructing a new SipDelegateImsConfiguration.
+     */
+    public static final class Builder {
+        private final long mVersion;
+        private final PersistableBundle mBundle;
+
+        /**
+         * Creates an empty implementation of SipDelegateImsConfiguration.
+         * @param version The version associated with the SipDelegateImsConfiguration being built.
+         *                See {@link #getVersion} for more information.
+         */
+        public Builder(int version) {
+            mVersion = version;
+            mBundle = new PersistableBundle();
+        }
+        /**
+         * Clones an existing implementation of SipDelegateImsConfiguration to handle situations
+         * where only a small number of parameters have changed from the previous configuration.
+         * <p>
+         * Automatically increments the version of this configuration by 1. See {@link #getVersion}
+         * for more information.
+         */
+        public Builder(@NonNull SipDelegateImsConfiguration config) {
+            mVersion = config.getVersion() + 1;
+            mBundle = config.copyBundle();
+        }
+        /**
+         * Put a string value into this configuration bundle for the given key.
+         */
+        // getString is available below.
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder addString(@NonNull @StringConfigKey String key,
+                @NonNull String value) {
+            mBundle.putString(key, value);
+            return this;
+        }
+
+        /**
+         * Replace the existing default value with a new value for a given key.
+         */
+        // getInt is available below.
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder addInt(@NonNull @IntConfigKey String key, int value) {
+            mBundle.putInt(key, value);
+            return this;
+        }
+
+        /**
+         * Replace the existing default value with a new value for a given key.
+         */
+        // getBoolean is available below.
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder addBoolean(@NonNull @BooleanConfigKey String key, boolean value) {
+            mBundle.putBoolean(key, value);
+            return this;
+        }
+
+        /**
+         * @return a new SipDelegateImsConfiguration from this Builder.
+         */
+        public @NonNull SipDelegateImsConfiguration build() {
+            return new SipDelegateImsConfiguration(mVersion, mBundle);
+        }
+    }
+
+    private final long mVersion;
+    private final PersistableBundle mBundle;
+
+    private SipDelegateImsConfiguration(long version, PersistableBundle bundle) {
+        mVersion = version;
+        mBundle = bundle;
+    }
+
+    private SipDelegateImsConfiguration(Parcel source) {
+        mVersion = source.readLong();
+        mBundle = source.readPersistableBundle();
+    }
+
+    /**
+     * @return {@code true} if this configuration object has a an entry for the key specified,
+     * {@code false} if it does not.
+     */
+    public boolean containsKey(@NonNull String key) {
+        return mBundle.containsKey(key);
+    }
+
+    /**
+     * @return the string value associated with a given key or {@code null} if it doesn't exist.
+     */
+    public @Nullable @StringConfigKey String getString(@NonNull String key) {
+        return mBundle.getString(key);
+    }
+
+    /**
+     * @return the integer value associated with a given key if it exists or the supplied default
+     * value if it does not.
+     */
+    public @IntConfigKey int getInt(@NonNull String key, int defaultValue) {
+        if (!mBundle.containsKey(key)) {
+            return defaultValue;
+        }
+        return mBundle.getInt(key);
+    }
+
+    /**
+     * @return the boolean value associated with a given key or the supplied default value if the
+     * value doesn't exist in the bundle.
+     */
+    public @BooleanConfigKey boolean getBoolean(@NonNull String key, boolean defaultValue) {
+        if (!mBundle.containsKey(key)) {
+            return defaultValue;
+        }
+        return mBundle.getBoolean(key);
+    }
+
+    /**
+     * @return a shallow copy of the full configuration.
+     */
+    public @NonNull PersistableBundle copyBundle() {
+        return new PersistableBundle(mBundle);
+    }
+
+    /**
+     * An integer representing the version number of this SipDelegateImsConfiguration.
+     * {@link SipMessage}s that are created using this configuration will also have a this
+     * version number associated with them, which will allow the IMS service to validate that the
+     * {@link SipMessage} was using the latest configuration during creation and not a stale
+     * configuration due to race conditions between the configuration being updated and the RCS
+     * application not receiving the updated configuration before generating a new message.
+     * <p>
+     * The version number should be a positive number that starts at 0 and increments sequentially
+     * as new {@link SipDelegateImsConfiguration} instances are created to update the IMS
+     * configuration state.
+     *
+     * @return the version number associated with this {@link SipDelegateImsConfiguration}.
+     */
+    public long getVersion() {
+        return mVersion;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeLong(mVersion);
+        dest.writePersistableBundle(mBundle);
+    }
+
+    public static final @NonNull Creator<SipDelegateImsConfiguration> CREATOR =
+            new Creator<SipDelegateImsConfiguration>() {
+        @Override
+        public SipDelegateImsConfiguration createFromParcel(Parcel source) {
+            return new SipDelegateImsConfiguration(source);
+        }
+
+        @Override
+        public SipDelegateImsConfiguration[] newArray(int size) {
+            return new SipDelegateImsConfiguration[size];
+        }
+    };
+
+    /**
+     * Temporary helper to transition from old form of config to new form.
+     * @return new config
+     * @hide
+     */
+    public SipDelegateConfiguration toNewConfig() {
+        // IP version is now included in call to InetSocketAddr
+        String transportTypeString = getString(KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING);
+        int transportType = (transportTypeString != null
+                && transportTypeString.equals(SIP_TRANSPORT_UDP))
+                ? SipDelegateConfiguration.SIP_TRANSPORT_UDP
+                : SipDelegateConfiguration.SIP_TRANSPORT_TCP;
+        SipDelegateConfiguration.Builder builder = new SipDelegateConfiguration.Builder(mVersion,
+                transportType,
+                getSocketAddr(getString(KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING),
+                        getInt(KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT, -1)),
+                getSocketAddr(getString(KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING),
+                        getInt(KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT, -1)));
+        builder.setSipCompactFormEnabled(
+                getBoolean(KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL, false));
+        builder.setSipKeepaliveEnabled(
+                getBoolean(KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL, false));
+        builder.setMaxUdpPayloadSizeBytes(getInt(KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT, -1));
+        builder.setPublicUserIdentifier(getString(KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING));
+        builder.setPrivateUserIdentifier(getString(KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING));
+        builder.setHomeDomain(getString(KEY_SIP_CONFIG_HOME_DOMAIN_STRING));
+        builder.setImei(getString(KEY_SIP_CONFIG_IMEI_STRING));
+        builder.setSipAuthenticationHeader(getString(KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING));
+        builder.setSipAuthenticationNonce(getString(KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING));
+        builder.setSipServiceRouteHeader(getString(KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING));
+        builder.setSipPathHeader(getString(KEY_SIP_CONFIG_PATH_HEADER_STRING));
+        builder.setSipUserAgentHeader(getString(KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING));
+        builder.setSipContactUserParameter(getString(KEY_SIP_CONFIG_URI_USER_PART_STRING));
+        builder.setSipPaniHeader(getString(KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING));
+        builder.setSipPlaniHeader(
+                getString(KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING));
+        builder.setSipCniHeader(getString(KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING));
+        builder.setSipAssociatedUriHeader(getString(KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING));
+        if (getBoolean(KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, false)) {
+            String uri = getString(KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING);
+            Uri gruuUri = null;
+            if (!TextUtils.isEmpty(uri)) {
+                gruuUri = Uri.parse(uri);
+            }
+            builder.setPublicGruuUri(gruuUri);
+        }
+        if (getBoolean(KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL, false)) {
+            builder.setIpSecConfiguration(new SipDelegateConfiguration.IpSecConfiguration(
+                    getInt(KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT, -1),
+                    getInt(KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT, -1),
+                    getInt(KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT, -1),
+                    getInt(KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT, -1),
+                    getInt(KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT, -1),
+                    getInt(KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT, -1),
+                    getString(KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING))
+            );
+        }
+        if (getBoolean(KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL, false)) {
+            builder.setNatSocketAddress(getSocketAddr(
+                    getString(KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING),
+                    getInt(KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT, -1)));
+        }
+        return builder.build();
+    }
+
+    private InetSocketAddress getSocketAddr(String ipAddr, int port) {
+        return new InetSocketAddress(InetAddresses.parseNumericAddress(ipAddr), port);
+    }
+}
diff --git a/android-35/android/telephony/ims/SipDelegateManager.java b/android-35/android/telephony/ims/SipDelegateManager.java
new file mode 100644
index 0000000..abf2105
--- /dev/null
+++ b/android-35/android/telephony/ims/SipDelegateManager.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.telephony.BinderCacheManager;
+import android.telephony.ims.aidl.IImsRcsController;
+import android.telephony.ims.aidl.SipDelegateConnectionAidlWrapper;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.DelegateConnectionMessageCallback;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.ITelephony;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages the creation and destruction of SipDelegates for the {@link ImsService} managing IMS
+ * for the subscription ID that this SipDelegateManager has been created for.
+ *
+ * This allows multiple IMS applications to forward SIP messages to/from their application for the
+ * purposes of providing a single IMS registration to the carrier's IMS network from potentially
+ * many IMS stacks implementing a subset of the supported MMTEL/RCS features.
+ * <p>
+ * This API is only supported if the device supports the
+ * {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION} feature.
+ * @hide
+ */
+@SystemApi
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION)
+public class SipDelegateManager {
+
+    /**
+     * The SIP message has failed being sent or received for an unknown reason.
+     * <p>
+     * The caller should retry a message that failed with this response.
+     */
+    public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0;
+
+    /**
+     * The remote service associated with this connection has died and the message was not
+     * properly sent/received.
+     * <p>
+     * This is considered a permanent error and the system will automatically begin the teardown and
+     * destruction of the SipDelegate. No further messages should be sent on this transport.
+     */
+    public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1;
+
+    /**
+     * The message has not been sent/received because the delegate is in the process of closing and
+     * has become unavailable. No further messages should be sent/received on this delegate.
+     */
+    public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2;
+
+    /**
+     * The SIP message has an invalid start line and the message can not be sent or the start line
+     * failed validation due to the request containing a restricted SIP request method.
+     * {@link SipDelegateConnection}s can not send SIP requests for the methods: REGISTER, PUBLISH,
+     * or OPTIONS.
+     */
+    public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3;
+
+    /**
+     * One or more of the header fields in the header section of the outgoing SIP message is invalid
+     * or contains a restricted header value and the SIP message can not be sent.
+     * {@link SipDelegateConnection}s can not send SIP SUBSCRIBE requests for the "Event" header
+     * value of "presence".
+     */
+    public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4;
+
+    /**
+     * The body content of the SIP message is invalid and the message can not be sent.
+     */
+    public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5;
+
+    /**
+     * The feature tag associated with the outgoing message does not match any known feature tags
+     * or it matches a denied tag and this message can not be sent.
+     */
+    public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6;
+
+    /**
+     * The feature tag associated with the outgoing message is not enabled for the associated
+     * SipDelegateConnection and can not be sent.
+     */
+    public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7;
+
+    /**
+     * The link to the network has been lost and the outgoing message has failed to send.
+     * <p>
+     * This message should be retried when connectivity to the network is re-established. See
+     * {@link android.net.ConnectivityManager.NetworkCallback} for how this can be determined.
+     */
+    public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8;
+
+    /**
+     * The outgoing SIP message has not been sent due to the SipDelegate not being registered for
+     * IMS at this time.
+     * <p>
+     * This is considered a temporary failure, the message should not be retried until an IMS
+     * registration change callback is received via
+     * {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged}
+     */
+    public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9;
+
+    /**
+     * The outgoing SIP message has not been sent because the {@link SipDelegateConfiguration}
+     * version associated with the outgoing {@link SipMessage} is now stale and has failed
+     * validation checks.
+     * <p>
+     * The @link SipMessage} should be recreated using the newest
+     * {@link SipDelegateConfiguration} and sent again.
+     */
+    public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10;
+
+    /**
+     * The outgoing SIP message has not been sent because the internal state of the associated
+     * {@link SipDelegate} is changing and has temporarily brought the transport down.
+     * <p>
+     * This is considered a temporary error and the {@link SipDelegateConnection} should resend the
+     * message once {@link DelegateRegistrationState#DEREGISTERING_REASON_FEATURE_TAGS_CHANGING} is
+     * no longer reported.
+     */
+    public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "MESSAGE_FAILURE_REASON_", value = {
+            MESSAGE_FAILURE_REASON_UNKNOWN,
+            MESSAGE_FAILURE_REASON_DELEGATE_DEAD,
+            MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+            MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+            MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS,
+            MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT,
+            MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG,
+            MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE,
+            MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE,
+            MESSAGE_FAILURE_REASON_NOT_REGISTERED,
+            MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION,
+            MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION
+    })
+    public @interface MessageFailureReason {}
+
+    /**@hide*/
+    public static final ArrayMap<Integer, String> MESSAGE_FAILURE_REASON_STRING_MAP =
+            new ArrayMap<>(11);
+    static {
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_UNKNOWN,
+                "MESSAGE_FAILURE_REASON_UNKNOWN");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_DELEGATE_DEAD,
+                "MESSAGE_FAILURE_REASON_DELEGATE_DEAD");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+                "MESSAGE_FAILURE_REASON_DELEGATE_CLOSED");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS,
+                "MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT,
+                "MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG,
+                "MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(
+                MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE,
+                "MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE,
+                "MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_NOT_REGISTERED,
+                "MESSAGE_FAILURE_REASON_NOT_REGISTERED");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION,
+                "MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(
+                MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
+                "MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION");
+    }
+
+    /**
+     * Access to use this feature tag has been denied for an unknown reason.
+     */
+    public static final int DENIED_REASON_UNKNOWN = 0;
+
+    /**
+     * This feature tag is allowed to be used by this SipDelegateConnection, but it is in use by
+     * another SipDelegateConnection and can not be associated with this delegate. The feature tag
+     * will stay in this state until the feature tag is release by the other application.
+     */
+    public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1;
+
+    /**
+     * Access to use this feature tag has been denied because this application does not have the
+     * permissions required to access this feature tag.
+     */
+    public static final int DENIED_REASON_NOT_ALLOWED = 2;
+
+    /**
+     * Access to use this feature tag has been denied because single registration is not allowed by
+     * the carrier at this time. The application should fall back to dual registration if
+     * applicable.
+     */
+    public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3;
+
+    /**
+     * This feature tag is not recognized as a valid feature tag by the SipDelegate and has been
+     * denied.
+     */
+    public static final int DENIED_REASON_INVALID = 4;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "DENIED_REASON_", value = {
+            DENIED_REASON_UNKNOWN,
+            DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE,
+            DENIED_REASON_NOT_ALLOWED,
+            DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED,
+            DENIED_REASON_INVALID
+    })
+    public @interface DeniedReason {}
+
+    /**
+     * The SipDelegate has closed due to an unknown reason.
+     */
+    public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0;
+
+    /**
+     * The SipDelegate has closed because the IMS service has died unexpectedly.
+     */
+    public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1;
+
+    /**
+     * The SipDelegate has closed because the IMS application has requested that the connection be
+     * destroyed.
+     */
+    public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2;
+
+    /**
+     * The SipDelegate has been closed due to the user disabling RCS.
+     */
+    public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 3;
+
+    /**
+     * The SipDelegate has been closed due to the subscription associated with this delegate being
+     * torn down.
+     */
+    public static final int SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN = 4;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "SIP_DELEGATE_DESTROY_REASON", value = {
+            SIP_DELEGATE_DESTROY_REASON_UNKNOWN,
+            SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD,
+            SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP,
+            SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS,
+            SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN
+    })
+    public @interface SipDelegateDestroyReason {}
+
+    private final Context mContext;
+    private final int mSubId;
+    private final BinderCacheManager<IImsRcsController> mBinderCache;
+    private final BinderCacheManager<ITelephony> mTelephonyBinderCache;
+
+    /**
+     * Only visible for testing. To instantiate an instance of this class, please use
+     * {@link ImsManager#getSipDelegateManager(int)}.
+     * @hide
+     */
+    @VisibleForTesting
+    public SipDelegateManager(Context context, int subId,
+            BinderCacheManager<IImsRcsController> binderCache,
+            BinderCacheManager<ITelephony> telephonyBinderCache) {
+        mContext = context;
+        mSubId = subId;
+        mBinderCache = binderCache;
+        mTelephonyBinderCache = telephonyBinderCache;
+    }
+
+    /**
+     * Determines if creating SIP delegates are supported for the subscription specified.
+     * <p>
+     * If SIP delegates are not supported on this device or the carrier associated with this
+     * subscription, creating a SIP delegate will always fail, as this feature is not supported.
+     * @return true if this device supports creating a SIP delegate and the carrier associated with
+     * this subscription supports single registration, false if creating SIP delegates is not
+     * supported.
+     * @throws ImsException If the remote ImsService is not available for any reason or the
+     * subscription associated with this instance is no longer active. See
+     * {@link ImsException#getCode()} for more information.
+     *
+     * @see CarrierConfigManager.Ims#KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL
+     * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION
+     */
+    @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
+    public boolean isSupported() throws ImsException {
+        try {
+            IImsRcsController controller = mBinderCache.getBinder();
+            if (controller == null) {
+                throw new ImsException("Telephony server is down",
+                        ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+            }
+            return controller.isSipDelegateSupported(mSubId);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(),
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Request that the ImsService implementation create a SipDelegate, which will configure the
+     * ImsService to forward SIP traffic that matches the filtering criteria set in supplied
+     * {@link DelegateRequest} to the application that the supplied callbacks are registered for.
+     * <p>
+     * This API requires that the caller is running as part of a long-running process and will
+     * always be available to handle incoming messages. One mechanism that can be used for this is
+     * the {@link android.service.carrier.CarrierMessagingClientService}, which the framework keeps
+     * a persistent binding to when the app is the default SMS application.
+     * <p>
+     * Note: the ability to create SipDelegates is only available applications running as the
+     * primary user.
+     * @param request The parameters that are associated with the SipDelegate creation request that
+     *                will be used to create the SipDelegate connection.
+     * @param executor The executor that will be used to call the callbacks associated with this
+     *          SipDelegate.
+     * @param dc The callback that will be used to notify the listener of the creation/destruction
+     *           of the remote SipDelegate as well as changes to the state of the remote SipDelegate
+     *           connection.
+     * @param mc The callback that will be used to notify the listener of new incoming SIP messages
+     *           as well as the status of messages that were sent by the associated
+     *           SipDelegateConnection.
+     * @throws ImsException Thrown if there was a problem communicating with the ImsService
+     * associated with this SipDelegateManager. See {@link ImsException#getCode()}.
+     */
+    @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
+    public void createSipDelegate(@NonNull DelegateRequest request, @NonNull Executor executor,
+            @NonNull DelegateConnectionStateCallback dc,
+            @NonNull DelegateConnectionMessageCallback mc) throws ImsException {
+        Objects.requireNonNull(request, "The DelegateRequest must not be null.");
+        Objects.requireNonNull(executor, "The Executor must not be null.");
+        Objects.requireNonNull(dc, "The DelegateConnectionStateCallback must not be null.");
+        Objects.requireNonNull(mc, "The DelegateConnectionMessageCallback must not be null.");
+        try {
+            SipDelegateConnectionAidlWrapper wrapper =
+                    new SipDelegateConnectionAidlWrapper(executor, dc, mc);
+            IImsRcsController controller = mBinderCache.listenOnBinder(wrapper,
+                    wrapper::binderDied);
+            if (controller == null) {
+                throw new ImsException("Telephony server is down",
+                        ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+            }
+            controller.createSipDelegate(mSubId, request, mContext.getOpPackageName(),
+                    wrapper.getStateCallbackBinder(), wrapper.getMessageCallbackBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(),
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Destroy a previously created {@link SipDelegateConnection} that was created using
+     * {@link #createSipDelegate}.
+     * <p>
+     * This will also clean up all related callbacks in the associated ImsService.
+     * @param delegateConnection The SipDelegateConnection to destroy.
+     * @param reason The reason for why this SipDelegateConnection was destroyed.
+     */
+    @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
+    public void destroySipDelegate(@NonNull SipDelegateConnection delegateConnection,
+            @SipDelegateDestroyReason int reason) {
+        Objects.requireNonNull(delegateConnection, "SipDelegateConnection can not be null.");
+        if (delegateConnection instanceof SipDelegateConnectionAidlWrapper) {
+            SipDelegateConnectionAidlWrapper w =
+                    (SipDelegateConnectionAidlWrapper) delegateConnection;
+            try {
+                IImsRcsController c = mBinderCache.removeRunnable(w);
+                c.destroySipDelegate(mSubId, w.getSipDelegateBinder(), reason);
+            } catch (RemoteException e) {
+                // Connection to telephony died, but this will signal destruction of SipDelegate
+                // eventually anyway, so return normally.
+                try {
+                    w.getStateCallbackBinder().onDestroyed(
+                            SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+                } catch (RemoteException ignore) {
+                    // Local to process.
+                }
+            }
+        } else {
+            throw new IllegalArgumentException("Unknown SipDelegateConnection implementation passed"
+                    + " into this method");
+        }
+    }
+
+    /**
+     * Trigger a full network registration as required by receiving a SIP message containing a
+     * permanent error from the network or never receiving a response to a SIP transaction request.
+     *
+     * @param connection The {@link SipDelegateConnection} that was being used when this error was
+     *         received.
+     * @param sipCode The SIP code response associated with the SIP message request that
+     *         triggered this condition.
+     * @param sipReason The SIP reason code associated with the SIP message request that triggered
+     *         this condition. May be {@code null} if there was no reason String provided from the
+     *         network.
+     */
+    @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
+    public void triggerFullNetworkRegistration(@NonNull SipDelegateConnection connection,
+            @IntRange(from = 100, to = 699) int sipCode, @Nullable String sipReason) {
+        Objects.requireNonNull(connection, "SipDelegateConnection can not be null.");
+        if (connection instanceof SipDelegateConnectionAidlWrapper) {
+            SipDelegateConnectionAidlWrapper w = (SipDelegateConnectionAidlWrapper) connection;
+            try {
+                IImsRcsController controller = mBinderCache.getBinder();
+                controller.triggerNetworkRegistration(mSubId, w.getSipDelegateBinder(), sipCode,
+                        sipReason);
+            } catch (RemoteException e) {
+                // Connection to telephony died, but this will signal destruction of SipDelegate
+                // eventually anyway, so return.
+            }
+        } else {
+            throw new IllegalArgumentException("Unknown SipDelegateConnection implementation passed"
+                    + " into this method");
+        }
+    }
+
+    /**
+     * Register a new callback, which is used to notify the registrant of changes to
+     * the state of the underlying  IMS service that is attached to telephony to
+     * implement IMS functionality. If the manager is created for
+     * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
+     * this throws an {@link ImsException}.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
+     * or that the calling app has carrier privileges
+     * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
+     * @param callback The callback instance being registered.
+     * @throws ImsException in the case that the callback can not be registered.
+     * See {@link ImsException#getCode} for more information on when this is called.
+     */
+    @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
+    public void registerImsStateCallback(@NonNull Executor executor,
+            @NonNull ImsStateCallback callback) throws ImsException {
+        Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+        Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+        callback.init(executor);
+        ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied);
+        if (telephony == null) {
+            throw new ImsException("Telephony server is down",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            telephony.registerImsStateCallback(
+                    mSubId, ImsFeature.FEATURE_RCS,
+                    callback.getCallbackBinder(), mContext.getOpPackageName());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException | IllegalStateException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Unregisters a previously registered callback.
+     *
+     * @param callback The callback instance to be unregistered.
+     */
+    public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
+        Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+
+        ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback);
+
+        try {
+            if (telephony != null) {
+                telephony.unregisterImsStateCallback(callback.getCallbackBinder());
+            }
+        } catch (RemoteException ignore) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Register a new callback, which is used to notify the registrant of changes
+     * to the state of the Sip Sessions managed remotely by the IMS stack.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @param executor the Executor that will be used to call the {@link SipDialogStateCallback}.
+     * @param callback The callback instance being registered.
+     * @throws ImsException in the case that the callback can not be registered.
+     * See {@link ImsException#getCode} for more information on when this is called.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void registerSipDialogStateCallback(@NonNull Executor executor,
+            @NonNull SipDialogStateCallback callback) throws ImsException {
+        Objects.requireNonNull(callback, "Must include a non-null SipDialogStateCallback.");
+        Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+        callback.attachExecutor(executor);
+        try {
+            IImsRcsController controller = mBinderCache.listenOnBinder(
+                    callback, callback::binderDied);
+            if (controller == null) {
+                throw new ImsException("Telephony server is down",
+                        ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+            }
+            controller.registerSipDialogStateCallback(mSubId, callback.getCallbackBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        } catch (IllegalStateException e) {
+            throw new IllegalStateException(e.getMessage());
+        }
+    }
+
+    /**
+     * Unregisters a previously registered callback.
+     *
+     *  <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @param callback The callback instance to be unregistered.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION}.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void unregisterSipDialogStateCallback(@NonNull SipDialogStateCallback callback)
+            throws ImsException {
+        Objects.requireNonNull(callback, "Must include a non-null SipDialogStateCallback.");
+
+        IImsRcsController controller = mBinderCache.removeRunnable(callback);
+        try {
+            if (controller == null) {
+                throw new ImsException("Telephony server is down",
+                        ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+            }
+            controller.unregisterSipDialogStateCallback(mSubId, callback.getCallbackBinder());
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        } catch (IllegalStateException e) {
+            throw new IllegalStateException(e.getMessage());
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/SipDetails.java b/android-35/android/telephony/ims/SipDetails.java
new file mode 100644
index 0000000..fe58eb3
--- /dev/null
+++ b/android-35/android/telephony/ims/SipDetails.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2022 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.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Contains the information for SIP.
+ */
+public final class SipDetails implements Parcelable {
+    /**
+     * Define a SIP method type related to this information.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "METHOD_", value = {
+            METHOD_UNKNOWN,
+            METHOD_REGISTER,
+            METHOD_PUBLISH,
+            METHOD_SUBSCRIBE
+    })
+    public @interface Method {}
+
+    public static final int METHOD_UNKNOWN = 0;
+
+    /**
+     * Indicates information related to the SIP registration method.
+     * See RFC 3261 for details.
+     */
+    public static final int METHOD_REGISTER = 1;
+    /**
+     * Indicates information related to the SIP publication method.
+     * See RFC 3903 for details.
+     */
+    public static final int METHOD_PUBLISH = 2;
+    /**
+     * Indicates information related to the SIP subscription method.
+     * See RFC 3856 for details.
+     */
+    public static final int METHOD_SUBSCRIBE = 3;
+
+    /**
+     * Builder for creating {@link SipDetails} instances.
+     * @hide
+     */
+    public static final class Builder {
+        private int mMethod;
+        // Command Sequence value defined in RFC3261 Section 8.1.1.5
+        private int mCseq = 0;
+        private int mResponseCode = 0;
+        private String mResponsePhrase = "";
+        private int mReasonHeaderCause = 0;
+        private String mReasonHeaderText = "";
+        private @Nullable String mCallId;
+
+        /**
+         * Build a new instance of {@link SipDetails}.
+         *
+         * @param method Indicates which SIP method this information is for.
+         */
+        public Builder(@Method int method) {
+            this.mMethod = method;
+        }
+
+        /**
+         * Sets the value of the CSeq header field for this SIP method.
+         * The CSeq header field serves as a way to identify and order transactions.
+         * It consists of a sequence number and a method.
+         * The method MUST match that of the request.
+         * Ref RFC3261 Section 8.1.1.5.
+         * @param cSeq The value of the CSeq header field.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setCSeq(int cSeq) {
+            this.mCseq = cSeq;
+            return this;
+        }
+
+        /**
+         * Sets the SIP response code and reason response for this SIP method.
+         * Ref RFC3261 Section 21.
+         * @param responseCode The SIP response code sent from the network for the
+         * operation token specified.
+         * @param responsePhrase The optional reason response from the network. If
+         * there is a reason header included in the response, that should take
+         * precedence over the reason provided in the status line. If the network
+         * provided no reason with the SIP code, the string should be empty.
+         * @return The same instance of the builder.
+         */
+        public Builder setSipResponseCode(int responseCode,
+                @NonNull String responsePhrase) {
+            this.mResponseCode = responseCode;
+            this.mResponsePhrase = responsePhrase;
+            return this;
+        }
+
+
+        /**
+         * Sets the detailed information of reason header for this SIP method.
+         * Ref RFC3326.
+         * @param reasonHeaderCause The “cause” parameter of the “reason”
+         * header included in the SIP message.
+         * @param reasonHeaderText The “text” parameter of the “reason”
+         * header included in the SIP message.
+         * @return The same instance of the builder.
+         */
+        public Builder setSipResponseReasonHeader(int reasonHeaderCause,
+                @NonNull String reasonHeaderText) {
+            this.mReasonHeaderCause = reasonHeaderCause;
+            this.mReasonHeaderText = reasonHeaderText;
+            return this;
+        }
+
+
+        /**
+         * Sets the value of the Call-ID header field for this SIP method.
+         * Ref RFC3261 Section 8.1.1.4.
+         * @param callId The value of the Call-ID header field.
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setCallId(@NonNull String callId) {
+            this.mCallId = callId;
+            return this;
+        }
+
+        /**
+         * @return a new SipDetails from this Builder.
+         */
+        public @NonNull SipDetails build() {
+            return new SipDetails(this);
+        }
+    }
+
+    private final int mMethod;
+    private final int mCseq;
+    private final int mResponseCode;
+    private final @NonNull String mResponsePhrase;
+    private final int mReasonHeaderCause;
+    private final @NonNull String mReasonHeaderText;
+    private final @Nullable String mCallId;
+
+    private SipDetails(Builder builder) {
+        mMethod = builder.mMethod;
+        mCseq = builder.mCseq;
+        mResponseCode = builder.mResponseCode;
+        mResponsePhrase = builder.mResponsePhrase;
+        mReasonHeaderCause = builder.mReasonHeaderCause;
+        mReasonHeaderText = builder.mReasonHeaderText;
+        mCallId = builder.mCallId;
+    }
+
+    /**
+     * Get the method type of this instance.
+     * @return The method type associated with this SIP information.
+     */
+    public @Method int getMethod() {
+        return mMethod;
+    }
+
+    /**
+     * Get the value of CSeq header field.
+     * The CSeq header field serves as a way to identify and order transactions.
+     * @return The command sequence value associated with this SIP information.
+     */
+    public int getCSeq() {
+        return mCseq;
+    }
+
+    /**
+     * Get the value of response code from the SIP response.
+     * The SIP response code sent from the network for the operation token specified.
+     * @return The SIP response code associated with this SIP information.
+     */
+    public int getResponseCode() {
+        return mResponseCode;
+    }
+
+    /**
+     * Get the value of reason from the SIP response.
+     * The optional reason response from the network. If
+     * there is a reason header included in the response, that should take
+     * precedence over the reason provided in the status line.
+     * @return The optional reason response associated with this SIP information. If the network
+     *          provided no reason with the SIP code, the string should be empty.
+     */
+    public @NonNull String getResponsePhrase() {
+        return mResponsePhrase;
+    }
+
+    /**
+     * Get the "cause" parameter of the "reason" header.
+     * @return The "cause" parameter of the reason header. If the SIP message from the network
+     *          does not have a reason header, it should be 0.
+     */
+    public int getReasonHeaderCause() {
+        return mReasonHeaderCause;
+    }
+
+    /**
+     * Get the "text" parameter of the "reason" header in the SIP message.
+     * @return The "text" parameter of the reason header. If the SIP message from the network
+     *          does not have a reason header, it can be empty.
+     */
+    public @NonNull String getReasonHeaderText() {
+        return mReasonHeaderText;
+    }
+
+    /**
+     * Get the value of the Call-ID header field for this SIP method.
+     * @return The Call-ID value associated with this SIP information. If the Call-ID value is
+     *          not set when ImsService notifies the framework, this value will be null.
+     */
+    public @Nullable String getCallId() {
+        return mCallId;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mMethod);
+        dest.writeInt(mCseq);
+        dest.writeInt(mResponseCode);
+        dest.writeString(mResponsePhrase);
+        dest.writeInt(mReasonHeaderCause);
+        dest.writeString(mReasonHeaderText);
+        dest.writeString(mCallId);
+    }
+
+    public static final @NonNull Creator<SipDetails> CREATOR =
+            new Creator<SipDetails>() {
+                @Override
+                public SipDetails createFromParcel(Parcel source) {
+                    return new SipDetails(source);
+                }
+
+                @Override
+                public SipDetails[] newArray(int size) {
+                    return new SipDetails[size];
+                }
+            };
+
+    /**
+     * Construct a SipDetails object from the given parcel.
+     */
+    private SipDetails(Parcel in) {
+        mMethod = in.readInt();
+        mCseq = in.readInt();
+        mResponseCode = in.readInt();
+        mResponsePhrase = in.readString();
+        mReasonHeaderCause = in.readInt();
+        mReasonHeaderText = in.readString();
+        mCallId = in.readString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SipDetails that = (SipDetails) o;
+        return mMethod == that.mMethod
+                && mCseq == that.mCseq
+                && mResponseCode == that.mResponseCode
+                && TextUtils.equals(mResponsePhrase, that.mResponsePhrase)
+                && mReasonHeaderCause == that.mReasonHeaderCause
+                && TextUtils.equals(mReasonHeaderText, that.mReasonHeaderText)
+                && TextUtils.equals(mCallId, that.mCallId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mMethod, mCseq, mResponseCode, mResponsePhrase, mReasonHeaderCause,
+                mReasonHeaderText, mCallId);
+    }
+
+    @Override
+    public String toString() {
+        return "SipDetails { methodType= " + mMethod + ", cSeq=" + mCseq
+                + ", ResponseCode=" + mResponseCode + ", ResponseCPhrase=" + mResponsePhrase
+                + ", ReasonHeaderCause=" + mReasonHeaderCause
+                + ", ReasonHeaderText=" + mReasonHeaderText + ", callId=" + mCallId + "}";
+    }
+}
diff --git a/android-35/android/telephony/ims/SipDialogState.java b/android-35/android/telephony/ims/SipDialogState.java
new file mode 100644
index 0000000..815c59a
--- /dev/null
+++ b/android-35/android/telephony/ims/SipDialogState.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2022 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.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The state of an ongoing SIP dialog.
+ * @hide
+ */
+@SystemApi
+public final class SipDialogState implements Parcelable {
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "STATE_", value = {STATE_EARLY, STATE_CONFIRMED, STATE_CLOSED})
+    public @interface SipDialogStateCode {}
+    /**
+     * The device has sent out a dialog starting event and is awaiting a confirmation.
+     */
+    public static final int STATE_EARLY = 0;
+
+    /**
+     * The device has received a 2XX response to the early dialog.
+     */
+    public static final int STATE_CONFIRMED = 1;
+
+    /**
+     * The device has received either a 3XX+ response to a pending dialog request or a BYE
+     * request has been sent on this dialog.
+     */
+    public static final int STATE_CLOSED = 2;
+
+    private final int mState;
+
+    /**
+     * Builder for {@link SipDialogState}.
+     * @hide
+     */
+    public static final class Builder {
+        private int mState = STATE_EARLY;
+
+        /**
+         * constructor
+         * @param state The state of SipDialog
+         */
+        public Builder(@SipDialogStateCode int state) {
+            mState = state;
+        }
+
+        /**
+         * Build the {@link SipDialogState}.
+         * @return The {@link SipDialogState} instance.
+         */
+        public @NonNull SipDialogState build() {
+            return new SipDialogState(this);
+        }
+    }
+
+    /**
+     * set Dialog state
+     */
+    private SipDialogState(@NonNull Builder builder) {
+        this.mState = builder.mState;
+    }
+
+    private SipDialogState(Parcel in) {
+        mState = in.readInt();
+    }
+
+    /**
+     * @return The state of the SIP dialog
+     */
+    public @SipDialogStateCode int getState() {
+        return mState;
+    }
+
+    public static final @NonNull Creator<SipDialogState> CREATOR = new Creator<SipDialogState>() {
+        @Override
+        public SipDialogState createFromParcel(@NonNull Parcel in) {
+            return new SipDialogState(in);
+        }
+
+        @Override
+        public SipDialogState[] newArray(int size) {
+            return new SipDialogState[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mState);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SipDialogState sipDialog = (SipDialogState) o;
+
+        return mState == sipDialog.mState;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mState);
+    }
+}
diff --git a/android-35/android/telephony/ims/SipDialogStateCallback.java b/android-35/android/telephony/ims/SipDialogStateCallback.java
new file mode 100644
index 0000000..5730bac
--- /dev/null
+++ b/android-35/android/telephony/ims/SipDialogStateCallback.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 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.ims;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Binder;
+
+import com.android.internal.telephony.ISipDialogStateCallback;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * This callback is used to notify listeners of SIP Dialog state changes.
+ * @hide
+ */
+@SystemApi
+public abstract class SipDialogStateCallback {
+
+    private CallbackBinder mCallback;
+    /**
+    * @hide
+    */
+    public void attachExecutor(@NonNull @CallbackExecutor Executor executor) {
+        if (executor == null) {
+            throw new IllegalArgumentException("SipDialogStateCallback Executor must be non-null");
+        }
+        mCallback = new CallbackBinder(this, executor);
+    }
+
+    private static class CallbackBinder extends ISipDialogStateCallback.Stub {
+        private WeakReference<SipDialogStateCallback> mSipDialogStateCallbackWeakRef;
+        private Executor mExecutor;
+
+        private CallbackBinder(SipDialogStateCallback callback, Executor executor) {
+            mSipDialogStateCallbackWeakRef = new WeakReference<SipDialogStateCallback>(callback);
+            mExecutor = executor;
+        }
+
+        Executor getExecutor() {
+            return mExecutor;
+        }
+
+        @Override
+        public void onActiveSipDialogsChanged(List<SipDialogState> dialogs) {
+            SipDialogStateCallback callback = mSipDialogStateCallbackWeakRef.get();
+            if (callback == null || dialogs == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> callback.onActiveSipDialogsChanged(dialogs)));
+        }
+    }
+
+    /**
+     * The state of one or more SIP dialogs has changed.
+     *
+     * @param dialogs A List of SipDialogState objects representing the state of the active
+     *               SIP Dialogs.
+     */
+    public abstract void onActiveSipDialogsChanged(@NonNull List<SipDialogState> dialogs);
+
+    /**
+     * An unexpected error has occurred and the Telephony process has crashed. This
+     * has caused this callback to be deregistered. The callback must be re-registered
+     * in order to continue listening to the IMS service state.
+     */
+    public abstract void onError();
+
+    /**
+     * The callback to notify the death of telephony process
+     * @hide
+     */
+    public final void binderDied() {
+        if (mCallback != null) {
+            mCallback.getExecutor().execute(() -> onError());
+        }
+    }
+
+    /**
+     * Return the callback binder
+     * @hide
+     */
+    public CallbackBinder getCallbackBinder() {
+        return mCallback;
+    }
+}
diff --git a/android-35/android/telephony/ims/SipMessage.java b/android-35/android/telephony/ims/SipMessage.java
new file mode 100644
index 0000000..acc6243
--- /dev/null
+++ b/android-35/android/telephony/ims/SipMessage.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.SipMessageParsingUtils;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Represents a partially encoded SIP message. See RFC 3261 for more information on how SIP
+ * messages are structured and used.
+ * <p>
+ * The SIP message is represented in a partially encoded form in order to allow for easier
+ * verification and should not be used as a generic SIP message container.
+ * @hide
+ */
+@SystemApi
+public final class SipMessage implements Parcelable {
+    // Should not be set to true for production!
+    private static final boolean IS_DEBUGGING = Build.IS_ENG;
+    private static final String CRLF = "\r\n";
+
+    private final String mStartLine;
+    private final String mHeaderSection;
+    private final byte[] mContent;
+    private final String mViaBranchParam;
+    private final String mCallIdParam;
+
+    /**
+     * Represents a partially encoded SIP message.
+     *
+     * @param startLine The start line of the message, containing either the request-line or
+     *                  status-line.
+     * @param headerSection A String containing the full unencoded SIP message header.
+     * @param content SIP message body.
+     */
+    public SipMessage(@NonNull String startLine, @NonNull String headerSection,
+            @NonNull byte[] content) {
+        Objects.requireNonNull(startLine, "Required parameter is null: startLine");
+        Objects.requireNonNull(headerSection, "Required parameter is null: headerSection");
+        Objects.requireNonNull(content, "Required parameter is null: content");
+
+        mStartLine = startLine;
+        mHeaderSection = headerSection;
+        mContent = content;
+
+        mViaBranchParam = SipMessageParsingUtils.getTransactionId(mHeaderSection);
+        if (TextUtils.isEmpty(mViaBranchParam)) {
+            throw new IllegalArgumentException("header section MUST contain a branch parameter "
+                    + "inside of the Via header.");
+        }
+        mCallIdParam = SipMessageParsingUtils.getCallId(mHeaderSection);
+    }
+
+    /**
+     * Private constructor used only for unparcelling.
+     */
+    private SipMessage(Parcel source) {
+        mStartLine = source.readString();
+        mHeaderSection = source.readString();
+        mContent = new byte[source.readInt()];
+        source.readByteArray(mContent);
+        mViaBranchParam = source.readString();
+        mCallIdParam = source.readString();
+    }
+
+    /**
+     * @return The start line of the SIP message, which contains either the request-line or
+     * status-line.
+     */
+    public @NonNull String getStartLine() {
+        return mStartLine;
+    }
+
+    /**
+     * @return The full, unencoded header section of the SIP message.
+     */
+    public @NonNull String getHeaderSection() {
+        return mHeaderSection;
+    }
+
+    /**
+     * @return the SIP message body.
+     */
+    public @NonNull byte[] getContent() {
+        return mContent;
+    }
+
+    /**
+     * @return the branch parameter enclosed in the Via header key's value. See RFC 3261 section
+     * 20.42 for more information on the Via header.
+     */
+    public @NonNull String getViaBranchParameter() {
+        return mViaBranchParam;
+    }
+
+    /**
+     * @return the value associated with the call-id header of this SIP message. See RFC 3261
+     * section 20.8 for more information on the call-id header. If {@code null}, then there was no
+     * call-id header found in this SIP message's headers.
+     */
+    public @Nullable String getCallIdParameter() {
+        return mCallIdParam;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mStartLine);
+        dest.writeString(mHeaderSection);
+        dest.writeInt(mContent.length);
+        dest.writeByteArray(mContent);
+        dest.writeString(mViaBranchParam);
+        dest.writeString(mCallIdParam);
+    }
+
+    public static final @NonNull Creator<SipMessage> CREATOR = new Creator<SipMessage>() {
+        @Override
+        public SipMessage createFromParcel(Parcel source) {
+            return new SipMessage(source);
+        }
+
+        @Override
+        public SipMessage[] newArray(int size) {
+            return new SipMessage[size];
+        }
+    };
+
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder();
+        b.append("StartLine: [");
+        if (IS_DEBUGGING) {
+            b.append(mStartLine);
+        } else {
+            b.append(sanitizeStartLineRequest(mStartLine));
+        }
+        b.append("], Header: [");
+        if (IS_DEBUGGING) {
+            b.append(mHeaderSection);
+        } else {
+            // only identify transaction id/call ID when it is available.
+            b.append("***");
+        }
+        b.append("], Content: ");
+        b.append(getContent().length == 0 ? "[NONE]" : "[NOT SHOWN]");
+        return b.toString();
+    }
+
+    /**
+     * Detect if this is a REQUEST and redact Request-URI portion here, as it contains PII.
+     */
+    private String sanitizeStartLineRequest(String startLine) {
+        if (!SipMessageParsingUtils.isSipRequest(startLine)) return startLine;
+        String[] splitLine = startLine.split(" ");
+        return splitLine[0] + " <Request-URI> " + splitLine[2];
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SipMessage that = (SipMessage) o;
+        return mStartLine.equals(that.mStartLine)
+                && mHeaderSection.equals(that.mHeaderSection)
+                && Arrays.equals(mContent, that.mContent);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mStartLine, mHeaderSection);
+        result = 31 * result + Arrays.hashCode(mContent);
+        return result;
+    }
+
+    /**
+     * According RFC-3261 section 7, SIP is a text protocol and uses the UTF-8 charset. Its format
+     * consists of a start-line, one or more header fields, an empty line indicating the end of the
+     * header fields, and an optional message-body.
+     *
+     * <p>
+     * Returns a byte array with UTF-8 format representation of the encoded SipMessage.
+     *
+     * @return byte array with UTF-8 format representation of the encoded SipMessage.
+     */
+    public @NonNull byte[] toEncodedMessage() {
+        byte[] header = new StringBuilder()
+                .append(mStartLine)
+                .append(mHeaderSection)
+                .append(CRLF)
+                .toString().getBytes(UTF_8);
+        byte[] sipMessage = new byte[header.length + mContent.length];
+        System.arraycopy(header, 0, sipMessage, 0, header.length);
+        System.arraycopy(mContent, 0, sipMessage, header.length, mContent.length);
+        return sipMessage;
+    }
+}
diff --git a/android-35/android/telephony/ims/SrvccCall.java b/android-35/android/telephony/ims/SrvccCall.java
new file mode 100644
index 0000000..cdc271e
--- /dev/null
+++ b/android-35/android/telephony/ims/SrvccCall.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2022 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.ims;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.PreciseCallStates;
+
+import java.util.Objects;
+
+/**
+ * A Parcelable object to represent the current state of an IMS call that is being tracked
+ * in the ImsService when an SRVCC begins. This information will be delivered to modem.
+ * @see SrvccStartedCallback
+ *
+ * @hide
+ */
+@SystemApi
+public final class SrvccCall implements Parcelable {
+    private static final String TAG = "SrvccCall";
+
+    /** The IMS call profile */
+    private ImsCallProfile mImsCallProfile;
+
+    /** The IMS call id */
+    private String mCallId;
+
+    /** The call state */
+    private @PreciseCallStates int mCallState;
+
+    private SrvccCall(Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * Constructs an instance of SrvccCall.
+     *
+     * @param callId the call ID associated with the IMS call
+     * @param callState the state of this IMS call
+     * @param imsCallProfile the profile associated with this IMS call
+     * @throws IllegalArgumentException if the callId or the imsCallProfile is null
+     */
+    public SrvccCall(@NonNull String callId, @PreciseCallStates int callState,
+            @NonNull ImsCallProfile imsCallProfile) {
+        if (callId == null) throw new IllegalArgumentException("callId is null");
+        if (imsCallProfile == null) throw new IllegalArgumentException("imsCallProfile is null");
+
+        mCallId = callId;
+        mCallState = callState;
+        mImsCallProfile = imsCallProfile;
+    }
+
+    /**
+     * @return the {@link ImsCallProfile} associated with this IMS call,
+     * which will be used to get the address, the name, and the audio direction
+     * including the call in pre-alerting state.
+     */
+    @NonNull
+    public ImsCallProfile getImsCallProfile() {
+        return mImsCallProfile;
+    }
+
+    /**
+     * @return the call ID associated with this IMS call.
+     *
+     * @see android.telephony.ims.stub.ImsCallSessionImplBase#getCallId().
+     */
+    @NonNull
+    public String getCallId() {
+        return mCallId;
+    }
+
+    /**
+     * @return the call state of the associated IMS call.
+     */
+    public @PreciseCallStates int getPreciseCallState() {
+        return mCallState;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "{ callId=" + mCallId
+                + ", callState=" + mCallState
+                + ", imsCallProfile=" + mImsCallProfile
+                + " }";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SrvccCall that = (SrvccCall) o;
+        return mImsCallProfile.equals(that.mImsCallProfile)
+                && mCallId.equals(that.mCallId)
+                && mCallState == that.mCallState;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mImsCallProfile, mCallId);
+        result = 31 * result + mCallState;
+        return result;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeString(mCallId);
+        out.writeInt(mCallState);
+        out.writeParcelable(mImsCallProfile, 0);
+    }
+
+    private void readFromParcel(Parcel in) {
+        mCallId = in.readString();
+        mCallState = in.readInt();
+        mImsCallProfile = in.readParcelable(ImsCallProfile.class.getClassLoader(),
+                android.telephony.ims.ImsCallProfile.class);
+    }
+
+    public static final @android.annotation.NonNull Creator<SrvccCall> CREATOR =
+            new Creator<SrvccCall>() {
+        @Override
+        public SrvccCall createFromParcel(Parcel in) {
+            return new SrvccCall(in);
+        }
+
+        @Override
+        public SrvccCall[] newArray(int size) {
+            return new SrvccCall[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java b/android-35/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
new file mode 100644
index 0000000..0514df2
--- /dev/null
+++ b/android-35/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.SipDetails;
+import android.telephony.ims.stub.CapabilityExchangeEventListener;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * The ICapabilityExchangeEventListener wrapper class to store the listener which is registered by
+ * the framework. This wrapper class also delivers the request to the framework when receive the
+ * request from the network.
+ * @hide
+ */
+public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventListener {
+
+    private static final String LOG_TAG = "CapExchangeListener";
+
+    private final ICapabilityExchangeEventListener mListenerBinder;
+
+    public CapabilityExchangeAidlWrapper(@Nullable ICapabilityExchangeEventListener listener) {
+        mListenerBinder = listener;
+    }
+
+    /**
+     * Receives the request of publishing capabilities from the network and deliver this request
+     * to the framework via the registered capability exchange event listener.
+     */
+    public void onRequestPublishCapabilities(int publishTriggerType) throws ImsException {
+        ICapabilityExchangeEventListener listener = mListenerBinder;
+        if (listener == null) {
+            return;
+        }
+        try {
+            listener.onRequestPublishCapabilities(publishTriggerType);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "request publish capabilities exception: " + e);
+            throw new ImsException("Remote is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Receives the unpublish notification and deliver this callback to the framework.
+     */
+    public void onUnpublish() throws ImsException {
+        ICapabilityExchangeEventListener listener = mListenerBinder;
+        if (listener == null) {
+            return;
+        }
+        try {
+            listener.onUnpublish();
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Unpublish exception: " + e);
+            throw new ImsException("Remote is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Receives the status of changes in the publishing connection from ims service
+     * and deliver this callback to the framework.
+     *
+     * @deprecated Replaced by {@link #onPublishUpdated(SipDetails)}, deprecated for
+     * sip information.
+     */
+    @Deprecated
+    public void onPublishUpdated(int reasonCode, @NonNull String reasonPhrase,
+            int reasonHeaderCause, @NonNull String reasonHeaderText) throws ImsException {
+        ICapabilityExchangeEventListener listener = mListenerBinder;
+        if (listener == null) {
+            return;
+        }
+        try {
+            SipDetails details = new SipDetails.Builder(SipDetails.METHOD_PUBLISH)
+                    .setSipResponseCode(reasonCode, reasonPhrase)
+                    .setSipResponseReasonHeader(reasonHeaderCause, reasonHeaderText)
+                    .build();
+            listener.onPublishUpdated(details);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "onPublishUpdated exception: " + e);
+            throw new ImsException("Remote is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Receives the status of changes in the publishing connection from ims service
+     * and deliver this callback to the framework.
+     */
+    public void onPublishUpdated(@NonNull SipDetails details) throws ImsException {
+        ICapabilityExchangeEventListener listener = mListenerBinder;
+        if (listener == null) {
+            return;
+        }
+        try {
+            listener.onPublishUpdated(details);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "onPublishUpdated exception: " + e);
+            throw new ImsException("Remote is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Receives the callback of the remote capability request from the network and deliver this
+     * request to the framework.
+     */
+    public void onRemoteCapabilityRequest(@NonNull Uri contactUri,
+            @NonNull Set<String> remoteCapabilities, @NonNull OptionsRequestCallback callback)
+            throws ImsException {
+        ICapabilityExchangeEventListener listener = mListenerBinder;
+        if (listener == null) {
+            return;
+        }
+
+        IOptionsRequestCallback internalCallback = new IOptionsRequestCallback.Stub() {
+            @Override
+            public void respondToCapabilityRequest(RcsContactUceCapability ownCapabilities,
+                    boolean isBlocked) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    callback.onRespondToCapabilityRequest(ownCapabilities, isBlocked);
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+            @Override
+            public void respondToCapabilityRequestWithError(int code, String reason) {
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    callback.onRespondToCapabilityRequestWithError(code, reason);
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+        };
+
+        try {
+            listener.onRemoteCapabilityRequest(contactUri, new ArrayList<>(remoteCapabilities),
+                    internalCallback);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote capability request exception: " + e);
+            throw new ImsException("Remote is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/aidl/RcsOptionsResponseAidlWrapper.java b/android-35/android/telephony/ims/aidl/RcsOptionsResponseAidlWrapper.java
new file mode 100644
index 0000000..47a96af
--- /dev/null
+++ b/android-35/android/telephony/ims/aidl/RcsOptionsResponseAidlWrapper.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.os.RemoteException;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback;
+
+import java.util.List;
+
+/**
+ * Implementation of the callback OptionsResponseCallback by wrapping the internal AIDL from
+ * telephony.
+ * @hide
+ */
+public class RcsOptionsResponseAidlWrapper implements OptionsResponseCallback {
+
+    private final IOptionsResponseCallback mResponseBinder;
+
+    public RcsOptionsResponseAidlWrapper(IOptionsResponseCallback responseBinder) {
+        mResponseBinder = responseBinder;
+    }
+
+    @Override
+    public void onCommandError(int code) {
+        try {
+            mResponseBinder.onCommandError(code);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public void onNetworkResponse(int code, String reason, List<String> theirCaps)
+            throws ImsException {
+        try {
+            mResponseBinder.onNetworkResponse(code, reason, theirCaps);
+        } catch (RemoteException e) {
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java b/android-35/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
new file mode 100644
index 0000000..40cb5ca
--- /dev/null
+++ b/android-35/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.SipDetails;
+import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback;
+
+/**
+ * Implementation of the callback PublishResponseCallback by wrapping the internal AIDL from
+ * telephony.
+ * @hide
+ */
+public class RcsPublishResponseAidlWrapper implements PublishResponseCallback {
+
+    private final IPublishResponseCallback mResponseBinder;
+
+    public RcsPublishResponseAidlWrapper(IPublishResponseCallback responseBinder) {
+        mResponseBinder = responseBinder;
+    }
+
+    @Override
+    public void onCommandError(int code) throws ImsException {
+        try {
+            mResponseBinder.onCommandError(code);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    @Override
+    @Deprecated
+    public void onNetworkResponse(int code, String reasonPhrase) throws ImsException {
+        SipDetails details = new SipDetails.Builder(SipDetails.METHOD_PUBLISH)
+                .setSipResponseCode(code, reasonPhrase)
+                .build();
+        try {
+            mResponseBinder.onNetworkResponse(details);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    @Override
+    @Deprecated
+    public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause,
+            String reasonHeaderText) throws ImsException {
+        SipDetails details = new SipDetails.Builder(SipDetails.METHOD_PUBLISH)
+                .setSipResponseCode(code, reasonPhrase)
+                .setSipResponseReasonHeader(reasonHeaderCause, reasonHeaderText)
+                .build();
+        try {
+            mResponseBinder.onNetworkResponse(details);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    @Override
+    public void onNetworkResponse(@NonNull SipDetails details)
+            throws ImsException {
+        try {
+            mResponseBinder.onNetworkResponse(details);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java b/android-35/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
new file mode 100644
index 0000000..2f54001
--- /dev/null
+++ b/android-35/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactTerminatedReason;
+import android.telephony.ims.SipDetails;
+import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of the callback OptionsResponseCallback by wrapping the internal AIDL from
+ * telephony.
+ * @hide
+ */
+public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallback {
+
+    private final ISubscribeResponseCallback mResponseBinder;
+
+    public RcsSubscribeResponseAidlWrapper(ISubscribeResponseCallback responseBinder) {
+        mResponseBinder = responseBinder;
+    }
+
+    @Override
+    public void onCommandError(int code) throws ImsException {
+        try {
+            mResponseBinder.onCommandError(code);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    @Override
+    @Deprecated
+    public void onNetworkResponse(int code, String reasonPhrase) throws ImsException {
+        try {
+            mResponseBinder.onNetworkResponse(new SipDetails.Builder(
+                    SipDetails.METHOD_SUBSCRIBE)
+                    .setSipResponseCode(code, reasonPhrase)
+                    .build());
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    @Override
+    @Deprecated
+    public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause,
+            String reasonHeaderText) throws ImsException {
+        try {
+            mResponseBinder.onNetworkResponse(new SipDetails.Builder(
+                    SipDetails.METHOD_SUBSCRIBE)
+                    .setSipResponseCode(code, reasonPhrase)
+                    .setSipResponseReasonHeader(reasonHeaderCause, reasonHeaderText)
+                    .build());
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    @Override
+    public void onNetworkResponse(@NonNull SipDetails details) throws ImsException {
+        try {
+            mResponseBinder.onNetworkResponse(details);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    @Override
+    public void onNotifyCapabilitiesUpdate(List<String> pidfXmls) throws ImsException {
+        try {
+            mResponseBinder.onNotifyCapabilitiesUpdate(pidfXmls);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    @Override
+    public void onResourceTerminated(List<Pair<Uri, String>> uriTerminatedReason)
+            throws ImsException {
+        try {
+            mResponseBinder.onResourceTerminated(getTerminatedReasonList(uriTerminatedReason));
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    private List<RcsContactTerminatedReason> getTerminatedReasonList(
+            List<Pair<Uri, String>> uriTerminatedReason) {
+        List<RcsContactTerminatedReason> uriTerminatedReasonList = new ArrayList<>();
+        if (uriTerminatedReason != null) {
+            for (Pair<Uri, String> pair : uriTerminatedReason) {
+                RcsContactTerminatedReason reason =
+                        new RcsContactTerminatedReason(pair.first, pair.second);
+                uriTerminatedReasonList.add(reason);
+            }
+        }
+        return uriTerminatedReasonList;
+    }
+
+    @Override
+    public void onTerminated(String reason, long retryAfterMilliseconds) throws ImsException {
+        try {
+            mResponseBinder.onTerminated(reason, retryAfterMilliseconds);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/android-35/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
new file mode 100644
index 0000000..1ccb464
--- /dev/null
+++ b/android-35/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateStateCallback;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.stub.SipDelegate;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+/**
+ * Implementation of callbacks by wrapping the internal AIDL from telephony. Also implements
+ * ISipDelegate internally when {@link DelegateStateCallback#onCreated(SipDelegate, Set)} is called
+ * in order to trampoline events back to telephony.
+ * @hide
+ */
+public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMessageCallback {
+    private static final String LOG_TAG = "SipDelegateAW";
+
+    private final ISipDelegate.Stub mDelegateBinder = new ISipDelegate.Stub() {
+        @Override
+        public void sendMessage(SipMessage sipMessage, long configVersion) {
+            SipDelegate d = mDelegate;
+            if (d == null) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> d.sendMessage(sipMessage, configVersion));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void notifyMessageReceived(String viaTransactionId)  {
+            SipDelegate d = mDelegate;
+            if (d == null) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> d.notifyMessageReceived(viaTransactionId));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+
+        }
+
+        @Override
+        public void notifyMessageReceiveError(String viaTransactionId, int reason) {
+            SipDelegate d = mDelegate;
+            if (d == null) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> d.notifyMessageReceiveError(viaTransactionId, reason));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+
+        }
+
+        @Override
+        public void cleanupSession(String callId)  {
+            SipDelegate d = mDelegate;
+            if (d == null) {
+                return;
+            }
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> d.cleanupSession(callId));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    };
+
+    private final ISipDelegateMessageCallback mMessageBinder;
+    private final ISipDelegateStateCallback mStateBinder;
+    private final Executor mExecutor;
+
+    private volatile SipDelegate mDelegate;
+
+    public SipDelegateAidlWrapper(Executor executor, ISipDelegateStateCallback stateBinder,
+            ISipDelegateMessageCallback messageBinder) {
+        mExecutor = executor;
+        mStateBinder = stateBinder;
+        mMessageBinder = messageBinder;
+    }
+
+    @Override
+    public void onMessageReceived(SipMessage message) {
+        try {
+            mMessageBinder.onMessageReceived(message);
+        } catch (RemoteException e) {
+            // BinderDied will be called on SipTransport instance to trigger destruction. Notify
+            // failure message failure locally for now.
+            SipDelegate d = mDelegate;
+            if (d != null) {
+                notifyLocalMessageFailedToBeReceived(message,
+                        SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+            }
+        }
+    }
+
+    @Override
+    public void onMessageSent(String viaTransactionId) {
+        try {
+            mMessageBinder.onMessageSent(viaTransactionId);
+        } catch (RemoteException e) {
+            // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+        }
+    }
+
+    @Override
+    public void onMessageSendFailure(String viaTransactionId, int reason) {
+        try {
+            mMessageBinder.onMessageSendFailure(viaTransactionId, reason);
+        } catch (RemoteException e) {
+            // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+        }
+    }
+
+    @Override
+    public void onCreated(@NonNull SipDelegate delegate,
+            @Nullable Set<FeatureTagState> deniedTags) {
+        mDelegate = delegate;
+        deniedTags = (deniedTags == null) ? Collections.emptySet() : deniedTags;
+        try {
+            mStateBinder.onCreated(mDelegateBinder, new ArrayList<>(deniedTags));
+        } catch (RemoteException e) {
+            // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+        }
+    }
+
+    @Override
+    public void onFeatureTagRegistrationChanged(DelegateRegistrationState registrationState) {
+        try {
+            mStateBinder.onFeatureTagRegistrationChanged(registrationState);
+        } catch (RemoteException e) {
+            // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+        }
+    }
+
+    @Override
+    public void onImsConfigurationChanged(@NonNull SipDelegateImsConfiguration config) {
+        try {
+            mStateBinder.onImsConfigurationChanged(config);
+        } catch (RemoteException e) {
+            // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+        }
+    }
+
+    @Override
+    public void onConfigurationChanged(@NonNull SipDelegateConfiguration config) {
+        try {
+            mStateBinder.onConfigurationChanged(config);
+        } catch (RemoteException e) {
+            // BinderDied will trigger destroySipDelegate, so just ignore this locally.
+        }
+    }
+
+    @Override
+    public void onDestroyed(int reasonCode) {
+        mDelegate = null;
+        try {
+            mStateBinder.onDestroyed(reasonCode);
+        } catch (RemoteException e) {
+            // Do not worry about this if the remote side is already dead.
+        }
+    }
+
+    public SipDelegate getDelegate()  {
+        return mDelegate;
+    }
+
+    public ISipDelegate getDelegateBinder() {
+        return mDelegateBinder;
+    }
+
+    public ISipDelegateStateCallback getStateCallbackBinder() {
+        return mStateBinder;
+    }
+
+    private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) {
+        String transactionId = m.getViaBranchParameter();
+        SipDelegate d = mDelegate;
+        if (d != null) {
+            mExecutor.execute(() -> d.notifyMessageReceiveError(transactionId, reason));
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/android-35/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
new file mode 100644
index 0000000..47ddcb9
--- /dev/null
+++ b/android-35/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.stub.DelegateConnectionMessageCallback;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Wrapper class implementing {@link SipDelegateConnection} using AIDL, which is returned to the
+ * local process. Also holds a reference to incoming connection message and state AIDL impl to
+ * trampoline events to callbacks as well as notify the local process in the event that the remote
+ * process becomes unavailable.
+ * <p>
+ * When the remote {@link SipDelegate} is created, this instance tracks the
+ * {@link ISipDelegate} associated with it and implements the
+ * {@link SipDelegateConnection} sent back to the local callback.
+ * @hide
+ */
+public class SipDelegateConnectionAidlWrapper implements SipDelegateConnection,
+        IBinder.DeathRecipient {
+    private static final String LOG_TAG = "SipDelegateCAW";
+
+    private final ISipDelegateConnectionStateCallback.Stub mStateBinder =
+            new ISipDelegateConnectionStateCallback.Stub() {
+        @Override
+        public void onCreated(ISipDelegate c) {
+            associateSipDelegate(c);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() ->
+                        mStateCallback.onCreated(SipDelegateConnectionAidlWrapper.this));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void onFeatureTagStatusChanged(DelegateRegistrationState registrationState,
+                List<FeatureTagState> deniedFeatureTags) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() ->
+                        mStateCallback.onFeatureTagStatusChanged(registrationState,
+                                new ArraySet<>(deniedFeatureTags)));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void onImsConfigurationChanged(SipDelegateImsConfiguration registeredSipConfig) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() ->
+                        mStateCallback.onImsConfigurationChanged(registeredSipConfig));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void onConfigurationChanged(SipDelegateConfiguration registeredSipConfig) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() ->
+                        mStateCallback.onConfigurationChanged(registeredSipConfig));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void onDestroyed(int reason) {
+            invalidateSipDelegateBinder();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() ->
+                        mStateCallback.onDestroyed(reason));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    };
+
+    private final ISipDelegateMessageCallback.Stub mMessageBinder =
+            new ISipDelegateMessageCallback.Stub() {
+                @Override
+                public void onMessageReceived(SipMessage message) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        mExecutor.execute(() ->
+                                mMessageCallback.onMessageReceived(message));
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+
+                @Override
+                public void onMessageSent(String viaTransactionId) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        mExecutor.execute(() ->
+                                mMessageCallback.onMessageSent(viaTransactionId));
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+
+                @Override
+                public void onMessageSendFailure(String viaTransactionId, int reason) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        mExecutor.execute(() ->
+                                mMessageCallback.onMessageSendFailure(viaTransactionId, reason));
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            };
+
+
+    private final Executor mExecutor;
+    private final DelegateConnectionStateCallback mStateCallback;
+    private final DelegateConnectionMessageCallback mMessageCallback;
+    private final AtomicReference<ISipDelegate> mDelegateBinder =
+            new AtomicReference<>();
+
+    /**
+     * Wrap the local state and message callbacks, calling the implementation of these interfaces
+     * when the remote process calls these methods.
+     */
+    public SipDelegateConnectionAidlWrapper(Executor executor,
+            DelegateConnectionStateCallback stateCallback,
+            DelegateConnectionMessageCallback messageCallback) {
+        mExecutor = executor;
+        mStateCallback = stateCallback;
+        mMessageCallback = messageCallback;
+    }
+
+    @Override
+    public void sendMessage(SipMessage sipMessage, long configVersion) {
+        try {
+            ISipDelegate conn = getSipDelegateBinder();
+            if (conn == null) {
+                notifyLocalMessageFailedToSend(sipMessage,
+                        SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+                return;
+            }
+            conn.sendMessage(sipMessage, configVersion);
+        } catch (RemoteException e) {
+            notifyLocalMessageFailedToSend(sipMessage,
+                        SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+        }
+    }
+
+    @Override
+    public void notifyMessageReceived(String viaTransactionId) {
+        try {
+            ISipDelegate conn = getSipDelegateBinder();
+            if (conn == null) {
+                return;
+            }
+            conn.notifyMessageReceived(viaTransactionId);
+        } catch (RemoteException e) {
+            // Nothing to do here, app will eventually get remote death callback.
+        }
+    }
+
+    @Override
+    public void notifyMessageReceiveError(String viaTransactionId, int reason) {
+        try {
+            ISipDelegate conn = getSipDelegateBinder();
+            if (conn == null) {
+                return;
+            }
+            conn.notifyMessageReceiveError(viaTransactionId, reason);
+        } catch (RemoteException e) {
+            // Nothing to do here, app will eventually get remote death callback.
+        }
+    }
+
+    @Override
+    public void cleanupSession(String callId) {
+        try {
+            ISipDelegate conn = getSipDelegateBinder();
+            if (conn == null) {
+                return;
+            }
+            conn.cleanupSession(callId);
+        } catch (RemoteException e) {
+            // Nothing to do here, app will eventually get remote death callback.
+        }
+    }
+
+    // Also called upon IImsRcsController death (telephony process dies).
+    @Override
+    public void binderDied() {
+        invalidateSipDelegateBinder();
+        mExecutor.execute(() -> mStateCallback.onDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD));
+    }
+
+    /**
+     * @return Implementation of state binder.
+     */
+    public ISipDelegateConnectionStateCallback getStateCallbackBinder() {
+        return mStateBinder;
+    }
+
+    /**
+     * @return Implementation of message binder.
+     */
+    public ISipDelegateMessageCallback getMessageCallbackBinder() {
+        return mMessageBinder;
+    }
+
+    /**
+     * @return The ISipDelegateConnection associated with this wrapper.
+     */
+    public ISipDelegate getSipDelegateBinder() {
+        return mDelegateBinder.get();
+    }
+
+    private void associateSipDelegate(ISipDelegate c) {
+        if (c != null) {
+            try {
+                c.asBinder().linkToDeath(this, 0 /*flags*/);
+            } catch (RemoteException e) {
+                // already dead.
+                c = null;
+            }
+        }
+        mDelegateBinder.set(c);
+    }
+
+    private void invalidateSipDelegateBinder() {
+        ISipDelegate oldVal = mDelegateBinder.getAndUpdate((unused) -> null);
+        if (oldVal != null) {
+            try {
+                oldVal.asBinder().unlinkToDeath(this, 0 /*flags*/);
+            } catch (NoSuchElementException e) {
+                Log.i(LOG_TAG, "invalidateSipDelegateBinder: " + e);
+            }
+        }
+    }
+
+    private void notifyLocalMessageFailedToSend(SipMessage m, int reason) {
+        String transactionId = m.getViaBranchParameter();
+        mExecutor.execute(() -> mMessageCallback.onMessageSendFailure(transactionId, reason));
+    }
+}
diff --git a/android-35/android/telephony/ims/compat/ImsService.java b/android-35/android/telephony/ims/compat/ImsService.java
new file mode 100644
index 0000000..303ba18
--- /dev/null
+++ b/android-35/android/telephony/ims/compat/ImsService.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2018 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.ims.compat;
+
+import android.annotation.Nullable;
+import android.app.Service;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Intent;
+import android.os.Build;
+import android.os.IBinder;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ims.compat.feature.ImsFeature;
+import android.telephony.ims.compat.feature.MMTelFeature;
+import android.telephony.ims.compat.feature.RcsFeature;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsServiceController;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
+ * ImsService must register the service in their AndroidManifest to be detected by the framework.
+ * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
+ * permission. Then, the ImsService definition in the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgImsService"
+ *     android:permission="android.permission.BIND_IMS_SERVICE" >
+ *     <!-- Apps must declare which features they support as metadata. The different categories are
+ *     defined below. In this example, the RCS_FEATURE feature is supported. -->
+ *     <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
+ *     <intent-filter>
+ *         <action android:name="android.telephony.ims.compat.ImsService" />
+ *     </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the ImsService you have defined in your manifest
+ * if you are either:
+ * 1) Defined as the default ImsService for the device in the device overlay using
+ *    "config_ims_package".
+ * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
+ *    {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
+ *
+ * The features that are currently supported in an ImsService are:
+ * - RCS_FEATURE: This ImsService implements the RcsFeature class.
+ * - MMTEL_FEATURE: This ImsService implements the MMTelFeature class.
+ * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the MMTelFeature class and will be
+ *   available to place emergency calls at all times. This MUST be implemented by the default
+ *   ImsService provided in the device overlay.
+ *   @hide
+ */
+public class ImsService extends Service {
+
+    private static final String LOG_TAG = "ImsService(Compat)";
+
+    /**
+     * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
+     * @hide
+     */
+    public static final String SERVICE_INTERFACE = "android.telephony.ims.compat.ImsService";
+
+    // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
+    // slot.
+    // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
+    // call ImsFeature#onFeatureRemoved.
+    private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
+
+        @Override
+        public IImsMMTelFeature createEmergencyMMTelFeature(int slotId) {
+            return createEmergencyMMTelFeatureInternal(slotId);
+        }
+
+        @Override
+        public IImsMMTelFeature createMMTelFeature(int slotId) {
+            return createMMTelFeatureInternal(slotId);
+        }
+
+        @Override
+        public IImsRcsFeature createRcsFeature(int slotId) {
+            return createRcsFeatureInternal(slotId);
+        }
+
+        @Override
+        public void removeImsFeature(int slotId, int featureType) {
+            ImsService.this.removeImsFeature(slotId, featureType);
+        }
+
+        @Override
+        public void addFeatureStatusCallback(int slotId, int featureType,
+                IImsFeatureStatusCallback c) {
+            addImsFeatureStatusCallback(slotId, featureType, c);
+        }
+
+        @Override
+        public void removeFeatureStatusCallback(int slotId, int featureType,
+                IImsFeatureStatusCallback c) {
+            removeImsFeatureStatusCallback(slotId, featureType, c);
+        }
+    };
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public ImsService() {
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        if(SERVICE_INTERFACE.equals(intent.getAction())) {
+            Log.i(LOG_TAG, "ImsService(Compat) Bound.");
+            return mImsServiceController;
+        }
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public SparseArray<ImsFeature> getFeatures(int slotId) {
+        return mFeaturesBySlot.get(slotId);
+    }
+
+    private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId) {
+        MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL);
+            return f.getBinder();
+        } else {
+            return null;
+        }
+    }
+
+    private IImsMMTelFeature createMMTelFeatureInternal(int slotId) {
+        MMTelFeature f = onCreateMMTelImsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.MMTEL);
+            return f.getBinder();
+        } else {
+            return null;
+        }
+    }
+
+    private IImsRcsFeature createRcsFeatureInternal(int slotId) {
+        RcsFeature f = onCreateRcsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.RCS);
+            return f.getBinder();
+        } else {
+            return null;
+        }
+    }
+
+    private void setupFeature(ImsFeature f, int slotId, int featureType) {
+        f.setContext(this);
+        f.setSlotId(slotId);
+        addImsFeature(slotId, featureType, f);
+        f.onFeatureReady();
+    }
+
+    private void addImsFeature(int slotId, int featureType, ImsFeature f) {
+        synchronized (mFeaturesBySlot) {
+            // Get SparseArray for Features, by querying slot Id
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                // Populate new SparseArray of features if it doesn't exist for this slot yet.
+                features = new SparseArray<>();
+                mFeaturesBySlot.put(slotId, features);
+            }
+            features.put(featureType, f);
+        }
+    }
+
+    private void addImsFeatureStatusCallback(int slotId, int featureType,
+            IImsFeatureStatusCallback c) {
+        synchronized (mFeaturesBySlot) {
+            // get ImsFeature associated with the slot/feature
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback. No ImsFeatures exist on"
+                        + " slot " + slotId);
+                return;
+            }
+            ImsFeature f = features.get(featureType);
+            if (f != null) {
+                f.addImsFeatureStatusCallback(c);
+            }
+        }
+    }
+
+    private void removeImsFeatureStatusCallback(int slotId, int featureType,
+            IImsFeatureStatusCallback c) {
+        synchronized (mFeaturesBySlot) {
+            // get ImsFeature associated with the slot/feature
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback. No ImsFeatures exist on"
+                        + " slot " + slotId);
+                return;
+            }
+            ImsFeature f = features.get(featureType);
+            if (f != null) {
+                f.removeImsFeatureStatusCallback(c);
+            }
+        }
+    }
+
+    private void removeImsFeature(int slotId, int featureType) {
+        synchronized (mFeaturesBySlot) {
+            // get ImsFeature associated with the slot/feature
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
+                        + slotId);
+                return;
+            }
+            ImsFeature f = features.get(featureType);
+            if (f == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
+                        + featureType + " exists on slot " + slotId);
+                return;
+            }
+            f.onFeatureRemoved();
+            features.remove(featureType);
+        }
+    }
+
+    /**
+     * @return An implementation of MMTelFeature that will be used by the system for MMTel
+     * functionality. Must be able to handle emergency calls at any time as well.
+     * @hide
+     */
+    public @Nullable MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+        return null;
+    }
+
+    /**
+     * @return An implementation of MMTelFeature that will be used by the system for MMTel
+     * functionality.
+     * @hide
+     */
+    public @Nullable MMTelFeature onCreateMMTelImsFeature(int slotId) {
+        return null;
+    }
+
+    /**
+     * @return An implementation of RcsFeature that will be used by the system for RCS.
+     * @hide
+     */
+    public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
+        return null;
+    }
+}
diff --git a/android-35/android/telephony/ims/compat/feature/ImsFeature.java b/android-35/android/telephony/ims/compat/feature/ImsFeature.java
new file mode 100644
index 0000000..6038a18
--- /dev/null
+++ b/android-35/android/telephony/ims/compat/feature/ImsFeature.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2018 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.ims.compat.feature;
+
+import android.annotation.IntDef;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.os.Build;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * Base class for all IMS features that are supported by the framework.
+ * @hide
+ */
+public abstract class ImsFeature {
+
+    private static final String LOG_TAG = "ImsFeature";
+
+    // Invalid feature value
+    public static final int INVALID = -1;
+    // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
+    // defined values in ImsServiceClass for compatibility purposes.
+    public static final int EMERGENCY_MMTEL = 0;
+    public static final int MMTEL = 1;
+    public static final int RCS = 2;
+    // Total number of features defined
+    public static final int MAX = 3;
+
+    // Integer values defining the state of the ImsFeature at any time.
+    @IntDef(flag = true,
+            value = {
+                    STATE_NOT_AVAILABLE,
+                    STATE_INITIALIZING,
+                    STATE_READY,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsState {}
+    public static final int STATE_NOT_AVAILABLE = 0;
+    public static final int STATE_INITIALIZING = 1;
+    public static final int STATE_READY = 2;
+
+    private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
+            new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
+    private @ImsState int mState = STATE_NOT_AVAILABLE;
+    private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+    protected Context mContext;
+
+    public void setContext(Context context) {
+        mContext = context;
+    }
+
+    public void setSlotId(int slotId) {
+        mSlotId = slotId;
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public int getFeatureState() {
+        return mState;
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    protected final void setFeatureState(@ImsState int state) {
+        if (mState != state) {
+            mState = state;
+            notifyFeatureState(state);
+        }
+    }
+
+    public void addImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
+        if (c == null) {
+            return;
+        }
+        try {
+            // If we have just connected, send queued status.
+            c.notifyImsFeatureStatus(mState);
+            // Add the callback if the callback completes successfully without a RemoteException.
+            synchronized (mStatusCallbacks) {
+                mStatusCallbacks.add(c);
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+        }
+    }
+
+    public void removeImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
+        if (c == null) {
+            return;
+        }
+        synchronized (mStatusCallbacks) {
+            mStatusCallbacks.remove(c);
+        }
+    }
+
+    /**
+     * Internal method called by ImsFeature when setFeatureState has changed.
+     * @param state
+     */
+    private void notifyFeatureState(@ImsState int state) {
+        synchronized (mStatusCallbacks) {
+            for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
+                 iter.hasNext(); ) {
+                IImsFeatureStatusCallback callback = iter.next();
+                try {
+                    Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
+                    callback.notifyImsFeatureStatus(state);
+                } catch (RemoteException e) {
+                    // remove if the callback is no longer alive.
+                    iter.remove();
+                    Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+                }
+            }
+        }
+    }
+
+    /**
+     * Called when the feature is ready to use.
+     */
+    public abstract void onFeatureReady();
+
+    /**
+     * Called when the feature is being removed and must be cleaned up.
+     */
+    public abstract void onFeatureRemoved();
+
+    /**
+     * @return Binder instance
+     */
+    public abstract IInterface getBinder();
+}
diff --git a/android-35/android/telephony/ims/compat/feature/MMTelFeature.java b/android-35/android/telephony/ims/compat/feature/MMTelFeature.java
new file mode 100644
index 0000000..d32e9b7
--- /dev/null
+++ b/android-35/android/telephony/ims/compat/feature/MMTelFeature.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2018 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.ims.compat.feature;
+
+import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.stub.ImsEcbmImplBase;
+import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsUtImplBase;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * Base implementation for MMTel.
+ * Any class wishing to use MMTelFeature should extend this class and implement all methods that the
+ * service supports.
+ *
+ * @hide
+ */
+
+public class MMTelFeature extends ImsFeature {
+
+    // Lock for feature synchronization
+    private final Object mLock = new Object();
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public MMTelFeature() {
+    }
+
+    private final IImsMMTelFeature mImsMMTelBinder = new IImsMMTelFeature.Stub() {
+
+        @Override
+        public int startSession(PendingIntent incomingCallIntent,
+                IImsRegistrationListener listener) throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.startSession(incomingCallIntent, listener);
+            }
+        }
+
+        @Override
+        public void endSession(int sessionId) throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.endSession(sessionId);
+            }
+        }
+
+        @Override
+        public boolean isConnected(int callSessionType, int callType)
+                throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.isConnected(callSessionType, callType);
+            }
+        }
+
+        @Override
+        public boolean isOpened() throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.isOpened();
+            }
+        }
+
+        @Override
+        public int getFeatureStatus() throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.getFeatureState();
+            }
+        }
+
+        @Override
+        public void addRegistrationListener(IImsRegistrationListener listener)
+                throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.addRegistrationListener(listener);
+            }
+        }
+
+        @Override
+        public void removeRegistrationListener(IImsRegistrationListener listener)
+                throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.removeRegistrationListener(listener);
+            }
+        }
+
+        @Override
+        public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType)
+                throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.createCallProfile(sessionId, callSessionType,  callType);
+            }
+        }
+
+        @Override
+        public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
+                throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.createCallSession(sessionId, profile, null);
+            }
+        }
+
+        @Override
+        public IImsCallSession getPendingCallSession(int sessionId, String callId)
+                throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.getPendingCallSession(sessionId, callId);
+            }
+        }
+
+        @Override
+        public IImsUt getUtInterface() throws RemoteException {
+            synchronized (mLock) {
+                ImsUtImplBase implBase = MMTelFeature.this.getUtInterface();
+                return implBase != null ? implBase.getInterface() : null;
+            }
+        }
+
+        @Override
+        public IImsConfig getConfigInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.getConfigInterface();
+            }
+        }
+
+        @Override
+        public void turnOnIms() throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.turnOnIms();
+            }
+        }
+
+        @Override
+        public void turnOffIms() throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.turnOffIms();
+            }
+        }
+
+        @Override
+        public IImsEcbm getEcbmInterface() throws RemoteException {
+            synchronized (mLock) {
+                ImsEcbmImplBase implBase = MMTelFeature.this.getEcbmInterface();
+                return implBase != null ? implBase.getImsEcbm() : null;
+            }
+        }
+
+        @Override
+        public void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.setUiTTYMode(uiTtyMode, onComplete);
+            }
+        }
+
+        @Override
+        public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+            synchronized (mLock) {
+                ImsMultiEndpointImplBase implBase = MMTelFeature.this.getMultiEndpointInterface();
+                return implBase != null ? implBase.getIImsMultiEndpoint() : null;
+            }
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IImsMMTelFeature getBinder() {
+        return mImsMMTelBinder;
+    }
+
+    /**
+     * Notifies the MMTel feature that you would like to start a session. This should always be
+     * done before making/receiving IMS calls. The IMS service will register the device to the
+     * operator's network with the credentials (from ISIM) periodically in order to receive calls
+     * from the operator's network. When the IMS service receives a new call, it will send out an
+     * intent with the provided action string. The intent contains a call ID extra
+     * {@link IImsCallSession#getCallId} and it can be used to take a call.
+     *
+     * @param incomingCallIntent When an incoming call is received, the IMS service will call
+     * {@link PendingIntent#send} to send back the intent to the caller with
+     * ImsManager#INCOMING_CALL_RESULT_CODE as the result code and the intent to fill in the call
+     * ID; It cannot be null.
+     * @param listener To listen to IMS registration events; It cannot be null
+     * @return an integer (greater than 0) representing the session id associated with the session
+     * that has been started.
+     */
+    public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) {
+        return 0;
+    }
+
+    /**
+     * End a previously started session using the associated sessionId.
+     * @param sessionId an integer (greater than 0) representing the ongoing session. See
+     * {@link #startSession}.
+     */
+    public void endSession(int sessionId) {
+    }
+
+    /**
+     * Checks if the IMS service has successfully registered to the IMS network with the specified
+     * service & call type.
+     *
+     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+     * @param callType a call type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
+     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
+     *        {@link ImsCallProfile#CALL_TYPE_VT}
+     *        {@link ImsCallProfile#CALL_TYPE_VS}
+     * @return true if the specified service id is connected to the IMS network; false otherwise
+     */
+    public boolean isConnected(int callSessionType, int callType) {
+        return false;
+    }
+
+    /**
+     * Checks if the specified IMS service is opened.
+     *
+     * @return true if the specified service id is opened; false otherwise
+     */
+    public boolean isOpened() {
+        return false;
+    }
+
+    /**
+     * Add a new registration listener for the client associated with the session Id.
+     * @param listener An implementation of IImsRegistrationListener.
+     */
+    public void addRegistrationListener(IImsRegistrationListener listener) {
+    }
+
+    /**
+     * Remove a previously registered listener using {@link #addRegistrationListener} for the client
+     * associated with the session Id.
+     * @param listener A previously registered IImsRegistrationListener
+     */
+    public void removeRegistrationListener(IImsRegistrationListener listener) {
+    }
+
+    /**
+     * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+     *
+     * @param sessionId a session id which is obtained from {@link #startSession}
+     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+     * @param callType a call type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
+     *        {@link ImsCallProfile#CALL_TYPE_VT}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+     *        {@link ImsCallProfile#CALL_TYPE_VS}
+     *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
+     *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
+     * @return a {@link ImsCallProfile} object
+     */
+    public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType) {
+        return null;
+    }
+
+    /**
+     * Creates an {@link ImsCallSession} with the specified call profile.
+     * Use other methods, if applicable, instead of interacting with
+     * {@link ImsCallSession} directly.
+     *
+     * @param sessionId a session id which is obtained from {@link #startSession}
+     * @param profile a call profile to make the call
+     */
+    public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+            IImsCallSessionListener listener) {
+        return null;
+    }
+
+    /**
+     * Retrieves the call session associated with a pending call.
+     *
+     * @param sessionId a session id which is obtained from {@link #startSession}
+     * @param callId a call id to make the call
+     */
+    public IImsCallSession getPendingCallSession(int sessionId, String callId) {
+        return null;
+    }
+
+    /**
+     * @return The Ut interface for the supplementary service configuration.
+     */
+    public ImsUtImplBase getUtInterface() {
+        return null;
+    }
+
+    /**
+     * @return The config interface for IMS Configuration
+     */
+    public IImsConfig getConfigInterface() {
+        return null;
+    }
+
+    /**
+     * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
+     */
+    public void turnOnIms() {
+    }
+
+    /**
+     * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
+     */
+    public void turnOffIms() {
+    }
+
+    /**
+     * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+     */
+    public ImsEcbmImplBase getEcbmInterface() {
+        return null;
+    }
+
+    /**
+     * Sets the current UI TTY mode for the MMTelFeature.
+     * @param uiTtyMode An integer containing the new UI TTY Mode.
+     * @param onComplete A {@link Message} to be used when the mode has been set.
+     */
+    public void setUiTTYMode(int uiTtyMode, Message onComplete) {
+    }
+
+    /**
+     * @return MultiEndpoint interface for DEP notifications
+     */
+    public ImsMultiEndpointImplBase getMultiEndpointInterface() {
+        return null;
+    }
+
+    @Override
+    public void onFeatureReady() {
+
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void onFeatureRemoved() {
+
+    }
+}
diff --git a/android-35/android/telephony/ims/compat/feature/RcsFeature.java b/android-35/android/telephony/ims/compat/feature/RcsFeature.java
new file mode 100644
index 0000000..228b330
--- /dev/null
+++ b/android-35/android/telephony/ims/compat/feature/RcsFeature.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 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.ims.compat.feature;
+
+
+import com.android.ims.internal.IImsRcsFeature;
+
+/**
+ * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
+ * this class and provide implementations of the RcsFeature methods that they support.
+ * @hide
+ */
+
+public class RcsFeature extends ImsFeature {
+
+    private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
+        // Empty Default Implementation.
+    };
+
+
+    public RcsFeature() {
+        super();
+    }
+
+    @Override
+    public void onFeatureReady() {
+
+    }
+
+    @Override
+    public void onFeatureRemoved() {
+
+    }
+
+    @Override
+    public final IImsRcsFeature getBinder() {
+        return mImsRcsBinder;
+    }
+}
diff --git a/android-35/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/android-35/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
new file mode 100755
index 0000000..798e801
--- /dev/null
+++ b/android-35/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -0,0 +1,687 @@
+/*
+ * Copyright (C) 2018 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.ims.compat.stub;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.CallQuality;
+import android.telephony.ServiceState;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsConferenceState;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsSuppServiceNotification;
+import android.telephony.ims.RtpHeaderExtension;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
+
+import java.util.List;
+
+/**
+ * Compat implementation of ImsCallSessionImplBase for older implementations.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsCallSessionImplBase extends IImsCallSession.Stub {
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public ImsCallSessionImplBase() {
+    }
+
+    @Override
+    // convert to old implementation of listener
+    public final void setListener(IImsCallSessionListener listener)
+            throws RemoteException {
+        setListener(new ImsCallSessionListenerConverter(listener));
+    }
+
+    /**
+     * Sets the listener to listen to the session events. An {@link ImsCallSession}
+     * can only hold one listener at a time. Subsequent calls to this method
+     * override the previous listener.
+     *
+     * @param listener to listen to the session events of this object
+     */
+    public void setListener(com.android.ims.internal.IImsCallSessionListener listener) {
+
+    }
+
+    /**
+     * Closes the object. This {@link ImsCallSessionImplBase} is not usable after being closed.
+     */
+    @Override
+    public void close() {
+
+    }
+
+    /**
+     * @return A String containing the unique call ID of this {@link ImsCallSessionImplBase}.
+     */
+    @Override
+    public String getCallId() {
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is associated
+     * with.
+     */
+    @Override
+    public ImsCallProfile getCallProfile() {
+        return null;
+    }
+
+    /**
+     * @return The local {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+     * associated with.
+     */
+    @Override
+    public ImsCallProfile getLocalCallProfile() {
+        return null;
+    }
+
+    /**
+     * @return The remote {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+     * associated with.
+     */
+    @Override
+    public ImsCallProfile getRemoteCallProfile() {
+        return null;
+    }
+
+    /**
+     * @param name The String extra key.
+     * @return The string extra value associated with the specified property.
+     */
+    @Override
+    public String getProperty(String name) {
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsCallSessionImplBase} state.
+     */
+    @Override
+    public int getState() {
+        return -1;
+    }
+
+    /**
+     * @return true if the {@link ImsCallSessionImplBase} is in a call, false otherwise.
+     */
+    @Override
+    public boolean isInCall() {
+        return false;
+    }
+
+    /**
+     * Mutes or unmutes the mic for the active call.
+     *
+     * @param muted true if the call should be muted, false otherwise.
+     */
+    @Override
+    public void setMute(boolean muted) {
+    }
+
+    /**
+     * Initiates an IMS call with the specified number and call profile.
+     * The session listener set in {@link #setListener(IImsCallSessionListener)} is called back upon
+     * defined session events.
+     * Only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param callee dialed string to make the call to
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see {@link ImsCallSession.Listener#callSessionStarted},
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    @Override
+    public void start(String callee, ImsCallProfile profile) {
+    }
+
+    /**
+     * Initiates an IMS call with the specified participants and call profile.
+     * The session listener set in {@link #setListener(IImsCallSessionListener)} is called back upon
+     * defined session events.
+     * The method is only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param participants participant list to initiate an IMS conference call
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see {@link ImsCallSession.Listener#callSessionStarted},
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    @Override
+    public void startConference(String[] participants, ImsCallProfile profile) {
+    }
+
+    /**
+     * Accepts an incoming call or session update.
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be answered
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+     * @see {@link ImsCallSession.Listener#callSessionStarted}
+     */
+    @Override
+    public void accept(int callType, ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Deflects an incoming call.
+     *
+     * @param deflectNumber number to deflect the call
+     */
+    @Override
+    public void deflect(String deflectNumber) {
+    }
+
+    /**
+     * Transfer an established call to given number, disconnecting the ongoing call
+     * when the transfer is complete.
+     *
+     * @param number number to transfer the call
+     * @param isConfirmationRequired when {@code true}, then the {@link ImsCallSessionImplBase}
+     * should wait until the transfer has successfully completed before disconnecting the current
+     * {@link ImsCallSessionImplBase}. When {@code false}, the {@link ImsCallSessionImplBase}
+     * should signal the network to perform the transfer, but should immediately disconnect the
+     * call regardless of the outcome of the transfer.
+     */
+    @Override
+    public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+    }
+
+    /**
+     * Transfer an established call to an existing ongoing session.
+     * When the transfer is complete, the current call gets disconnected locally.
+     */
+    @Override
+    public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
+    }
+
+    /**
+     * Rejects an incoming call or session update.
+     *
+     * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    @Override
+    public void reject(int reason) {
+    }
+
+    /**
+     * Terminates a call.
+     *
+     * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
+     *
+     * @see {@link ImsCallSession.Listener#callSessionTerminated}
+     */
+    @Override
+    public void terminate(int reason) {
+    }
+
+    /**
+     * Puts a call on hold. When it succeeds, {@link ImsCallSession.Listener#callSessionHeld} is
+     * called.
+     *
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+     * @see {@link ImsCallSession.Listener#callSessionHeld},
+     * {@link ImsCallSession.Listener#callSessionHoldFailed}
+     */
+    @Override
+    public void hold(ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Continues a call that's on hold. When it succeeds,
+     * {@link ImsCallSession.Listener#callSessionResumed} is called.
+     *
+     * @param profile stream media profile with {@link ImsStreamMediaProfile} to resume the call
+     * @see {@link ImsCallSession.Listener#callSessionResumed},
+     * {@link ImsCallSession.Listener#callSessionResumeFailed}
+     */
+    @Override
+    public void resume(ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Merges the active and held call. When the merge starts,
+     * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
+     * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
+     * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
+     * fails.
+     *
+     * @see {@link ImsCallSession.Listener#callSessionMergeStarted},
+     * {@link ImsCallSession.Listener#callSessionMergeComplete},
+     *      {@link ImsCallSession.Listener#callSessionMergeFailed}
+     */
+    @Override
+    public void merge() {
+    }
+
+    /**
+     * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be updated
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+     * @see {@link ImsCallSession.Listener#callSessionUpdated},
+     * {@link ImsCallSession.Listener#callSessionUpdateFailed}
+     */
+    @Override
+    public void update(int callType, ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Extends this call to the conference call with the specified recipients.
+     *
+     * @param participants participant list to be invited to the conference call after extending the
+     * call
+     * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
+     * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
+     */
+    @Override
+    public void extendToConference(String[] participants) {
+    }
+
+    /**
+     * Requests the conference server to invite an additional participants to the conference.
+     *
+     * @param participants participant list to be invited to the conference call
+     * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
+     *      {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
+     */
+    @Override
+    public void inviteParticipants(String[] participants) {
+    }
+
+    /**
+     * Requests the conference server to remove the specified participants from the conference.
+     *
+     * @param participants participant list to be removed from the conference call
+     * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
+     *      {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
+     */
+    @Override
+    public void removeParticipants(String[] participants) {
+    }
+
+    /**
+     * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    @Override
+    public void sendDtmf(char c, Message result) {
+    }
+
+    /**
+     * Start a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    @Override
+    public void startDtmf(char c) {
+    }
+
+    /**
+     * Stop a DTMF code.
+     */
+    @Override
+    public void stopDtmf() {
+    }
+
+    /**
+     * Sends an USSD message.
+     *
+     * @param ussdMessage USSD message to send
+     */
+    @Override
+    public void sendUssd(String ussdMessage) {
+    }
+
+    @Override
+    public IImsVideoCallProvider getVideoCallProvider() {
+        return null;
+    }
+
+    /**
+     * Determines if the current session is multiparty.
+     * @return {@code True} if the session is multiparty.
+     */
+    @Override
+    public boolean isMultiparty() {
+        return false;
+    }
+
+    /**
+     * Device issues RTT modify request
+     * @param toProfile The profile with requested changes made
+     */
+    @Override
+    public void sendRttModifyRequest(ImsCallProfile toProfile) {
+    }
+
+    /**
+     * Device responds to Remote RTT modify request
+     * @param status true if the the request was accepted or false of the request is defined.
+     */
+    @Override
+    public void sendRttModifyResponse(boolean status) {
+    }
+
+    /**
+     * Device sends RTT message
+     * @param rttMessage RTT message to be sent
+     */
+    @Override
+    public void sendRttMessage(String rttMessage) {
+    }
+
+    /**
+     * Device sends RTP header extensions.
+     * @param headerExtensions The header extensions to send.
+     */
+    @Override
+    public void sendRtpHeaderExtensions(@NonNull List<RtpHeaderExtension> headerExtensions) {
+        // no-op; not supported in compat layer.
+    }
+
+    /**
+     * Deliver the bitrate for the indicated media type, direction and bitrate to the upper layer.
+     *
+     * @param mediaType MediaType is used to identify media stream such as audio or video.
+     * @param direction Direction of this packet stream (e.g. uplink or downlink).
+     * @param bitsPerSecond This value is the bitrate received from the NW through the Recommended
+     *        bitrate MAC Control Element message and ImsStack converts this value from MAC bitrate
+     *        to audio/video codec bitrate (defined in TS26.114).
+     * @hide
+     */
+    @Override
+    public void callSessionNotifyAnbr(int mediaType, int direction, int bitsPerSecond) {
+        // no-op; not supported in compat layer.
+    }
+
+    /**
+     * There are two different ImsCallSessionListeners that need to reconciled here, we need to
+     * convert the "old" version of the com.android.ims.internal.IImsCallSessionListener to the
+     * "new" version of the Listener android.telephony.ims.ImsCallSessionListener when calling
+     * back to the framework.
+     */
+    private class ImsCallSessionListenerConverter
+            extends com.android.ims.internal.IImsCallSessionListener.Stub {
+
+        private final IImsCallSessionListener mNewListener;
+
+        public ImsCallSessionListenerConverter(IImsCallSessionListener listener) {
+            mNewListener = listener;
+        }
+
+        @Override
+        public void callSessionProgressing(IImsCallSession i,
+                ImsStreamMediaProfile imsStreamMediaProfile) throws RemoteException {
+            mNewListener.callSessionProgressing(imsStreamMediaProfile);
+        }
+
+        @Override
+        public void callSessionStarted(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionInitiated(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionStartFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionInitiatedFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionTerminated(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionTerminated(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionHeld(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionHeld(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionHoldFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionHoldFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionHoldReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionHoldReceived(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionResumed(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionResumed(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionResumeFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionResumeFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionResumeReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionResumeReceived(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionMergeStarted(IImsCallSession i, IImsCallSession newSession,
+                ImsCallProfile profile)
+                throws RemoteException {
+            mNewListener.callSessionMergeStarted(newSession, profile);
+        }
+
+        @Override
+        public void callSessionMergeComplete(IImsCallSession iImsCallSession)
+                throws RemoteException {
+            mNewListener.callSessionMergeComplete(iImsCallSession);
+        }
+
+        @Override
+        public void callSessionMergeFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionMergeFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionUpdated(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionUpdated(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionUpdateFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionUpdateFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionUpdateReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionUpdateReceived(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionConferenceExtended(IImsCallSession i, IImsCallSession newSession,
+                ImsCallProfile imsCallProfile) throws RemoteException {
+            mNewListener.callSessionConferenceExtended(newSession, imsCallProfile);
+        }
+
+        @Override
+        public void callSessionConferenceExtendFailed(IImsCallSession i,
+                ImsReasonInfo imsReasonInfo) throws RemoteException {
+            mNewListener.callSessionConferenceExtendFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionConferenceExtendReceived(IImsCallSession i,
+                IImsCallSession newSession, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionConferenceExtendReceived(newSession, imsCallProfile);
+        }
+
+        @Override
+        public void callSessionInviteParticipantsRequestDelivered(IImsCallSession i)
+                throws RemoteException {
+            mNewListener.callSessionInviteParticipantsRequestDelivered();
+        }
+
+        @Override
+        public void callSessionInviteParticipantsRequestFailed(IImsCallSession i,
+                ImsReasonInfo imsReasonInfo) throws RemoteException {
+            mNewListener.callSessionInviteParticipantsRequestFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession i)
+                throws RemoteException {
+            mNewListener.callSessionRemoveParticipantsRequestDelivered();
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestFailed(IImsCallSession i,
+                ImsReasonInfo imsReasonInfo) throws RemoteException {
+            mNewListener.callSessionRemoveParticipantsRequestFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionConferenceStateUpdated(IImsCallSession i,
+                ImsConferenceState imsConferenceState) throws RemoteException {
+            mNewListener.callSessionConferenceStateUpdated(imsConferenceState);
+        }
+
+        @Override
+        public void callSessionUssdMessageReceived(IImsCallSession i, int mode, String message)
+                throws RemoteException {
+            mNewListener.callSessionUssdMessageReceived(mode, message);
+        }
+
+        @Override
+        public void callSessionHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech,
+                ImsReasonInfo reasonInfo) throws RemoteException {
+            mNewListener.callSessionHandover(
+                    ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+                    ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
+        }
+
+        @Override
+        public void callSessionHandoverFailed(IImsCallSession i, int srcAccessTech,
+                int targetAccessTech, ImsReasonInfo reasonInfo) throws RemoteException {
+            mNewListener.callSessionHandoverFailed(
+                    ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+                    ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
+        }
+
+        @Override
+        public void callSessionMayHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech)
+                throws RemoteException {
+            mNewListener.callSessionMayHandover(
+                    ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+                    ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech));
+        }
+
+        @Override
+        public void callSessionTtyModeReceived(IImsCallSession iImsCallSession, int mode)
+                throws RemoteException {
+            mNewListener.callSessionTtyModeReceived(mode);
+        }
+
+        @Override
+        public void callSessionMultipartyStateChanged(IImsCallSession i, boolean isMultiparty)
+                throws RemoteException {
+            mNewListener.callSessionMultipartyStateChanged(isMultiparty);
+        }
+
+        @Override
+        public void callSessionSuppServiceReceived(IImsCallSession i,
+                ImsSuppServiceNotification imsSuppServiceNotification) throws RemoteException {
+            mNewListener.callSessionSuppServiceReceived(imsSuppServiceNotification);
+        }
+
+        @Override
+        public void callSessionRttModifyRequestReceived(IImsCallSession i,
+                ImsCallProfile imsCallProfile) throws RemoteException {
+            mNewListener.callSessionRttModifyRequestReceived(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionRttModifyResponseReceived(int status) throws RemoteException {
+            mNewListener.callSessionRttModifyResponseReceived(status);
+        }
+
+        @Override
+        public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
+            mNewListener.callSessionRttMessageReceived(rttMessage);
+        }
+
+        @Override
+        public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile)
+                throws RemoteException {
+            mNewListener.callSessionRttAudioIndicatorChanged(profile);
+        }
+
+        @Override
+        public void callSessionTransferred() throws RemoteException {
+            mNewListener.callSessionTransferred();
+        }
+
+        @Override
+        public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionTransferFailed(reasonInfo);
+        }
+
+        @Override
+        public void callQualityChanged(CallQuality callQuality) throws RemoteException {
+            mNewListener.callQualityChanged(callQuality);
+        }
+
+        @Override
+        public void callSessionSendAnbrQuery(int mediaType, int direction,
+                int bitsPerSecond) throws RemoteException {
+            mNewListener.callSessionSendAnbrQuery(mediaType, direction, bitsPerSecond);
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/compat/stub/ImsConfigImplBase.java b/android-35/android/telephony/ims/compat/stub/ImsConfigImplBase.java
new file mode 100644
index 0000000..a8278ae
--- /dev/null
+++ b/android-35/android/telephony/ims/compat/stub/ImsConfigImplBase.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2017 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.ims.compat.stub;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsConfigListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+
+
+/**
+ * Base implementation of ImsConfig.
+ * Override the methods that your implementation of ImsConfig supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsConfig maintained by other ImsServices.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface.
+ * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes.
+ * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in
+ * during initialization, or times when a lot of configuration parameters are being set/get
+ * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed
+ * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be
+ * performed every time.
+ * @hide
+ */
+
+public class ImsConfigImplBase {
+
+    static final private String TAG = "ImsConfigImplBase";
+
+    ImsConfigStub mImsConfigStub;
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public ImsConfigImplBase(Context context) {
+        mImsConfigStub = new ImsConfigStub(this, context);
+    }
+
+    /**
+     * Gets the value for ims service/capabilities parameters from the provisioned
+     * value storage. Synchronous blocking call.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @return value in Integer format.
+     */
+    public int getProvisionedValue(int item) throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Gets the value for ims service/capabilities parameters from the provisioned
+     * value storage. Synchronous blocking call.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @return value in String format.
+     */
+    public String getProvisionedStringValue(int item) throws RemoteException {
+        return null;
+    }
+
+    /**
+     * Sets the value for IMS service/capabilities parameters by the operator device
+     * management entity. It sets the config item value in the provisioned storage
+     * from which the main value is derived. Synchronous blocking call.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in Integer format.
+     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+     */
+    public int setProvisionedValue(int item, int value) throws RemoteException {
+        return ImsConfig.OperationStatusConstants.FAILED;
+    }
+
+    /**
+     * Sets the value for IMS service/capabilities parameters by the operator device
+     * management entity. It sets the config item value in the provisioned storage
+     * from which the main value is derived.  Synchronous blocking call.
+     *
+     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in String format.
+     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+     */
+    public int setProvisionedStringValue(int item, String value) throws RemoteException {
+        return ImsConfig.OperationStatusConstants.FAILED;
+    }
+
+    /**
+     * Gets the value of the specified IMS feature item for specified network type.
+     * This operation gets the feature config value from the main storage (i.e. final
+     * value). Asynchronous non-blocking call.
+     *
+     * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+     * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+     * @param listener feature value returned asynchronously through listener.
+     */
+    public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+            throws RemoteException {
+    }
+
+    /**
+     * Sets the value for IMS feature item for specified network type.
+     * This operation stores the user setting in setting db from which main db
+     * is derived.
+     *
+     * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+     * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+     * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants.
+     * @param listener, provided if caller needs to be notified for set result.
+     */
+    public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+            throws RemoteException {
+    }
+
+    /**
+     * Gets the value for IMS VoLTE provisioned.
+     * This should be the same as the operator provisioned value if applies.
+     */
+    public boolean getVolteProvisioned() throws RemoteException {
+        return false;
+    }
+
+    /**
+     * Gets the value for IMS feature item video quality.
+     *
+     * @param listener Video quality value returned asynchronously through listener.
+     */
+    public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+    }
+
+    /**
+     * Sets the value for IMS feature item video quality.
+     *
+     * @param quality, defines the value of video quality.
+     * @param listener, provided if caller needs to be notified for set result.
+     */
+    public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException {
+    }
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public IImsConfig getIImsConfig() { return mImsConfigStub; }
+
+    /**
+     * Updates provisioning value and notifies the framework of the change.
+     * Doesn't call #setProvisionedValue and assumes the result succeeded.
+     * This should only be used by modem when they implicitly changed provisioned values.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in Integer format.
+     */
+    public final void notifyProvisionedValueChanged(int item, int value) {
+        mImsConfigStub.updateCachedValue(item, value, true);
+    }
+
+    /**
+     * Updates provisioning value and notifies the framework of the change.
+     * Doesn't call #setProvisionedValue and assumes the result succeeded.
+     * This should only be used by modem when they implicitly changed provisioned values.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in String format.
+     */
+    public final void notifyProvisionedValueChanged(int item, String value) {
+        mImsConfigStub.updateCachedValue(item, value, true);
+    }
+
+    /**
+     * Implements the IImsConfig AIDL interface, which is called by potentially many processes
+     * in order to get/set configuration parameters.
+     *
+     * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl
+     * with actual implementations from vendors. This class caches provisioned values from
+     * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in,
+     * it first checks cache layer. If missed, it will call the vendor implementation of
+     * ImsConfigImplBase API.
+     * and cache the return value if the set succeeds.
+     *
+     * Provides APIs to get/set the IMS service feature/capability/parameters.
+     * The config items include:
+     * 1) Items provisioned by the operator.
+     * 2) Items configured by user. Mainly service feature class.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    static public class ImsConfigStub extends IImsConfig.Stub {
+        Context mContext;
+        WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
+        private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
+        private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
+
+        @VisibleForTesting
+        public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Context context) {
+            mContext = context;
+            mImsConfigImplBaseWeakReference =
+                    new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
+        }
+
+        /**
+         * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+         * if missed, it will call ImsConfigImplBase.getProvisionedValue.
+         * Synchronous blocking call.
+         *
+         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @return value in Integer format.
+         */
+        @Override
+        public synchronized int getProvisionedValue(int item) throws RemoteException {
+            if (mProvisionedIntValue.containsKey(item)) {
+                return mProvisionedIntValue.get(item);
+            } else {
+                int retVal = getImsConfigImpl().getProvisionedValue(item);
+                if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
+                    updateCachedValue(item, retVal, false);
+                }
+                return retVal;
+            }
+        }
+
+        /**
+         * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+         * if missed, it will call #ImsConfigImplBase.getProvisionedValue.
+         * Synchronous blocking call.
+         *
+         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @return value in String format.
+         */
+        @Override
+        public synchronized String getProvisionedStringValue(int item) throws RemoteException {
+            if (mProvisionedIntValue.containsKey(item)) {
+                return mProvisionedStringValue.get(item);
+            } else {
+                String retVal = getImsConfigImpl().getProvisionedStringValue(item);
+                if (retVal != null) {
+                    updateCachedValue(item, retVal, false);
+                }
+                return retVal;
+            }
+        }
+
+        /**
+         * Sets the value for IMS service/capabilities parameters by the operator device
+         * management entity. It sets the config item value in the provisioned storage
+         * from which the main value is derived, and write it into local cache.
+         * Synchronous blocking call.
+         *
+         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @param value in Integer format.
+         * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+         */
+        @Override
+        public synchronized int setProvisionedValue(int item, int value) throws RemoteException {
+            mProvisionedIntValue.remove(item);
+            int retVal = getImsConfigImpl().setProvisionedValue(item, value);
+            if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+                updateCachedValue(item, value, true);
+            } else {
+                Log.d(TAG, "Set provision value of " + item +
+                        " to " + value + " failed with error code " + retVal);
+            }
+
+            return retVal;
+        }
+
+        /**
+         * Sets the value for IMS service/capabilities parameters by the operator device
+         * management entity. It sets the config item value in the provisioned storage
+         * from which the main value is derived, and write it into local cache.
+         * Synchronous blocking call.
+         *
+         * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @param value in String format.
+         * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+         */
+        @Override
+        public synchronized int setProvisionedStringValue(int item, String value)
+                throws RemoteException {
+            mProvisionedStringValue.remove(item);
+            int retVal = getImsConfigImpl().setProvisionedStringValue(item, value);
+            if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+                updateCachedValue(item, value, true);
+            }
+
+            return retVal;
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.getFeatureValue.
+         */
+        @Override
+        public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+                throws RemoteException {
+            getImsConfigImpl().getFeatureValue(feature, network, listener);
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.setFeatureValue.
+         */
+        @Override
+        public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+                throws RemoteException {
+            getImsConfigImpl().setFeatureValue(feature, network, value, listener);
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.getVolteProvisioned.
+         */
+        @Override
+        public boolean getVolteProvisioned() throws RemoteException {
+            return getImsConfigImpl().getVolteProvisioned();
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.getVideoQuality.
+         */
+        @Override
+        public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+            getImsConfigImpl().getVideoQuality(listener);
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.setVideoQuality.
+         */
+        @Override
+        public void setVideoQuality(int quality, ImsConfigListener listener)
+                throws RemoteException {
+            getImsConfigImpl().setVideoQuality(quality, listener);
+        }
+
+        private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
+            ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
+            if (ref == null) {
+                throw new RemoteException("Fail to get ImsConfigImpl");
+            } else {
+                return ref;
+            }
+        }
+
+        private void sendImsConfigChangedIntent(int item, int value) {
+            sendImsConfigChangedIntent(item, Integer.toString(value));
+        }
+
+        private void sendImsConfigChangedIntent(int item, String value) {
+            Intent configChangedIntent = new Intent(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
+            configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
+            configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
+            if (mContext != null) {
+                mContext.sendBroadcast(configChangedIntent);
+            }
+        }
+
+        protected synchronized void updateCachedValue(int item, int value, boolean notifyChange) {
+            mProvisionedIntValue.put(item, value);
+            if (notifyChange) {
+                sendImsConfigChangedIntent(item, value);
+            }
+        }
+
+        protected synchronized void updateCachedValue(
+                int item, String value, boolean notifyChange) {
+            mProvisionedStringValue.put(item, value);
+            if (notifyChange) {
+                sendImsConfigChangedIntent(item, value);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/android-35/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java b/android-35/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
new file mode 100644
index 0000000..c689460
--- /dev/null
+++ b/android-35/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 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.ims.compat.stub;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.telephony.ims.ImsCallForwardInfo;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
+import android.telephony.ims.ImsSsInfo;
+
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of ImsUtListener, which implements stub versions of the methods
+ * in the IImsUtListener AIDL. Override the methods that your implementation of
+ * ImsUtListener supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsUtListener maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsUtListenerImplBase extends IImsUtListener.Stub {
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    public ImsUtListenerImplBase() {
+    }
+
+    /**
+     * Notifies the result of the supplementary service configuration udpate.
+     */
+    @Override
+    public void utConfigurationUpdated(IImsUt ut, int id) throws RemoteException {
+    }
+
+    @Override
+    public void utConfigurationUpdateFailed(IImsUt ut, int id, ImsReasonInfo error)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies the result of the supplementary service configuration query.
+     */
+    @Override
+    public void utConfigurationQueried(IImsUt ut, int id, Bundle ssInfo) throws RemoteException {
+    }
+
+    @Override
+    public void utConfigurationQueryFailed(IImsUt ut, int id, ImsReasonInfo error)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies the result of a line identification supplementary service query.
+     */
+    @Override
+    public void lineIdentificationSupplementaryServiceResponse(int id, ImsSsInfo config) {
+    }
+
+    /**
+     * Notifies the status of the call barring supplementary service.
+     */
+    @Override
+    public void utConfigurationCallBarringQueried(IImsUt ut, int id, ImsSsInfo[] cbInfo)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies the status of the call forwarding supplementary service.
+     */
+    @Override
+    public void utConfigurationCallForwardQueried(IImsUt ut, int id, ImsCallForwardInfo[] cfInfo)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies the status of the call waiting supplementary service.
+     */
+    @Override
+    public void utConfigurationCallWaitingQueried(IImsUt ut, int id, ImsSsInfo[] cwInfo)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies client when Supplementary Service indication is received
+     */
+    @Override
+    public void onSupplementaryServiceIndication(ImsSsData ssData) {}
+}
diff --git a/android-35/android/telephony/ims/feature/CapabilityChangeRequest.java b/android-35/android/telephony/ims/feature/CapabilityChangeRequest.java
new file mode 100644
index 0000000..f3791d1
--- /dev/null
+++ b/android-35/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2018 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.ims.feature;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Used by the framework to enable and disable MMTEL and RCS capabilities. See
+ * MmTelFeature#changeEnabledCapabilities and RcsFeature#changeEnabledCapabilities.
+ * {@hide}
+ */
+@SystemApi
+public final class CapabilityChangeRequest implements Parcelable {
+
+    /**
+     * Contains a MMTEL feature capability {@link MmTelFeature.MmTelCapabilities} and RCS feature
+     * capability {@link RcsFeature.RcsImsCapabilities}, along with an associated technology,
+     * defined as
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+     */
+    public static class CapabilityPair {
+        private final int mCapability;
+        private final int radioTech;
+
+        public CapabilityPair(int capability,
+                @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+            this.mCapability = capability;
+            this.radioTech = radioTech;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof CapabilityPair)) return false;
+
+            CapabilityPair that = (CapabilityPair) o;
+
+            if (getCapability() != that.getCapability()) return false;
+            return getRadioTech() == that.getRadioTech();
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public int hashCode() {
+            int result = getCapability();
+            result = 31 * result + getRadioTech();
+            return result;
+        }
+
+        /**
+         * @return The stored capability, defined as {@link MmTelFeature.MmTelCapabilities} and
+         * {@link RcsFeature.RcsImsCapabilities}
+         */
+        public int getCapability() {
+            return mCapability;
+        }
+
+        /**
+         * @return the stored radio technology, defined as
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} or
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+         */
+        public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() {
+            return radioTech;
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "CapabilityPair{"
+                    + "mCapability=" + mCapability
+                    + ", radioTech=" + radioTech + '}';
+        }
+    }
+
+    // Pair contains <radio tech, mCapability>
+    private final Set<CapabilityPair> mCapabilitiesToEnable;
+    // Pair contains <radio tech, mCapability>
+    private final Set<CapabilityPair> mCapabilitiesToDisable;
+
+    /** @hide */
+    public CapabilityChangeRequest() {
+        mCapabilitiesToEnable = new ArraySet<>();
+        mCapabilitiesToDisable = new ArraySet<>();
+    }
+
+    /**
+     * Add one or many capabilities to the request to be enabled.
+     *
+     * @param capabilities A bitfield of capabilities to enable, valid values are defined in
+     *   {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}.
+     * @param radioTech  the radio tech that these capabilities should be enabled for, valid
+     *   values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+     */
+    public void addCapabilitiesToEnableForTech(int capabilities,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech);
+    }
+
+    /**
+     * Add one or many capabilities to the request to be disabled.
+     * @param capabilities A bitfield of capabilities to diable, valid values are defined in
+     *   {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}.
+     * @param radioTech the radio tech that these capabilities should be disabled for, valid
+     *   values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+     */
+    public void addCapabilitiesToDisableForTech(int capabilities,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech);
+    }
+
+    /**
+     * @return a {@link List} of {@link CapabilityPair}s that are requesting to be enabled.
+     */
+    public List<CapabilityPair> getCapabilitiesToEnable() {
+        return new ArrayList<>(mCapabilitiesToEnable);
+    }
+
+    /**
+     * @return a {@link List} of {@link CapabilityPair}s that are requesting to be disabled.
+     */
+    public List<CapabilityPair> getCapabilitiesToDisable() {
+        return new ArrayList<>(mCapabilitiesToDisable);
+    }
+
+    // Iterate through capabilities bitfield and add each one as a pair associated with the radio
+    // technology
+    private void addAllCapabilities(Set<CapabilityPair> set, int capabilities, int tech) {
+        long highestCapability = Long.highestOneBit(capabilities);
+        for (int i = 1; i <= highestCapability; i *= 2) {
+            if ((i & capabilities) > 0) {
+                set.add(new CapabilityPair(/*capability*/ i, /*radioTech*/ tech));
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    protected CapabilityChangeRequest(Parcel in) {
+        int enableSize = in.readInt();
+        mCapabilitiesToEnable = new ArraySet<>(enableSize);
+        for (int i = 0; i < enableSize; i++) {
+            mCapabilitiesToEnable.add(new CapabilityPair(/*capability*/ in.readInt(),
+                    /*radioTech*/ in.readInt()));
+        }
+        int disableSize = in.readInt();
+        mCapabilitiesToDisable = new ArraySet<>(disableSize);
+        for (int i = 0; i < disableSize; i++) {
+            mCapabilitiesToDisable.add(new CapabilityPair(/*capability*/ in.readInt(),
+                    /*radioTech*/ in.readInt()));
+        }
+    }
+
+    public static final @android.annotation.NonNull Creator<CapabilityChangeRequest> CREATOR =
+            new Creator<CapabilityChangeRequest>() {
+                @Override
+                public CapabilityChangeRequest createFromParcel(Parcel in) {
+                    return new CapabilityChangeRequest(in);
+                }
+
+                @Override
+                public CapabilityChangeRequest[] newArray(int size) {
+                    return new CapabilityChangeRequest[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCapabilitiesToEnable.size());
+        for (CapabilityPair pair : mCapabilitiesToEnable) {
+            dest.writeInt(pair.getCapability());
+            dest.writeInt(pair.getRadioTech());
+        }
+        dest.writeInt(mCapabilitiesToDisable.size());
+        for (CapabilityPair pair : mCapabilitiesToDisable) {
+            dest.writeInt(pair.getCapability());
+            dest.writeInt(pair.getRadioTech());
+        }
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "CapabilityChangeRequest{"
+                + "mCapabilitiesToEnable=" + mCapabilitiesToEnable
+                + ", mCapabilitiesToDisable=" + mCapabilitiesToDisable + '}';
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CapabilityChangeRequest)) return false;
+
+        CapabilityChangeRequest
+                that = (CapabilityChangeRequest) o;
+
+        if (!mCapabilitiesToEnable.equals(that.mCapabilitiesToEnable)) return false;
+        return mCapabilitiesToDisable.equals(that.mCapabilitiesToDisable);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int hashCode() {
+        int result = mCapabilitiesToEnable.hashCode();
+        result = 31 * result + mCapabilitiesToDisable.hashCode();
+        return result;
+    }
+}
diff --git a/android-35/android/telephony/ims/feature/ConnectionFailureInfo.java b/android-35/android/telephony/ims/feature/ConnectionFailureInfo.java
new file mode 100644
index 0000000..88d9aae
--- /dev/null
+++ b/android-35/android/telephony/ims/feature/ConnectionFailureInfo.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2022 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.ims.feature;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides details on why transmitting IMS traffic failed.
+ *
+ * @hide
+ */
+public final class ConnectionFailureInfo implements Parcelable {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = "REASON_",
+        value = {
+            REASON_NONE,
+            REASON_ACCESS_DENIED,
+            REASON_NAS_FAILURE,
+            REASON_RACH_FAILURE,
+            REASON_RLC_FAILURE,
+            REASON_RRC_REJECT,
+            REASON_RRC_TIMEOUT,
+            REASON_NO_SERVICE,
+            REASON_PDN_NOT_AVAILABLE,
+            REASON_RF_BUSY,
+            REASON_UNSPECIFIED
+        })
+    public @interface FailureReason {}
+
+    /** Default value */
+    public static final int REASON_NONE = 0;
+    /** Access class check failed */
+    public static final int REASON_ACCESS_DENIED = 1;
+    /** 3GPP Non-access stratum failure */
+    public static final int REASON_NAS_FAILURE = 2;
+    /** Random access failure */
+    public static final int REASON_RACH_FAILURE = 3;
+    /** Radio link failure */
+    public static final int REASON_RLC_FAILURE = 4;
+    /** Radio connection establishment rejected by network */
+    public static final int REASON_RRC_REJECT = 5;
+    /** Radio connection establishment timed out */
+    public static final int REASON_RRC_TIMEOUT = 6;
+    /** Device currently not in service */
+    public static final int REASON_NO_SERVICE = 7;
+    /** The PDN is no more active */
+    public static final int REASON_PDN_NOT_AVAILABLE = 8;
+    /** Radio resource is busy with another subscription */
+    public static final int REASON_RF_BUSY = 9;
+    /** Unspecified reason */
+    public static final int REASON_UNSPECIFIED = 0xFFFF;
+
+    private static final SparseArray<String> sReasonMap;
+    static {
+        sReasonMap = new SparseArray<>();
+        sReasonMap.set(REASON_NONE, "NONE");
+        sReasonMap.set(REASON_ACCESS_DENIED, "ACCESS_DENIED");
+        sReasonMap.set(REASON_NAS_FAILURE, "NAS_FAILURE");
+        sReasonMap.set(REASON_RACH_FAILURE, "RACH_FAILURE");
+        sReasonMap.set(REASON_RLC_FAILURE, "RLC_FAILURE");
+        sReasonMap.set(REASON_RRC_REJECT, "RRC_REJECT");
+        sReasonMap.set(REASON_RRC_TIMEOUT, "RRC_TIMEOUT");
+        sReasonMap.set(REASON_NO_SERVICE, "NO_SERVICE");
+        sReasonMap.set(REASON_PDN_NOT_AVAILABLE, "PDN_NOT_AVAILABLE");
+        sReasonMap.set(REASON_RF_BUSY, "RF_BUSY");
+        sReasonMap.set(REASON_UNSPECIFIED, "UNSPECIFIED");
+    }
+
+    /** The reason of failure */
+    private final @FailureReason int mReason;
+
+    /**
+     * Failure cause code from network or modem specific to the failure
+     *
+     * Reference: 3GPP TS 24.401 Annex A (Cause values for EPS mobility management)
+     * Reference: 3GPP TS 24.501 Annex A (Cause values for 5GS mobility management)
+     */
+    private final int mCauseCode;
+
+    /** Retry wait time provided by network in milliseconds */
+    private final int mWaitTimeMillis;
+
+    private ConnectionFailureInfo(Parcel in) {
+        mReason = in.readInt();
+        mCauseCode = in.readInt();
+        mWaitTimeMillis = in.readInt();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param reason The reason of failure.
+     * @param causeCode Failure cause code from network or modem specific to the failure.
+     *        See 3GPP TS 24.401 Annex A (Cause values for EPS mobility management) and
+     *        3GPP TS 24.501 Annex A (Cause values for 5GS mobility management).
+     * @param waitTimeMillis Retry wait time provided by network in milliseconds.
+     * @hide
+     */
+    public ConnectionFailureInfo(@FailureReason int reason, int causeCode, int waitTimeMillis) {
+        mReason = reason;
+        mCauseCode = causeCode;
+        mWaitTimeMillis = waitTimeMillis;
+    }
+
+    /**
+     * @return the reason for the failure.
+     */
+    public @FailureReason int getReason() {
+        return mReason;
+    }
+
+    /**
+     * @return the cause code from the network or modem specific to the failure.
+     */
+    public int getCauseCode() {
+        return mCauseCode;
+    }
+
+    /**
+     * @return the retry wait time provided by the network in milliseconds.
+     */
+    public int getWaitTimeMillis() {
+        return mWaitTimeMillis;
+    }
+
+    /**
+     * @return the string format of {@link ConnectionFailureInfo}
+     */
+    @NonNull
+    @Override
+    public String toString() {
+        String reason = sReasonMap.get(mReason, "UNKNOWN");
+        return "ConnectionFailureInfo :: {" + mReason + " : " + reason + ", "
+                + mCauseCode + ", " + mWaitTimeMillis + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mReason);
+        out.writeInt(mCauseCode);
+        out.writeInt(mWaitTimeMillis);
+    }
+
+    public static final @NonNull Creator<ConnectionFailureInfo> CREATOR =
+            new Creator<ConnectionFailureInfo>() {
+        @Override
+        public ConnectionFailureInfo createFromParcel(Parcel in) {
+            return new ConnectionFailureInfo(in);
+        }
+
+        @Override
+        public ConnectionFailureInfo[] newArray(int size) {
+            return new ConnectionFailureInfo[size];
+        }
+    };
+}
diff --git a/android-35/android/telephony/ims/feature/ImsFeature.java b/android-35/android/telephony/ims/feature/ImsFeature.java
new file mode 100644
index 0000000..a8fb36b
--- /dev/null
+++ b/android-35/android/telephony/ims/feature/ImsFeature.java
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2018 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.ims.feature;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.RemoteCallbackListExt;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
+
+/**
+ * Base class for all IMS features that are supported by the framework. Use a concrete subclass
+ * of {@link ImsFeature}, such as {@link MmTelFeature} or {@link RcsFeature}.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class ImsFeature {
+
+    private static final String LOG_TAG = "ImsFeature";
+
+    /**
+     * Invalid feature value
+     * @hide
+     */
+    public static final int FEATURE_INVALID = -1;
+    // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
+    // defined values in ImsServiceClass for compatibility purposes.
+    /**
+     * This feature supports emergency calling over MMTEL. If defined, the framework will try to
+     * place an emergency call over IMS first. If it is not defined, the framework will only use
+     * CSFB for emergency calling.
+     * @hide
+     */
+    @SystemApi
+    public static final int FEATURE_EMERGENCY_MMTEL = 0;
+    /**
+     * This feature supports the MMTEL feature.
+     * @hide
+     */
+    @SystemApi
+    public static final int FEATURE_MMTEL = 1;
+    /**
+     * This feature supports the RCS feature.
+     * @hide
+     */
+    @SystemApi
+    public static final int FEATURE_RCS = 2;
+    /**
+     * Total number of features defined
+     * @hide
+     */
+    public static final int FEATURE_MAX = 3;
+
+    /**
+     * Used for logging purposes.
+     * @hide
+     */
+    public static final Map<Integer, String> FEATURE_LOG_MAP = Map.of(
+            FEATURE_EMERGENCY_MMTEL, "EMERGENCY_MMTEL",
+            FEATURE_MMTEL, "MMTEL",
+            FEATURE_RCS, "RCS");
+
+    /**
+     * Integer values defining IMS features that are supported in ImsFeature.
+     * @hide
+     */
+    @IntDef(flag = true,
+            value = {
+                    FEATURE_EMERGENCY_MMTEL,
+                    FEATURE_MMTEL,
+                    FEATURE_RCS
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FeatureType {}
+
+    /**
+     * Integer values defining the state of the ImsFeature at any time.
+     * @hide
+     */
+    @IntDef(flag = true,
+            value = {
+                    STATE_UNAVAILABLE,
+                    STATE_INITIALIZING,
+                    STATE_READY,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsState {}
+
+    /**
+     * This {@link ImsFeature}'s state is unavailable and should not be communicated with. This will
+     * remove all bindings back to the framework. Any attempt to communicate with the framework
+     * during this time will result in an {@link IllegalStateException}.
+     * @hide
+     */
+    @SystemApi
+    public static final int STATE_UNAVAILABLE = 0;
+    /**
+     * This {@link ImsFeature} state is initializing and should not be communicated with. This will
+     * remove all bindings back to the framework. Any attempt to communicate with the framework
+     * during this time will result in an {@link IllegalStateException}.
+     * @hide
+     */
+    @SystemApi
+    public static final int STATE_INITIALIZING = 1;
+    /**
+     * This {@link ImsFeature} is ready for communication. Do not attempt to call framework methods
+     * until {@see #onFeatureReady()} is called.
+     * @hide
+     */
+    @SystemApi
+    public static final int STATE_READY = 2;
+
+    /**
+     * Used for logging purposes.
+     * @hide
+     */
+    public static final Map<Integer, String> STATE_LOG_MAP = Map.of(
+            STATE_UNAVAILABLE, "UNAVAILABLE",
+            STATE_INITIALIZING, "INITIALIZING",
+            STATE_READY, "READY");
+
+    /**
+     * Integer values defining the result codes that should be returned from
+     * {@link #changeEnabledCapabilities} when the framework tries to set a feature's capability.
+     * @hide
+     */
+    @IntDef(flag = true,
+            value = {
+                    CAPABILITY_ERROR_GENERIC,
+                    CAPABILITY_SUCCESS
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsCapabilityError {}
+
+    /**
+     * The capability was unable to be changed.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_ERROR_GENERIC = -1;
+    /**
+     * The capability was able to be changed.
+     * @hide
+     */
+    @SystemApi
+    public static final int CAPABILITY_SUCCESS = 0;
+
+    /**
+     * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
+     * provided.
+     */
+    protected static class CapabilityCallbackProxy {
+        private final IImsCapabilityCallback mCallback;
+
+        /** @hide */
+        public CapabilityCallbackProxy(IImsCapabilityCallback c) {
+            mCallback = c;
+        }
+
+        /**
+         * This method notifies the provided framework callback that the request to change the
+         * indicated capability has failed and has not changed.
+         *
+         * @param capability The Capability that will be notified to the framework, defined as
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}.
+         * @param radioTech The radio tech that this capability failed for, defined as
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} or
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}.
+         * @param reason The reason this capability was unable to be changed, defined as
+         * {@link #CAPABILITY_ERROR_GENERIC} or {@link #CAPABILITY_SUCCESS}.
+         */
+        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                @ImsCapabilityError int reason) {
+            if (mCallback == null) {
+                return;
+            }
+            try {
+                mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder.");
+            }
+        }
+    }
+
+    /**
+     * Contains the IMS capabilities defined and supported by an ImsFeature in the form of a
+     * bit-mask.
+     *
+     * @deprecated This class is not used directly, but rather extended in subclasses of
+     * {@link ImsFeature} to provide service specific capabilities.
+     * @see MmTelFeature.MmTelCapabilities
+     * @hide
+     */
+    // Not Actually deprecated, but we need to remove it from the @SystemApi surface.
+    @Deprecated
+    @SystemApi // SystemApi only because it was leaked through type usage in a previous release.
+    @TestApi
+    public static class Capabilities {
+        /** @deprecated Use getters and accessors instead. */
+        // Not actually deprecated, but we need to remove it from the @SystemApi surface eventually.
+        protected int mCapabilities = 0;
+
+        /**
+         * @hide
+         */
+        public Capabilities() {
+        }
+
+        /**
+         * @hide
+         */
+        protected Capabilities(int capabilities) {
+            mCapabilities = capabilities;
+        }
+
+        /**
+         * @param capabilities Capabilities to be added to the configuration in the form of a
+         *     bit mask.
+         * @hide
+         */
+        public void addCapabilities(int capabilities) {
+            mCapabilities |= capabilities;
+        }
+
+        /**
+         * @param capabilities Capabilities to be removed to the configuration in the form of a
+         *     bit mask.
+         * @hide
+         */
+        public void removeCapabilities(int capabilities) {
+            mCapabilities &= ~capabilities;
+        }
+
+        /**
+         * @return true if all of the capabilities specified are capable.
+         * @hide
+         */
+        public boolean isCapable(int capabilities) {
+            return (mCapabilities & capabilities) == capabilities;
+        }
+
+        /**
+         * @return a deep copy of the Capabilites.
+         * @hide
+         */
+        public Capabilities copy() {
+            return new Capabilities(mCapabilities);
+        }
+
+        /**
+         * @return a bitmask containing the capability flags directly.
+         * @hide
+         */
+        public int getMask() {
+            return mCapabilities;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof Capabilities)) return false;
+
+            Capabilities that = (Capabilities) o;
+
+            return mCapabilities == that.mCapabilities;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public int hashCode() {
+            return mCapabilities;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public String toString() {
+            return "Capabilities: " + Integer.toBinaryString(mCapabilities);
+        }
+    }
+
+    /** @hide */
+    protected Context mContext;
+    /** @hide */
+    protected final Object mLock = new Object();
+
+    private final RemoteCallbackListExt<IImsFeatureStatusCallback> mStatusCallbacks =
+            new RemoteCallbackListExt<>();
+    private @ImsState int mState = STATE_UNAVAILABLE;
+    private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+    private final RemoteCallbackListExt<IImsCapabilityCallback> mCapabilityCallbacks =
+            new RemoteCallbackListExt<>();
+    private Capabilities mCapabilityStatus = new Capabilities();
+
+    /**
+     * @hide
+     */
+    public void initialize(Context context, int slotId) {
+        mContext = context;
+        mSlotId = slotId;
+    }
+
+    /**
+     * @return The SIM slot index associated with this ImsFeature.
+     *
+     * @see SubscriptionManager#getSubscriptionId(int) for more information on getting the
+     * subscription ID associated with this slot.
+     * @hide
+     */
+    @SystemApi
+    public final int getSlotIndex() {
+        return mSlotId;
+    }
+
+    /**
+     * @return The current state of the ImsFeature, set previously by {@link #setFeatureState(int)}
+     * or {@link #STATE_UNAVAILABLE} if it has not been updated  yet.
+     * @hide
+     */
+    @SystemApi
+    public @ImsState int getFeatureState() {
+        synchronized (mLock) {
+            return mState;
+        }
+    }
+
+    /**
+     * Set the state of the ImsFeature. The state is used as a signal to the framework to start or
+     * stop communication, depending on the state sent.
+     * @param state The ImsFeature's state, defined as {@link #STATE_UNAVAILABLE},
+     * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
+     * @hide
+     */
+    @SystemApi
+    public final void setFeatureState(@ImsState int state) {
+        boolean isNotify = false;
+        synchronized (mLock) {
+            if (mState != state) {
+                mState = state;
+                isNotify = true;
+            }
+        }
+        if (isNotify) {
+            notifyFeatureState(state);
+        }
+    }
+
+    /**
+     * Not final for testing, but shouldn't be extended!
+     * @hide
+     */
+    @VisibleForTesting
+    public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
+        try {
+            synchronized (mStatusCallbacks) {
+                // Add the callback if the callback completes successfully without a RemoteException
+                mStatusCallbacks.register(c);
+                // If we have just connected, send queued status.
+                c.notifyImsFeatureStatus(getFeatureState());
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Not final for testing, but shouldn't be extended!
+     * @hide
+     */
+    @VisibleForTesting
+    public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
+        synchronized (mStatusCallbacks) {
+            mStatusCallbacks.unregister(c);
+        }
+    }
+
+    /**
+     * Internal method called by ImsFeature when setFeatureState has changed.
+     */
+    private void notifyFeatureState(@ImsState int state) {
+        synchronized (mStatusCallbacks) {
+            mStatusCallbacks.broadcastAction((c) -> {
+                try {
+                    c.notifyImsFeatureStatus(state);
+                } catch (RemoteException e) {
+                    Log.w(LOG_TAG, e + " notifyFeatureState() - Skipping "
+                            + "callback.");
+                }
+            });
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public final void addCapabilityCallback(IImsCapabilityCallback c) {
+        mCapabilityCallbacks.register(c);
+        try {
+            // Notify the Capability callback that was just registered of the current capabilities.
+            c.onCapabilitiesStatusChanged(queryCapabilityStatus().mCapabilities);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "addCapabilityCallback: error accessing callback: " + e.getMessage());
+        }
+    }
+
+    /**
+     * @hide
+     */
+    final void removeCapabilityCallback(IImsCapabilityCallback c) {
+        mCapabilityCallbacks.unregister(c);
+    }
+
+    /**@hide*/
+    final void queryCapabilityConfigurationInternal(int capability, int radioTech,
+            IImsCapabilityCallback c) {
+        boolean enabled = queryCapabilityConfiguration(capability, radioTech);
+        try {
+            if (c != null) {
+                c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
+            }
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
+        }
+    }
+
+    /**
+     * @return the cached capabilities status for this feature.
+     * @hide
+     */
+    @VisibleForTesting
+    public Capabilities queryCapabilityStatus() {
+        synchronized (mLock) {
+            return mCapabilityStatus.copy();
+        }
+    }
+
+    /**
+     * Called internally to request the change of enabled capabilities.
+     * @hide
+     */
+    @VisibleForTesting
+    public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request,
+            IImsCapabilityCallback c) {
+        if (request == null) {
+            throw new IllegalArgumentException(
+                    "ImsFeature#requestChangeEnabledCapabilities called with invalid params.");
+        }
+        changeEnabledCapabilities(request, new CapabilityCallbackProxy(c));
+    }
+
+    /**
+     * Called by the ImsFeature when the capabilities status has changed.
+     *
+     * @param caps the new {@link Capabilities} status of the {@link ImsFeature}.
+     *
+     * @hide
+     */
+    protected final void notifyCapabilitiesStatusChanged(Capabilities caps) {
+        synchronized (mLock) {
+            mCapabilityStatus = caps.copy();
+        }
+
+        synchronized (mCapabilityCallbacks) {
+            mCapabilityCallbacks.broadcastAction((callback) -> {
+                try {
+                    Log.d(LOG_TAG, "ImsFeature notifyCapabilitiesStatusChanged Capabilities = "
+                            + caps.mCapabilities);
+                    callback.onCapabilitiesStatusChanged(caps.mCapabilities);
+                } catch (RemoteException e) {
+                    Log.w(LOG_TAG, e + " notifyCapabilitiesStatusChanged() - Skipping "
+                            + "callback.");
+                }
+            });
+        }
+    }
+
+    /**
+     * Provides the ImsFeature with the ability to return the framework Capability Configuration
+     * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
+     * includes a capability A to enable or disable, this method should return the correct enabled
+     * status for capability A.
+     * @param capability The capability that we are querying the configuration for.
+     * @return true if the capability is enabled, false otherwise.
+     * @hide
+     */
+    @SuppressWarnings("HiddenAbstractMethod")
+    public abstract boolean queryCapabilityConfiguration(int capability, int radioTech);
+
+    /**
+     * Features should override this method to receive Capability preference change requests from
+     * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
+     * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for
+     * each failed capability.
+     *
+     * @param request A {@link CapabilityChangeRequest} containing requested capabilities to
+     *     enable/disable.
+     * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework
+     * setting a subset of these capabilities fail, using
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}.
+     */
+    public abstract void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c);
+
+    /**
+     * Called when the framework is removing this feature and it needs to be cleaned up.
+     */
+    public abstract void onFeatureRemoved();
+
+    /**
+     * Called after this ImsFeature has been initialized and has been set to the
+     * {@link ImsState#STATE_READY} state.
+     * <p>
+     * Any attempt by this feature to access the framework before this method is called will return
+     * with an {@link IllegalStateException}.
+     * The IMS provider should use this method to trigger registration for this feature on the IMS
+     * network, if needed.
+     */
+    public abstract void onFeatureReady();
+
+    /**
+     * @return Binder instance that the framework will use to communicate with this feature.
+     * @hide
+     */
+    @SuppressWarnings("HiddenAbstractMethod")
+    protected abstract IInterface getBinder();
+}
diff --git a/android-35/android/telephony/ims/feature/ImsTrafficSessionCallback.java b/android-35/android/telephony/ims/feature/ImsTrafficSessionCallback.java
new file mode 100644
index 0000000..245ee15
--- /dev/null
+++ b/android-35/android/telephony/ims/feature/ImsTrafficSessionCallback.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.ims.feature;
+
+import android.annotation.NonNull;
+
+/**
+ * A callback class used to receive the result of {@link MmTelFeature#startImsTrafficSession}.
+ * @hide
+ */
+public interface ImsTrafficSessionCallback {
+
+    /** The modem is ready to process the IMS traffic. */
+    void onReady();
+
+    /**
+     * Notifies that any IMS traffic is not sent to network due to any failure
+     * on cellular networks. IMS service shall call {@link MmTelFeature#stopImsTrafficSession()}
+     * when receiving this callback.
+     *
+     * @param info The information of the failure.
+     */
+    void onError(@NonNull ConnectionFailureInfo info);
+}
diff --git a/android-35/android/telephony/ims/feature/MmTelFeature.java b/android-35/android/telephony/ims/feature/MmTelFeature.java
new file mode 100644
index 0000000..3f02ae9
--- /dev/null
+++ b/android-35/android/telephony/ims/feature/MmTelFeature.java
@@ -0,0 +1,1909 @@
+/*
+ * Copyright (C) 2018 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.ims.feature;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.telecom.TelecomManager;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsCallSessionListener;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.MediaQualityStatus;
+import android.telephony.ims.MediaThreshold;
+import android.telephony.ims.RtpHeaderExtensionType;
+import android.telephony.ims.SrvccCall;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsMmTelListener;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.aidl.IImsTrafficSessionCallback;
+import android.telephony.ims.aidl.ISrvccStartedCallback;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+import android.telephony.ims.stub.ImsEcbmImplBase;
+import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
+import android.telephony.ims.stub.ImsUtImplBase;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsUt;
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.server.telecom.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
+ *
+ * Any class wishing to use MmTelFeature should extend this class and implement all methods that the
+ * service supports.
+ */
+public class MmTelFeature extends ImsFeature {
+
+    private static final String LOG_TAG = "MmTelFeature";
+    private Executor mExecutor;
+    private ImsSmsImplBase mSmsImpl;
+
+    private HashMap<ImsTrafficSessionCallback, ImsTrafficSessionCallbackWrapper> mTrafficCallbacks =
+            new HashMap<>();
+    /**
+     * Creates a new MmTelFeature using the Executor set in {@link ImsService#getExecutor}
+     * @hide
+     */
+    @SystemApi
+    public MmTelFeature() {
+    }
+
+    /**
+     * Create a new MmTelFeature using the Executor specified for methods being called by the
+     * framework.
+     * @param executor The executor for the framework to use when executing the methods overridden
+     * by the implementation of MmTelFeature.
+     * @hide
+     */
+    @SystemApi
+    public MmTelFeature(@NonNull Executor executor) {
+        super();
+        mExecutor = executor;
+    }
+
+    private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
+
+        @Override
+        public void setListener(IImsMmTelListener l) {
+            executeMethodAsyncNoException(() -> MmTelFeature.this.setListener(l), "setListener");
+        }
+
+        @Override
+        public int getFeatureState() throws RemoteException {
+            return executeMethodAsyncForResult(() -> MmTelFeature.this.getFeatureState(),
+                    "getFeatureState");
+        }
+
+        @Override
+        public ImsCallProfile createCallProfile(int callSessionType, int callType)
+                throws RemoteException {
+            return executeMethodAsyncForResult(() -> MmTelFeature.this.createCallProfile(
+                    callSessionType, callType), "createCallProfile");
+        }
+
+        @Override
+        public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types)
+                throws RemoteException {
+            executeMethodAsync(() -> MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(
+                    new ArraySet<>(types)), "changeOfferedRtpHeaderExtensionTypes");
+        }
+
+        @Override
+        public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            IImsCallSession result = executeMethodAsyncForResult(() -> {
+                try {
+                    return createCallSessionInterface(profile);
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                    return null;
+                }
+            }, "createCallSession");
+
+            if (exceptionRef.get() != null) {
+                throw exceptionRef.get();
+            }
+
+            return result;
+        }
+
+        @Override
+        public int shouldProcessCall(String[] numbers) {
+            Integer result = executeMethodAsyncForResultNoException(() ->
+                    MmTelFeature.this.shouldProcessCall(numbers), "shouldProcessCall");
+            if (result != null) {
+                return result.intValue();
+            } else {
+                return PROCESS_CALL_CSFB;
+            }
+        }
+
+        @Override
+        public IImsUt getUtInterface() throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            IImsUt result = executeMethodAsyncForResult(() -> {
+                try {
+                    return MmTelFeature.this.getUtInterface();
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                    return null;
+                }
+            }, "getUtInterface");
+
+            if (exceptionRef.get() != null) {
+                throw exceptionRef.get();
+            }
+
+            return result;
+        }
+
+        @Override
+        public IImsEcbm getEcbmInterface() throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            IImsEcbm result = executeMethodAsyncForResult(() -> {
+                try {
+                    return MmTelFeature.this.getEcbmInterface();
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                    return null;
+                }
+            }, "getEcbmInterface");
+
+            if (exceptionRef.get() != null) {
+                throw exceptionRef.get();
+            }
+
+            return result;
+        }
+
+        @Override
+        public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
+            executeMethodAsync(() -> MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage),
+                    "setUiTtyMode");
+        }
+
+        @Override
+        public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            IImsMultiEndpoint result = executeMethodAsyncForResult(() -> {
+                try {
+                    return MmTelFeature.this.getMultiEndpointInterface();
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                    return null;
+                }
+            }, "getMultiEndpointInterface");
+
+            if (exceptionRef.get() != null) {
+                throw exceptionRef.get();
+            }
+
+            return result;
+        }
+
+        @Override
+        public int queryCapabilityStatus() {
+            Integer result = executeMethodAsyncForResultNoException(() -> MmTelFeature.this
+                    .queryCapabilityStatus().mCapabilities, "queryCapabilityStatus");
+
+            if (result != null) {
+                return result.intValue();
+            } else {
+                return 0;
+            }
+        }
+
+        @Override
+        public void addCapabilityCallback(IImsCapabilityCallback c) {
+            executeMethodAsyncNoException(() -> MmTelFeature.this
+                    .addCapabilityCallback(c), "addCapabilityCallback");
+        }
+
+        @Override
+        public void removeCapabilityCallback(IImsCapabilityCallback c) {
+            executeMethodAsyncNoException(() -> MmTelFeature.this
+                    .removeCapabilityCallback(c), "removeCapabilityCallback");
+        }
+
+        @Override
+        public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
+                IImsCapabilityCallback c) {
+            executeMethodAsyncNoException(() -> MmTelFeature.this
+                            .requestChangeEnabledCapabilities(request, c),
+                    "changeCapabilitiesConfiguration");
+        }
+
+        @Override
+        public void queryCapabilityConfiguration(int capability, int radioTech,
+                IImsCapabilityCallback c) {
+            executeMethodAsyncNoException(() -> queryCapabilityConfigurationInternal(
+                    capability, radioTech, c), "queryCapabilityConfiguration");
+        }
+
+        @Override
+        public void setMediaQualityThreshold(@MediaQualityStatus.MediaSessionType int sessionType,
+                MediaThreshold mediaThreshold) {
+            if (mediaThreshold != null) {
+                executeMethodAsyncNoException(() -> setMediaThreshold(sessionType, mediaThreshold),
+                        "setMediaQualityThreshold");
+            } else {
+                executeMethodAsyncNoException(() -> clearMediaThreshold(sessionType),
+                        "clearMediaQualityThreshold");
+            }
+        }
+
+        @Override
+        public MediaQualityStatus queryMediaQualityStatus(
+                @MediaQualityStatus.MediaSessionType int sessionType)
+                throws RemoteException {
+            return executeMethodAsyncForResult(() -> MmTelFeature.this.queryMediaQualityStatus(
+                    sessionType), "queryMediaQualityStatus");
+        }
+
+        @Override
+        public void setSmsListener(IImsSmsListener l) {
+            executeMethodAsyncNoException(() -> MmTelFeature.this.setSmsListener(l),
+                    "setSmsListener", getImsSmsImpl().getExecutor());
+        }
+
+        @Override
+        public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
+                byte[] pdu) {
+            executeMethodAsyncNoException(() -> MmTelFeature.this
+                    .sendSms(token, messageRef, format, smsc, retry, pdu), "sendSms",
+                    getImsSmsImpl().getExecutor());
+        }
+
+        @Override
+        public void onMemoryAvailable(int token) {
+            executeMethodAsyncNoException(() -> MmTelFeature.this
+                    .onMemoryAvailable(token), "onMemoryAvailable", getImsSmsImpl().getExecutor());
+        }
+
+        @Override
+        public void acknowledgeSms(int token, int messageRef, int result) {
+            executeMethodAsyncNoException(() -> MmTelFeature.this
+                    .acknowledgeSms(token, messageRef, result), "acknowledgeSms",
+                    getImsSmsImpl().getExecutor());
+        }
+
+        @Override
+        public void acknowledgeSmsWithPdu(int token, int messageRef, int result, byte[] pdu) {
+            executeMethodAsyncNoException(() -> MmTelFeature.this
+                    .acknowledgeSms(token, messageRef, result, pdu), "acknowledgeSms",
+                    getImsSmsImpl().getExecutor());
+        }
+
+        @Override
+        public void acknowledgeSmsReport(int token, int messageRef, int result) {
+            executeMethodAsyncNoException(() -> MmTelFeature.this
+                    .acknowledgeSmsReport(token, messageRef, result), "acknowledgeSmsReport",
+                    getImsSmsImpl().getExecutor());
+        }
+
+        @Override
+        public String getSmsFormat() {
+            return executeMethodAsyncForResultNoException(() -> MmTelFeature.this
+                    .getSmsFormat(), "getSmsFormat", getImsSmsImpl().getExecutor());
+        }
+
+        @Override
+        public void onSmsReady() {
+            executeMethodAsyncNoException(() -> MmTelFeature.this.onSmsReady(),
+                    "onSmsReady", getImsSmsImpl().getExecutor());
+        }
+
+        @Override
+        public void notifySrvccStarted(final ISrvccStartedCallback cb) {
+            executeMethodAsyncNoException(
+                    () -> MmTelFeature.this.notifySrvccStarted(
+                            (profiles) -> {
+                                try {
+                                    cb.onSrvccCallNotified(profiles);
+                                } catch (Exception e) {
+                                    Log.e(LOG_TAG, "onSrvccCallNotified e=" + e);
+                                }
+                            }),
+                    "notifySrvccStarted");
+        }
+
+        @Override
+        public void notifySrvccCompleted() {
+            executeMethodAsyncNoException(
+                    () -> MmTelFeature.this.notifySrvccCompleted(), "notifySrvccCompleted");
+        }
+
+        @Override
+        public void notifySrvccFailed() {
+            executeMethodAsyncNoException(
+                    () -> MmTelFeature.this.notifySrvccFailed(), "notifySrvccFailed");
+        }
+
+        @Override
+        public void notifySrvccCanceled() {
+            executeMethodAsyncNoException(
+                    () -> MmTelFeature.this.notifySrvccCanceled(), "notifySrvccCanceled");
+        }
+
+        @Override
+        public void setTerminalBasedCallWaitingStatus(boolean enabled) throws RemoteException {
+            synchronized (mLock) {
+                try {
+                    MmTelFeature.this.setTerminalBasedCallWaitingStatus(enabled);
+                } catch (ServiceSpecificException se) {
+                    throw new ServiceSpecificException(se.errorCode, se.getMessage());
+                } catch (Exception e) {
+                    throw new RemoteException(e.getMessage());
+                }
+            }
+        }
+
+        // Call the methods with a clean calling identity on the executor and wait indefinitely for
+        // the future to return.
+        private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+            try {
+                CompletableFuture.runAsync(
+                        () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+
+        private void executeMethodAsyncNoException(Runnable r, String errorLogName) {
+            try {
+                CompletableFuture.runAsync(
+                        () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+            }
+        }
+
+        private void executeMethodAsyncNoException(Runnable r, String errorLogName,
+                Executor executor) {
+            try {
+                CompletableFuture.runAsync(
+                        () -> TelephonyUtils.runWithCleanCallingIdentity(r), executor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+            }
+        }
+
+        private <T> T executeMethodAsyncForResult(Supplier<T> r,
+                String errorLogName) throws RemoteException {
+            CompletableFuture<T> future = CompletableFuture.supplyAsync(
+                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+            try {
+                return future.get();
+            } catch (ExecutionException | InterruptedException e) {
+                Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+
+        private <T> T executeMethodAsyncForResultNoException(Supplier<T> r,
+                String errorLogName) {
+            CompletableFuture<T> future = CompletableFuture.supplyAsync(
+                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+            try {
+                return future.get();
+            } catch (ExecutionException | InterruptedException e) {
+                Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                return null;
+            }
+        }
+
+        private <T> T executeMethodAsyncForResultNoException(Supplier<T> r,
+                String errorLogName, Executor executor) {
+            CompletableFuture<T> future = CompletableFuture.supplyAsync(
+                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), executor);
+            try {
+                return future.get();
+            } catch (ExecutionException | InterruptedException e) {
+                Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                return null;
+            }
+        }
+    };
+
+    /**
+     * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
+     * The capabilities that are used in MmTelFeature are defined as
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_UT},
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}, and
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER}.
+     *
+     * The capabilities of this MmTelFeature will be set by the framework.
+     */
+    public static class MmTelCapabilities extends Capabilities {
+
+        /**
+         * Create a new empty {@link MmTelCapabilities} instance.
+         * @see #addCapabilities(int)
+         * @see #removeCapabilities(int)
+         * @hide
+         */
+        @SystemApi
+        public MmTelCapabilities() {
+            super();
+        }
+
+        /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.
+         * @hide
+         */
+        @Deprecated
+        @SystemApi
+        public MmTelCapabilities(Capabilities c) {
+            mCapabilities = c.mCapabilities;
+        }
+
+        /**
+         * Create a new {link @MmTelCapabilities} instance with the provided capabilities.
+         * @param capabilities The capabilities that are supported for MmTel in the form of a
+         *                     bitfield.
+         * @hide
+         */
+        @SystemApi
+        public MmTelCapabilities(@MmTelCapability int capabilities) {
+            super(capabilities);
+        }
+
+        /** @hide */
+        @IntDef(flag = true,
+                value = {
+                        CAPABILITY_TYPE_VOICE,
+                        CAPABILITY_TYPE_VIDEO,
+                        CAPABILITY_TYPE_UT,
+                        CAPABILITY_TYPE_SMS,
+                        CAPABILITY_TYPE_CALL_COMPOSER,
+                        CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY
+                })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface MmTelCapability {}
+
+        /**
+         * Undefined capability type for initialization
+         * This is used to check the upper range of MmTel capability
+         * @hide
+         */
+        public static final int CAPABILITY_TYPE_NONE = 0;
+
+        /**
+         * This MmTelFeature supports Voice calling (IR.92)
+         */
+        public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
+
+        /**
+         * This MmTelFeature supports Video (IR.94)
+         */
+        public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;
+
+        /**
+         * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
+         */
+        public static final int CAPABILITY_TYPE_UT = 1 << 2;
+
+        /**
+         * This MmTelFeature supports SMS (IR.92)
+         */
+        public static final int CAPABILITY_TYPE_SMS = 1 << 3;
+
+        /**
+         * This MmTelFeature supports Call Composer (section 2.4 of RC.20). This is the superset
+         * Call Composer, meaning that all subset types of Call Composers must be enabled when this
+         * capability is enabled
+         */
+        public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4;
+
+
+        /**
+         * This MmTelFeature supports Business-only Call Composer. This is a subset of
+         * {@code CAPABILITY_TYPE_CALL_COMPOSER} that only supports business related
+         * information for calling (e.g. information to signal if the call is a business call) in
+         * the SIP header.  When enabling {@code CAPABILITY_TYPE_CALL_COMPOSER}, the
+         * {@code CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY} capability must also be enabled.
+         */
+        @FlaggedApi(Flags.FLAG_BUSINESS_CALL_COMPOSER)
+        public static final int CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY = 1 << 5;
+
+        /**
+         * This is used to check the upper range of MmTel capability
+         * @hide
+         */
+        public static final int CAPABILITY_TYPE_MAX =
+                CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY + 1;
+
+        /**
+         * @hide
+         */
+        @Override
+        @SystemApi
+        public final void addCapabilities(@MmTelCapability int capabilities) {
+            super.addCapabilities(capabilities);
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        @SystemApi
+        public final void removeCapabilities(@MmTelCapability int capability) {
+            super.removeCapabilities(capability);
+        }
+
+        /**
+         * @param capabilities a bitmask of one or more capabilities.
+         *
+         * @return true if all queried capabilities are true, otherwise false.
+         */
+        @Override
+        public final boolean isCapable(@MmTelCapability int capabilities) {
+            return super.isCapable(capabilities);
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder("MmTel Capabilities - [");
+            builder.append("Voice: ");
+            builder.append(isCapable(CAPABILITY_TYPE_VOICE));
+            builder.append(" Video: ");
+            builder.append(isCapable(CAPABILITY_TYPE_VIDEO));
+            builder.append(" UT: ");
+            builder.append(isCapable(CAPABILITY_TYPE_UT));
+            builder.append(" SMS: ");
+            builder.append(isCapable(CAPABILITY_TYPE_SMS));
+            builder.append(" CALL_COMPOSER: ");
+            builder.append(isCapable(CAPABILITY_TYPE_CALL_COMPOSER));
+            builder.append(" BUSINESS_COMPOSER_ONLY: ");
+            builder.append(isCapable(CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY));
+            builder.append("]");
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Listener that the framework implements for communication from the MmTelFeature.
+     * @hide
+     */
+    public static class Listener extends IImsMmTelListener.Stub {
+
+        /**
+         * Called when the IMS provider receives an incoming call.
+         * @param c The {@link ImsCallSession} associated with the new call.
+         * @param callId The call ID of the session of the new incoming call.
+         * @param extras A bundle containing extra parameters related to the call. See
+         * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
+         * @return the listener to listen to the session events. An {@link ImsCallSession} can only
+         *         hold one listener at a time. see {@link ImsCallSessionListener}.
+         *         If this method returns {@code null}, then the call could not be placed.
+         * @hide
+         */
+        @Override
+        @Nullable
+        public IImsCallSessionListener onIncomingCall(IImsCallSession c,
+                String callId, Bundle extras) {
+            return null;
+        }
+
+        /**
+         * Called when the IMS provider implicitly rejects an incoming call during setup.
+         * @param callProfile An {@link ImsCallProfile} with the call details.
+         * @param reason The {@link ImsReasonInfo} reason for call rejection.
+         * @hide
+         */
+        @Override
+        public void onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) {
+
+        }
+
+        /**
+         * Updates the Listener when the voice message count for IMS has changed.
+         * @param count an integer representing the new message count.
+         * @hide
+         */
+        @Override
+        public void onVoiceMessageCountUpdate(int count) {
+
+        }
+
+        /**
+         * Called to set the audio handler for this connection.
+         * @param imsAudioHandler an {@link ImsAudioHandler} used to handle the audio
+         *        for this IMS call.
+         * @hide
+         */
+        @Override
+        public void onAudioModeIsVoipChanged(int imsAudioHandler) {
+
+        }
+
+        /**
+         * Called when the IMS triggers EPS fallback procedure.
+         *
+         * @param reason specifies the reason that causes EPS fallback.
+         * @hide
+         */
+        @Override
+        public void onTriggerEpsFallback(@EpsFallbackReason int reason) {
+
+        }
+
+        /**
+         * Called when the IMS notifies the upcoming traffic type to the radio.
+         *
+         * @param token A nonce to identify the request
+         * @param trafficType The {@link ImsTrafficType} type for IMS traffic.
+         * @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType}
+         *        type of the radio access network.
+         * @param trafficDirection Indicates whether traffic is originated by mobile originated or
+         *        mobile terminated use case eg. MO/MT call/SMS etc.
+         * @param callback The callback to receive the result.
+         * @hide
+         */
+        @Override
+        public void onStartImsTrafficSession(int token,
+                @ImsTrafficType int trafficType,
+                @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+                @ImsTrafficDirection int trafficDirection,
+                IImsTrafficSessionCallback callback) {
+
+        }
+
+        /**
+         * Called when the IMS notifies the traffic type has been stopped.
+         *
+         * @param token A nonce registered with {@link #onStartImsTrafficSession}.
+         * @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType}
+         *        type of the radio access network.
+         * @hide
+         */
+        @Override
+        public void onModifyImsTrafficSession(int token,
+                @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType) {
+
+        }
+
+        /**
+         * Called when the IMS notifies the traffic type has been stopped.
+         *
+         * @param token A nonce registered with {@link #onStartImsTrafficSession}.
+         * @hide
+         */
+        @Override
+        public void onStopImsTrafficSession(int token) {
+
+        }
+
+        /**
+         * Called when the IMS provider notifies {@link MediaQualityStatus}.
+         *
+         * @param status media quality status currently measured.
+         * @hide
+         */
+        @Override
+        public void onMediaQualityStatusChanged(MediaQualityStatus status) {
+
+        }
+    }
+
+    /**
+     * A wrapper class of {@link ImsTrafficSessionCallback}.
+     * @hide
+     */
+    public static class ImsTrafficSessionCallbackWrapper {
+        public static final int INVALID_TOKEN = -1;
+
+        private static final int MAX_TOKEN = 0x10000;
+
+        private static final AtomicInteger sTokenGenerator = new AtomicInteger();
+
+        /** Callback to receive the response */
+        private IImsTrafficSessionCallbackStub mCallback = null;
+        /** Identifier to distinguish each IMS traffic request */
+        private int mToken = INVALID_TOKEN;
+
+        private ImsTrafficSessionCallback mImsTrafficSessionCallback;
+
+        private ImsTrafficSessionCallbackWrapper(ImsTrafficSessionCallback callback) {
+            mImsTrafficSessionCallback = callback;
+        }
+
+        /**
+         * Updates the callback.
+         *
+         * The mToken should be kept since it is used to identify the traffic notified to the modem
+         * until calling {@link MmtelFEature#stopImsTrafficSession}.
+         */
+        final void update(@NonNull @CallbackExecutor Executor executor) {
+            if (executor == null) {
+                throw new IllegalArgumentException(
+                        "ImsTrafficSessionCallback Executor must be non-null");
+            }
+
+            if (mCallback == null) {
+                // initial start of Ims traffic.
+                mCallback = new IImsTrafficSessionCallbackStub(
+                        mImsTrafficSessionCallback, executor);
+                mToken = generateToken();
+            } else {
+                // handover between cellular and Wi-Fi
+                mCallback.update(executor);
+            }
+        }
+
+        /**
+         * Using a static class and weak reference here to avoid memory leak caused by the
+         * {@link IImsTrafficSessionCallback.Stub} callback retaining references to the outside
+         * {@link ImsTrafficSessionCallback}.
+         */
+        private static class IImsTrafficSessionCallbackStub
+                extends IImsTrafficSessionCallback.Stub {
+            private WeakReference<ImsTrafficSessionCallback> mImsTrafficSessionCallbackWeakRef;
+            private Executor mExecutor;
+
+            IImsTrafficSessionCallbackStub(ImsTrafficSessionCallback imsTrafficCallback,
+                    Executor executor) {
+                mImsTrafficSessionCallbackWeakRef =
+                        new WeakReference<ImsTrafficSessionCallback>(imsTrafficCallback);
+                mExecutor = executor;
+            }
+
+            void update(Executor executor) {
+                mExecutor = executor;
+            }
+
+            @Override
+            public void onReady() {
+                ImsTrafficSessionCallback callback = mImsTrafficSessionCallbackWeakRef.get();
+                if (callback == null) return;
+
+                Binder.withCleanCallingIdentity(
+                        () -> mExecutor.execute(() -> callback.onReady()));
+            }
+
+            @Override
+            public void onError(ConnectionFailureInfo info) {
+                ImsTrafficSessionCallback callback = mImsTrafficSessionCallbackWeakRef.get();
+                if (callback == null) return;
+
+                Binder.withCleanCallingIdentity(
+                        () -> mExecutor.execute(() -> callback.onError(info)));
+            }
+        }
+
+        /**
+         * Returns the callback binder.
+         */
+        final IImsTrafficSessionCallbackStub getCallbackBinder() {
+            return mCallback;
+        }
+
+        /**
+         * Returns the token.
+         */
+        final int getToken() {
+            return mToken;
+        }
+
+        /**
+         * Resets the members.
+         * It's called by {@link MmTelFeature#stopImsTrafficSession}.
+         */
+        final void reset() {
+            mCallback = null;
+            mToken = INVALID_TOKEN;
+        }
+
+        private static int generateToken() {
+            int token = sTokenGenerator.incrementAndGet();
+            if (token == MAX_TOKEN) sTokenGenerator.set(0);
+            return token;
+        }
+    }
+
+    /**
+     * To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the
+     * outgoing call as IMS.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROCESS_CALL_IMS = 0;
+    /**
+     * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
+     * not process the outgoing call as IMS and should instead use circuit switch.
+     * @hide
+     */
+    @SystemApi
+    public static final int PROCESS_CALL_CSFB = 1;
+
+    /** @hide */
+    @IntDef(flag = true,
+            value = {
+                    PROCESS_CALL_IMS,
+                    PROCESS_CALL_CSFB
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProcessCallResult {}
+
+    /**
+     * If the flag is present and true, it indicates that the incoming call is for USSD.
+     * <p>
+     * This is an optional boolean flag.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
+
+    /**
+     * If this flag is present and true, this call is marked as an unknown dialing call instead
+     * of an incoming call. An example of such a call is a call that is originated by sending
+     * commands (like AT commands) directly to the modem without Android involvement or dialing
+     * calls appearing over IMS when the modem does a silent redial from circuit-switched to IMS in
+     * certain situations.
+     * <p>
+     * This is an optional boolean flag.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_IS_UNKNOWN_CALL =
+            "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
+
+    /** @hide */
+    @IntDef(
+        prefix = "AUDIO_HANDLER_",
+        value = {
+            AUDIO_HANDLER_ANDROID,
+            AUDIO_HANDLER_BASEBAND
+        })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsAudioHandler {}
+
+    /**
+    * Audio Handler - Android
+    * @hide
+    */
+    @SystemApi
+    public static final int AUDIO_HANDLER_ANDROID = 0;
+
+    /**
+    * Audio Handler - Baseband
+    * @hide
+    */
+    @SystemApi
+    public static final int AUDIO_HANDLER_BASEBAND = 1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = "EPS_FALLBACK_REASON_",
+        value = {
+            EPS_FALLBACK_REASON_INVALID,
+            EPS_FALLBACK_REASON_NO_NETWORK_TRIGGER,
+            EPS_FALLBACK_REASON_NO_NETWORK_RESPONSE,
+        })
+    public @interface EpsFallbackReason {}
+
+    /**
+     * Default value. Internal use only.
+     * This value should not be used to trigger EPS fallback.
+     * @hide
+     */
+    public static final int EPS_FALLBACK_REASON_INVALID = -1;
+
+    /**
+     * If the network only supports the EPS fallback in 5G NR SA for voice calling and the EPS
+     * Fallback procedure by the network during the call setup is not triggered, UE initiated
+     * fallback will be triggered with this reason. The modem shall locally release the 5G NR
+     * SA RRC connection and acquire the LTE network and perform a tracking area update
+     * procedure. After the EPS fallback procedure is completed, the call setup for voice will
+     * be established if there is no problem.
+     *
+     * @hide
+     */
+    public static final int EPS_FALLBACK_REASON_NO_NETWORK_TRIGGER = 1;
+
+    /**
+     * If the UE doesn't receive any response for SIP INVITE within a certain timeout in 5G NR
+     * SA for MO voice calling, the device determines that voice call is not available in 5G and
+     * terminates all active SIP dialogs and SIP requests and enters IMS non-registered state.
+     * In that case, UE initiated fallback will be triggered with this reason. The modem shall
+     * reset modem's data buffer of IMS PDU to prevent the ghost call. After the EPS fallback
+     * procedure is completed, VoLTE call could be tried if there is no problem.
+     *
+     * @hide
+     */
+    public static final int EPS_FALLBACK_REASON_NO_NETWORK_RESPONSE = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = "IMS_TRAFFIC_TYPE_",
+        value = {
+            IMS_TRAFFIC_TYPE_NONE,
+            IMS_TRAFFIC_TYPE_EMERGENCY,
+            IMS_TRAFFIC_TYPE_EMERGENCY_SMS,
+            IMS_TRAFFIC_TYPE_VOICE,
+            IMS_TRAFFIC_TYPE_VIDEO,
+            IMS_TRAFFIC_TYPE_SMS,
+            IMS_TRAFFIC_TYPE_REGISTRATION,
+            IMS_TRAFFIC_TYPE_UT_XCAP
+        })
+    public @interface ImsTrafficType {}
+
+    /**
+     * Default value for initialization. Internal use only.
+     * @hide
+     */
+    public static final int IMS_TRAFFIC_TYPE_NONE = -1;
+    /**
+     * Emergency call
+     * @hide
+     */
+    public static final int IMS_TRAFFIC_TYPE_EMERGENCY = 0;
+    /**
+     * Emergency SMS
+     * @hide
+     */
+    public static final int IMS_TRAFFIC_TYPE_EMERGENCY_SMS = 1;
+    /**
+     * Voice call
+     * @hide
+     */
+    public static final int IMS_TRAFFIC_TYPE_VOICE = 2;
+    /**
+     * Video call
+     * @hide
+     */
+    public static final int IMS_TRAFFIC_TYPE_VIDEO = 3;
+    /**
+     * SMS over IMS
+     * @hide
+     */
+    public static final int IMS_TRAFFIC_TYPE_SMS = 4;
+    /**
+     * IMS registration and subscription for reg event package (signaling)
+     * @hide
+     */
+    public static final int IMS_TRAFFIC_TYPE_REGISTRATION = 5;
+    /**
+     * Ut/XCAP (XML Configuration Access Protocol)
+     * @hide
+     */
+    public static final int IMS_TRAFFIC_TYPE_UT_XCAP = 6;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            prefix = { "IMS_TRAFFIC_DIRECTION_" },
+            value = {IMS_TRAFFIC_DIRECTION_INCOMING, IMS_TRAFFIC_DIRECTION_OUTGOING})
+    public @interface ImsTrafficDirection {}
+
+    /**
+     * Indicates that the traffic is an incoming traffic.
+     * @hide
+     */
+    public static final int IMS_TRAFFIC_DIRECTION_INCOMING = 0;
+    /**
+     * Indicates that the traffic is an outgoing traffic.
+     * @hide
+     */
+    public static final int IMS_TRAFFIC_DIRECTION_OUTGOING = 1;
+
+    private IImsMmTelListener mListener;
+
+    /**
+     * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
+     *     notifies the framework.
+     */
+    private void setListener(IImsMmTelListener listener) {
+        synchronized (mLock) {
+            mListener = listener;
+            if (mListener != null) {
+                onFeatureReady();
+            }
+        }
+    }
+
+    /**
+     * @return the listener associated with this MmTelFeature. May be null if it has not been set
+     * by the framework yet.
+     */
+    private IImsMmTelListener getListener() {
+        synchronized (mLock) {
+            return mListener;
+        }
+    }
+
+    /**
+     * The current capability status that this MmTelFeature has defined is available. This
+     * configuration will be used by the platform to figure out which capabilities are CURRENTLY
+     * available to be used.
+     *
+     * Should be a subset of the capabilities that are enabled by the framework in
+     * {@link #changeEnabledCapabilities}.
+     * @return A copy of the current MmTelFeature capability status.
+     * @hide
+     */
+    @Override
+    @SystemApi
+    public @NonNull final MmTelCapabilities queryCapabilityStatus() {
+        return new MmTelCapabilities(super.queryCapabilityStatus());
+    }
+
+    /**
+     * Notify the framework that the status of the Capabilities has changed. Even though the
+     * MmTelFeature capability may be enabled by the framework, the status may be disabled due to
+     * the feature being unavailable from the network.
+     * @param c The current capability status of the MmTelFeature. If a capability is disabled, then
+     * the status of that capability is disabled. This can happen if the network does not currently
+     * support the capability that is enabled. A capability that is disabled by the framework (via
+     * {@link #changeEnabledCapabilities}) should also show the status as disabled.
+     * @hide
+     */
+    @SystemApi
+    public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
+        if (c == null) {
+            throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
+        }
+        super.notifyCapabilitiesStatusChanged(c);
+    }
+
+    /**
+     * Notify the framework that the measured media quality has crossed a threshold set by {@link
+     * MmTelFeature#setMediaThreshold}
+     *
+     * @param status current media quality status measured.
+     * @hide
+     */
+    @SystemApi
+    public final void notifyMediaQualityStatusChanged(
+            @NonNull MediaQualityStatus status) {
+        if (status == null) {
+            throw new IllegalArgumentException(
+                    "MediaQualityStatus must be non-null!");
+        }
+        Log.i(LOG_TAG, "notifyMediaQualityStatusChanged " + status);
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        try {
+            listener.onMediaQualityStatusChanged(status);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify the framework of an incoming call.
+     * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
+     * @param extras A bundle containing extra parameters related to the call. See
+     * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
+     * @hide
+     *
+     * @deprecated use {@link #notifyIncomingCall(ImsCallSessionImplBase, String, Bundle)} instead
+     */
+    @Deprecated
+    @SystemApi
+    public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
+            @NonNull Bundle extras) {
+        if (c == null || extras == null) {
+            throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
+                    + "null.");
+        }
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        try {
+            c.setDefaultExecutor(MmTelFeature.this.mExecutor);
+            listener.onIncomingCall(c.getServiceImpl(), null, extras);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify the framework of an incoming call.
+     * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
+     * @param callId The call ID of the session of the new incoming call.
+     * @param extras A bundle containing extra parameters related to the call. See
+     * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
+     * @return The listener used by the framework to listen to call session events created
+     *         from the ImsService.
+     *         If this method returns {@code null}, then the call could not be placed.
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public final ImsCallSessionListener notifyIncomingCall(
+            @NonNull ImsCallSessionImplBase c, @NonNull String callId, @NonNull Bundle extras) {
+        if (c == null || callId == null || extras == null) {
+            throw new IllegalArgumentException("ImsCallSessionImplBase, callId, and Bundle can "
+                    + "not be null.");
+        }
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        try {
+            c.setDefaultExecutor(MmTelFeature.this.mExecutor);
+            IImsCallSessionListener isl =
+                    listener.onIncomingCall(c.getServiceImpl(), callId, extras);
+            if (isl != null) {
+                ImsCallSessionListener iCSL = new ImsCallSessionListener(isl);
+                iCSL.setDefaultExecutor(MmTelFeature.this.mExecutor);
+                return iCSL;
+            } else {
+                return null;
+            }
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify the framework that a call has been implicitly rejected by this MmTelFeature
+     * during call setup.
+     * @param callProfile The {@link ImsCallProfile} IMS call profile with details.
+     *        This can be null if no call information is available for the rejected call.
+     * @param reason The {@link ImsReasonInfo} call rejection reason.
+     * @hide
+     */
+    @SystemApi
+    public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
+            @NonNull ImsReasonInfo reason) {
+        if (callProfile == null || reason == null) {
+            throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
+                    + "null.");
+        }
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        try {
+            listener.onRejectedCall(callProfile, reason);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     *
+     * @hide
+     */
+    public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        try {
+            listener.onIncomingCall(c, null, extras);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notify the framework of a change in the Voice Message count.
+     * @link count the new Voice Message count.
+     * @hide
+     */
+    @SystemApi
+    public final void notifyVoiceMessageCountUpdate(int count) {
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        try {
+            listener.onVoiceMessageCountUpdate(count);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Sets the audio handler for this connection. The vendor IMS stack will invoke this API
+     * to inform Telephony/Telecom layers about which audio handlers i.e. either Android or Modem
+     * shall be used for handling the IMS call audio.
+     *
+     * @param imsAudioHandler {@link MmTelFeature#ImsAudioHandler} used to handle the audio
+     *        for this IMS call.
+     * @hide
+     */
+    @SystemApi
+    public final void setCallAudioHandler(@ImsAudioHandler int imsAudioHandler) {
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        try {
+            listener.onAudioModeIsVoipChanged(imsAudioHandler);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Triggers the EPS fallback procedure.
+     *
+     * @param reason specifies the reason that causes EPS fallback.
+     * @hide
+     */
+    public final void triggerEpsFallback(@EpsFallbackReason int reason) {
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        try {
+            listener.onTriggerEpsFallback(reason);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Starts a new IMS traffic session with the framework.
+     *
+     * This API notifies the NAS and RRC layers of the modem that IMS traffic of type
+     * {@link ImsTrafficType} is starting for the IMS session represented by a
+     * {@link ImsTrafficSessionCallback}. The {@link ImsTrafficSessionCallback}
+     * will notify the caller when IMS traffic is ready to start via the
+     * {@link ImsTrafficSessionCallback#onReady()} callback. If there was an error starting
+     * IMS traffic for the specified traffic type, {@link ImsTrafficSessionCallback#onError()} will
+     * be called, which will also notify the caller of the reason of the failure.
+     *
+     * If there is a handover that changes the {@link AccessNetworkConstants#RadioAccessNetworkType}
+     * of this IMS traffic session, then {@link #modifyImsTrafficSession} should be called. This is
+     * used, for example, when a WiFi <-> cellular handover occurs.
+     *
+     * Once the IMS traffic session is finished, {@link #stopImsTrafficSession} must be called.
+     *
+     * Note: This API will be used to prioritize RF resources in case of DSDS. The service priority
+     * is EMERGENCY > EMERGENCY SMS > VOICE > VIDEO > SMS > REGISTRATION > Ut/XCAP. RF
+     * shall be prioritized to the subscription which handles the higher priority service.
+     * When both subscriptions are handling the same type of service, then RF shall be
+     * prioritized to the voice preferred sub.
+     *
+     * @param trafficType The {@link ImsTrafficType} type for IMS traffic.
+     * @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType} type of
+     *        the radio access network.
+     * @param trafficDirection Indicates whether traffic is originated by mobile originated or
+     *        mobile terminated use case eg. MO/MT call/SMS etc.
+     * @param executor The Executor that will be used to call the {@link ImsTrafficSessionCallback}.
+     * @param callback The session representing the IMS Session associated with a specific
+     *        trafficType. This callback instance should only be used for the specified traffic type
+     *        until {@link #stopImsTrafficSession} is called.
+     *
+     * @see modifyImsTrafficSession
+     * @see stopImsTrafficSession
+     *
+     * @hide
+     */
+    public final void startImsTrafficSession(@ImsTrafficType int trafficType,
+            @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+            @ImsTrafficDirection int trafficDirection,
+            @NonNull Executor executor, @NonNull ImsTrafficSessionCallback callback) {
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        // TODO: retrieve from the callback list
+        ImsTrafficSessionCallbackWrapper callbackWrapper = mTrafficCallbacks.get(callback);
+        if (callbackWrapper == null) {
+            callbackWrapper = new ImsTrafficSessionCallbackWrapper(callback);
+            mTrafficCallbacks.put(callback, callbackWrapper);
+        }
+        try {
+            callbackWrapper.update(executor);
+            listener.onStartImsTrafficSession(callbackWrapper.getToken(),
+                    trafficType, accessNetworkType, trafficDirection,
+                    callbackWrapper.getCallbackBinder());
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Modifies an existing IMS traffic session represented by the associated
+     * {@link ImsTrafficSessionCallback}.
+     *
+     * The {@link ImsTrafficSessionCallback} will notify the caller when IMS traffic is ready to
+     * start after modification using the {@link ImsTrafficSessionCallback#onReady()} callback.
+     * If there was an error modifying IMS traffic for the new radio access network type type,
+     * {@link ImsTrafficSessionCallback#onError()} will be called, which will also notify the
+     * caller of the reason of the failure.
+     *
+     * @param accessNetworkType The {@link AccessNetworkConstants#RadioAccessNetworkType} type of
+     *        the radio access network.
+     * @param callback The callback registered with {@link #startImsTrafficSession}.
+     *
+     * @see startImsTrafficSession
+     * @see stopImsTrafficSession
+     *
+     * @hide
+     */
+    public final void modifyImsTrafficSession(
+            @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+            @NonNull ImsTrafficSessionCallback callback) {
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        ImsTrafficSessionCallbackWrapper callbackWrapper = mTrafficCallbacks.get(callback);
+        if (callbackWrapper == null) {
+            // should not reach here.
+            throw new IllegalStateException("Unknown ImsTrafficSessionCallback instance.");
+        }
+        try {
+            listener.onModifyImsTrafficSession(callbackWrapper.getToken(), accessNetworkType);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Notifies the framework that the IMS traffic session represented by the associated
+     * {@link ImsTrafficSessionCallback} has ended.
+     *
+     * @param callback The callback registered with {@link #startImsTrafficSession}.
+     *
+     * @see startImsTrafficSession
+     * @see modifyImsTrafficSession
+     *
+     * @hide
+     */
+    public final void stopImsTrafficSession(@NonNull ImsTrafficSessionCallback callback) {
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        ImsTrafficSessionCallbackWrapper callbackWrapper = mTrafficCallbacks.get(callback);
+        if (callbackWrapper == null) {
+            // should not reach here.
+            throw new IllegalStateException("Unknown ImsTrafficSessionCallback instance.");
+        }
+        try {
+            listener.onStopImsTrafficSession(callbackWrapper.getToken());
+            callbackWrapper.reset();
+            mTrafficCallbacks.remove(callback);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Provides the MmTelFeature with the ability to return the framework Capability Configuration
+     * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
+     * includes a capability A to enable or disable, this method should return the correct enabled
+     * status for capability A.
+     * @param capability The capability that we are querying the configuration for.
+     * @return true if the capability is enabled, false otherwise.
+     * @hide
+     */
+    @Override
+    @SystemApi
+    public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        // Base implementation - Override to provide functionality
+        return false;
+    }
+
+    /**
+     * The MmTelFeature should override this method to handle the enabling/disabling of
+     * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
+     * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
+     * could not be set to their new values,
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
+     * individually for each capability whose processing resulted in an error.
+     *
+     * Enabling/Disabling a capability here indicates that the capability should be registered or
+     * deregistered (depending on the capability change) and become available or unavailable to
+     * the framework.
+     * @hide
+     */
+    @Override
+    @SystemApi
+    public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
+            @NonNull CapabilityCallbackProxy c) {
+        // Base implementation, no-op
+    }
+
+    /**
+     * Called by the framework to pass {@link MediaThreshold}. The MmTelFeature should override this
+     * method to get Media quality threshold. This will pass the consolidated threshold values from
+     * Telephony framework. IMS provider needs to monitor media quality of active call and notify
+     * media quality {@link #notifyMediaQualityStatusChanged(MediaQualityStatus)} when the measured
+     * media quality crosses at least one of {@link MediaThreshold} set by this.
+     *
+     * @param mediaSessionType media session type for this Threshold info.
+     * @param mediaThreshold media threshold information
+     * @hide
+     */
+    @SystemApi
+    public void setMediaThreshold(
+            @MediaQualityStatus.MediaSessionType int mediaSessionType,
+            @NonNull MediaThreshold mediaThreshold) {
+        // Base Implementation - Should be overridden.
+        Log.d(LOG_TAG, "setMediaThreshold is not supported." + mediaThreshold);
+    }
+
+    /**
+     * The MmTelFeature should override this method to clear Media quality thresholds that were
+     * registered and stop media quality status updates.
+     *
+     * @param mediaSessionType media session type
+     * @hide
+     */
+    @SystemApi
+    public void clearMediaThreshold(@MediaQualityStatus.MediaSessionType int mediaSessionType) {
+        // Base Implementation - Should be overridden.
+        Log.d(LOG_TAG, "clearMediaThreshold is not supported." + mediaSessionType);
+    }
+
+    /**
+     * IMS provider should override this method to return currently measured media quality status.
+     *
+     * <p/>
+     * If media quality status is not yet measured after call is active, it needs to notify media
+     * quality status {@link #notifyMediaQualityStatusChanged(MediaQualityStatus)} when the first
+     * measurement is done.
+     *
+     * @param mediaSessionType media session type
+     * @return Current media quality status. It could be null if media quality status is not
+     *         measured yet or {@link MediaThreshold} was not set corresponding to the media session
+     *         type.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public MediaQualityStatus queryMediaQualityStatus(
+            @MediaQualityStatus.MediaSessionType int mediaSessionType) {
+        // Base Implementation - Should be overridden.
+        Log.d(LOG_TAG, "queryMediaQualityStatus is not supported." + mediaSessionType);
+        return null;
+    }
+
+    /**
+     * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+     *
+     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+     * @param callType a call type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
+     *        {@link ImsCallProfile#CALL_TYPE_VT}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+     *        {@link ImsCallProfile#CALL_TYPE_VS}
+     *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
+     *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
+     * @return a {@link ImsCallProfile} object
+     * @hide
+     */
+    @SystemApi
+    public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * Called by the framework to report a change to the RTP header extension types which should be
+     * offered during SDP negotiation (see RFC8285 for more information).
+     * <p>
+     * The {@link ImsService} should report the RTP header extensions which were accepted during
+     * SDP negotiation using {@link ImsCallProfile#setAcceptedRtpHeaderExtensionTypes(Set)}.
+     *
+     * @param extensionTypes The RTP header extensions the framework wishes to offer during
+     *                       outgoing and incoming call setup.  An empty list indicates that there
+     *                       are no framework defined RTP header extension types to offer.
+     * @hide
+     */
+    @SystemApi
+    public void changeOfferedRtpHeaderExtensionTypes(
+            @NonNull Set<RtpHeaderExtensionType> extensionTypes) {
+        // Base implementation - should be overridden if RTP header extension handling is supported.
+    }
+
+    /**
+     * @hide
+     */
+    public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
+            throws RemoteException {
+        ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
+        if (s != null) {
+            s.setDefaultExecutor(mExecutor);
+            return s.getServiceImpl();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Creates an {@link ImsCallSession} with the specified call profile.
+     * Use other methods, if applicable, instead of interacting with
+     * {@link ImsCallSession} directly.
+     *
+     * @param profile a call profile to make the call
+     * @hide
+     */
+    @SystemApi
+    public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * Called by the framework to determine if the outgoing call, designated by the outgoing
+     * {@link String}s, should be processed as an IMS call or CSFB call. If this method's
+     * functionality is not overridden, the platform will process every call as IMS as long as the
+     * MmTelFeature reports that the {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE} capability is
+     * available.
+     * @param numbers An array of {@link String}s that will be used for placing the call. There can
+     *         be multiple {@link String}s listed in the case when we want to place an outgoing
+     *         call as a conference.
+     * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
+     *        call will be placed over IMS or via CSFB.
+     * @hide
+     */
+    @SystemApi
+    public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
+        return PROCESS_CALL_IMS;
+    }
+
+    /**
+     *
+     * @hide
+     */
+    protected IImsUt getUtInterface() throws RemoteException {
+        ImsUtImplBase utImpl = getUt();
+        if (utImpl != null) {
+            utImpl.setDefaultExecutor(mExecutor);
+            return utImpl.getInterface();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    protected IImsEcbm getEcbmInterface() throws RemoteException {
+        ImsEcbmImplBase ecbmImpl = getEcbm();
+        if (ecbmImpl != null) {
+            ecbmImpl.setDefaultExecutor(mExecutor);
+            return ecbmImpl.getImsEcbm();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+        ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
+        if (multiendpointImpl != null) {
+            multiendpointImpl.setDefaultExecutor(mExecutor);
+            return multiendpointImpl.getIImsMultiEndpoint();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public @NonNull ImsSmsImplBase getImsSmsImpl() {
+        synchronized (mLock) {
+            if (mSmsImpl == null) {
+                mSmsImpl = getSmsImplementation();
+                mSmsImpl.setDefaultExecutor(mExecutor);
+            }
+            return mSmsImpl;
+        }
+    }
+
+    /**
+     * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
+     * configuration.
+     * @hide
+     */
+    @SystemApi
+    public @NonNull ImsUtImplBase getUt() {
+        // Base Implementation - Should be overridden
+        return new ImsUtImplBase();
+    }
+
+    /**
+     * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
+     * calls that support it.
+     * @hide
+     */
+    @SystemApi
+    public @NonNull ImsEcbmImplBase getEcbm() {
+        // Base Implementation - Should be overridden
+        return new ImsEcbmImplBase();
+    }
+
+    /**
+     * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
+     * package processing for multi-endpoint.
+     * @hide
+     */
+    @SystemApi
+    public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
+        // Base Implementation - Should be overridden
+        return new ImsMultiEndpointImplBase();
+    }
+
+    /**
+     * Sets the current UI TTY mode for the MmTelFeature.
+     * @param mode An integer containing the new UI TTY Mode, can consist of
+     *         {@link TelecomManager#TTY_MODE_OFF},
+     *         {@link TelecomManager#TTY_MODE_FULL},
+     *         {@link TelecomManager#TTY_MODE_HCO},
+     *         {@link TelecomManager#TTY_MODE_VCO}
+     * @param onCompleteMessage If non-null, this MmTelFeature should call this {@link Message} when
+     *         the operation is complete by using the associated {@link android.os.Messenger} in
+     *         {@link Message#replyTo}. For example:
+     * {@code
+     *     // Set UI TTY Mode and other operations...
+     *     try {
+     *         // Notify framework that the mode was changed.
+     *         Messenger uiMessenger = onCompleteMessage.replyTo;
+     *         uiMessenger.send(onCompleteMessage);
+     *     } catch (RemoteException e) {
+     *         // Remote side is dead
+     *     }
+     * }
+     * @hide
+     */
+    @SystemApi
+    public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
+        // Base Implementation - Should be overridden
+    }
+
+    /**
+     * Notifies the MmTelFeature of the enablement status of terminal based call waiting
+     *
+     * If the terminal based call waiting is provisioned,
+     * IMS controls the enablement of terminal based call waiting which is defined
+     * in 3GPP TS 24.615.
+     *
+     * @param enabled user setting controlling whether or not call waiting is enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void setTerminalBasedCallWaitingStatus(boolean enabled) {
+        // Base Implementation - Should be overridden by IMS service
+        throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                "Not implemented on device.");
+    }
+
+    /**
+     * Notifies the MmTelFeature that the network has initiated an SRVCC (Single radio voice
+     * call continuity) for all IMS calls. When the network initiates an SRVCC, calls from
+     * the LTE domain are handed over to the legacy circuit switched domain. The modem requires
+     * knowledge of ongoing calls in the IMS domain in order to complete the SRVCC operation.
+     * <p>
+     * @param consumer The callback used to notify the framework of the list of IMS calls and their
+     * state at the time of the SRVCC.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void notifySrvccStarted(@NonNull Consumer<List<SrvccCall>> consumer) {
+        // Base Implementation - Should be overridden by IMS service
+    }
+
+    /**
+     * Notifies the MmTelFeature that the SRVCC is completed and the calls have been moved
+     * over to the circuit-switched domain.
+     * {@link android.telephony.CarrierConfigManager.ImsVoice#KEY_SRVCC_TYPE_INT_ARRAY}
+     * specifies the calls can be moved. Other calls will be disconnected.
+     * <p>
+     * The MmTelFeature may now release all resources related to the IMS calls.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void notifySrvccCompleted() {
+        // Base Implementation - Should be overridden by IMS service
+    }
+
+    /**
+     * Notifies the MmTelFeature that the SRVCC has failed.
+     *
+     * The handover can fail by encountering a failure at the radio level
+     * or temporary MSC server internal errors in handover procedure.
+     * Refer to 3GPP TS 23.216 section 8 Handover Failure.
+     * <p>
+     * IMS service will recover and continue calls over IMS.
+     * Per TS 24.237 12.2.4.2, UE shall send SIP UPDATE request containing the reason-text
+     * set to "failure to transition to CS domain".
+     *
+     * @hide
+     */
+    @SystemApi
+    public void notifySrvccFailed() {
+        // Base Implementation - Should be overridden by IMS service
+    }
+
+    /**
+     * Notifies the MmTelFeature that the SRVCC has been canceled.
+     *
+     * Since the state of network can be changed, the network can decide to terminate
+     * the handover procedure before its completion and to return to its state before the handover
+     * procedure was triggered.
+     * Refer to 3GPP TS 23.216 section 8.1.3 Handover Cancellation.
+     *
+     * <p>
+     * IMS service will recover and continue calls over IMS.
+     * Per TS 24.237 12.2.4.2, UE shall send SIP UPDATE request containing the reason-text
+     * set to "handover canceled".
+     *
+     * @hide
+     */
+    @SystemApi
+    public void notifySrvccCanceled() {
+        // Base Implementation - Should be overridden by IMS service
+    }
+
+    private void setSmsListener(IImsSmsListener listener) {
+        getImsSmsImpl().registerSmsListener(listener);
+    }
+
+    private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
+            byte[] pdu) {
+        getImsSmsImpl().sendSms(token, messageRef, format, smsc, isRetry, pdu);
+    }
+
+    private void onMemoryAvailable(int token) {
+        getImsSmsImpl().onMemoryAvailable(token);
+    }
+
+    private void acknowledgeSms(int token, int messageRef,
+            @ImsSmsImplBase.DeliverStatusResult int result) {
+        getImsSmsImpl().acknowledgeSms(token, messageRef, result);
+    }
+
+    private void acknowledgeSms(int token, int messageRef,
+            @ImsSmsImplBase.DeliverStatusResult int result, byte[] pdu) {
+        getImsSmsImpl().acknowledgeSms(token, messageRef, result, pdu);
+    }
+
+    private void acknowledgeSmsReport(int token, int messageRef,
+            @ImsSmsImplBase.StatusReportResult int result) {
+        getImsSmsImpl().acknowledgeSmsReport(token, messageRef, result);
+    }
+
+    private void onSmsReady() {
+        getImsSmsImpl().onReady();
+    }
+
+    /**
+     * Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
+     * non-functional implementation is returned.
+     *
+     * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
+     * Provider.
+     * @hide
+     */
+    @SystemApi
+    public @NonNull ImsSmsImplBase getSmsImplementation() {
+        return new ImsSmsImplBase();
+    }
+
+    private String getSmsFormat() {
+        return getImsSmsImpl().getSmsFormat();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @Override
+    @SystemApi
+    public void onFeatureRemoved() {
+        // Base Implementation - Should be overridden
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    @Override
+    @SystemApi
+    public void onFeatureReady() {
+        // Base Implementation - Should be overridden
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IImsMmTelFeature getBinder() {
+        return mImsMMTelBinder;
+    }
+
+    /**
+     * Set default Executor from ImsService.
+     * @param executor The default executor for the framework to use when executing the methods
+     * overridden by the implementation of MmTelFeature.
+     * @hide
+     */
+    public final void setDefaultExecutor(@NonNull Executor executor) {
+        if (mExecutor == null) {
+            mExecutor = executor;
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/feature/RcsFeature.java b/android-35/android/telephony/ims/feature/RcsFeature.java
new file mode 100644
index 0000000..1862412
--- /dev/null
+++ b/android-35/android/telephony/ims/feature/RcsFeature.java
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2018 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.ims.feature;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.ims.ImsRcsManager;
+import android.telephony.ims.aidl.CapabilityExchangeAidlWrapper;
+import android.telephony.ims.aidl.ICapabilityExchangeEventListener;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IOptionsResponseCallback;
+import android.telephony.ims.aidl.IPublishResponseCallback;
+import android.telephony.ims.aidl.ISubscribeResponseCallback;
+import android.telephony.ims.aidl.RcsOptionsResponseAidlWrapper;
+import android.telephony.ims.aidl.RcsPublishResponseAidlWrapper;
+import android.telephony.ims.aidl.RcsSubscribeResponseAidlWrapper;
+import android.telephony.ims.stub.CapabilityExchangeEventListener;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.RcsCapabilityExchangeImplBase;
+import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback;
+import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback;
+import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback;
+import android.util.Log;
+
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
+/**
+ * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
+ * this class and provide implementations of the RcsFeature methods that they support.
+ * @hide
+ */
+@SystemApi
+public class RcsFeature extends ImsFeature {
+
+    private static final String LOG_TAG = "RcsFeature";
+
+    private static final class RcsFeatureBinder extends IImsRcsFeature.Stub {
+        // Reference the outer class in order to have better test coverage metrics instead of
+        // creating a inner class referencing the outer class directly.
+        private final RcsFeature mReference;
+        private Executor mExecutor;
+
+        RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) {
+            mReference = classRef;
+            mExecutor = executor;
+        }
+
+        @Override
+        public int queryCapabilityStatus() throws RemoteException {
+            return executeMethodAsyncForResult(
+                    () -> mReference.queryCapabilityStatus().mCapabilities,
+                    "queryCapabilityStatus");
+        }
+
+        @Override
+        public void addCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
+            executeMethodAsync(() -> mReference.addCapabilityCallback(c), "addCapabilityCallback");
+        }
+
+        @Override
+        public void removeCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
+            executeMethodAsync(() -> mReference.removeCapabilityCallback(c),
+                    "removeCapabilityCallback");
+        }
+
+        @Override
+        public void changeCapabilitiesConfiguration(CapabilityChangeRequest r,
+                IImsCapabilityCallback c) throws RemoteException {
+            executeMethodAsync(() -> mReference.requestChangeEnabledCapabilities(r, c),
+                    "changeCapabilitiesConfiguration");
+        }
+
+        @Override
+        public void queryCapabilityConfiguration(int capability, int radioTech,
+                IImsCapabilityCallback c) throws RemoteException {
+            executeMethodAsync(() -> mReference.queryCapabilityConfigurationInternal(capability,
+                    radioTech, c), "queryCapabilityConfiguration");
+        }
+
+        @Override
+        public int getFeatureState() throws RemoteException {
+            return executeMethodAsyncForResult(mReference::getFeatureState, "getFeatureState");
+        }
+
+        // RcsCapabilityExchangeImplBase specific APIs
+        @Override
+        public void setCapabilityExchangeEventListener(
+                @Nullable ICapabilityExchangeEventListener listener) throws RemoteException {
+            // Set the listener wrapper to null if the listener passed in is null. This will notify
+            // the RcsFeature to trigger the destruction of active capability exchange interface.
+            CapabilityExchangeEventListener listenerWrapper = listener != null
+                    ? new CapabilityExchangeAidlWrapper(listener) : null;
+            executeMethodAsync(() -> mReference.setCapabilityExchangeEventListener(listenerWrapper),
+                    "setCapabilityExchangeEventListener");
+        }
+
+        @Override
+        public void publishCapabilities(@NonNull String pidfXml,
+                @NonNull IPublishResponseCallback callback) throws RemoteException {
+            PublishResponseCallback callbackWrapper = new RcsPublishResponseAidlWrapper(callback);
+            executeMethodAsync(() -> mReference.getCapabilityExchangeImplBaseInternal()
+                    .publishCapabilities(pidfXml, callbackWrapper), "publishCapabilities");
+        }
+
+        @Override
+        public void subscribeForCapabilities(@NonNull List<Uri> uris,
+                @NonNull ISubscribeResponseCallback callback) throws RemoteException {
+            SubscribeResponseCallback wrapper = new RcsSubscribeResponseAidlWrapper(callback);
+            executeMethodAsync(() -> mReference.getCapabilityExchangeImplBaseInternal()
+                    .subscribeForCapabilities(uris, wrapper), "subscribeForCapabilities");
+        }
+
+        @Override
+        public void sendOptionsCapabilityRequest(@NonNull Uri contactUri,
+                @NonNull List<String> myCapabilities, @NonNull IOptionsResponseCallback callback)
+                throws RemoteException {
+            OptionsResponseCallback callbackWrapper = new RcsOptionsResponseAidlWrapper(callback);
+            executeMethodAsync(() -> mReference.getCapabilityExchangeImplBaseInternal()
+                    .sendOptionsCapabilityRequest(contactUri, new HashSet<>(myCapabilities),
+                        callbackWrapper), "sendOptionsCapabilityRequest");
+        }
+
+        // Call the methods with a clean calling identity on the executor and wait indefinitely for
+        // the future to return.
+        private void executeMethodAsync(Runnable r, String errorLogName)
+                throws RemoteException {
+            // call with a clean calling identity on the executor and wait indefinitely for the
+            // future to return.
+            try {
+                CompletableFuture.runAsync(
+                        () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+
+        private <T> T executeMethodAsyncForResult(Supplier<T> r,
+                String errorLogName) throws RemoteException {
+            // call with a clean calling identity on the executor and wait indefinitely for the
+            // future to return.
+            CompletableFuture<T> future = CompletableFuture.supplyAsync(
+                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+            try {
+                return future.get();
+            } catch (ExecutionException | InterruptedException e) {
+                Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Contains the capabilities defined and supported by a {@link RcsFeature} in the
+     * form of a bitmask. The capabilities that are used in the RcsFeature are
+     * defined as:
+     * {@link ImsRcsManager.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
+     * {@link ImsRcsManager.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
+     *
+     * The enabled capabilities of this RcsFeature will be set by the framework
+     * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
+     * After the capabilities have been set, the RcsFeature may then perform the necessary bring up
+     * of the capability and notify the capability status as true using
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
+     * framework that the capability is available for usage.
+     */
+    public static class RcsImsCapabilities extends Capabilities {
+
+        /**
+         * Use {@link ImsRcsManager.RcsImsCapabilityFlag} instead in case used for public API
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = {
+                CAPABILITY_TYPE_NONE,
+                CAPABILITY_TYPE_OPTIONS_UCE,
+                CAPABILITY_TYPE_PRESENCE_UCE
+        })
+        public @interface RcsImsCapabilityFlag {}
+
+        /**
+         * Undefined capability type for initialization
+         */
+        public static final int CAPABILITY_TYPE_NONE = 0;
+
+        /**
+         * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
+         * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
+         * If not set, this RcsFeature should not service capability requests.
+         */
+        public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
+
+        /**
+         * This carrier supports User Capability Exchange using a presence server as defined by the
+         * framework. If set, the RcsFeature should support capability exchange using a presence
+         * server. If not set, this RcsFeature should not publish capabilities or service capability
+         * requests using presence.
+         */
+        public static final int CAPABILITY_TYPE_PRESENCE_UCE =  1 << 1;
+
+        /**
+         * This is used to check the upper range of RCS capability
+         * @hide
+         */
+        public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_PRESENCE_UCE + 1;
+
+        /**
+         * Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
+         * @param capabilities The capabilities that are supported for RCS in the form of a
+         * bitfield.
+         */
+        public RcsImsCapabilities(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) {
+            super(capabilities);
+        }
+
+        /**
+         * Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
+         * @param capabilities The capabilities instance that are supported for RCS
+         */
+        private RcsImsCapabilities(Capabilities capabilities) {
+            super(capabilities.getMask());
+        }
+
+        @Override
+        public void addCapabilities(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) {
+            super.addCapabilities(capabilities);
+        }
+
+        @Override
+        public void removeCapabilities(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) {
+            super.removeCapabilities(capabilities);
+        }
+
+        @Override
+        public boolean isCapable(@ImsRcsManager.RcsImsCapabilityFlag int capabilities) {
+            return super.isCapable(capabilities);
+        }
+    }
+
+    private Executor mExecutor;
+    private final RcsFeatureBinder mImsRcsBinder;
+    private RcsCapabilityExchangeImplBase mCapabilityExchangeImpl;
+    private CapabilityExchangeEventListener mCapExchangeEventListener;
+
+    /**
+     * Create a new RcsFeature.
+     * <p>
+     * Method stubs called from the framework will be called asynchronously. To specify the
+     * {@link Executor} that the methods stubs will be called, use
+     * {@link RcsFeature#RcsFeature(Executor)} instead.
+     */
+    public RcsFeature() {
+        super();
+        // Run on the Binder threads that call them.
+        mImsRcsBinder = new RcsFeatureBinder(this, mExecutor);
+    }
+
+    /**
+     * Create a new RcsFeature using the Executor specified for methods being called by the
+     * framework.
+     * @param executor The executor for the framework to use when executing the methods overridden
+     * by the implementation of RcsFeature.
+     */
+    public RcsFeature(@NonNull Executor executor) {
+        super();
+        if (executor == null) {
+            throw new IllegalArgumentException("executor can not be null.");
+        }
+        mExecutor = executor;
+        // Run on the Binder thread by default.
+        mImsRcsBinder = new RcsFeatureBinder(this, mExecutor);
+    }
+
+    /**
+     * Called when the RcsFeature is initialized.
+     *
+     * @param context The context that is used in the ImsService.
+     * @param slotId The slot ID associated with the RcsFeature.
+     * @hide
+     */
+    @Override
+    public void initialize(@NonNull Context context, @NonNull int slotId) {
+        super.initialize(context, slotId);
+        // Notify that the RcsFeature is ready.
+        mExecutor.execute(() -> onFeatureReady());
+    }
+
+    /**
+     * Query the current {@link RcsImsCapabilities} status set by the RcsFeature. If a capability is
+     * set, the {@link RcsFeature} has brought up the capability and is ready for framework
+     * requests. To change the status of the capabilities
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
+     * @return A copy of the current RcsFeature capability status.
+     */
+    @Override
+    public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
+        return new RcsImsCapabilities(super.queryCapabilityStatus());
+    }
+
+    /**
+     * Notify the framework that the capabilities status has changed. If a capability is enabled,
+     * this signals to the framework that the capability has been initialized and is ready.
+     * Call {@link #queryCapabilityStatus()} to return the current capability status.
+     * @param capabilities The current capability status of the RcsFeature.
+     */
+    public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) {
+        if (capabilities == null) {
+            throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
+        }
+        super.notifyCapabilitiesStatusChanged(capabilities);
+    }
+
+    /**
+     * Provides the RcsFeature with the ability to return the framework capability configuration set
+     * by the framework. When the framework calls
+     * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to
+     * enable or disable capability A, this method should return the correct configuration for
+     * capability A afterwards (until it has changed).
+     * @param capability The capability that we are querying the configuration for.
+     * @param radioTech The radio technology type that we are querying.
+     * @return true if the capability is enabled, false otherwise.
+     */
+    public boolean queryCapabilityConfiguration(
+            @ImsRcsManager.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        // Base Implementation - Override to provide functionality
+        return false;
+    }
+    /**
+     * Called from the framework when the {@link RcsImsCapabilities} that have been configured for
+     * this {@link RcsFeature} has changed.
+     * <p>
+     * For each newly enabled capability flag, the corresponding capability should be brought up in
+     * the {@link RcsFeature} and registered on the network. For each newly disabled capability
+     * flag, the corresponding capability should be brought down, and deregistered. Once a new
+     * capability has been initialized and is ready for usage, the status of that capability should
+     * also be set to true using {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This
+     * will notify the framework that the capability is ready.
+     * <p>
+     * If for some reason one or more of these capabilities can not be enabled/disabled,
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should
+     * be called for each capability change that resulted in an error.
+     * @param request The request to change the capability.
+     * @param callback To notify the framework that the result of the capability changes.
+     */
+    @Override
+    public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
+            @NonNull CapabilityCallbackProxy callback) {
+        // Base Implementation - Override to provide functionality
+    }
+
+    /**
+     * Retrieve the implementation of UCE for this {@link RcsFeature}, which can use either
+     * presence or OPTIONS for capability exchange.
+     *
+     * Will only be requested by the framework if capability exchange is configured
+     * as capable during a
+     * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
+     * operation and the RcsFeature sets the status of the capability to true using
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
+     *
+     * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
+     * event to the framework.
+     * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
+     * exchange if it is supported by the device.
+     */
+    public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
+            @NonNull CapabilityExchangeEventListener listener) {
+        // Base Implementation, override to implement functionality
+        return new RcsCapabilityExchangeImplBase();
+    }
+
+    /**
+     * Remove the given CapabilityExchangeImplBase instance.
+     * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be destroyed.
+     */
+    public void destroyCapabilityExchangeImpl(
+            @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
+        // Override to implement the process of destroying RcsCapabilityExchangeImplBase instance.
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureRemoved() {
+
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureReady() {
+
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IImsRcsFeature getBinder() {
+        return mImsRcsBinder;
+    }
+
+    /**
+     * Set the capability exchange listener.
+     * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
+     * event to the framework.
+     */
+    private void setCapabilityExchangeEventListener(
+            @Nullable CapabilityExchangeEventListener listener) {
+        synchronized (mLock) {
+            mCapExchangeEventListener = listener;
+            if (mCapExchangeEventListener != null) {
+                initRcsCapabilityExchangeImplBase(mCapExchangeEventListener);
+            } else {
+                // Remove the RcsCapabilityExchangeImplBase instance when the capability exchange
+                // instance has been removed in the framework.
+                if (mCapabilityExchangeImpl != null) {
+                    destroyCapabilityExchangeImpl(mCapabilityExchangeImpl);
+                }
+                mCapabilityExchangeImpl = null;
+            }
+        }
+    }
+
+    /**
+     * Initialize the RcsCapabilityExchangeImplBase instance if the capability exchange instance
+     * has already been created in the framework.
+     * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
+     * event to the framework.
+     */
+    private void initRcsCapabilityExchangeImplBase(
+            @NonNull CapabilityExchangeEventListener listener) {
+        synchronized (mLock) {
+            // Remove the original instance
+            if (mCapabilityExchangeImpl != null) {
+                destroyCapabilityExchangeImpl(mCapabilityExchangeImpl);
+            }
+            mCapabilityExchangeImpl = createCapabilityExchangeImpl(listener);
+        }
+    }
+
+    /**
+     * @return the {@link RcsCapabilityExchangeImplBase} associated with the RcsFeature.
+     */
+    private @NonNull RcsCapabilityExchangeImplBase getCapabilityExchangeImplBaseInternal() {
+        synchronized (mLock) {
+            // The method should not be called if the instance of RcsCapabilityExchangeImplBase has
+            // not been created yet.
+            if (mCapabilityExchangeImpl == null) {
+                throw new IllegalStateException("Session is not available.");
+            }
+            return mCapabilityExchangeImpl;
+        }
+    }
+
+    /**
+     * Set default Executor from ImsService.
+     * @param executor The default executor for the framework to use when executing the methods
+     * overridden by the implementation of RcsFeature.
+     * @hide
+     */
+    public final void setDefaultExecutor(@NonNull Executor executor) {
+        if (mImsRcsBinder.mExecutor == null) {
+            mExecutor = executor;
+            mImsRcsBinder.mExecutor = executor;
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/android-35/android/telephony/ims/stub/CapabilityExchangeEventListener.java
new file mode 100644
index 0000000..28c2d59
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.SipDetails;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
+
+import java.util.Set;
+
+/**
+ * The interface that is used by the framework to listen to events from the vendor RCS stack
+ * regarding capabilities exchange using presence server and OPTIONS.
+ * @hide
+ */
+@SystemApi
+public interface CapabilityExchangeEventListener {
+    /**
+     * Interface used by the framework to respond to OPTIONS requests.
+     */
+    interface OptionsRequestCallback {
+        /**
+         * Respond to a remote capability request from the contact specified with the
+         * capabilities of this device.
+         * @param ownCapabilities The capabilities of this device.
+         * @param isBlocked Whether or not the user has blocked the number requesting the
+         *         capabilities of this device. If true, the device should respond to the OPTIONS
+         *         request with a 200 OK response and no capabilities.
+         */
+        void onRespondToCapabilityRequest(@NonNull RcsContactUceCapability ownCapabilities,
+                boolean isBlocked);
+
+        /**
+         * Respond to a remote capability request from the contact specified with the
+         * specified error.
+         * @param code The SIP response code to respond with.
+         * @param reason A non-null String containing the reason associated with the SIP code.
+         */
+        void onRespondToCapabilityRequestWithError(@IntRange(from = 100, to = 699) int code,
+                @NonNull String reason);
+    }
+
+    /**
+     * Trigger the framework to provide a capability update using
+     * {@link RcsCapabilityExchangeImplBase#publishCapabilities}.
+     * <p>
+     * This is typically used when trying to generate an initial PUBLISH for a new subscription to
+     * the network. The device will cache all presence publications after boot until this method is
+     * called the first time.
+     * @param publishTriggerType The reason for the capability update request.
+     * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
+     */
+    void onRequestPublishCapabilities(
+            @RcsUceAdapter.StackPublishTriggerType int publishTriggerType) throws ImsException;
+
+    /**
+     * Notify the framework that the device's capabilities have been unpublished
+     * from the network.
+     *
+     * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
+     */
+    void onUnpublish() throws ImsException;
+
+    /**
+     * Notify the framework that the ImsService has refreshed the PUBLISH
+     * internally, which has resulted in a new PUBLISH result.
+     * <p>
+     * This method must be called to notify the framework of SUCCESS (200 OK) and FAILURE (300+)
+     * codes in order to keep the AOSP stack up to date.
+     * @param reasonCode The SIP response code sent from the network.
+     * @param reasonPhrase The optional reason response from the network. If the
+     * network provided no reason with the sip code, the string should be empty.
+     * @param reasonHeaderCause The “cause” parameter of the “reason” header
+     * included in the SIP message.
+     * @param reasonHeaderText The “text” parameter of the “reason” header
+     * included in the SIP message.
+     * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
+     * currently connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+     * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
+     * cases when the Telephony stack has crashed.
+     *
+     * @deprecated Replaced sip information with the newly added
+     * {@link #onPublishUpdated(SipDetails)}.
+     */
+    @Deprecated
+    default void onPublishUpdated(int reasonCode, @NonNull String reasonPhrase,
+            int reasonHeaderCause, @NonNull String reasonHeaderText) throws ImsException {
+        onPublishUpdated(new SipDetails.Builder(SipDetails.METHOD_PUBLISH)
+                .setSipResponseCode(reasonCode, reasonPhrase)
+                .setSipResponseReasonHeader(reasonHeaderCause, reasonHeaderText)
+                .build());
+    }
+
+    /**
+     * Notify the framework that the ImsService has refreshed the PUBLISH
+     * internally, which has resulted in a new PUBLISH result.
+     * <p>
+     * This method must be called to notify the framework of SUCCESS (200 OK) and FAILURE (300+)
+     * codes in order to keep the AOSP stack up to date.
+     * @param details The SIP information received in response to a publish operation.
+     * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
+     * currently connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+     * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
+     * cases when the Telephony stack has crashed.
+     */
+    default void onPublishUpdated(@NonNull SipDetails details)
+            throws ImsException {
+    }
+    /**
+     * Inform the framework of an OPTIONS query from a remote device for this device's UCE
+     * capabilities.
+     * <p>
+     * The framework will respond via the
+     * {@link OptionsRequestCallback#onRespondToCapabilityRequest} or
+     * {@link OptionsRequestCallback#onRespondToCapabilityRequestWithError}.
+     * @param contactUri The URI associated with the remote contact that is
+     * requesting capabilities.
+     * @param remoteCapabilities The remote contact's capability information. The capability
+     * information is in the format defined in RCC.07 section 2.6.1.3.
+     * @param callback The callback of this request which is sent from the remote user.
+     * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
+     * currently connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+     * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
+     * cases when the Telephony stack has crashed.
+     */
+    void onRemoteCapabilityRequest(@NonNull Uri contactUri,
+            @NonNull Set<String> remoteCapabilities,
+            @NonNull OptionsRequestCallback callback) throws ImsException;
+}
diff --git a/android-35/android/telephony/ims/stub/DelegateConnectionMessageCallback.java b/android-35/android/telephony/ims/stub/DelegateConnectionMessageCallback.java
new file mode 100644
index 0000000..eefe849
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/DelegateConnectionMessageCallback.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+
+/**
+ * The callback associated with a {@link SipDelegateConnection}, which handles newly received
+ * messages as well as the result of sending a SIP message.
+ * @hide
+ */
+@SystemApi
+public interface DelegateConnectionMessageCallback {
+
+    /**
+     * A new {@link SipMessage} has been received from the delegate.
+     * @param message the {@link SipMessage} routed to this RCS application.
+     */
+    void onMessageReceived(@NonNull SipMessage message);
+
+    /**
+     * A message previously sent to the SIP delegate using
+     * {@link SipDelegateConnection#sendMessage} has been successfully sent.
+     * @param viaTransactionId The transaction ID found in the via header field of the
+     *                         previously sent {@link SipMessage}.
+     */
+    void onMessageSent(@NonNull String viaTransactionId);
+
+    /**
+     * A message previously sent to the SIP delegate using
+     * {@link SipDelegateConnection#sendMessage} has failed to be sent.
+     * @param viaTransactionId The Transaction ID found in the via header field of the
+     *                         previously sent {@link SipMessage}.
+     * @param reason The reason for the failure.
+     */
+    void onMessageSendFailure(@NonNull String viaTransactionId,
+            @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/android-35/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/android-35/android/telephony/ims/stub/DelegateConnectionStateCallback.java
new file mode 100644
index 0000000..42c53f2
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/DelegateConnectionStateCallback.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+
+import java.util.Set;
+
+/**
+ * The callback associated with a {@link SipDelegateConnection} that manages the state of the
+ * SipDelegateConnection.
+ * <p>
+ * After {@link SipDelegateManager#createSipDelegate} is used to request a new
+ * {@link SipDelegateConnection} be created, {@link #onCreated} will be called with the
+ * {@link SipDelegateConnection} instance that must be used to communicate with the remote
+ * {@link SipDelegate}.
+ * <p>
+ * After, {@link #onFeatureTagStatusChanged} will always be called at least once with the current
+ * status of the feature tags that have been requested. The application may receive multiple
+ * {@link #onFeatureTagStatusChanged} callbacks over the lifetime of the associated
+ * {@link SipDelegateConnection}, which will signal changes to how SIP messages associated with
+ * those feature tags will be handled.
+ * <p>
+ * In order to start sending SIP messages, the SIP configuration parameters will need to be
+ * received, so the messaging application should make no assumptions about these parameters and wait
+ * until {@link #onConfigurationChanged(SipDelegateConfiguration)} has been called. This is
+ * guaranteed to happen after the first {@link #onFeatureTagStatusChanged} if there is at least one
+ * feature tag that has been successfully associated with the {@link SipDelegateConnection}. If all
+ * feature tags were denied, no IMS configuration will be sent.
+ * <p>
+ * The {@link SipDelegateConnection} will stay associated with this RCS application until either the
+ * RCS application calls {@link SipDelegateManager#destroySipDelegate} or telephony destroys the
+ * {@link SipDelegateConnection}. In both cases, {@link #onDestroyed(int)}  will be called.
+ * Telephony destroying the {@link SipDelegateConnection} instance is rare and will only happen in
+ * rare cases, such as if telephony itself or IMS service dies unexpectedly. See
+ * {@link SipDelegateManager.SipDelegateDestroyReason} reasons for more information on all of the
+ * cases that will trigger the {@link SipDelegateConnection} to be destroyed.
+ *
+ * @hide
+ */
+@SystemApi
+public interface DelegateConnectionStateCallback {
+
+    /**
+     * A {@link SipDelegateConnection} has been successfully created for the
+     * {@link DelegateRequest} used when calling {@link SipDelegateManager#createSipDelegate}.
+     */
+    void onCreated(@NonNull SipDelegateConnection c);
+
+    /**
+     * The status of the RCS feature tags that were requested as part of the initial
+     * {@link DelegateRequest}.
+     * <p>
+     * There are four states that each RCS feature tag can be in: registered, deregistering,
+     * deregistered, and denied.
+     * <p>
+     * When a feature tag is considered registered, SIP messages associated with that feature tag
+     * may be sent and received freely.
+     * <p>
+     * When a feature tag is deregistering, the network IMS registration still contains the feature
+     * tag, however the IMS service and associated {@link SipDelegate} is in the progress of
+     * modifying the IMS registration to remove this feature tag and requires the application to
+     * perform an action before the IMS registration can change. The specific action required for
+     * the SipDelegate to continue modifying the IMS registration can be found in the definition of
+     * each {@link DelegateRegistrationState.DeregisteringReason}.
+     * <p>
+     * When a feature tag is in the deregistered state, new out-of-dialog SIP messages for that
+     * feature tag will be rejected, however due to network race conditions, the RCS application
+     * should still be able to handle new out-of-dialog SIP requests from the network. This may not
+     * be possible, however, if the IMS registration itself was lost. See the
+     * {@link DelegateRegistrationState.DeregisteredReason} reasons for more information on how SIP
+     * messages are handled in each of these cases.
+     * <p>
+     * If a feature tag is denied, no incoming messages will be routed to the associated
+     * {@link DelegateConnectionMessageCallback} and all outgoing SIP messages related to this
+     * feature tag will be rejected. See {@link SipDelegateManager.DeniedReason}
+     * reasons for more information about the conditions when this will happen.
+     * <p>
+     * The set of feature tags contained in the registered, deregistering, deregistered, and denied
+     * lists will always equal the set of feature tags requested in the initial
+     * {@link DelegateRequest}.
+     * <p>
+     * Transitions of feature tags from registered, deregistering, and deregistered and vice-versa
+     * may happen quite often, however transitions to/from denied are rare and only occur if the
+     * user has changed the role of your application to add/remove support for one or more requested
+     * feature tags or carrier provisioning has enabled or disabled single registration entirely.
+     * Please see {@link SipDelegateManager.DeniedReason} reasons for an explanation of each of
+     * these cases as well as what may cause them to change.
+     *
+     * @param registrationState The new IMS registration state of each of the feature tags
+     *     associated with the {@link SipDelegate}.
+     * @param deniedFeatureTags A list of {@link FeatureTagState} objects, each containing a feature
+     *     tag associated with this {@link SipDelegateConnection} that has no access to
+     *     send/receive SIP messages as well as a reason for why the feature tag is denied. For more
+     *     information on the reason why the feature tag was denied access, see the
+     *     {@link SipDelegateManager.DeniedReason} reasons.
+     */
+    void onFeatureTagStatusChanged(@NonNull DelegateRegistrationState registrationState,
+            @NonNull Set<FeatureTagState> deniedFeatureTags);
+
+
+    /**
+     * IMS configuration of the underlying IMS stack used by this IMS application for construction
+     * of the SIP messages that will be sent over the carrier's network.
+     * <p>
+     * There should never be assumptions made about the configuration of the underling IMS stack and
+     * the IMS application should wait for this indication before sending out any outgoing SIP
+     * messages.
+     * <p>
+     * Configuration may change due to IMS registration changes as well as
+     * other optional events on the carrier network. If IMS stack is already registered at the time
+     * of callback registration, then this method shall be invoked with the current configuration.
+     * Otherwise, there may be a delay in this method being called if initial IMS registration has
+     * not compleed yet.
+     *
+     * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network.
+     * @removed Will not be in final API, use
+     * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead}.
+     */
+    @Deprecated
+    default void onImsConfigurationChanged(
+            @NonNull SipDelegateImsConfiguration registeredSipConfig) {
+        onConfigurationChanged(registeredSipConfig.toNewConfig());
+    }
+
+    /**
+     * IMS configuration of the underlying IMS stack used by this IMS application for construction
+     * of the SIP messages that will be sent over the carrier's network.
+     * <p>
+     * There should never be assumptions made about the configuration of the underling IMS stack and
+     * the IMS application should wait for this indication before sending out any outgoing SIP
+     * messages.
+     * <p>
+     * Configuration may change due to IMS registration changes as well as
+     * other optional events on the carrier network. If IMS stack is already registered at the time
+     * of callback registration, then this method shall be invoked with the current configuration.
+     * Otherwise, there may be a delay in this method being called if initial IMS registration has
+     * not compleed yet.
+     *
+     * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network.
+     */
+    void onConfigurationChanged(@NonNull SipDelegateConfiguration registeredSipConfig);
+
+    /**
+     * The previously created {@link SipDelegateConnection} instance delivered via
+     * {@link #onCreated(SipDelegateConnection)} has been destroyed. This interface should no longer
+     * be used for any SIP message handling.
+     *
+     * @param reason The reason for the failure.
+     */
+    void onDestroyed(@SipDelegateManager.SipDelegateDestroyReason int reason);
+}
diff --git a/android-35/android/telephony/ims/stub/ImsCallSessionImplBase.java b/android-35/android/telephony/ims/stub/ImsCallSessionImplBase.java
new file mode 100644
index 0000000..30a0684
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -0,0 +1,821 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsCallSessionListener;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsVideoCallProvider;
+import android.telephony.ims.RtpHeaderExtension;
+import android.telephony.ims.RtpHeaderExtensionType;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
+/**
+ * Base implementation of IImsCallSession, which implements stub versions of the methods available.
+ *
+ * Override the methods that your implementation of ImsCallSession supports.
+ *
+ * @hide
+ */
+@SystemApi
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsCallSession maintained by other ImsServices.
+public class ImsCallSessionImplBase implements AutoCloseable {
+
+    private static final String LOG_TAG = "ImsCallSessionImplBase";
+    /**
+     * Notify USSD Mode.
+     */
+    public static final int USSD_MODE_NOTIFY = 0;
+    /**
+     * Request USSD Mode
+     */
+    public static final int USSD_MODE_REQUEST = 1;
+
+    /** @hide */
+    @IntDef(
+        prefix = "MEDIA_STREAM_TYPE_",
+        value = {
+            MEDIA_STREAM_TYPE_AUDIO,
+            MEDIA_STREAM_TYPE_VIDEO
+        })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaStreamType {}
+
+    /**
+     * Media Stream Type - Audio
+     * @hide
+     */
+    public static final int MEDIA_STREAM_TYPE_AUDIO = 1;
+    /**
+     * Media Stream Type - Video
+     * @hide
+     */
+    public static final int MEDIA_STREAM_TYPE_VIDEO = 2;
+
+    /** @hide */
+    @IntDef(
+        prefix = "MEDIA_STREAM_DIRECTION_",
+        value = {
+            MEDIA_STREAM_DIRECTION_UPLINK,
+            MEDIA_STREAM_DIRECTION_DOWNLINK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaStreamDirection {}
+
+    /**
+     * Media Stream Direction - Uplink
+     * @hide
+     */
+    public static final int MEDIA_STREAM_DIRECTION_UPLINK = 1;
+    /**
+     * Media Stream Direction - Downlink
+     * @hide
+     */
+    public static final int MEDIA_STREAM_DIRECTION_DOWNLINK = 2;
+
+    /**
+     * Defines IMS call session state.
+     */
+    public static class State {
+        public static final int IDLE = 0;
+        public static final int INITIATED = 1;
+        public static final int NEGOTIATING = 2;
+        public static final int ESTABLISHING = 3;
+        public static final int ESTABLISHED = 4;
+
+        public static final int RENEGOTIATING = 5;
+        public static final int REESTABLISHING = 6;
+
+        public static final int TERMINATING = 7;
+        public static final int TERMINATED = 8;
+
+        public static final int INVALID = (-1);
+
+        /**
+         * Converts the state to string.
+         */
+        public static String toString(int state) {
+            switch (state) {
+                case IDLE:
+                    return "IDLE";
+                case INITIATED:
+                    return "INITIATED";
+                case NEGOTIATING:
+                    return "NEGOTIATING";
+                case ESTABLISHING:
+                    return "ESTABLISHING";
+                case ESTABLISHED:
+                    return "ESTABLISHED";
+                case RENEGOTIATING:
+                    return "RENEGOTIATING";
+                case REESTABLISHING:
+                    return "REESTABLISHING";
+                case TERMINATING:
+                    return "TERMINATING";
+                case TERMINATED:
+                    return "TERMINATED";
+                default:
+                    return "UNKNOWN";
+            }
+        }
+
+        /**
+         * @hide
+         */
+        private State() {
+        }
+    }
+
+    private Executor mExecutor = Runnable::run;
+
+    // Non-final for injection by tests
+    private IImsCallSession mServiceImpl = new IImsCallSession.Stub() {
+        @Override
+        public void close() {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.close(), "close");
+        }
+
+        @Override
+        public String getCallId() {
+            return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallId(),
+                    "getCallId");
+        }
+
+        @Override
+        public ImsCallProfile getCallProfile() {
+            return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallProfile(),
+                    "getCallProfile");
+        }
+
+        @Override
+        public ImsCallProfile getLocalCallProfile() {
+            return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
+                    .getLocalCallProfile(), "getLocalCallProfile");
+        }
+
+        @Override
+        public ImsCallProfile getRemoteCallProfile() {
+            return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
+                    .getRemoteCallProfile(), "getRemoteCallProfile");
+        }
+
+        @Override
+        public String getProperty(String name) {
+            return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getProperty(name),
+                    "getProperty");
+        }
+
+        @Override
+        public int getState() {
+            return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getState(),
+                    "getState");
+        }
+
+        @Override
+        public boolean isInCall() {
+            return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isInCall(),
+                    "isInCall");
+        }
+
+        @Override
+        public void setListener(IImsCallSessionListener listener) {
+            ImsCallSessionListener iCSL = new ImsCallSessionListener(listener);
+            iCSL.setDefaultExecutor(mExecutor);
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.setListener(iCSL), "setListener");
+        }
+
+        @Override
+        public void setMute(boolean muted) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.setMute(muted), "setMute");
+        }
+
+        @Override
+        public void start(String callee, ImsCallProfile profile) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.start(callee, profile), "start");
+        }
+
+        @Override
+        public void startConference(String[] participants, ImsCallProfile profile) throws
+                RemoteException {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.startConference(participants,
+                    profile), "startConference");
+        }
+
+        @Override
+        public void accept(int callType, ImsStreamMediaProfile profile) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.accept(callType, profile),
+                    "accept");
+        }
+
+        @Override
+        public void deflect(String deflectNumber) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.deflect(deflectNumber),
+                    "deflect");
+        }
+
+        @Override
+        public void reject(int reason) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.reject(reason), "reject");
+        }
+
+        @Override
+        public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.transfer(number,
+                    isConfirmationRequired), "transfer");
+        }
+
+        @Override
+        public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
+            executeMethodAsync(() -> {
+                ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase();
+                otherSession.setServiceImpl(transferToSession);
+                ImsCallSessionImplBase.this.transfer(otherSession);
+            }, "consultativeTransfer");
+        }
+
+        @Override
+        public void terminate(int reason) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.terminate(reason), "terminate");
+        }
+
+        @Override
+        public void hold(ImsStreamMediaProfile profile) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.hold(profile), "hold");
+        }
+
+        @Override
+        public void resume(ImsStreamMediaProfile profile) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.resume(profile), "resume");
+        }
+
+        @Override
+        public void merge() {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.merge(), "merge");
+        }
+
+        @Override
+        public void update(int callType, ImsStreamMediaProfile profile) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.update(callType, profile),
+                    "update");
+        }
+
+        @Override
+        public void extendToConference(String[] participants) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.extendToConference(participants),
+                    "extendToConference");
+        }
+
+        @Override
+        public void inviteParticipants(String[] participants) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.inviteParticipants(participants),
+                    "inviteParticipants");
+        }
+
+        @Override
+        public void removeParticipants(String[] participants) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.removeParticipants(participants),
+                    "removeParticipants");
+        }
+
+        @Override
+        public void sendDtmf(char c, Message result) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.sendDtmf(c, result), "sendDtmf");
+        }
+
+        @Override
+        public void startDtmf(char c) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.startDtmf(c), "startDtmf");
+        }
+
+        @Override
+        public void stopDtmf() {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.stopDtmf(), "stopDtmf");
+        }
+
+        @Override
+        public void sendUssd(String ussdMessage) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.sendUssd(ussdMessage), "sendUssd");
+        }
+
+        @Override
+        public IImsVideoCallProvider getVideoCallProvider() {
+            return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
+                    .getVideoCallProvider(), "getVideoCallProvider");
+        }
+
+        @Override
+        public boolean isMultiparty() {
+            return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isMultiparty(),
+                    "isMultiparty");
+        }
+
+        @Override
+        public void sendRttModifyRequest(ImsCallProfile toProfile) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile),
+                    "sendRttModifyRequest");
+        }
+
+        @Override
+        public void sendRttModifyResponse(boolean status) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyResponse(status),
+                    "sendRttModifyResponse");
+        }
+
+        @Override
+        public void sendRttMessage(String rttMessage) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttMessage(rttMessage),
+                    "sendRttMessage");
+        }
+
+        @Override
+        public void sendRtpHeaderExtensions(@NonNull List<RtpHeaderExtension> extensions) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRtpHeaderExtensions(
+                    new ArraySet<RtpHeaderExtension>(extensions)), "sendRtpHeaderExtensions");
+        }
+
+        @Override
+        public void callSessionNotifyAnbr(int mediaType, int direction, int bitsPerSecond) {
+            executeMethodAsync(() -> ImsCallSessionImplBase.this.callSessionNotifyAnbr(
+                    mediaType, direction, bitsPerSecond), "callSessionNotifyAnbr");
+        }
+
+        // Call the methods with a clean calling identity on the executor and wait indefinitely for
+        // the future to return.
+        private void executeMethodAsync(Runnable r, String errorLogName) {
+            try {
+                CompletableFuture.runAsync(
+                        () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(LOG_TAG, "ImsCallSessionImplBase Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+            }
+        }
+
+        private <T> T executeMethodAsyncForResult(Supplier<T> r,
+                String errorLogName) {
+            CompletableFuture<T> future = CompletableFuture.supplyAsync(
+                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+            try {
+                return future.get();
+            } catch (ExecutionException | InterruptedException e) {
+                Log.w(LOG_TAG, "ImsCallSessionImplBase Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                return null;
+            }
+        }
+    };
+
+    /**
+     * @hide
+     */
+    public final void setListener(IImsCallSessionListener listener) throws RemoteException {
+        setListener(new ImsCallSessionListener(listener));
+    }
+
+    /**
+     * Sets the listener to listen to the session events. An {@link ImsCallSession}
+     * can only hold one listener at a time. Subsequent calls to this method
+     * override the previous listener.
+     *
+     * @param listener {@link ImsCallSessionListener} used to notify the framework of updates
+     * to the ImsCallSession
+
+     * @deprecated use {@link android.telephony.ims.feature.MmTelFeature#notifyIncomingCall(
+     * ImsCallSessionImplBase, String, Bundle)} to get the listener instead
+     */
+    @Deprecated
+    public void setListener(ImsCallSessionListener listener) {
+    }
+
+    /**
+     * Closes the object. This {@link ImsCallSessionImplBase} is not usable after being closed.
+     */
+    @Override
+    public void close() {
+
+    }
+
+    /**
+     * @return A String containing the unique call ID of this {@link ImsCallSessionImplBase}.
+     */
+    public String getCallId() {
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is associated
+     * with.
+     */
+    public ImsCallProfile getCallProfile() {
+        return null;
+    }
+
+    /**
+     * @return The local {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+     * associated with.
+     */
+    public ImsCallProfile getLocalCallProfile() {
+        return null;
+    }
+
+    /**
+     * @return The remote {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+     * associated with.
+     */
+    public ImsCallProfile getRemoteCallProfile() {
+        return null;
+    }
+
+    /**
+     * @param name The String extra key.
+     * @return The string extra value associated with the specified property.
+     */
+    public String getProperty(String name) {
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsCallSessionImplBase} state, defined in
+     * {@link ImsCallSessionImplBase.State}.
+     */
+    public int getState() {
+        return ImsCallSessionImplBase.State.INVALID;
+    }
+
+    /**
+     * @return true if the {@link ImsCallSessionImplBase} is in a call, false otherwise.
+     */
+    public boolean isInCall() {
+        return false;
+    }
+
+    /**
+     * Mutes or unmutes the mic for the active call.
+     *
+     * @param muted true if the call should be muted, false otherwise.
+     */
+    public void setMute(boolean muted) {
+    }
+
+    /**
+     * Initiates an IMS call with the specified number and call profile.
+     * The session listener set in {@link #setListener(ImsCallSessionListener)} is called back upon
+     * defined session events.
+     * Only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param callee dialed string to make the call to
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see {@link ImsCallSession.Listener#callSessionStarted},
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    public void start(String callee, ImsCallProfile profile) {
+    }
+
+    /**
+     * Initiates an IMS call with the specified participants and call profile.
+     * The session listener set in {@link #setListener(ImsCallSessionListener)} is called back upon
+     * defined session events.
+     * The method is only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param participants participant list to initiate an IMS conference call
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see {@link ImsCallSession.Listener#callSessionStarted},
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    public void startConference(String[] participants, ImsCallProfile profile) {
+    }
+
+    /**
+     * Accepts an incoming call or session update.
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be answered
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+     * @see {@link ImsCallSession.Listener#callSessionStarted}
+     */
+    public void accept(int callType, ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Deflects an incoming call.
+     *
+     * @param deflectNumber number to deflect the call
+     */
+    public void deflect(String deflectNumber) {
+    }
+
+    /**
+     * Rejects an incoming call or session update.
+     *
+     * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
+     *               The {@link android.telecom.InCallService} (dialer app) can use the
+     *               {@link android.telecom.Call#reject(int)} API to reject a call while specifying
+     *               a user-indicated reason for rejecting the call.
+     *               Normal call declines ({@link android.telecom.Call#REJECT_REASON_DECLINED}) will
+     *               map to {@link ImsReasonInfo#CODE_USER_DECLINE}.
+     *               Unwanted calls ({@link android.telecom.Call#REJECT_REASON_UNWANTED}) will map
+     *               to {@link ImsReasonInfo#CODE_SIP_USER_MARKED_UNWANTED}.
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    public void reject(int reason) {
+    }
+
+    /**
+     * Transfer an established call to given number
+     *
+     * @param number number to transfer the call
+     * @param isConfirmationRequired if {@code True}, indicates a confirmed transfer,
+     * if {@code False} it indicates an unconfirmed transfer.
+     * @hide
+     */
+    public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+    }
+
+    /**
+     * Transfer an established call to another call session
+     *
+     * @param otherSession The other ImsCallSession to transfer the ongoing session to.
+     * @hide
+     */
+    public void transfer(@NonNull ImsCallSessionImplBase otherSession) {
+    }
+
+    /**
+     * Terminates a call.
+     *
+     * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
+     *
+     * @see {@link ImsCallSession.Listener#callSessionTerminated}
+     */
+    public void terminate(int reason) {
+    }
+
+    /**
+     * Puts a call on hold. When it succeeds, {@link ImsCallSession.Listener#callSessionHeld} is
+     * called.
+     *
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+     * @see {@link ImsCallSession.Listener#callSessionHeld},
+     * {@link ImsCallSession.Listener#callSessionHoldFailed}
+     */
+    public void hold(ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Continues a call that's on hold. When it succeeds,
+     * {@link ImsCallSession.Listener#callSessionResumed} is called.
+     *
+     * @param profile stream media profile with {@link ImsStreamMediaProfile} to resume the call
+     * @see {@link ImsCallSession.Listener#callSessionResumed},
+     * {@link ImsCallSession.Listener#callSessionResumeFailed}
+     */
+    public void resume(ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Merges the active and held call. When the merge starts,
+     * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
+     * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
+     * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
+     * fails.
+     *
+     * @see {@link ImsCallSession.Listener#callSessionMergeStarted},
+     * {@link ImsCallSession.Listener#callSessionMergeComplete},
+     *      {@link ImsCallSession.Listener#callSessionMergeFailed}
+     */
+    public void merge() {
+    }
+
+    /**
+     * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be updated
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+     * @see {@link ImsCallSession.Listener#callSessionUpdated},
+     * {@link ImsCallSession.Listener#callSessionUpdateFailed}
+     */
+    public void update(int callType, ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Extends this call to the conference call with the specified recipients.
+     *
+     * @param participants participant list to be invited to the conference call after extending the
+     * call
+     * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
+     * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
+     */
+    public void extendToConference(String[] participants) {
+    }
+
+    /**
+     * Requests the conference server to invite an additional participants to the conference.
+     *
+     * @param participants participant list to be invited to the conference call
+     * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
+     *      {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
+     */
+    public void inviteParticipants(String[] participants) {
+    }
+
+    /**
+     * Requests the conference server to remove the specified participants from the conference.
+     *
+     * @param participants participant list to be removed from the conference call
+     * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
+     *      {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
+     */
+    public void removeParticipants(String[] participants) {
+    }
+
+    /**
+     * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     * @param result If non-null, the {@link Message} to send when the operation is complete. This
+     *         is done by using the associated {@link android.os.Messenger} in
+     *         {@link Message#replyTo}. For example:
+     * {@code
+     *     // Send DTMF and other operations...
+     *     try {
+     *         // Notify framework that the DTMF was sent.
+     *         Messenger dtmfMessenger = result.replyTo;
+     *         if (dtmfMessenger != null) {
+     *             dtmfMessenger.send(result);
+     *         }
+     *     } catch (RemoteException e) {
+     *         // Remote side is dead
+     *     }
+     * }
+     */
+    public void sendDtmf(char c, Message result) {
+    }
+
+    /**
+     * Start a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    public void startDtmf(char c) {
+    }
+
+    /**
+     * Stop a DTMF code.
+     */
+    public void stopDtmf() {
+    }
+
+    /**
+     * Sends an USSD message.
+     *
+     * @param ussdMessage USSD message to send
+     */
+    public void sendUssd(String ussdMessage) {
+    }
+
+    /**
+     * See {@link #getImsVideoCallProvider()}, used directly in older ImsService implementations.
+     * @hide
+     */
+    public IImsVideoCallProvider getVideoCallProvider() {
+        ImsVideoCallProvider provider = getImsVideoCallProvider();
+        return provider != null ? provider.getInterface() : null;
+    }
+
+    /**
+     * @return The {@link ImsVideoCallProvider} implementation contained within the IMS service
+     * process.
+     */
+    public ImsVideoCallProvider getImsVideoCallProvider() {
+        return null;
+    }
+
+    /**
+     * Determines if the current session is multiparty.
+     * @return {@code True} if the session is multiparty.
+     */
+    public boolean isMultiparty() {
+        return false;
+    }
+
+    /**
+     * Device issues RTT modify request
+     * @param toProfile The profile with requested changes made
+     */
+    public void sendRttModifyRequest(ImsCallProfile toProfile) {
+    }
+
+    /**
+     * Device responds to Remote RTT modify request
+     * @param status true if the the request was accepted or false of the request is defined.
+     */
+    public void sendRttModifyResponse(boolean status) {
+    }
+
+    /**
+     * Device sends RTT message
+     * @param rttMessage RTT message to be sent
+     */
+    public void sendRttMessage(String rttMessage) {
+    }
+
+    /**
+     * Device requests that {@code rtpHeaderExtensions} are sent as a header extension with the next
+     * RTP packet sent by the IMS stack.
+     * <p>
+     * The {@link RtpHeaderExtensionType}s negotiated during SDP (Session Description Protocol)
+     * signalling determine the {@link RtpHeaderExtension}s which can be sent using this method.
+     * See RFC8285 for more information.
+     * <p>
+     * By specification, the RTP header extension is an unacknowledged transmission and there is no
+     * guarantee that the header extension will be delivered by the network to the other end of the
+     * call.
+     * @param rtpHeaderExtensions The RTP header extensions to be included in the next RTP header.
+     */
+    public void sendRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) {
+    }
+
+    /**
+     * Deliver the bitrate for the indicated media type, direction and bitrate to the upper layer.
+     *
+     * @param mediaType MediaType is used to identify media stream such as audio or video.
+     * @param direction Direction of this packet stream (e.g. uplink or downlink).
+     * @param bitsPerSecond This value is the bitrate received from the NW through the Recommended
+     *        bitrate MAC Control Element message and ImsStack converts this value from MAC bitrate
+     *        to audio/video codec bitrate (defined in TS26.114).
+     * @hide
+     */
+    public void callSessionNotifyAnbr(@MediaStreamType int mediaType,
+            @MediaStreamDirection int direction, @IntRange(from = 0) int bitsPerSecond) {
+        // Base Implementation - Should be overridden by IMS service
+        Log.i(LOG_TAG, "ImsCallSessionImplBase callSessionNotifyAnbr - mediaType: " + mediaType);
+    }
+
+    /** @hide */
+    public IImsCallSession getServiceImpl() {
+        return mServiceImpl;
+    }
+
+    /** @hide */
+    public void setServiceImpl(IImsCallSession serviceImpl) {
+        mServiceImpl = serviceImpl;
+    }
+
+    /**
+     * Set default Executor from MmTelFeature.
+     * @param executor The default executor for the framework to use when executing the methods
+     * overridden by the implementation of ImsCallSession.
+     * @hide
+     */
+    public final void setDefaultExecutor(@NonNull Executor executor) {
+        mExecutor = executor;
+    }
+}
diff --git a/android-35/android/telephony/ims/stub/ImsConfigImplBase.java b/android-35/android/telephony/ims/stub/ImsConfigImplBase.java
new file mode 100644
index 0000000..ad8a936
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -0,0 +1,904 @@
+/*
+ * Copyright (C) 2018 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.ims.stub;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RcsClientConfiguration;
+import android.telephony.ims.RcsConfig;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
+import android.telephony.ims.aidl.IRcsConfigCallback;
+import android.util.Log;
+
+import com.android.ims.ImsConfig;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.RemoteCallbackListExt;
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+
+/**
+ * Controls the modification of IMS specific configurations. For more information on the supported
+ * IMS configuration constants, see {@link ImsConfig}.
+ *
+ * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface.
+ * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes.
+ * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in
+ * during initialization, or times when a lot of configuration parameters are being set/get
+ * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed
+ * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be
+ * performed every time.
+ * @hide
+ */
+@SystemApi
+public class ImsConfigImplBase {
+
+    private static final String TAG = "ImsConfigImplBase";
+
+    /**
+     * Implements the IImsConfig AIDL interface, which is called by potentially many processes
+     * in order to get/set configuration parameters.
+     *
+     * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl
+     * with actual implementations from vendors. This class caches provisioned values from
+     * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in,
+     * it first checks cache layer. If missed, it will call the vendor implementation of
+     * ImsConfigImplBase API.
+     * and cache the return value if the set succeeds.
+     *
+     * Provides APIs to get/set the IMS service feature/capability/parameters.
+     * The config items include:
+     * 1) Items provisioned by the operator.
+     * 2) Items configured by user. Mainly service feature class.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    static public class ImsConfigStub extends IImsConfig.Stub {
+        WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
+        private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
+        private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
+        private final Object mLock = new Object();
+        private Executor mExecutor;
+
+        @VisibleForTesting
+        public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Executor executor) {
+            mExecutor = executor;
+            mImsConfigImplBaseWeakReference =
+                    new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
+        }
+
+        @Override
+        public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            executeMethodAsync(()-> {
+                try {
+                    getImsConfigImpl().addImsConfigCallback(c);
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                }
+            }, "addImsConfigCallback");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception addImsConfigCallback");
+                throw exceptionRef.get();
+            }
+        }
+
+        @Override
+        public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            executeMethodAsync(()-> {
+                try {
+                    getImsConfigImpl().removeImsConfigCallback(c);
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                }
+            }, "removeImsConfigCallback");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception removeImsConfigCallback");
+                throw exceptionRef.get();
+            }
+        }
+
+        /**
+         * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+         * if missed, it will call ImsConfigImplBase.getConfigInt.
+         * Synchronous blocking call.
+         *
+         * @param item integer key
+         * @return value in Integer format or {@link #CONFIG_RESULT_UNKNOWN} if
+         * unavailable.
+         */
+        @Override
+        public int getConfigInt(int item) throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            int retVal = executeMethodAsyncForResult(()-> {
+                int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
+                synchronized (mLock) {
+                    if (mProvisionedIntValue.containsKey(item)) {
+                        return mProvisionedIntValue.get(item);
+                    } else {
+                        try {
+                            returnVal = getImsConfigImpl().getConfigInt(item);
+                            if (returnVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
+                                mProvisionedIntValue.put(item, returnVal);
+                            }
+                        } catch (RemoteException e) {
+                            exceptionRef.set(e);
+                            return returnVal;
+                        }
+                    }
+                }
+                return returnVal;
+            }, "getConfigInt");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception getConfigString");
+                throw exceptionRef.get();
+            }
+
+            return retVal;
+        }
+
+        /**
+         * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+         * if missed, it will call #ImsConfigImplBase.getConfigString.
+         * Synchronous blocking call.
+         *
+         * @param item integer key
+         * @return value in String format.
+         */
+        @Override
+        public String getConfigString(int item) throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            String retVal = executeMethodAsyncForResult(()-> {
+                String returnVal = null;
+                synchronized (mLock) {
+                    if (mProvisionedStringValue.containsKey(item)) {
+                        returnVal = mProvisionedStringValue.get(item);
+                    } else {
+                        try {
+                            returnVal = getImsConfigImpl().getConfigString(item);
+                            if (returnVal != null) {
+                                mProvisionedStringValue.put(item, returnVal);
+                            }
+                        } catch (RemoteException e) {
+                            exceptionRef.set(e);
+                            return returnVal;
+                        }
+                    }
+                }
+                return returnVal;
+            }, "getConfigString");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception getConfigString");
+                throw exceptionRef.get();
+            }
+
+            return retVal;
+        }
+
+        /**
+         * Sets the value for IMS service/capabilities parameters by the operator device
+         * management entity. It sets the config item value in the provisioned storage
+         * from which the main value is derived, and write it into local cache.
+         * Synchronous blocking call.
+         *
+         * @param item integer key
+         * @param value in Integer format.
+         * @return the result of setting the configuration value, defined as either
+         * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+         */
+        @Override
+        public int setConfigInt(int item, int value) throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            int retVal = executeMethodAsyncForResult(()-> {
+                int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
+                try {
+                    synchronized (mLock) {
+                        mProvisionedIntValue.remove(item);
+                        returnVal = getImsConfigImpl().setConfig(item, value);
+                        if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+                            mProvisionedIntValue.put(item, value);
+                        } else {
+                            Log.d(TAG, "Set provision value of " + item
+                                    + " to " + value + " failed with error code " + returnVal);
+                        }
+                    }
+                    notifyImsConfigChanged(item, value);
+                    return returnVal;
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                    return returnVal;
+                }
+            }, "setConfigInt");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception setConfigInt");
+                throw exceptionRef.get();
+            }
+
+            return retVal;
+        }
+
+        /**
+         * Sets the value for IMS service/capabilities parameters by the operator device
+         * management entity. It sets the config item value in the provisioned storage
+         * from which the main value is derived, and write it into local cache.
+         * Synchronous blocking call.
+         *
+         * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @param value in String format.
+         * @return the result of setting the configuration value, defined as either
+         * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+         */
+        @Override
+        public int setConfigString(int item, String value)
+                throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            int retVal = executeMethodAsyncForResult(()-> {
+                int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
+                try {
+                    synchronized (mLock) {
+                        mProvisionedStringValue.remove(item);
+                        returnVal = getImsConfigImpl().setConfig(item, value);
+                        if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+                            mProvisionedStringValue.put(item, value);
+                        }
+                    }
+                    notifyImsConfigChanged(item, value);
+                    return returnVal;
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                    return returnVal;
+                }
+            }, "setConfigString");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception setConfigInt");
+                throw exceptionRef.get();
+            }
+
+            return retVal;
+        }
+
+        @Override
+        public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            executeMethodAsync(()-> {
+                try {
+                    getImsConfigImpl().updateImsCarrierConfigs(bundle);
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                }
+            }, "updateImsCarrierConfigs");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception updateImsCarrierConfigs");
+                throw exceptionRef.get();
+            }
+        }
+
+        private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
+            ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
+            if (ref == null) {
+                throw new RemoteException("Fail to get ImsConfigImpl");
+            } else {
+                return ref;
+            }
+        }
+
+        @Override
+        public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)
+                throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            executeMethodAsync(()-> {
+                try {
+                    getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed);
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                }
+            }, "notifyRcsAutoConfigurationReceived");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationReceived");
+                throw exceptionRef.get();
+            }
+        }
+
+        @Override
+        public void notifyRcsAutoConfigurationRemoved()
+                throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            executeMethodAsync(()-> {
+                try {
+                    getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved();
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                }
+            }, "notifyRcsAutoConfigurationRemoved");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationRemoved");
+                throw exceptionRef.get();
+            }
+        }
+
+        private void notifyImsConfigChanged(int item, int value) throws RemoteException {
+            getImsConfigImpl().notifyConfigChanged(item, value);
+        }
+
+        private void notifyImsConfigChanged(int item, String value) throws RemoteException {
+            getImsConfigImpl().notifyConfigChanged(item, value);
+        }
+
+        protected void updateCachedValue(int item, int value) {
+            synchronized (mLock) {
+                mProvisionedIntValue.put(item, value);
+            }
+        }
+
+        protected void updateCachedValue(int item, String value) {
+            synchronized (mLock) {
+                mProvisionedStringValue.put(item, value);
+            }
+        }
+
+        @Override
+        public void addRcsConfigCallback(IRcsConfigCallback c) throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            executeMethodAsync(()-> {
+                try {
+                    getImsConfigImpl().addRcsConfigCallback(c);
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                }
+            }, "addRcsConfigCallback");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception addRcsConfigCallback");
+                throw exceptionRef.get();
+            }
+        }
+
+        @Override
+        public void removeRcsConfigCallback(IRcsConfigCallback c) throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            executeMethodAsync(()-> {
+                try {
+                    getImsConfigImpl().removeRcsConfigCallback(c);
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                }
+            }, "removeRcsConfigCallback");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception removeRcsConfigCallback");
+                throw exceptionRef.get();
+            }
+        }
+
+        @Override
+        public void triggerRcsReconfiguration() throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            executeMethodAsync(()-> {
+                try {
+                    getImsConfigImpl().triggerAutoConfiguration();
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                }
+            }, "triggerRcsReconfiguration");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception triggerRcsReconfiguration");
+                throw exceptionRef.get();
+            }
+        }
+
+        @Override
+        public void setRcsClientConfiguration(RcsClientConfiguration rcc) throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            executeMethodAsync(()-> {
+                try {
+                    getImsConfigImpl().setRcsClientConfiguration(rcc);
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                }
+            }, "setRcsClientConfiguration");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception setRcsClientConfiguration");
+                throw exceptionRef.get();
+            }
+        }
+
+        @Override
+        public void notifyIntImsConfigChanged(int item, int value) throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            executeMethodAsync(()-> {
+                try {
+                    notifyImsConfigChanged(item, value);
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                }
+            }, "notifyIntImsConfigChanged");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception notifyIntImsConfigChanged");
+                throw exceptionRef.get();
+            }
+        }
+
+        @Override
+        public void notifyStringImsConfigChanged(int item, String value) throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            executeMethodAsync(()-> {
+                try {
+                    notifyImsConfigChanged(item, value);
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                }
+            }, "notifyStringImsConfigChanged");
+
+            if (exceptionRef.get() != null) {
+                Log.d(TAG, "ImsConfigImplBase Exception notifyStringImsConfigChanged");
+                throw exceptionRef.get();
+            }
+        }
+
+        /**
+         * Clear cached configuration value.
+         */
+        public void clearCachedValue() {
+            Log.i(TAG, "clearCachedValue");
+            synchronized (mLock) {
+                mProvisionedIntValue.clear();
+                mProvisionedStringValue.clear();
+            }
+        }
+
+        // Call the methods with a clean calling identity on the executor and wait indefinitely for
+        // the future to return.
+        private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+            try {
+                CompletableFuture.runAsync(
+                        () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+
+        private <T> T executeMethodAsyncForResult(Supplier<T> r,
+                String errorLogName) throws RemoteException {
+            CompletableFuture<T> future = CompletableFuture.supplyAsync(
+                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+            try {
+                return future.get();
+            } catch (ExecutionException | InterruptedException e) {
+                Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * The configuration requested resulted in an unknown result. This may happen if the
+     * IMS configurations are unavailable.
+     */
+    public static final int CONFIG_RESULT_UNKNOWN = ProvisioningManager.PROVISIONING_RESULT_UNKNOWN;
+
+    /**
+     * Setting the configuration value completed.
+     */
+    public static final int CONFIG_RESULT_SUCCESS = 0;
+    /**
+     * Setting the configuration value failed.
+     */
+    public static final int CONFIG_RESULT_FAILED =  1;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CONFIG_RESULT_", value = {
+            CONFIG_RESULT_SUCCESS,
+            CONFIG_RESULT_FAILED
+    })
+    public @interface SetConfigResult {}
+
+    private final RemoteCallbackListExt<IImsConfigCallback> mCallbacks =
+            new RemoteCallbackListExt<>();
+    private final RemoteCallbackListExt<IRcsConfigCallback> mRcsCallbacks =
+            new RemoteCallbackListExt<>();
+    private byte[] mRcsConfigData;
+    private final Object mRcsConfigDataLock = new Object();
+    ImsConfigStub mImsConfigStub;
+
+    /**
+     * Create an ImsConfig using the Executor specified for methods being called by the
+     * framework.
+     * @param executor The executor for the framework to use when executing the methods overridden
+     * by the implementation of ImsConfig.
+     */
+    public ImsConfigImplBase(@NonNull Executor executor) {
+        mImsConfigStub = new ImsConfigStub(this, executor);
+    }
+
+    /**
+     * @hide
+     */
+    public ImsConfigImplBase(@NonNull Context context) {
+        mImsConfigStub = new ImsConfigStub(this, null);
+    }
+
+    /**
+     * Create an ImsConfig using the Executor defined in {@link ImsService#getExecutor}
+     */
+    public ImsConfigImplBase() {
+        mImsConfigStub = new ImsConfigStub(this, null);
+    }
+
+    /**
+     * Adds a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks
+     * notified when a value in the configuration changes.
+     * @param c callback to add.
+     */
+    private void addImsConfigCallback(IImsConfigCallback c) {
+        mCallbacks.register(c);
+    }
+    /**
+     * Removes a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks
+     * notified when a value in the configuration changes.
+     * @param c callback to remove.
+     */
+    private void removeImsConfigCallback(IImsConfigCallback c) {
+        mCallbacks.unregister(c);
+    }
+
+    /**
+     * @param item
+     * @param value
+     */
+    private final void notifyConfigChanged(int item, int value) {
+        // can be null in testing
+        if (mCallbacks == null) {
+            return;
+        }
+        synchronized (mCallbacks) {
+            mCallbacks.broadcastAction(c -> {
+                try {
+                    c.onIntConfigChanged(item, value);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "notifyConfigChanged(int): dead binder in notify, skipping.");
+                }
+            });
+        }
+    }
+
+    private void notifyConfigChanged(int item, String value) {
+        // can be null in testing
+        if (mCallbacks == null) {
+            return;
+        }
+        synchronized (mCallbacks) {
+            mCallbacks.broadcastAction(c -> {
+                try {
+                    c.onStringConfigChanged(item, value);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "notifyConfigChanged(string): dead binder in notify, skipping.");
+                }
+            });
+        }
+    }
+
+    private void addRcsConfigCallback(IRcsConfigCallback c) {
+        mRcsCallbacks.register(c);
+
+        // This is used to avoid calling the binder out of the synchronized scope.
+        byte[] cloneRcsConfigData;
+        synchronized (mRcsConfigDataLock) {
+            if (mRcsConfigData == null) {
+                return;
+            }
+            cloneRcsConfigData = mRcsConfigData.clone();
+        }
+
+        try {
+            c.onConfigurationChanged(cloneRcsConfigData);
+        } catch (RemoteException e) {
+            Log.w(TAG, "dead binder to call onConfigurationChanged, skipping.");
+        }
+    }
+
+    private void removeRcsConfigCallback(IRcsConfigCallback c) {
+        mRcsCallbacks.unregister(c);
+    }
+
+    private void onNotifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) {
+        // cache uncompressed config
+        final byte[] rcsConfigData = isCompressed ? RcsConfig.decompressGzip(config) : config;
+
+        synchronized (mRcsConfigDataLock) {
+            if (Arrays.equals(mRcsConfigData, config)) {
+                return;
+            }
+            mRcsConfigData = rcsConfigData;
+        }
+
+        // can be null in testing
+        if (mRcsCallbacks != null) {
+            synchronized (mRcsCallbacks) {
+                mRcsCallbacks.broadcastAction(c -> {
+                    try {
+                        // config is cloned here so modifications to the config passed to the
+                        // vendor do not accidentally modify the cache.
+                        c.onConfigurationChanged(rcsConfigData.clone());
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "dead binder in notifyRcsAutoConfigurationReceived, skipping.");
+                    }
+                });
+            }
+        }
+        notifyRcsAutoConfigurationReceived(config, isCompressed);
+    }
+
+    private void onNotifyRcsAutoConfigurationRemoved() {
+        synchronized (mRcsConfigDataLock) {
+            mRcsConfigData = null;
+        }
+        if (mRcsCallbacks != null) {
+            synchronized (mRcsCallbacks) {
+                mRcsCallbacks.broadcastAction(c -> {
+                    try {
+                        c.onConfigurationReset();
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "dead binder in notifyRcsAutoConfigurationRemoved, skipping.");
+                    }
+                });
+            }
+        }
+        notifyRcsAutoConfigurationRemoved();
+    }
+
+    /**
+     * @hide
+     */
+    public IImsConfig getIImsConfig() { return mImsConfigStub; }
+
+    /**
+     * Updates provisioning value and notifies the framework of the change.
+     * Doesn't call {@link #setConfig(int,int)} and assumes the result succeeded.
+     * This should only be used when the IMS implementer implicitly changed provisioned values.
+     *
+     * @param item an integer key.
+     * @param value in Integer format.
+     */
+    public final void notifyProvisionedValueChanged(int item, int value) {
+        mImsConfigStub.updateCachedValue(item, value);
+
+        try {
+            mImsConfigStub.notifyImsConfigChanged(item, value);
+        } catch (RemoteException e) {
+            Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead.");
+        }
+    }
+
+    /**
+     * Updates provisioning value and notifies the framework of the change.
+     * Doesn't call {@link #setConfig(int,String)} and assumes the result succeeded.
+     * This should only be used when the IMS implementer implicitly changed provisioned values.
+     *
+     * @param item an integer key.
+     * @param value in String format.
+     */
+    public final void notifyProvisionedValueChanged(int item, String value) {
+        mImsConfigStub.updateCachedValue(item, value);
+
+        try {
+            mImsConfigStub.notifyImsConfigChanged(item, value);
+        } catch (RemoteException e) {
+            Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead.");
+        }
+    }
+
+    /**
+     * The framework has received an RCS autoconfiguration XML file for provisioning.
+     *
+     * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format.
+     * @param isCompressed The XML file is compressed in gzip format and must be decompressed
+     *         before being read.
+     *
+     */
+    public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
+    }
+
+    /**
+     * The RCS autoconfiguration XML file is removed or invalid.
+     */
+    public void notifyRcsAutoConfigurationRemoved() {
+    }
+
+    /**
+     * Sets the configuration value for this ImsService.
+     *
+     * @param item an integer key.
+     * @param value an integer containing the configuration value.
+     * @return the result of setting the configuration value.
+     */
+    public @SetConfigResult int setConfig(int item, int value) {
+        // Base Implementation - To be overridden.
+        return CONFIG_RESULT_FAILED;
+    }
+
+    /**
+     * Sets the configuration value for this ImsService.
+     *
+     * @param item an integer key.
+     * @param value a String containing the new configuration value.
+     * @return Result of setting the configuration value.
+     */
+    public @SetConfigResult int setConfig(int item, String value) {
+        // Base Implementation - To be overridden.
+        return CONFIG_RESULT_FAILED;
+    }
+
+    /**
+     * Gets the currently stored value configuration value from the ImsService for {@code item}.
+     *
+     * @param item an integer key.
+     * @return configuration value, stored in integer format or {@link #CONFIG_RESULT_UNKNOWN} if
+     * unavailable.
+     */
+    public int getConfigInt(int item) {
+        // Base Implementation - To be overridden.
+        return CONFIG_RESULT_UNKNOWN;
+    }
+
+    /**
+     * Gets the currently stored value configuration value from the ImsService for {@code item}.
+     *
+     * @param item an integer key.
+     * @return configuration value, stored in String format or {@code null} if unavailable.
+     */
+    public String getConfigString(int item) {
+        // Base Implementation - To be overridden.
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    public void updateImsCarrierConfigs(PersistableBundle bundle) {
+        // Base Implementation - Should be overridden
+    }
+
+    /**
+     * Default messaging application parameters are sent to the ACS client
+     * using this interface.
+     * @param rcc RCS client configuration {@link RcsClientConfiguration}
+     */
+    public void setRcsClientConfiguration(@NonNull RcsClientConfiguration rcc) {
+        // Base Implementation - Should be overridden
+    }
+
+    /**
+     * Reconfiguration triggered by the RCS application. Most likely cause
+     * is the 403 forbidden to a SIP/HTTP request
+     */
+    public void triggerAutoConfiguration() {
+        // Base Implementation - Should be overridden
+    }
+
+    /**
+     * Errors during autoconfiguration connection setup are notified by the
+     * ACS client using this interface.
+     * @param errorCode HTTP error received during connection setup.
+     * @param errorString reason phrase received with the error
+     */
+    public final void notifyAutoConfigurationErrorReceived(int errorCode,
+            @NonNull String errorString) {
+        // can be null in testing
+        if (mRcsCallbacks == null) {
+            return;
+        }
+        synchronized (mRcsCallbacks) {
+            mRcsCallbacks.broadcastAction(c -> {
+                try {
+                    c.onAutoConfigurationErrorReceived(errorCode, errorString);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping.");
+                }
+            });
+        }
+    }
+
+    /**
+     * Notifies application that pre-provisioning config is received.
+     *
+     * <p>Some carriers using ACS (auto configuration server) may send a carrier-specific
+     * pre-provisioning configuration XML if the user has not been provisioned for RCS
+     * services yet. When such provisioning XML is received, ACS client must call this
+     * method to notify the application with the XML.
+     *
+     * @param configXml the pre-provisioning config in carrier specified format.
+     */
+    public final void notifyPreProvisioningReceived(@NonNull byte[] configXml) {
+        // can be null in testing
+        if (mRcsCallbacks == null) {
+            return;
+        }
+        synchronized (mRcsCallbacks) {
+            mRcsCallbacks.broadcastAction(c -> {
+                try {
+                    c.onPreProvisioningReceived(configXml);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "dead binder in notifyPreProvisioningReceived, skipping.");
+                }
+            });
+        }
+    }
+
+    /**
+     * Set default Executor from ImsService.
+     * @param executor The default executor for the framework to use when executing the methods
+     * overridden by the implementation of ImsConfig.
+     * @hide
+     */
+    public final void setDefaultExecutor(@NonNull Executor executor) {
+        if (mImsConfigStub.mExecutor == null) {
+            mImsConfigStub.mExecutor = executor;
+        }
+    }
+
+    /**
+     * Clear all cached config data. This will be called when the config data is no longer valid
+     * such as when the SIM was removed.
+     * @hide
+     */
+    public final void clearConfigurationCache() {
+        mImsConfigStub.clearCachedValue();
+
+        synchronized (mRcsConfigDataLock) {
+            mRcsConfigData = null;
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/stub/ImsEcbmImplBase.java b/android-35/android/telephony/ims/stub/ImsEcbmImplBase.java
new file mode 100644
index 0000000..84b2253
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsEcbmListener;
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
+
+
+/**
+ * Base implementation of ImsEcbm, which implements stub versions of the methods
+ * in the IImsEcbm AIDL. Override the methods that your implementation of ImsEcbm supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsEcbm maintained by other ImsServices.
+ *
+ * @hide
+ */
+@SystemApi
+public class ImsEcbmImplBase {
+    private static final String TAG = "ImsEcbmImplBase";
+
+    private final Object mLock = new Object();
+    private IImsEcbmListener mListener;
+    private Executor mExecutor = Runnable::run;
+
+    private final IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
+        @Override
+        public void setListener(IImsEcbmListener listener) {
+            executeMethodAsync(() -> {
+                if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+                    Log.w(TAG, "setListener: discarding dead Binder");
+                    mListener = null;
+                }
+                if (mListener != null && listener != null && Objects.equals(
+                        mListener.asBinder(), listener.asBinder())) {
+                    return;
+                }
+                if (listener == null) {
+                    mListener = null;
+                } else if (listener != null && mListener == null) {
+                    mListener = listener;
+                } else {
+                    // Warn that the listener is being replaced while active
+                    Log.w(TAG, "setListener is being called when there is already an active "
+                            + "listener");
+                    mListener = listener;
+                }
+            }, "setListener");
+        }
+
+        @Override
+        public void exitEmergencyCallbackMode() {
+            executeMethodAsync(() -> ImsEcbmImplBase.this.exitEmergencyCallbackMode(),
+                    "exitEmergencyCallbackMode");
+        }
+
+        // Call the methods with a clean calling identity on the executor and wait indefinitely for
+        // the future to return.
+        private void executeMethodAsync(Runnable r, String errorLogName) {
+            try {
+                CompletableFuture.runAsync(
+                        () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(TAG, "ImsEcbmImplBase Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+            }
+        }
+    };
+
+    /** @hide */
+    public IImsEcbm getImsEcbm() {
+        return mImsEcbm;
+    }
+
+    /**
+     * This method should be implemented by the IMS provider. Framework will trigger this method to
+     * request to come out of ECBM mode
+     */
+    public void exitEmergencyCallbackMode() {
+        Log.d(TAG, "exitEmergencyCallbackMode() not implemented");
+    }
+
+    /**
+     * Notifies the framework when the device enters Emergency Callback Mode.
+     *
+     * @throws RuntimeException if the connection to the framework is not available.
+     */
+    public final void enteredEcbm() {
+        Log.d(TAG, "Entered ECBM.");
+        IImsEcbmListener listener;
+        synchronized (mLock) {
+            listener = mListener;
+        }
+        if (listener != null) {
+            try {
+                listener.enteredECBM();
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Notifies the framework when the device exits Emergency Callback Mode.
+     *
+     * @throws RuntimeException if the connection to the framework is not available.
+     */
+    public final void exitedEcbm() {
+        Log.d(TAG, "Exited ECBM.");
+        IImsEcbmListener listener;
+        synchronized (mLock) {
+            listener = mListener;
+        }
+        if (listener != null) {
+            try {
+                listener.exitedECBM();
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Set default Executor from MmTelFeature.
+     * @param executor The default executor for the framework to use when executing the methods
+     * overridden by the implementation of ImsEcbm.
+     * @hide
+     */
+    public final void setDefaultExecutor(@NonNull Executor executor) {
+        mExecutor = executor;
+    }
+}
diff --git a/android-35/android/telephony/ims/stub/ImsFeatureConfiguration.java b/android-35/android/telephony/ims/stub/ImsFeatureConfiguration.java
new file mode 100644
index 0000000..cd9ebbf
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/ImsFeatureConfiguration.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2018 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.ims.stub;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.feature.ImsFeature;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+/**
+ * Container class for IMS Feature configuration. This class contains the features that the
+ * ImsService supports, which are defined in {@link ImsFeature} as
+ * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+ * {@link ImsFeature#FEATURE_RCS}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsFeatureConfiguration implements Parcelable {
+
+    public static final class FeatureSlotPair {
+        /**
+         * SIM slot that this feature is associated with.
+         */
+        public final int slotId;
+        /**
+         * The feature that this slotId supports. Supported values are
+         * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+         * {@link ImsFeature#FEATURE_RCS}.
+         */
+        public final @ImsFeature.FeatureType int featureType;
+
+        /**
+         * A mapping from slotId to IMS Feature type.
+         * @param slotId the SIM slot ID associated with this feature.
+         * @param featureType The feature that this slotId supports. Supported values are
+         * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+         * {@link ImsFeature#FEATURE_RCS}.
+         */
+        public FeatureSlotPair(int slotId, @ImsFeature.FeatureType int featureType) {
+            this.slotId = slotId;
+            this.featureType = featureType;
+        }
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            FeatureSlotPair that = (FeatureSlotPair) o;
+
+            if (slotId != that.slotId) return false;
+            return featureType == that.featureType;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = slotId;
+            result = 31 * result + featureType;
+            return result;
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "{s=" + slotId + ", f=" + ImsFeature.FEATURE_LOG_MAP.get(featureType) + "}";
+        }
+    }
+
+    /**
+     * Features that this ImsService supports.
+     */
+    private final Set<FeatureSlotPair> mFeatures;
+
+    /**
+     * Builder for {@link ImsFeatureConfiguration}.
+     */
+    public static class Builder {
+            ImsFeatureConfiguration mConfig;
+        public Builder() {
+            mConfig = new ImsFeatureConfiguration();
+        }
+
+        /**
+         * Adds an IMS feature associated with a SIM slot ID.
+         * @param slotId The slot ID associated with the IMS feature.
+         * @param featureType The feature that the slot ID supports. Supported values are
+         * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+         * {@link ImsFeature#FEATURE_RCS}.
+         * @return a {@link Builder} to continue constructing the ImsFeatureConfiguration.
+         */
+        public Builder addFeature(int slotId, @ImsFeature.FeatureType int featureType) {
+            mConfig.addFeature(slotId, featureType);
+            return this;
+        }
+
+        public ImsFeatureConfiguration build() {
+            return mConfig;
+        }
+    }
+
+    /**
+     * Creates with all registration features empty.
+     * @hide
+     */
+    public ImsFeatureConfiguration() {
+        mFeatures = new ArraySet<>();
+    }
+
+    /**
+     * Configuration of the ImsService, which describes which features the ImsService supports
+     * (for registration).
+     * @param features a set of {@link FeatureSlotPair}s that describe which features this
+     *         ImsService supports.
+     * @hide
+     */
+    public ImsFeatureConfiguration(Set<FeatureSlotPair> features) {
+        mFeatures = new ArraySet<>();
+
+        if (features != null) {
+            mFeatures.addAll(features);
+        }
+    }
+
+    /**
+     * @return a set of supported slot ID to feature type pairs contained within a
+     * {@link FeatureSlotPair}.
+     */
+    public Set<FeatureSlotPair> getServiceFeatures() {
+        return new ArraySet<>(mFeatures);
+    }
+
+    /**
+     * @hide
+     */
+    void addFeature(int slotId, int feature) {
+        mFeatures.add(new FeatureSlotPair(slotId, feature));
+    }
+
+    /** @hide */
+    protected ImsFeatureConfiguration(Parcel in) {
+        int featurePairLength = in.readInt();
+        // length
+        mFeatures = new ArraySet<>(featurePairLength);
+        for (int i = 0; i < featurePairLength; i++) {
+            // pair of reads for each entry (slotId->featureType)
+            mFeatures.add(new FeatureSlotPair(in.readInt(), in.readInt()));
+        }
+    }
+
+    public static final @android.annotation.NonNull Creator<ImsFeatureConfiguration> CREATOR
+            = new Creator<ImsFeatureConfiguration>() {
+        @Override
+        public ImsFeatureConfiguration createFromParcel(Parcel in) {
+            return new ImsFeatureConfiguration(in);
+        }
+
+        @Override
+        public ImsFeatureConfiguration[] newArray(int size) {
+            return new ImsFeatureConfiguration[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        FeatureSlotPair[] featureSlotPairs = new FeatureSlotPair[mFeatures.size()];
+        mFeatures.toArray(featureSlotPairs);
+        // length of list
+        dest.writeInt(featureSlotPairs.length);
+        // then pairs of integers for each entry (slotId->featureType).
+        for (FeatureSlotPair featureSlotPair : featureSlotPairs) {
+            dest.writeInt(featureSlotPair.slotId);
+            dest.writeInt(featureSlotPair.featureType);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ImsFeatureConfiguration)) return false;
+
+        ImsFeatureConfiguration
+                that = (ImsFeatureConfiguration) o;
+
+        return mFeatures.equals(that.mFeatures);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int hashCode() {
+        return mFeatures.hashCode();
+    }
+}
diff --git a/android-35/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/android-35/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
new file mode 100644
index 0000000..a723cd8
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.ims.ImsExternalCallState;
+import android.util.Log;
+
+import com.android.ims.internal.IImsExternalCallStateListener;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
+
+/**
+ * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
+ * in the IImsMultiEndpoint AIDL. Override the methods that your implementation of
+ * ImsMultiEndpoint supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsMultiEndpoint maintained by other ImsServices.
+ *
+ * @hide
+ */
+@SystemApi
+public class ImsMultiEndpointImplBase {
+    private static final String TAG = "MultiEndpointImplBase";
+
+    private IImsExternalCallStateListener mListener;
+    private final Object mLock = new Object();
+    private Executor mExecutor = Runnable::run;
+
+    private final IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
+
+        @Override
+        public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+            executeMethodAsync(() -> {
+                if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+                    Log.w(TAG, "setListener: discarding dead Binder");
+                    mListener = null;
+                }
+                if (mListener != null && listener != null && Objects.equals(
+                        mListener.asBinder(), listener.asBinder())) {
+                    return;
+                }
+
+                if (listener == null) {
+                    mListener = null;
+                } else if (listener != null && mListener == null) {
+                    mListener = listener;
+                } else {
+                    // Warn that the listener is being replaced while active
+                    Log.w(TAG, "setListener is being called when there is already an active "
+                            + "listener");
+                    mListener = listener;
+                }
+            }, "setListener");
+        }
+
+        @Override
+        public void requestImsExternalCallStateInfo() throws RemoteException {
+            executeMethodAsync(() -> ImsMultiEndpointImplBase.this
+                    .requestImsExternalCallStateInfo(), "requestImsExternalCallStateInfo");
+        }
+
+        // Call the methods with a clean calling identity on the executor and wait indefinitely for
+        // the future to return.
+        private void executeMethodAsync(Runnable r, String errorLogName) {
+            try {
+                CompletableFuture.runAsync(
+                        () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(TAG, "ImsMultiEndpointImplBase Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+            }
+        }
+    };
+
+    /** @hide */
+    public IImsMultiEndpoint getIImsMultiEndpoint() {
+        return mImsMultiEndpoint;
+    }
+
+    /**
+     * Notifies framework when Dialog Event Package update is received
+     *
+     * @throws RuntimeException if the connection to the framework is not available.
+     */
+    public final void onImsExternalCallStateUpdate(List<ImsExternalCallState> externalCallDialogs) {
+        Log.d(TAG, "ims external call state update triggered.");
+        IImsExternalCallStateListener listener;
+        synchronized (mLock) {
+            listener = mListener;
+        }
+        if (listener != null) {
+            try {
+                listener.onImsExternalCallStateUpdate(externalCallDialogs);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * This method should be implemented by the IMS provider. Framework will trigger this to get the
+     * latest Dialog Event Package information. Should
+     */
+    public void requestImsExternalCallStateInfo() {
+        Log.d(TAG, "requestImsExternalCallStateInfo() not implemented");
+    }
+
+    /**
+     * Set default Executor from MmTelFeature.
+     * @param executor The default executor for the framework to use when executing the methods
+     * overridden by the implementation of ImsMultiEndpoint.
+     * @hide
+     */
+    public final void setDefaultExecutor(@NonNull Executor executor) {
+        mExecutor = executor;
+    }
+}
diff --git a/android-35/android/telephony/ims/stub/ImsRegistrationImplBase.java b/android-35/android/telephony/ims/stub/ImsRegistrationImplBase.java
new file mode 100644
index 0000000..99c26b0
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.SipDetails;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.util.Log;
+
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.RemoteCallbackListExt;
+import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.internal.util.ArrayUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Controls IMS registration for this ImsService and notifies the framework when the IMS
+ * registration for this ImsService has changed status.
+ * <p>
+ * Note: There is no guarantee on the thread that the calls from the framework will be called on. It
+ * is the implementors responsibility to handle moving the calls to a working thread if required.
+ */
+public class ImsRegistrationImplBase {
+
+    private static final String LOG_TAG = "ImsRegistrationImplBase";
+    /**
+     * @hide
+     */
+    // Defines the underlying radio technology type that we have registered for IMS over.
+    @IntDef(value = {
+                    REGISTRATION_TECH_NONE,
+                    REGISTRATION_TECH_LTE,
+                    REGISTRATION_TECH_IWLAN,
+                    REGISTRATION_TECH_CROSS_SIM,
+                    REGISTRATION_TECH_NR,
+                    REGISTRATION_TECH_3G
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsRegistrationTech {}
+    /**
+     * No registration technology specified, used when we are not registered.
+     */
+    public static final int REGISTRATION_TECH_NONE = -1;
+    /**
+     * This ImsService is registered to IMS via LTE.
+     */
+    public static final int REGISTRATION_TECH_LTE = 0;
+    /**
+     * This ImsService is registered to IMS via IWLAN.
+     */
+    public static final int REGISTRATION_TECH_IWLAN = 1;
+
+    /**
+     * This ImsService is registered to IMS via internet over second subscription.
+     */
+    public static final int REGISTRATION_TECH_CROSS_SIM = 2;
+
+    /**
+     * This ImsService is registered to IMS via NR.
+     */
+    public static final int REGISTRATION_TECH_NR = 3;
+
+    /**
+     * This ImsService is registered to IMS via 3G.
+     */
+    public static final int REGISTRATION_TECH_3G = 4;
+
+    /**
+     * This is used to check the upper range of registration tech
+     * @hide
+     */
+    public static final int REGISTRATION_TECH_MAX = REGISTRATION_TECH_3G + 1;
+
+    // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current
+    // state.
+    // The unknown state is set as the initialization state. This is so that we do not call back
+    // with NOT_REGISTERED in the case where the ImsService has not updated the registration state
+    // yet.
+    private static final int REGISTRATION_STATE_UNKNOWN = -1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = {"REASON_"},
+        value = {
+            REASON_UNKNOWN,
+            REASON_SIM_REMOVED,
+            REASON_SIM_REFRESH,
+            REASON_ALLOWED_NETWORK_TYPES_CHANGED,
+            REASON_NON_IMS_CAPABLE_NETWORK,
+            REASON_RADIO_POWER_OFF,
+            REASON_HANDOVER_FAILED,
+            REASON_VOPS_NOT_SUPPORTED,
+        })
+    public @interface ImsDeregistrationReason{}
+
+    /**
+     * Unspecified reason.
+     * @hide
+     */
+    public static final int REASON_UNKNOWN = 0;
+
+    /**
+     * Since SIM is removed, the credentials for IMS service is also removed.
+     * @hide
+     */
+    public static final int REASON_SIM_REMOVED = 1;
+
+    /**
+     * Detach from the network shall be performed due to the SIM refresh. IMS service should be
+     * deregistered before that procedure.
+     * @hide
+     */
+    public static final int REASON_SIM_REFRESH = 2;
+
+    /**
+     * The allowed network types have changed, resulting in a network type
+     * that does not support IMS.
+     * @hide
+     */
+    public static final int REASON_ALLOWED_NETWORK_TYPES_CHANGED = 3;
+
+   /**
+     * The device camped on a network that does not support IMS.
+     * @hide
+     */
+    public static final int REASON_NON_IMS_CAPABLE_NETWORK = 4;
+
+    /**
+     * IMS service should be deregistered from the network before turning off the radio.
+     * @hide
+     */
+    public static final int REASON_RADIO_POWER_OFF = 5;
+
+    /**
+     * Since the handover is failed or not allowed, the data service for IMS shall be
+     * disconnected.
+     * @hide
+     */
+    public static final int REASON_HANDOVER_FAILED = 6;
+
+    /**
+     * The network is changed to a network that does not support voice over IMS.
+     * @hide
+     */
+    public static final int REASON_VOPS_NOT_SUPPORTED = 7;
+
+    private Executor mExecutor;
+
+    /**
+     * Create a new ImsRegistration.
+     * <p>
+     * Method stubs called from the framework will be called asynchronously. To specify the
+     * {@link Executor} that the methods stubs will be called, use
+     * {@link ImsRegistrationImplBase#ImsRegistrationImplBase(Executor)} instead.
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public ImsRegistrationImplBase() {
+        super();
+    }
+
+    /**
+     * Create a ImsRegistration using the Executor specified for methods being called by the
+     * framework.
+     * @param executor The executor for the framework to use when executing the methods overridden
+     * by the implementation of ImsRegistration.
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public ImsRegistrationImplBase(@NonNull Executor executor) {
+        super();
+        mExecutor = executor;
+    }
+
+    private final IImsRegistration mBinder = new IImsRegistration.Stub() {
+
+        @Override
+        public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
+            return executeMethodAsyncForResult(() -> (mRegistrationAttributes == null)
+                    ? REGISTRATION_TECH_NONE : mRegistrationAttributes.getRegistrationTechnology(),
+                    "getRegistrationTechnology");
+        }
+
+        @Override
+        public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            executeMethodAsync(() -> {
+                try {
+                    ImsRegistrationImplBase.this.addRegistrationCallback(c);
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                }
+            }, "addRegistrationCallback");
+
+            if (exceptionRef.get() != null) {
+                throw exceptionRef.get();
+            }
+        }
+
+        @Override
+        public void addEmergencyRegistrationCallback(IImsRegistrationCallback c)
+                throws RemoteException {
+            AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+            executeMethodAsync(() -> {
+                try {
+                    ImsRegistrationImplBase.this.addEmergencyRegistrationCallback(c);
+                } catch (RemoteException e) {
+                    exceptionRef.set(e);
+                }
+            }, "addEmergencyRegistrationCallback");
+
+            if (exceptionRef.get() != null) {
+                throw exceptionRef.get();
+            }
+        }
+
+        @Override
+        public void removeEmergencyRegistrationCallback(IImsRegistrationCallback c)
+                throws RemoteException {
+            executeMethodAsync(() ->
+                    ImsRegistrationImplBase.this.removeEmergencyRegistrationCallback(c),
+                    "removeEmergencyRegistrationCallback");
+        }
+
+        @Override
+        public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+            executeMethodAsync(() -> ImsRegistrationImplBase.this.removeRegistrationCallback(c),
+                    "removeRegistrationCallback");
+        }
+
+        @Override
+        public void triggerFullNetworkRegistration(int sipCode, String sipReason) {
+            executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
+                    .triggerFullNetworkRegistration(sipCode, sipReason),
+                    "triggerFullNetworkRegistration");
+        }
+
+        @Override
+        public void triggerUpdateSipDelegateRegistration() {
+            executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
+                    .updateSipDelegateRegistration(), "triggerUpdateSipDelegateRegistration");
+        }
+
+        @Override
+        public void triggerSipDelegateDeregistration() {
+            executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
+                    .triggerSipDelegateDeregistration(), "triggerSipDelegateDeregistration");
+        }
+
+        @Override
+        public void triggerDeregistration(@ImsDeregistrationReason int reason) {
+            executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
+                    .triggerDeregistration(reason), "triggerDeregistration");
+        }
+
+        // Call the methods with a clean calling identity on the executor and wait indefinitely for
+        // the future to return.
+        private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+            try {
+                CompletableFuture.runAsync(
+                        () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+
+        private void executeMethodAsyncNoException(Runnable r, String errorLogName) {
+            try {
+                CompletableFuture.runAsync(
+                        () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+            }
+        }
+
+        private <T> T executeMethodAsyncForResult(Supplier<T> r,
+                String errorLogName) throws RemoteException {
+            CompletableFuture<T> future = CompletableFuture.supplyAsync(
+                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+            try {
+                return future.get();
+            } catch (ExecutionException | InterruptedException e) {
+                Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+    };
+
+    private final RemoteCallbackListExt<IImsRegistrationCallback> mCallbacks =
+            new RemoteCallbackListExt<>();
+    private final RemoteCallbackListExt<IImsRegistrationCallback> mEmergencyCallbacks =
+            new RemoteCallbackListExt<>();
+    private final Object mLock = new Object();
+    // Locked on mLock
+    private ImsRegistrationAttributes mRegistrationAttributes;
+    private ImsRegistrationAttributes mEmergencyRegistrationAttributes;
+    // Locked on mLock
+    private int mRegistrationState = REGISTRATION_STATE_UNKNOWN;
+    private int mEmergencyRegistrationState = REGISTRATION_STATE_UNKNOWN;
+    // Locked on mLock, create unspecified disconnect cause.
+    private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo();
+    private ImsReasonInfo mEmergencyLastDisconnectCause = new ImsReasonInfo();
+    // Locked on mLock
+    private int mLastDisconnectSuggestedAction = RegistrationManager.SUGGESTED_ACTION_NONE;
+    private int mEmergencyLastDisconnectSuggestedAction = RegistrationManager.SUGGESTED_ACTION_NONE;
+    private int mLastDisconnectRadioTech = REGISTRATION_TECH_NONE;
+    private int mEmergencyLastDisconnectRadioTech = REGISTRATION_TECH_NONE;
+
+    // We hold onto the uris each time they change so that we can send it to a callback when its
+    // first added.
+    private Uri[] mUris = new Uri[0];
+    private boolean mUrisSet = false;
+
+    /**
+     * @hide
+     */
+    public final IImsRegistration getBinder() {
+        return mBinder;
+    }
+
+    private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+        // This is purposefully not synchronized with broadcastToCallbacksLocked because the
+        // list of callbacks to notify is copied over from the original list modified here. I also
+        // do not want to risk introducing a deadlock by using the same mCallbacks Object to
+        // synchronize on outgoing and incoming operations.
+        mCallbacks.register(c);
+        updateNewCallbackWithState(c, false);
+    }
+
+    private void removeRegistrationCallback(IImsRegistrationCallback c) {
+        // This is purposefully not synchronized with broadcastToCallbacksLocked because the
+        // list of callbacks to notify is copied over from the original list modified here. I also
+        // do not want to risk introducing a deadlock by using the same mCallbacks Object to
+        // synchronize on outgoing and incoming operations.
+        mCallbacks.unregister(c);
+    }
+
+    private void addEmergencyRegistrationCallback(IImsRegistrationCallback c)
+            throws RemoteException {
+        mEmergencyCallbacks.register(c);
+        updateNewCallbackWithState(c, true);
+    }
+
+    private void removeEmergencyRegistrationCallback(IImsRegistrationCallback c) {
+        mEmergencyCallbacks.unregister(c);
+    }
+
+    /**
+     * Called by the framework to request that the ImsService perform the network registration
+     * of all SIP delegates associated with this ImsService.
+     * <p>
+     * If the SIP delegate feature tag configuration has changed, then this method will be
+     * called in order to let the ImsService know that it can pick up these changes in the IMS
+     * registration.
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public void updateSipDelegateRegistration() {
+        // Stub implementation, ImsService should implement this
+    }
+
+
+    /**
+     * Called by the framework to request that the ImsService perform the network deregistration of
+     * all SIP delegates associated with this ImsService.
+     * <p>
+     * This is typically called in situations where the user has changed the configuration of the
+     * device (for example, the default messaging application) and the framework is reconfiguring
+     * the tags associated with each IMS application.
+     * <p>
+     * This should not affect the registration of features managed by the ImsService itself, such as
+     * feature tags related to MMTEL registration.
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public void triggerSipDelegateDeregistration() {
+        // Stub implementation, ImsService should implement this
+    }
+
+    /**
+     * Called by the framework to notify the ImsService that a SIP delegate connection has received
+     * a SIP message containing a permanent failure response (such as a 403) or an indication that a
+     * SIP response timer has timed out in response to an outgoing SIP message. This method will be
+     * called when this condition occurs to trigger the ImsService to tear down the full IMS
+     * registration and re-register again.
+     *
+     * @param sipCode The SIP error code that represents a permanent failure that was received in
+     *    response to a request generated by the IMS application. See RFC3261 7.2 for the general
+     *    classes of responses available here, however the codes that generate this condition may
+     *    be carrier specific.
+     * @param sipReason The reason associated with the SIP error code. {@code null} if there was no
+     *    reason associated with the error.
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public void triggerFullNetworkRegistration(@IntRange(from = 100, to = 699) int sipCode,
+            @Nullable String sipReason) {
+        // Stub implementation, ImsService should implement this
+    }
+
+    /**
+     * Requests IMS stack to perform graceful IMS deregistration before radio performing
+     * network detach in the events of SIM remove, refresh or and so on. The radio waits for
+     * the IMS deregistration, which will be notified by telephony via
+     * {@link android.hardware.radio.ims.IRadioIms#updateImsRegistrationInfo()},
+     * or a certain timeout interval to start the network detach procedure.
+     *
+     * @param reason the reason why the deregistration is triggered.
+     * @hide
+     */
+    public void triggerDeregistration(@ImsDeregistrationReason int reason) {
+        // Stub Implementation, can be overridden by ImsService
+    }
+
+    /**
+     * Notify the framework that the device is connected to the IMS network.
+     *
+     * @param imsRadioTech the radio access technology.
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
+        onRegistered(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
+    }
+
+    /**
+     * Notify the framework that the device is connected to the IMS network.
+     *
+     * @param attributes The attributes associated with the IMS registration.
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
+        boolean isEmergency = isEmergency(attributes);
+        if (isEmergency) {
+            updateToEmergencyState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED);
+        } else {
+            updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED);
+        }
+        broadcastToCallbacksLocked((c) -> {
+            try {
+                c.onRegistered(attributes);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + "onRegistered(int, Set) - Skipping callback.");
+            }
+        }, isEmergency);
+    }
+
+    /**
+     * Notify the framework that the device is trying to connect the IMS network.
+     *
+     * @param imsRadioTech the radio access technology.
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
+        onRegistering(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
+    }
+
+    /**
+     * Notify the framework that the device is trying to connect the IMS network.
+     *
+     * @param attributes The attributes associated with the IMS registration.
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
+        boolean isEmergency = isEmergency(attributes);
+        if (isEmergency) {
+            updateToEmergencyState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING);
+        } else {
+            updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING);
+        }
+        broadcastToCallbacksLocked((c) -> {
+            try {
+                c.onRegistering(attributes);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + "onRegistering(int, Set) - Skipping callback.");
+            }
+        }, isEmergency);
+    }
+
+    /**
+     * Notify the framework that the device is disconnected from the IMS network.
+     * <p>
+     * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo)}, you should ensure that any
+     * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent
+     * to the framework.  For example,
+     * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}
+     * and
+     * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}
+     * may be set to unavailable to ensure the framework knows these services are no longer
+     * available due to de-registration.  If you do not report capability changes impacted by
+     * de-registration, the framework will not know which features are no longer available as a
+     * result.
+     *
+     * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public final void onDeregistered(ImsReasonInfo info) {
+        // Default impl to keep backwards compatibility with old implementations
+        onDeregistered(info, RegistrationManager.SUGGESTED_ACTION_NONE, REGISTRATION_TECH_NONE);
+    }
+
+    /**
+     * Notify the framework that the device is disconnected from the IMS network.
+     * <p>
+     * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo,int)}, you should ensure that any
+     * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent
+     * to the framework.  For example,
+     * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}
+     * and
+     * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}
+     * may be set to unavailable to ensure the framework knows these services are no longer
+     * available due to de-registration.  If you do not report capability changes impacted by
+     * de-registration, the framework will not know which features are no longer available as a
+     * result.
+     *
+     * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+     * @param suggestedAction the expected behavior of radio protocol stack.
+     * @param imsRadioTech the network type on which IMS registration has failed.
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public final void onDeregistered(@Nullable ImsReasonInfo info,
+            @RegistrationManager.SuggestedAction int suggestedAction,
+            @ImsRegistrationTech int imsRadioTech) {
+        // Impl to keep backwards compatibility with old implementations
+        ImsRegistrationAttributes attributes = mRegistrationAttributes != null
+                ? new ImsRegistrationAttributes(imsRadioTech,
+                        mRegistrationAttributes.getTransportType(),
+                        mRegistrationAttributes.getAttributeFlags(),
+                        mRegistrationAttributes.getFeatureTags()) :
+                new ImsRegistrationAttributes.Builder(imsRadioTech).build();
+        onDeregistered(info, suggestedAction, attributes);
+    }
+
+    /**
+     * Notify the framework that the device is disconnected from the IMS network.
+     * <p>
+     * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo,int)}, you should ensure that any
+     * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent
+     * to the framework.  For example,
+     * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}
+     * and
+     * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}
+     * may be set to unavailable to ensure the framework knows these services are no longer
+     * available due to de-registration.  If you do not report capability changes impacted by
+     * de-registration, the framework will not know which features are no longer available as a
+     * result.
+     *
+     * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+     * @param suggestedAction the expected behavior of radio protocol stack.
+     * @param attributes The attributes associated with the IMS registration
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE)
+    public final void onDeregistered(@Nullable ImsReasonInfo info,
+                                     @RegistrationManager.SuggestedAction int suggestedAction,
+                                     @NonNull ImsRegistrationAttributes attributes) {
+        boolean isEmergency = isEmergency(attributes);
+        int imsRadioTech = attributes.getRegistrationTechnology();
+        if (isEmergency) {
+            updateToDisconnectedEmergencyState(info, suggestedAction, imsRadioTech);
+        } else {
+            updateToDisconnectedState(info, suggestedAction, imsRadioTech);
+        }
+        // ImsReasonInfo should never be null.
+        final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
+
+        broadcastToCallbacksLocked((c) -> {
+            try {
+                c.onDeregistered(reasonInfo, suggestedAction, imsRadioTech);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback.");
+            }
+        }, isEmergency);
+    }
+
+    /**
+     * Notify the framework that the device is disconnected from the IMS network.
+     * <p>
+     * Note: Before calling {@link #onDeregistered(ImsReasonInfo, SipDetails)}, ImsService should
+     * ensure that any changes to {@link android.telephony.ims.feature.ImsFeature} capability
+     * availability is sent to the framework.
+     * For example,
+     * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}
+     * and
+     * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}
+     * may be set to unavailable to ensure the framework knows these services are no longer
+     * available due to de-registration.  If ImsService do not report capability changes impacted
+     * by de-registration, the framework will not know which features are no longer available as a
+     * result.
+     *
+     * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+     * @param details the {@link SipDetails} related to disconnected Ims registration
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public final void onDeregistered(@Nullable ImsReasonInfo info,
+            @NonNull SipDetails details) {
+        onDeregistered(info, RegistrationManager.SUGGESTED_ACTION_NONE, REGISTRATION_TECH_NONE,
+                details);
+    }
+
+    /**
+     * Notify the framework that the device is disconnected from the IMS network.
+     * <p>
+     * Note: Before calling {@link #onDeregistered(ImsReasonInfo, SipDetails)}, ImsService should
+     * ensure that any changes to {@link android.telephony.ims.feature.ImsFeature} capability
+     * availability is sent to the framework.
+     * For example,
+     * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}
+     * and
+     * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}
+     * may be set to unavailable to ensure the framework knows these services are no longer
+     * available due to de-registration.  If ImsService do not report capability changes impacted
+     * by de-registration, the framework will not know which features are no longer available as a
+     * result.
+     *
+     * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+     * @param suggestedAction the expected behavior of radio protocol stack.
+     * @param imsRadioTech the network type on which IMS registration has failed.
+     * @param details the {@link SipDetails} related to disconnected Ims registration
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public final void onDeregistered(@Nullable ImsReasonInfo info,
+            @RegistrationManager.SuggestedAction int suggestedAction,
+            @ImsRegistrationTech int imsRadioTech, @NonNull SipDetails details) {
+        updateToDisconnectedState(info, suggestedAction, imsRadioTech);
+        // ImsReasonInfo should never be null.
+        final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
+        broadcastToCallbacksLocked((c) -> {
+            try {
+                c.onDeregisteredWithDetails(reasonInfo, suggestedAction, imsRadioTech, details);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback.");
+            }
+        }, false);
+    }
+
+    /**
+     * Notify the framework that the handover from the current radio technology to the technology
+     * defined in {@code imsRadioTech} has failed.
+     * @param imsRadioTech The technology that has failed to be changed. Valid values are
+     * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
+     * {@link #REGISTRATION_TECH_CROSS_SIM}.
+     * @param info The {@link ImsReasonInfo} for the failure to change technology.
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
+            ImsReasonInfo info) {
+        ImsRegistrationAttributes attributes = mRegistrationAttributes != null
+                ? new ImsRegistrationAttributes(imsRadioTech,
+                        mRegistrationAttributes.getTransportType(),
+                        mRegistrationAttributes.getAttributeFlags(),
+                        mRegistrationAttributes.getFeatureTags()) :
+                new ImsRegistrationAttributes.Builder(imsRadioTech).build();
+        onTechnologyChangeFailed(info, attributes);
+    }
+
+    /**
+     * Notify the framework that the handover from the current radio technology to the technology
+     * defined in {@code imsRadioTech} has failed.
+     * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
+     * {@link #REGISTRATION_TECH_CROSS_SIM}.
+     * @param info The {@link ImsReasonInfo} for the failure to change technology.
+     * @param attributes The attributes associated with the IMS registration
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE)
+    public final void onTechnologyChangeFailed(@Nullable ImsReasonInfo info,
+                                               @NonNull ImsRegistrationAttributes attributes) {
+        boolean isEmergency = isEmergency(attributes);
+        int imsRadioTech = attributes.getRegistrationTechnology();
+        final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
+        broadcastToCallbacksLocked(c -> {
+            try {
+                c.onTechnologyChangeFailed(imsRadioTech, reasonInfo);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + "onTechnologyChangeFailed() - Skipping callback.");
+            }
+        }, isEmergency);
+    }
+
+    /**
+     * Invoked when the {@link Uri}s associated to this device's subscriber have changed.
+     * These {@link Uri}s' are filtered out during conference calls.
+     *
+     * The {@link Uri}s are not guaranteed to be different between subsequent calls.
+     * @param uris changed uris
+     * @hide This API is not part of the Android public SDK API
+     */
+    @SystemApi
+    public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
+        synchronized (mLock) {
+            mUris = ArrayUtils.cloneOrNull(uris);
+            mUrisSet = true;
+        }
+        broadcastToCallbacksLocked((c) -> onSubscriberAssociatedUriChanged(c, uris), false);
+    }
+
+    private boolean isEmergency(ImsRegistrationAttributes attributes) {
+        if (attributes == null) {
+            return false;
+        } else {
+            return (attributes.getAttributeFlags()
+                    & ImsRegistrationAttributes.ATTR_REGISTRATION_TYPE_EMERGENCY) != 0;
+        }
+    }
+
+    /**
+     * Broadcast the specified operation in ta synchronized manner so that multiple threads do not
+     * try to call broadcast at the same time, which will generate an error.
+     * @param c The Consumer lambda method containing the callback to call.
+     */
+    private void broadcastToCallbacksLocked(Consumer<IImsRegistrationCallback> c,
+                                            boolean isEmergency) {
+        // One broadcast can happen at a time, so synchronize threads so only one
+        // beginBroadcast/endBroadcast happens at a time.
+        if (isEmergency) {
+            synchronized (mEmergencyCallbacks) {
+                mEmergencyCallbacks.broadcastAction(c);
+            }
+        } else {
+            synchronized (mCallbacks) {
+                mCallbacks.broadcastAction(c);
+            }
+        }
+    }
+
+    private void onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris) {
+        try {
+            callback.onSubscriberAssociatedUriChanged(uris);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, e + "onSubscriberAssociatedUriChanged() - Skipping callback.");
+        }
+    }
+
+    private void updateToState(ImsRegistrationAttributes attributes, int newState) {
+        synchronized (mLock) {
+            mRegistrationAttributes = attributes;
+            mRegistrationState = newState;
+            mLastDisconnectCause = null;
+            mLastDisconnectSuggestedAction = RegistrationManager.SUGGESTED_ACTION_NONE;
+            mLastDisconnectRadioTech = REGISTRATION_TECH_NONE;
+        }
+    }
+
+    private void updateToEmergencyState(ImsRegistrationAttributes attributes, int newState) {
+        synchronized (mLock) {
+            mEmergencyRegistrationAttributes = attributes;
+            mEmergencyRegistrationState = newState;
+            mEmergencyLastDisconnectCause = null;
+            mEmergencyLastDisconnectSuggestedAction = RegistrationManager.SUGGESTED_ACTION_NONE;
+            mEmergencyLastDisconnectRadioTech = REGISTRATION_TECH_NONE;
+        }
+    }
+
+    private void updateToDisconnectedState(ImsReasonInfo info,
+            @RegistrationManager.SuggestedAction int suggestedAction,
+            @ImsRegistrationTech int imsRadioTech) {
+        synchronized (mLock) {
+            //We don't want to send this info over if we are disconnected
+            mUrisSet = false;
+            mUris = null;
+
+            updateToState(new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NONE).build(),
+                    RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
+            if (info != null) {
+                mLastDisconnectCause = info;
+                mLastDisconnectSuggestedAction = suggestedAction;
+                mLastDisconnectRadioTech = imsRadioTech;
+            } else {
+                Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided.");
+                mLastDisconnectCause = new ImsReasonInfo();
+            }
+        }
+    }
+
+    private void updateToDisconnectedEmergencyState(ImsReasonInfo info,
+                 @RegistrationManager.SuggestedAction int suggestedAction,
+                 @ImsRegistrationTech int imsRadioTech) {
+        synchronized (mLock) {
+            //We don't want to send this info over if we are disconnected
+            mUrisSet = false;
+            mUris = null;
+
+            updateToEmergencyState(new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NONE)
+                            .build(),
+                    RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
+            if (info != null) {
+                mEmergencyLastDisconnectCause = info;
+                mEmergencyLastDisconnectSuggestedAction = suggestedAction;
+                mEmergencyLastDisconnectRadioTech = imsRadioTech;
+            } else {
+                Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided.");
+                mEmergencyLastDisconnectCause = new ImsReasonInfo();
+            }
+        }
+    }
+
+    /**
+     * @param c the newly registered callback that will be updated with the current registration
+     *         state.
+     */
+    private void updateNewCallbackWithState(IImsRegistrationCallback c, boolean isEmergencyCallback)
+            throws RemoteException {
+        int state;
+        ImsRegistrationAttributes attributes;
+        ImsReasonInfo disconnectInfo;
+        int suggestedAction;
+        int imsDisconnectRadioTech;
+        boolean urisSet;
+        Uri[] uris;
+        synchronized (mLock) {
+            state = isEmergencyCallback ? mEmergencyRegistrationState : mRegistrationState;
+            attributes = isEmergencyCallback ? mEmergencyRegistrationAttributes :
+                    mRegistrationAttributes;
+            disconnectInfo = isEmergencyCallback ? mEmergencyLastDisconnectCause :
+                    mLastDisconnectCause;
+            suggestedAction = isEmergencyCallback ? mEmergencyLastDisconnectSuggestedAction :
+                    mLastDisconnectSuggestedAction;
+            imsDisconnectRadioTech = isEmergencyCallback ? mEmergencyLastDisconnectRadioTech :
+                    mLastDisconnectRadioTech;
+            urisSet = mUrisSet;
+            uris = mUris;
+        }
+        switch (state) {
+            case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED: {
+                c.onDeregistered(disconnectInfo, suggestedAction, imsDisconnectRadioTech);
+                break;
+            }
+            case RegistrationManager.REGISTRATION_STATE_REGISTERING: {
+                c.onRegistering(attributes);
+                break;
+            }
+            case RegistrationManager.REGISTRATION_STATE_REGISTERED: {
+                c.onRegistered(attributes);
+                break;
+            }
+            case REGISTRATION_STATE_UNKNOWN: {
+                // Do not callback if the state has not been updated yet by the ImsService.
+                break;
+            }
+        }
+        if (urisSet) {
+            onSubscriberAssociatedUriChanged(c, uris);
+        }
+    }
+
+    /**
+     * Set default Executor from ImsService.
+     * @param executor The default executor for the framework to use when executing the methods
+     * overridden by the implementation of Registration.
+     * @hide
+     */
+    public final void setDefaultExecutor(@NonNull Executor executor) {
+        if (mExecutor == null) {
+            mExecutor = executor;
+        }
+    }
+
+    /**
+     * Clear the cached data when the subscription is no longer valid
+     * such as when a sim is removed.
+     * @hide
+     */
+    public final void clearRegistrationCache() {
+        synchronized (mLock) {
+            mUris = null;
+            mUrisSet = false;
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/stub/ImsSmsImplBase.java b/android-35/android/telephony/ims/stub/ImsSmsImplBase.java
new file mode 100644
index 0000000..e7e0ec2
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -0,0 +1,561 @@
+/*
+ * Copyright (C) 2018 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.ims.stub;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * Base implementation for SMS over IMS.
+ *
+ * Any service wishing to provide SMS over IMS should extend this class and implement all methods
+ * that the service supports.
+ *
+ * @hide
+ */
+@SystemApi
+public class ImsSmsImplBase {
+    private static final String LOG_TAG = "SmsImplBase";
+
+    /** @hide */
+    @IntDef({
+            SEND_STATUS_OK,
+            SEND_STATUS_ERROR,
+            SEND_STATUS_ERROR_RETRY,
+            SEND_STATUS_ERROR_FALLBACK
+        })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SendStatusResult {}
+    /**
+     * Message was sent successfully.
+     */
+    public static final int SEND_STATUS_OK = 1;
+
+    /**
+     * IMS provider failed to send the message and platform should not retry falling back to sending
+     * the message using the radio.
+     */
+    public static final int SEND_STATUS_ERROR = 2;
+
+    /**
+     * IMS provider failed to send the message and platform should retry again after setting TP-RD
+     * bit to high.
+     */
+    public static final int SEND_STATUS_ERROR_RETRY = 3;
+
+    /**
+     * IMS provider failed to send the message and platform should retry falling back to sending
+     * the message using the radio.
+     */
+    public static final int SEND_STATUS_ERROR_FALLBACK = 4;
+
+    /** @hide */
+    @IntDef({
+            DELIVER_STATUS_OK,
+            DELIVER_STATUS_ERROR_GENERIC,
+            DELIVER_STATUS_ERROR_NO_MEMORY,
+            DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED
+        })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DeliverStatusResult {}
+    /**
+     * Message was delivered successfully.
+     */
+    public static final int DELIVER_STATUS_OK = 1;
+
+    /**
+     * Message was not delivered.
+     */
+    public static final int DELIVER_STATUS_ERROR_GENERIC = 2;
+
+    /**
+     * Message was not delivered due to lack of memory.
+     */
+    public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3;
+
+    /**
+     * Message was not delivered as the request is not supported.
+     */
+    public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4;
+
+    /** @hide */
+    @IntDef({
+            STATUS_REPORT_STATUS_OK,
+            STATUS_REPORT_STATUS_ERROR
+        })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StatusReportResult {}
+
+    /**
+     * Status Report was set successfully.
+     */
+    public static final int STATUS_REPORT_STATUS_OK = 1;
+
+    /**
+     * Error while setting status report.
+     */
+    public static final int STATUS_REPORT_STATUS_ERROR = 2;
+
+    /**
+     * No network error was generated while processing the SMS message.
+     */
+    // Should match SmsResponse.NO_ERROR_CODE
+    public static final int RESULT_NO_NETWORK_ERROR = -1;
+
+    // Lock for feature synchronization
+    private final Object mLock = new Object();
+    private IImsSmsListener mListener;
+    private Executor mExecutor;
+
+    /**
+     * Create a new ImsSmsImplBase using the Executor set in MmTelFeature
+     */
+    public ImsSmsImplBase() {
+    }
+
+    /**
+     * Create a new ImsSmsImplBase with specified executor.
+     * <p>
+     * @param executor Default executor for ImsSmsImplBase
+     */
+    public ImsSmsImplBase(@NonNull Executor executor) {
+        mExecutor = executor;
+    }
+
+    /**
+     * Registers a listener responsible for handling tasks like delivering messages.
+     *
+     * @param listener listener to register.
+     *
+     * @hide
+     */
+    public final void registerSmsListener(IImsSmsListener listener) {
+        synchronized (mLock) {
+            mListener = listener;
+        }
+    }
+
+    /**
+     * This method will be triggered by the platform when the user attempts to send an SMS. This
+     * method should be implemented by the IMS providers to provide implementation of sending an SMS
+     * over IMS.
+     *
+     * @param token unique token generated by the platform that should be used when triggering
+     *             callbacks for this specific message.
+     * @param messageRef the message reference, which may be 1 byte if it is in
+     *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+     *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+     * @param format the format of the message.
+     * @param smsc the Short Message Service Center address.
+     * @param isRetry whether it is a retry of an already attempted message or not.
+     * @param pdu PDU representing the contents of the message.
+     */
+    public void sendSms(int token, @IntRange(from = 0, to = 65535) int messageRef,
+            @SmsMessage.Format String format, String smsc, boolean isRetry,
+            byte[] pdu) {
+        // Base implementation returns error. Should be overridden.
+        try {
+            onSendSmsResult(token, messageRef, SEND_STATUS_ERROR,
+                    SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+        } catch (RuntimeException e) {
+            Log.e(LOG_TAG, "Can not send sms: " + e.getMessage());
+        }
+    }
+
+    /**
+     * This method will be triggered by the platform when memory becomes available to receive SMS
+     * after a memory full event. This method should be implemented by IMS providers to
+     * send RP-SMMA notification from SMS Relay Layer to server over IMS as per section 7.3.2 of
+     * TS 124.11. Once the RP-SMMA Notification is sent to the network. The network will deliver all
+     * the pending messages which failed due to Unavailability of Memory.
+     *
+     * @param token unique token generated in {@link ImsSmsDispatcher#onMemoryAvailable(void)} that
+     *  should be used when triggering callbacks for this specific message.
+     *
+     * @hide
+     */
+    public void onMemoryAvailable(int token) {
+        // Base Implementation - Should be overridden
+    }
+
+    /**
+     * This method will be triggered by the platform after
+     * {@link #onSmsReceived(int, String, byte[])} has been called to deliver the result to the IMS
+     * provider.
+     *
+     * If the framework needs to provide the PDU used to acknowledge the SMS,
+     * {@link #acknowledgeSms(int, int, int, byte[])} will be called.
+     *
+     * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
+     * @param messageRef the message reference, which may be 1 byte if it is in
+     *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+     *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+     * @param result result of delivering the message.
+     */
+    public void acknowledgeSms(int token, @IntRange(from = 0, to = 65535)  int messageRef,
+            @DeliverStatusResult int result) {
+        Log.e(LOG_TAG, "acknowledgeSms() not implemented.");
+    }
+
+    /**
+     * This method will be called by the platform after
+     * {@link #onSmsReceived(int, String, byte[])} has been called to acknowledge an incoming SMS.
+     *
+     * This method is only called in cases where the framework needs to provide the PDU such as the
+     * case where we provide the Short Message Transfer Layer PDU (see 3GPP TS 23.040). Otherwise,
+     * {@link #acknowledgeSms(int, int, int)} will be used.
+     *
+     * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
+     * @param messageRef the message reference, which may be 1 byte if it is in
+     *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+     *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+     * @param result result of delivering the message.
+     * @param pdu PDU representing the contents of the message.
+     */
+    public void acknowledgeSms(int token, @IntRange(from = 0, to = 65535)  int messageRef,
+            @DeliverStatusResult int result, @NonNull byte[] pdu) {
+        Log.e(LOG_TAG, "acknowledgeSms() not implemented. acknowledgeSms(int, int, int) called.");
+        acknowledgeSms(token, messageRef, result);
+    }
+
+    /**
+     * This method will be triggered by the platform after
+     * {@link #onSmsStatusReportReceived(int, int, String, byte[])} or
+     * {@link #onSmsStatusReportReceived(int, String, byte[])} has been called to provide the
+     * result to the IMS provider.
+     *
+     * @param token token provided in {@link #onSmsStatusReportReceived(int, int, String, byte[])}
+     *              or {@link #onSmsStatusReportReceived(int, String, byte[])}
+     * @param messageRef the message reference, which may be 1 byte if it is in
+     *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+     *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+     * @param result result of delivering the message.
+     */
+    public void acknowledgeSmsReport(int token, @IntRange(from = 0, to = 65535) int messageRef,
+            @StatusReportResult int result) {
+        Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented.");
+    }
+
+    /**
+     * This method should be triggered by the IMS providers when there is an incoming message. The
+     * platform will deliver the message to the messages database and notify the IMS provider of the
+     * result by calling {@link #acknowledgeSms(int, int, int)}.
+     *
+     * This method must not be called before {@link #onReady()} is called or the call will fail. If
+     * the platform is not available, {@link #acknowledgeSms(int, int, int)} will be called with the
+     * {@link #DELIVER_STATUS_ERROR_GENERIC} result code.
+     * @param token unique token generated by IMS providers that the platform will use to trigger
+     *              callbacks for this message.
+     * @param format the format of the message.
+     * @param pdu PDU representing the contents of the message.
+     * @throws RuntimeException if called before {@link #onReady()} is triggered.
+     */
+    public final void onSmsReceived(int token, @SmsMessage.Format String format, byte[] pdu)
+            throws RuntimeException {
+        IImsSmsListener listener = null;
+        synchronized (mLock) {
+            listener = mListener;
+        }
+
+        if (listener == null) {
+            throw new RuntimeException("Feature not ready.");
+        }
+        try {
+            listener.onSmsReceived(token, format, pdu);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
+            SmsMessage message = SmsMessage.createFromPdu(pdu, format);
+            if (message != null && message.mWrappedSmsMessage != null) {
+                acknowledgeSms(token, message.mWrappedSmsMessage.mMessageRef,
+                        DELIVER_STATUS_ERROR_GENERIC);
+            } else {
+                Log.w(LOG_TAG, "onSmsReceived: Invalid pdu entered.");
+                acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
+            }
+        }
+    }
+
+    /**
+     * This method should be triggered by the IMS providers when an outgoing SMS message has been
+     * sent successfully.
+     *
+     * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+     * @param messageRef the message reference, which may be 1 byte if it is in
+     *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+     *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+     *
+     * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
+     * connection to the framework is not available. If this happens attempting to send the SMS
+     * should be aborted.
+     */
+    public final void onSendSmsResultSuccess(int token,
+            @IntRange(from = 0, to = 65535) int messageRef) throws RuntimeException {
+        IImsSmsListener listener = null;
+        synchronized (mLock) {
+            listener = mListener;
+        }
+
+        if (listener == null) {
+            throw new RuntimeException("Feature not ready.");
+        }
+        try {
+            listener.onSendSmsResult(token, messageRef, SEND_STATUS_OK,
+                    SmsManager.RESULT_ERROR_NONE, RESULT_NO_NETWORK_ERROR);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This method should be triggered by the IMS providers to pass the result of the sent message
+     * to the platform.
+     *
+     * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+     * @param messageRef the message reference, which may be 1 byte if it is in
+     *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+     *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+     * @param status result of sending the SMS.
+     * @param reason reason in case status is failure.
+     *
+     * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
+     * connection to the framework is not available. If this happens attempting to send the SMS
+     * should be aborted.
+     * @deprecated Use {@link #onSendSmsResultSuccess(int, int)} or
+     * {@link #onSendSmsResultError(int, int, int, int, int)} to notify the framework of the SMS
+     * send result.
+     */
+    @Deprecated
+    public final void onSendSmsResult(int token, @IntRange(from = 0, to = 65535) int messageRef,
+            @SendStatusResult int status, @SmsManager.Result int reason) throws RuntimeException {
+        IImsSmsListener listener = null;
+        synchronized (mLock) {
+            listener = mListener;
+        }
+
+        if (listener == null) {
+            throw new RuntimeException("Feature not ready.");
+        }
+        try {
+            listener.onSendSmsResult(token, messageRef, status, reason,
+                    RESULT_NO_NETWORK_ERROR);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This method should be triggered by the IMS providers when an outgoing message fails to be
+     * sent due to an error generated while processing the message or after being sent to the
+     * network.
+     *
+     * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+     * @param messageRef the message reference, which may be 1 byte if it is in
+     *     {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+     *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+     * @param status result of sending the SMS.
+     * @param networkErrorCode the error code reported by the carrier network if sending this SMS
+     *  has resulted in an error or {@link #RESULT_NO_NETWORK_ERROR} if no network error was
+     *  generated. See 3GPP TS 24.011 Section 7.3.4 for valid error codes and more information.
+     *
+     * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
+     * connection to the framework is not available. If this happens attempting to send the SMS
+     * should be aborted.
+     */
+    public final void onSendSmsResultError(int token,
+            @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status,
+            @SmsManager.Result int reason, int networkErrorCode) throws RuntimeException {
+        IImsSmsListener listener = null;
+        synchronized (mLock) {
+            listener = mListener;
+        }
+
+        if (listener == null) {
+            throw new RuntimeException("Feature not ready.");
+        }
+        try {
+            listener.onSendSmsResult(token, messageRef, status, reason, networkErrorCode);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This API is used to report the result of sending
+     * RP-SMMA to framework based on received network responses(RP-ACK,
+     * RP-ERROR or SIP error).
+     *
+     * @param token provided in {@link #onMemoryAvailable()}.
+     * @param result based on RP-ACK or RP_ERROR
+     * @param networkErrorCode the error code reported by the carrier
+     * network if sending this SMS has resulted in an error or
+     * {@link #RESULT_NO_NETWORK_ERROR} if no network error was generated. See
+     * 3GPP TS 24.011 Section 7.3.4 for valid error codes and more
+     * information.
+     *
+     * @hide
+     */
+    public final void onMemoryAvailableResult(int token, @SendStatusResult int result,
+            int networkErrorCode) throws RuntimeException {
+        IImsSmsListener listener = null;
+        synchronized (mLock) {
+            listener = mListener;
+        }
+
+        if (listener == null) {
+            throw new RuntimeException("Feature not ready.");
+        }
+        try {
+            listener.onMemoryAvailableResult(token, result, networkErrorCode);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * This method should be triggered by the IMS providers when the status report of the sent
+     * message is received. The platform will handle the report and notify the IMS provider of the
+     * result by calling {@link #acknowledgeSmsReport(int, int, int)}.
+     *
+     * This method must not be called before {@link #onReady()} is called or the call will fail. If
+     * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called
+     * with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
+     * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+     * @param messageRef the message reference, which may be 1 byte if it is in
+     *     {@link SmsMessage#FORMAT_3GPP} format or 2 bytes if it is in
+     *     {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+     * @param format the format of the message.
+     * @param pdu PDU representing the content of the status report.
+     * @throws RuntimeException if called before {@link #onReady()} is triggered
+     *
+     * @deprecated Use {@link #onSmsStatusReportReceived(int, String, byte[])} instead without the
+     * message reference.
+     */
+    @Deprecated
+    public final void onSmsStatusReportReceived(int token,
+            @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format,
+            byte[] pdu) throws RuntimeException {
+        IImsSmsListener listener = null;
+        synchronized (mLock) {
+            listener = mListener;
+        }
+
+        if (listener == null) {
+            throw new RuntimeException("Feature not ready.");
+        }
+        try {
+            listener.onSmsStatusReportReceived(token, format, pdu);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
+            acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR);
+        }
+    }
+
+    /**
+     * This method should be triggered by the IMS providers when the status report of the sent
+     * message is received. The platform will handle the report and notify the IMS provider of the
+     * result by calling {@link #acknowledgeSmsReport(int, int, int)}.
+     *
+     * This method must not be called before {@link #onReady()} is called or the call will fail. If
+     * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called
+     * with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
+     * @param token unique token generated by IMS providers that the platform will use to trigger
+     *              callbacks for this message.
+     * @param format the format of the message.
+     * @param pdu PDU representing the content of the status report.
+     * @throws RuntimeException if called before {@link #onReady()} is triggered
+     */
+    public final void onSmsStatusReportReceived(int token, @SmsMessage.Format String format,
+            byte[] pdu) throws RuntimeException {
+        IImsSmsListener listener = null;
+        synchronized (mLock) {
+            listener = mListener;
+        }
+
+        if (listener == null) {
+            throw new RuntimeException("Feature not ready.");
+        }
+        try {
+            listener.onSmsStatusReportReceived(token, format, pdu);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
+            SmsMessage message = SmsMessage.createFromPdu(pdu, format);
+            if (message != null && message.mWrappedSmsMessage != null) {
+                acknowledgeSmsReport(
+                        token,
+                        message.mWrappedSmsMessage.mMessageRef,
+                        STATUS_REPORT_STATUS_ERROR);
+            } else {
+                Log.w(LOG_TAG, "onSmsStatusReportReceived: Invalid pdu entered.");
+                acknowledgeSmsReport(token, 0, STATUS_REPORT_STATUS_ERROR);
+            }
+        }
+    }
+
+    /**
+     * Returns the SMS format that the ImsService expects.
+     *
+     * @return  The expected format of the SMS messages.
+     */
+    public @SmsMessage.Format String getSmsFormat() {
+      return SmsMessage.FORMAT_3GPP;
+    }
+
+    /**
+     * Called when ImsSmsImpl has been initialized and communication with the framework is set up.
+     * Any attempt by this class to access the framework before this method is called will return
+     * with a {@link RuntimeException}.
+     */
+    public void onReady() {
+        // Base Implementation - Should be overridden
+    }
+
+    /**
+     * Set default Executor for ImsSmsImplBase.
+     *
+     * @param executor The default executor for the framework to use when executing the methods
+     * overridden by the implementation of ImsSms.
+     * @hide
+     */
+    public final void setDefaultExecutor(@NonNull Executor executor) {
+        if (mExecutor == null) {
+            mExecutor = executor;
+        }
+    }
+
+    /**
+     * Get Executor from ImsSmsImplBase.
+     * If there is no settings for the executor, all ImsSmsImplBase method calls will use
+     * Runnable::run as default
+     *
+     * @return an Executor used to execute methods in ImsSms called remotely by the framework.
+     * @hide
+     */
+    public @NonNull Executor getExecutor() {
+        return mExecutor != null ? mExecutor : Runnable::run;
+    }
+}
diff --git a/android-35/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java b/android-35/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java
new file mode 100644
index 0000000..92f1a01
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsStreamMediaSession;
+
+/**
+ * Base implementation of ImsStreamMediaSession, which implements stub versions of the methods
+ * in the IImsStreamMediaSession AIDL. Override the methods that your implementation of
+ * ImsStreamMediaSession supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsStreamMediaSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsStreamMediaSessionImplBase extends IImsStreamMediaSession.Stub {
+
+    @Override
+    public void close() throws RemoteException {
+
+    }
+}
diff --git a/android-35/android/telephony/ims/stub/ImsUtImplBase.java b/android-35/android/telephony/ims/stub/ImsUtImplBase.java
new file mode 100644
index 0000000..11cdeed
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/ImsUtImplBase.java
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.telephony.ims.ImsUtListener;
+import android.util.Log;
+
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
+/**
+ * Base implementation of IMS UT interface, which implements stubs. Override these methods to
+ * implement functionality.
+ *
+ * @hide
+ */
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsUt maintained by other ImsServices.
+@SystemApi
+public class ImsUtImplBase {
+    private static final String TAG = "ImsUtImplBase";
+    /**
+     * Bar all incoming calls. (See 3GPP TS 24.611)
+     * @hide
+     */
+    public static final int CALL_BARRING_ALL_INCOMING = 1;
+
+    /**
+     * Bar all outgoing calls. (See 3GPP TS 24.611)
+     * @hide
+     */
+    public static final int CALL_BARRING_ALL_OUTGOING = 2;
+
+    /**
+     * Bar all outgoing international calls. (See 3GPP TS 24.611)
+     * @hide
+     */
+    public static final int CALL_BARRING_OUTGOING_INTL = 3;
+
+    /**
+     * Bar all outgoing international calls, excluding those to the home PLMN country
+     * (See 3GPP TS 24.611)
+     * @hide
+     */
+    public static final int CALL_BARRING_OUTGOING_INTL_EXCL_HOME = 4;
+
+    /**
+     * Bar all incoming calls when roaming (See 3GPP TS 24.611)
+     * @hide
+     */
+    public static final int CALL_BLOCKING_INCOMING_WHEN_ROAMING = 5;
+
+    /**
+     * Enable Anonymous Communication Rejection (See 3GPP TS 24.611)
+     * @hide
+     */
+    public static final int CALL_BARRING_ANONYMOUS_INCOMING = 6;
+
+    /**
+     * Bar all incoming and outgoing calls. (See 3GPP TS 24.611)
+     * @hide
+     */
+    public static final int CALL_BARRING_ALL = 7;
+
+    /**
+     * Bar all outgoing service requests, including calls. (See 3GPP TS 24.611)
+     * @hide
+     */
+    public static final int CALL_BARRING_OUTGOING_ALL_SERVICES = 8;
+
+    /**
+     * Bar all incoming service requests, including calls. (See 3GPP TS 24.611)
+     * @hide
+     */
+    public static final int CALL_BARRING_INCOMING_ALL_SERVICES = 9;
+
+    /**
+     * Bar specific incoming calls. (See 3GPP TS 24.611)
+     * @hide
+     */
+    public static final int CALL_BARRING_SPECIFIC_INCOMING_CALLS = 10;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CALL_BARRING_", value = {CALL_BARRING_ALL_INCOMING, CALL_BARRING_ALL_OUTGOING,
+            CALL_BARRING_OUTGOING_INTL, CALL_BARRING_OUTGOING_INTL_EXCL_HOME,
+            CALL_BLOCKING_INCOMING_WHEN_ROAMING, CALL_BARRING_ANONYMOUS_INCOMING,
+            CALL_BARRING_ALL, CALL_BARRING_OUTGOING_ALL_SERVICES,
+            CALL_BARRING_INCOMING_ALL_SERVICES, CALL_BARRING_SPECIFIC_INCOMING_CALLS})
+    public @interface CallBarringMode {}
+
+    /**
+     * Constant used to denote an invalid return value.
+     * @hide
+     */
+    public static final int INVALID_RESULT = -1;
+
+    private Executor mExecutor = Runnable::run;
+
+    private final IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
+        private final Object mLock = new Object();
+        private ImsUtListener mUtListener;
+
+        @Override
+        public void close() throws RemoteException {
+            executeMethodAsync(() ->ImsUtImplBase.this.close(), "close");
+        }
+
+        @Override
+        public int queryCallBarring(int cbType) throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallBarring(cbType),
+                    "queryCallBarring");
+        }
+
+        @Override
+        public int queryCallForward(int condition, String number) throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallForward(
+                    condition, number), "queryCallForward");
+        }
+
+        @Override
+        public int queryCallWaiting() throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallWaiting(),
+                    "queryCallWaiting");
+        }
+
+        @Override
+        public int queryCLIR() throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCLIR(), "queryCLIR");
+        }
+
+        @Override
+        public int queryCLIP() throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCLIP(), "queryCLIP");
+        }
+
+        @Override
+        public int queryCOLR() throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCOLR(), "queryCOLR");
+        }
+
+        @Override
+        public int queryCOLP() throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCOLP(), "queryCOLP");
+        }
+
+        @Override
+        public int transact(Bundle ssInfo) throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.transact(ssInfo),
+                    "transact");
+        }
+
+        @Override
+        public int updateCallBarring(int cbType, int action, String[] barrList) throws
+                RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallBarring(
+                    cbType, action, barrList), "updateCallBarring");
+        }
+
+        @Override
+        public int updateCallForward(int action, int condition, String number, int serviceClass,
+                int timeSeconds) throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallForward(
+                    action, condition, number, serviceClass, timeSeconds), "updateCallForward");
+        }
+
+        @Override
+        public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallWaiting(
+                    enable, serviceClass), "updateCallWaiting");
+        }
+
+        @Override
+        public int updateCLIR(int clirMode) throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCLIR(clirMode),
+                    "updateCLIR");
+        }
+
+        @Override
+        public int updateCLIP(boolean enable) throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCLIP(enable),
+                    "updateCLIP");
+        }
+
+        @Override
+        public int updateCOLR(int presentation) throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCOLR(presentation),
+                    "updateCOLR");
+        }
+
+        @Override
+        public int updateCOLP(boolean enable) throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCOLP(enable),
+                    "updateCOLP");
+        }
+
+        @Override
+        public void setListener(IImsUtListener listener) throws RemoteException {
+            executeMethodAsync(() -> {
+                if (mUtListener != null
+                        && !mUtListener.getListenerInterface().asBinder().isBinderAlive()) {
+                    Log.w(TAG, "setListener: discarding dead Binder");
+                    mUtListener = null;
+                }
+                if (mUtListener != null && listener != null && Objects.equals(
+                        mUtListener.getListenerInterface().asBinder(), listener.asBinder())) {
+                    return;
+                }
+
+                if (listener == null) {
+                    mUtListener = null;
+                } else if (listener != null && mUtListener == null) {
+                    mUtListener = new ImsUtListener(listener);
+                } else {
+                    // Warn that the listener is being replaced while active
+                    Log.w(TAG, "setListener is being called when there is already an active "
+                            + "listener");
+                    mUtListener = new ImsUtListener(listener);
+                }
+
+                ImsUtImplBase.this.setListener(mUtListener);
+            }, "setListener");
+        }
+
+        @Override
+        public int queryCallBarringForServiceClass(int cbType, int serviceClass)
+                throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this
+                    .queryCallBarringForServiceClass(cbType, serviceClass),
+                    "queryCallBarringForServiceClass");
+        }
+
+        @Override
+        public int updateCallBarringForServiceClass(int cbType, int action,
+                String[] barrList, int serviceClass) throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this
+                    .updateCallBarringForServiceClass(cbType, action, barrList, serviceClass),
+                    "updateCallBarringForServiceClass");
+        }
+
+        @Override
+        public int updateCallBarringWithPassword(int cbType, int action, String[] barrList,
+                int serviceClass, String password) throws RemoteException {
+            return executeMethodAsyncForResult(() -> ImsUtImplBase.this
+                    .updateCallBarringWithPassword(cbType, action, barrList, serviceClass,
+                    password), "updateCallBarringWithPassword");
+        }
+
+        // Call the methods with a clean calling identity on the executor and wait indefinitely for
+        // the future to return.
+        private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+            try {
+                CompletableFuture.runAsync(
+                        () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(TAG, "ImsUtImplBase Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+
+        private <T> T executeMethodAsyncForResult(Supplier<T> r,
+                String errorLogName) throws RemoteException {
+            CompletableFuture<T> future = CompletableFuture.supplyAsync(
+                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+            try {
+                return future.get();
+            } catch (ExecutionException | InterruptedException e) {
+                Log.w(TAG, "ImsUtImplBase Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+    };
+
+    /**
+     * Called when the framework no longer needs to interact with the IMS UT implementation any
+     * longer.
+     */
+    public void close() {
+
+    }
+
+    /**
+     * Retrieves the call barring configuration.
+     * @param cbType
+     */
+    public int queryCallBarring(int cbType) {
+        return -1;
+    }
+
+    /**
+     * Retrieves the configuration of the call barring for specified service class.
+     */
+    public int queryCallBarringForServiceClass(int cbType, int serviceClass) {
+        return -1;
+    }
+
+    /**
+     * Retrieves the configuration of the call forward.
+     */
+    public int queryCallForward(int condition, String number) {
+        return -1;
+    }
+
+    /**
+     * Retrieves the configuration of the call waiting.
+     */
+    public int queryCallWaiting() {
+        return -1;
+    }
+
+    /**
+     * Retrieves the default CLIR setting.
+     * @hide
+     */
+    public int queryCLIR() {
+        return queryClir();
+    }
+
+    /**
+     * Retrieves the CLIP call setting.
+     * @hide
+     */
+    public int queryCLIP() {
+        return queryClip();
+    }
+
+    /**
+     * Retrieves the COLR call setting.
+     * @hide
+     */
+    public int queryCOLR() {
+        return queryColr();
+    }
+
+    /**
+     * Retrieves the COLP call setting.
+     * @hide
+     */
+    public int queryCOLP() {
+        return queryColp();
+    }
+
+    /**
+     * Retrieves the default CLIR setting.
+     */
+    public int queryClir() {
+        return -1;
+    }
+
+    /**
+     * Retrieves the CLIP call setting.
+     */
+    public int queryClip() {
+        return -1;
+    }
+
+    /**
+     * Retrieves the COLR call setting.
+     */
+    public int queryColr() {
+        return -1;
+    }
+
+    /**
+     * Retrieves the COLP call setting.
+     */
+    public int queryColp() {
+        return -1;
+    }
+
+    /**
+     * Updates or retrieves the supplementary service configuration.
+     */
+    public int transact(Bundle ssInfo) {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the call barring.
+     */
+    public int updateCallBarring(@CallBarringMode int cbType, int action, String[] barrList) {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the call barring for specified service class.
+     */
+    public int updateCallBarringForServiceClass(@CallBarringMode int cbType, int action,
+            String[] barrList, int serviceClass) {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the call barring for specified service class with password.
+     * @hide
+     */
+    public int updateCallBarringWithPassword(int cbType, int action, @Nullable String[] barrList,
+            int serviceClass, @NonNull String password) {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the call forward.
+     */
+    public int updateCallForward(int action, int condition, String number, int serviceClass,
+            int timeSeconds) {
+        return 0;
+    }
+
+    /**
+     * Updates the configuration of the call waiting.
+     */
+    public int updateCallWaiting(boolean enable, int serviceClass) {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the CLIR supplementary service.
+     * @hide
+     */
+    public int updateCLIR(int clirMode) {
+        return updateClir(clirMode);
+    }
+
+    /**
+     * Updates the configuration of the CLIP supplementary service.
+     * @hide
+     */
+    public int updateCLIP(boolean enable) {
+        return updateClip(enable);
+    }
+
+    /**
+     * Updates the configuration of the COLR supplementary service.
+     * @hide
+     */
+    public int updateCOLR(int presentation) {
+        return updateColr(presentation);
+    }
+
+    /**
+     * Updates the configuration of the COLP supplementary service.
+     * @hide
+     */
+    public int updateCOLP(boolean enable) {
+        return updateColp(enable);
+    }
+
+    /**
+     * Updates the configuration of the CLIR supplementary service.
+     */
+    public int updateClir(int clirMode) {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the CLIP supplementary service.
+     */
+    public int updateClip(boolean enable) {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the COLR supplementary service.
+     */
+    public int updateColr(int presentation) {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the COLP supplementary service.
+     */
+    public int updateColp(boolean enable) {
+        return -1;
+    }
+
+    /**
+     * Sets the listener.
+     */
+    public void setListener(ImsUtListener listener) {
+    }
+
+    /**
+     * @hide
+     */
+    public IImsUt getInterface() {
+        return mServiceImpl;
+    }
+
+    /**
+     * Set default Executor from MmTelFeature.
+     * @param executor The default executor for the framework to use when executing the methods
+     * overridden by the implementation of ImsUT.
+     * @hide
+     */
+    public final void setDefaultExecutor(@NonNull Executor executor) {
+        mExecutor = executor;
+    }
+}
diff --git a/android-35/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/android-35/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
new file mode 100644
index 0000000..66442a6
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -0,0 +1,505 @@
+/*
+ * Copyright (c) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.SipDetails;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+/**
+ * Extend this base class to implement RCS User Capability Exchange (UCE) for the AOSP platform
+ * using the vendor ImsService.
+ * <p>
+ * See RCC.07 for more details on UCE as well as how UCE should be implemented.
+ * @hide
+ */
+@SystemApi
+public class RcsCapabilityExchangeImplBase {
+
+    private static final String LOG_TAG = "RcsCapExchangeImplBase";
+
+    /**
+     * Service is unknown.
+     */
+    public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0;
+
+    /**
+     * The command failed with an unknown error.
+     */
+    public static final int COMMAND_CODE_GENERIC_FAILURE = 1;
+
+    /**
+     * Invalid parameter(s).
+     */
+    public static final int COMMAND_CODE_INVALID_PARAM = 2;
+
+    /**
+     * Fetch error.
+     */
+    public static final int COMMAND_CODE_FETCH_ERROR = 3;
+
+    /**
+     * Request timed out.
+     */
+    public static final int COMMAND_CODE_REQUEST_TIMEOUT = 4;
+
+    /**
+     * Failure due to insufficient memory available.
+     */
+    public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5;
+
+    /**
+     * Network connection is lost.
+     */
+    public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6;
+
+    /**
+     * Requested feature/resource is not supported.
+     */
+    public static final int COMMAND_CODE_NOT_SUPPORTED = 7;
+
+    /**
+     * Contact or resource is not found.
+     */
+    public static final int COMMAND_CODE_NOT_FOUND = 8;
+
+    /**
+     * Service is not available.
+     */
+    public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 9;
+
+    /**
+     * Command resulted in no change in state, ignoring.
+     */
+    public static final int COMMAND_CODE_NO_CHANGE = 10;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "COMMAND_CODE_", value = {
+            COMMAND_CODE_SERVICE_UNKNOWN,
+            COMMAND_CODE_GENERIC_FAILURE,
+            COMMAND_CODE_INVALID_PARAM,
+            COMMAND_CODE_FETCH_ERROR,
+            COMMAND_CODE_REQUEST_TIMEOUT,
+            COMMAND_CODE_INSUFFICIENT_MEMORY,
+            COMMAND_CODE_LOST_NETWORK_CONNECTION,
+            COMMAND_CODE_NOT_SUPPORTED,
+            COMMAND_CODE_NOT_FOUND,
+            COMMAND_CODE_SERVICE_UNAVAILABLE,
+            COMMAND_CODE_NO_CHANGE
+    })
+    public @interface CommandCode {}
+
+    /**
+     * Interface used by the framework to receive the response of the publish request.
+     */
+    public interface PublishResponseCallback {
+        /**
+         * Notify the framework that the command associated with the
+         * {@link #publishCapabilities(String, PublishResponseCallback)} has failed.
+         *
+         * @param code The reason why the associated command has failed.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework. This can happen if the {@link RcsFeature}
+         * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+         * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases
+         * when the Telephony stack has crashed.
+         */
+        void onCommandError(@CommandCode int code) throws ImsException;
+
+        /**
+         * Provide the framework with a subsequent network response update to
+         * {@link #publishCapabilities(String, PublishResponseCallback)}.
+         *
+         * If this network response also contains a “Reason” header, then the
+         * {@link #onNetworkResponse(int, String, int, String)} method should be used instead.
+         *
+         * @param sipCode The SIP response code sent from the network for the operation
+         * token specified.
+         * @param reason The optional reason response from the network. If there is a reason header
+         * included in the response, that should take precedence over the reason provided in the
+         * status line. If the network provided no reason with the sip code, the string should be
+         * empty.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework. This can happen if the {@link RcsFeature}
+         * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+         * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases
+         * when the Telephony stack has crashed.
+         *
+         * @deprecated Replaced sip information with the newly added
+         * {@link #onNetworkResponse(SipDetails)}.
+         */
+        @Deprecated
+        void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
+                @NonNull String reason) throws ImsException;
+
+        /**
+         * Provide the framework with a subsequent network response update to
+         * {@link #publishCapabilities(String, PublishResponseCallback)} that also
+         * includes a reason provided in the “reason” header. See RFC3326 for more
+         * information.
+         *
+         * @param sipCode The SIP response code sent from the network.
+         * @param reasonPhrase The optional reason response from the network. If the
+         * network provided no reason with the sip code, the string should be empty.
+         * @param reasonHeaderCause The “cause” parameter of the “reason” header
+         * included in the SIP message.
+         * @param reasonHeaderText The “text” parameter of the “reason” header
+         * included in the SIP message.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework. This can happen if the
+         * {@link RcsFeature} is not
+         * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+         * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+         * rare cases when the Telephony stack has crashed.
+         *
+         * @deprecated Replaced sip information with the newly added
+         * {@link #onNetworkResponse(SipDetails)}.
+         */
+        @Deprecated
+        void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
+                @NonNull String reasonPhrase,
+                @IntRange(from = 100, to = 699) int reasonHeaderCause,
+                @NonNull String reasonHeaderText) throws ImsException;
+
+        /**
+         * Provide the framework with a subsequent network response update to
+         * {@link #publishCapabilities(String, PublishResponseCallback)}.
+         *
+         * @param details The SIP information received in response to a publish operation.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework. This can happen if the {@link RcsFeature}
+         * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+         * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases
+         * when the Telephony stack has crashed.
+         */
+        default void onNetworkResponse(@NonNull SipDetails details) throws ImsException {
+            if (TextUtils.isEmpty(details.getReasonHeaderText())) {
+                onNetworkResponse(details.getResponseCode(), details.getResponsePhrase());
+            } else {
+                onNetworkResponse(details.getResponseCode(), details.getResponsePhrase(),
+                        details.getReasonHeaderCause(), details.getReasonHeaderText());
+            }
+        }
+    }
+
+    /**
+     * Interface used by the framework to respond to OPTIONS requests.
+     */
+    public interface OptionsResponseCallback {
+        /**
+         * Notify the framework that the command associated with this callback has failed.
+         *
+         * @param code The reason why the associated command has failed.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework. This can happen if the
+         * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature}
+         * has not received the {@link ImsFeature#onFeatureReady()} callback. This may also happen
+         * in rare cases when the Telephony stack has crashed.
+         */
+        void onCommandError(@CommandCode int code) throws ImsException;
+
+        /**
+         * Send the response of a SIP OPTIONS capability exchange to the framework.
+         * @param sipCode The SIP response code that was sent by the network in response
+         * to the request sent by {@link #sendOptionsCapabilityRequest}.
+         * @param reason The optional SIP response reason sent by the network.
+         * If none was sent, this should be an empty string.
+         * @param theirCaps the contact's UCE capabilities associated with the
+         * capability request.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
+         * currently connected to the framework. This can happen if the
+         * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+         * {@link RcsFeature} has not received the
+         * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
+         * cases when the Telephony stack has crashed.
+         */
+        void onNetworkResponse(int sipCode, @NonNull String reason,
+                @NonNull List<String> theirCaps) throws ImsException;
+    }
+
+    /**
+     * Interface used by the framework to receive the response of the subscribe request.
+     */
+    public interface SubscribeResponseCallback {
+        /**
+         * Notify the framework that the command associated with this callback has failed.
+         * <p>
+         * Must only be called when there was an error generating a SUBSCRIBE request due to an
+         * IMS stack error. This is a terminating event, so no other callback event will be
+         * expected after this callback.
+         *
+         * @param code The reason why the associated command has failed.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework. This can happen if the
+         * {@link RcsFeature} is not
+         * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+         * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+         * rare cases when the Telephony stack has crashed.
+         */
+        void onCommandError(@CommandCode int code) throws ImsException;
+
+        /**
+         * Notify the framework of the response to the SUBSCRIBE request from
+         * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)}.
+         * <p>
+         * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the
+         * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate},
+         * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the
+         * subsequent NOTIFY responses to the subscription.
+         *
+         * If this network response also contains a “Reason” header, then the
+         * {@link #onNetworkResponse(int, String, int, String)} method should be used instead.
+         *
+         * @param sipCode The SIP response code sent from the network for the operation
+         * token specified.
+         * @param reason The optional reason response from the network. If the network
+         *  provided no reason with the sip code, the string should be empty.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework. This can happen if the
+         * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+         * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback.
+         * This may also happen in rare cases when the Telephony stack has crashed.
+         *
+         * @deprecated Replaced sip information with the newly added
+         * {@link #onNetworkResponse(SipDetails)}.
+         */
+        @Deprecated
+        void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
+                @NonNull String reason) throws ImsException;
+
+        /**
+         * Notify the framework  of the response to the SUBSCRIBE request from
+         * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)} that also
+         * includes a reason provided in the “reason” header. See RFC3326 for more
+         * information.
+         *
+         * @param sipCode The SIP response code sent from the network,
+         * @param reasonPhrase The optional reason response from the network. If the
+         * network provided no reason with the sip code, the string should be empty.
+         * @param reasonHeaderCause The “cause” parameter of the “reason” header
+         * included in the SIP message.
+         * @param reasonHeaderText The “text” parameter of the “reason” header
+         * included in the SIP message.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework. This can happen if the
+         * {@link RcsFeature} is not
+         * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+         * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+         * rare cases when the Telephony stack has crashed.
+         *
+         * @deprecated Replaced sip information with the newly added
+         * {@link #onNetworkResponse(SipDetails)}.
+         */
+        @Deprecated
+        void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
+                @NonNull String reasonPhrase,
+                @IntRange(from = 100, to = 699) int reasonHeaderCause,
+                @NonNull String reasonHeaderText) throws ImsException;
+
+        /**
+         * Notify the framework of the response to the SUBSCRIBE request from
+         * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)}.
+         * <p>
+         * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the
+         * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate},
+         * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the
+         * subsequent NOTIFY responses to the subscription.
+         *
+         * @param details The SIP information related to this request.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework. This can happen if the
+         * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+         * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback.
+         * This may also happen in rare cases when the Telephony stack has crashed.
+         */
+        default void onNetworkResponse(@NonNull SipDetails details) throws ImsException {
+            if (TextUtils.isEmpty(details.getReasonHeaderText())) {
+                onNetworkResponse(details.getResponseCode(), details.getResponsePhrase());
+            } else {
+                onNetworkResponse(details.getResponseCode(), details.getResponsePhrase(),
+                        details.getReasonHeaderCause(), details.getReasonHeaderText());
+            }
+        };
+
+        /**
+         * Notify the framework of the latest XML PIDF documents included in the network response
+         * for the requested contacts' capabilities requested by the Framework using
+         * {@link RcsUceAdapter#requestCapabilities(List, Executor,
+         * RcsUceAdapter.CapabilitiesCallback)}.
+         * <p>
+         * The expected format for the PIDF XML is defined in RFC3861. Each XML document must be a
+         * "application/pidf+xml" object and start with a root <presence> element. For NOTIFY
+         * responses that contain RLMI information and potentially multiple PIDF XMLs, each
+         * PIDF XML should be separated and added as a separate item in the List. This should be
+         * called every time a new NOTIFY event is received with new capability information.
+         *
+         * @param pidfXmls The list of the PIDF XML data for the contact URIs that it subscribed
+         * for.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework.
+         * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+         * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
+         * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+         * rare cases when the Telephony stack has crashed.
+         */
+        void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException;
+
+        /**
+         * Notify the framework that a resource in the RLMI XML contained in the NOTIFY response
+         * for the ongoing SUBSCRIBE dialog has been terminated.
+         * <p>
+         * This will be used to notify the framework that a contact URI that the IMS stack has
+         * subscribed to on the Resource List Server has been terminated as well as the reason why.
+         * Usually this means that there will not be any capability information for the contact URI
+         * that they subscribed for. See RFC 4662 for more information.
+         *
+         * @param uriTerminatedReason The contact URIs which have been terminated. Each pair in the
+         * list is the contact URI and its terminated reason.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework.
+         * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+         * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
+         * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+         * rare cases when the Telephony stack has crashed.
+         */
+        void onResourceTerminated(
+                @NonNull List<Pair<Uri, String>> uriTerminatedReason) throws ImsException;
+
+        /**
+         * The subscription associated with a previous
+         * {@link RcsUceAdapter#requestCapabilities(List, Executor,
+         * RcsUceAdapter.CapabilitiesCallback)}
+         * operation has been terminated. This will mostly be due to the network sending a final
+         * NOTIFY response due to the subscription expiring, but this may also happen due to a
+         * network error.
+         *
+         * @param reason The reason for the request being unable to process.
+         * @param retryAfterMilliseconds The time in milliseconds the requesting application should
+         * wait before retrying, if non-zero.
+         * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+         * not currently connected to the framework.
+         * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+         * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
+         * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+         * rare cases when the Telephony stack has crashed.
+         */
+        void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException;
+    }
+
+    /**
+     * Create a new RcsCapabilityExchangeImplBase instance.
+     */
+    public RcsCapabilityExchangeImplBase() {
+    }
+
+    /**
+     * The user capabilities of one or multiple contacts have been requested by the framework.
+     * <p>
+     * The implementer must follow up this call with an
+     * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed.
+     * The response from the network to the SUBSCRIBE request must be sent back to the framework
+     * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}.
+     * As NOTIFY requests come in from the network, the requested contact’s capabilities should be
+     * sent back to the framework using
+     * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and
+     * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)}
+     * should be called with the presence information for the contacts specified.
+     * <p>
+     * Once the subscription is terminated,
+     * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the
+     * framework to finish listening for NOTIFY responses.
+     *
+     * @param uris A {@link Collection} of the {@link Uri}s that the framework is requesting the
+     * UCE capabilities for.
+     * @param cb The callback of the subscribe request.
+     */
+    // executor used is defined in the constructor.
+    @SuppressLint("ExecutorRegistration")
+    public void subscribeForCapabilities(@NonNull Collection<Uri> uris,
+            @NonNull SubscribeResponseCallback cb) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "subscribeForCapabilities called with no implementation.");
+        try {
+            cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED);
+        } catch (ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
+    }
+
+    /**
+     * The capabilities of this device have been updated and should be published to the network.
+     * <p>
+     * If this operation succeeds, network response updates should be sent to the framework using
+     * {@link PublishResponseCallback#onNetworkResponse(int, String)}.
+     * @param pidfXml The XML PIDF document containing the capabilities of this device to be sent
+     * to the carrier’s presence server.
+     * @param cb The callback of the publish request
+     */
+    // executor used is defined in the constructor.
+    @SuppressLint("ExecutorRegistration")
+    public void publishCapabilities(@NonNull String pidfXml, @NonNull PublishResponseCallback cb) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "publishCapabilities called with no implementation.");
+        try {
+            cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED);
+        } catch (ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
+    }
+
+    /**
+     * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism
+     * in order to receive the capabilities of the remote user in response.
+     * <p>
+     * The implementer must use {@link OptionsResponseCallback} to send the response of
+     * this query from the network back to the framework.
+     * @param contactUri The URI of the remote user that we wish to get the capabilities of.
+     * @param myCapabilities The capabilities of this device to send to the remote user.
+     * @param callback The callback of this request which is sent from the remote user.
+     */
+    // executor used is defined in the constructor.
+    @SuppressLint("ExecutorRegistration")
+    public void sendOptionsCapabilityRequest(@NonNull Uri contactUri,
+            @NonNull Set<String> myCapabilities, @NonNull OptionsResponseCallback callback) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "sendOptionsCapabilityRequest called with no implementation.");
+        try {
+            callback.onCommandError(COMMAND_CODE_NOT_SUPPORTED);
+        } catch (ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
+    }
+}
diff --git a/android-35/android/telephony/ims/stub/SipDelegate.java b/android-35/android/telephony/ims/stub/SipDelegate.java
new file mode 100644
index 0000000..7dbefb4
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/SipDelegate.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.SipDelegateConfiguration;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+
+/**
+ * The {@link SipDelegate} is implemented by the {@link ImsService} and allows a privileged
+ * IMS application to use this delegate to send SIP messages as well as acknowledge the receipt of
+ * incoming SIP messages delivered to the application over the existing IMS registration, allowing
+ * for a single IMS registration for multiple IMS applications.
+ * <p>
+ * Once the SIP delegate is created for that application,
+ * {@link ImsRegistrationImplBase#updateSipDelegateRegistration()} will be called, indicating that
+ * the application is finished setting up SipDelegates and the existing IMS registration may be
+ * modified to include the features managed by these SipDelegates.
+ * <p>
+ * This SipDelegate will need to notify the remote application of the registration of these features
+ * as well as the associated {@link SipDelegateConfiguration} before the application can start
+ * sending/receiving SIP messages via the transport. See
+ * {@link android.telephony.ims.DelegateStateCallback} for more information.
+ * @hide
+ */
+@SystemApi
+public interface SipDelegate {
+
+    /**
+     * The framework calls this method when a remote RCS application wishes to send a new outgoing
+     * SIP message.
+     * <p>
+     * Once sent, this SIP delegate should notify the remote application of the success or
+     * failure using {@link DelegateMessageCallback#onMessageSent(String)} or
+     * {@link DelegateMessageCallback#onMessageSendFailure(String, int)}.
+     * @param message The SIP message to be sent over the operator’s network.
+     * @param configVersion The SipDelegateImsConfiguration version used to construct the
+     *         SipMessage. See {@link SipDelegateConfiguration} for more information. If the
+     *         version specified here does not match the most recently constructed
+     *         {@link SipDelegateConfiguration}, this message should fail validation checks and
+     *         {@link DelegateMessageCallback#onMessageSendFailure} should be called with code
+     *         {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION}.
+     */
+    void sendMessage(@NonNull SipMessage message, long configVersion);
+
+    /**
+     * The remote IMS application has closed a SIP session and the routing resources associated
+     * with the SIP session using the provided Call-ID may now be cleaned up.
+     * <p>
+     * Typically, a SIP session will be considered closed when all associated dialogs receive a
+     * BYE request. After the session has been closed, the IMS application will call
+     * {@link SipDelegateConnection#cleanupSession(String)} to signal to the framework that
+     * resources can be released. In some cases, the framework will request that the ImsService
+     * close the session due to the open SIP session holding up an event such as applying a
+     * provisioning change or handing over to another transport type. See
+     * {@link DelegateRegistrationState}.
+     *
+     * @param callId The call-ID header value associated with the ongoing SIP Session that the
+     *         framework is requesting be cleaned up.
+     */
+    void cleanupSession(@NonNull String callId);
+
+    /**
+     * The remote application has received the SIP message and is processing it.
+     * @param viaTransactionId The Transaction ID found in the via header field of the
+     *                         previously sent {@link SipMessage}.
+     */
+    void notifyMessageReceived(@NonNull String viaTransactionId);
+
+    /**
+     * The remote application has either not received the SIP message or there was an error
+     * processing it.
+     * @param viaTransactionId The Transaction ID found in the via header field of the
+     *                         previously sent {@link SipMessage}.
+     * @param reason The reason why the message was not correctly received.
+     */
+    void notifyMessageReceiveError(@NonNull String viaTransactionId,
+            @SipDelegateManager.MessageFailureReason int reason);
+}
diff --git a/android-35/android/telephony/ims/stub/SipTransportImplBase.java b/android-35/android/telephony/ims/stub/SipTransportImplBase.java
new file mode 100644
index 0000000..52538cb
--- /dev/null
+++ b/android-35/android/telephony/ims/stub/SipTransportImplBase.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.DelegateStateCallback;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.aidl.ISipDelegateStateCallback;
+import android.telephony.ims.aidl.ISipTransport;
+import android.telephony.ims.aidl.SipDelegateAidlWrapper;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * The ImsService implements this class to manage the creation and destruction of
+ * {@link SipDelegate}s.
+ *
+ * {@link SipDelegate}s allow the ImsService to forward SIP traffic generated and consumed by IMS
+ * applications as a delegate to the associated carrier's IMS Network in order to support using a
+ * single IMS registration for all MMTEL and RCS signalling traffic.
+ * @hide
+ */
+@SystemApi
+public class SipTransportImplBase {
+    private static final String LOG_TAG = "SipTransportIB";
+
+    private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            // Clean up all binders in this case.
+            mBinderExecutor.execute(() -> binderDiedInternal(null));
+        }
+        @Override
+        public void binderDied(IBinder who) {
+            mBinderExecutor.execute(() -> binderDiedInternal(who));
+        }
+    };
+
+    private final ISipTransport.Stub mSipTransportImpl = new ISipTransport.Stub() {
+        @Override
+        public void createSipDelegate(int subId, DelegateRequest request,
+                ISipDelegateStateCallback dc, ISipDelegateMessageCallback mc) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mBinderExecutor.execute(() -> createSipDelegateInternal(subId, request, dc, mc));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void destroySipDelegate(ISipDelegate delegate, int reason) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mBinderExecutor.execute(() -> destroySipDelegateInternal(delegate, reason));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    };
+
+    private Executor mBinderExecutor;
+    private final ArrayList<SipDelegateAidlWrapper> mDelegates = new ArrayList<>();
+
+    /**
+     * Create a new SipTransport.
+     * <p>
+     * Method stubs called from the framework will be called asynchronously. To specify the
+     * {@link Executor} that the methods stubs will be called, use
+     * {@link SipTransportImplBase#SipTransportImplBase(Executor)} instead.
+     */
+    public SipTransportImplBase() {
+        super();
+    }
+
+    /**
+     * Create an implementation of SipTransportImplBase.
+     *
+     * @param executor The executor that remote calls from the framework will be called on. This
+     *                 includes the methods here as well as the methods in {@link SipDelegate}.
+     */
+    public SipTransportImplBase(@NonNull Executor executor) {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        }
+
+        mBinderExecutor = executor;
+    }
+
+    /**
+     * Called by the Telephony framework to request the creation of a new {@link SipDelegate}.
+     * <p>
+     * The implementation must call
+     * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} with
+     * the {@link SipDelegate} that is associated with the {@link DelegateRequest}.
+     * <p>
+     * This method will be called on the Executor specified in
+     * {@link SipTransportImplBase#SipTransportImplBase(Executor)}.
+     *
+     * @param subscriptionId The subscription ID associated with the requested {@link SipDelegate}.
+     * @param request A SIP delegate request containing the parameters that the remote RCS
+     * application wishes to use.
+     * @param dc A callback back to the remote application to be used to communicate state callbacks
+     *           for the SipDelegate.
+     * @param mc A callback back to the remote application to be used to send SIP messages to the
+     *           remote application and acknowledge the sending of outgoing SIP messages.
+     */
+    // executor used is defined in the constructor.
+    @SuppressLint("ExecutorRegistration")
+    public void createSipDelegate(int subscriptionId, @NonNull DelegateRequest request,
+            @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) {
+        throw new UnsupportedOperationException("createSipDelegate not implemented!");
+    }
+
+    /**
+     * Destroys the SipDelegate associated with a remote IMS application.
+     * <p>
+     * After the delegate is destroyed, {@link DelegateStateCallback#onDestroyed(int)} must be
+     * called to notify listeners of its destruction to release associated resources.
+     * <p>
+     * This method will be called on the Executor specified in
+     * {@link SipTransportImplBase#SipTransportImplBase(Executor)}.
+     * @param delegate The delegate to be destroyed.
+     * @param reason The reason the remote connection to this {@link SipDelegate} is being
+     *         destroyed.
+     */
+    public void destroySipDelegate(@NonNull SipDelegate delegate,
+            @SipDelegateManager.SipDelegateDestroyReason int reason) {
+        throw new UnsupportedOperationException("destroySipDelegate not implemented!");
+    }
+
+    private void createSipDelegateInternal(int subId, DelegateRequest r,
+            ISipDelegateStateCallback cb, ISipDelegateMessageCallback mc) {
+        SipDelegateAidlWrapper wrapper = new SipDelegateAidlWrapper(mBinderExecutor, cb, mc);
+        mDelegates.add(wrapper);
+        linkDeathRecipient(wrapper);
+        createSipDelegate(subId, r, wrapper, wrapper);
+    }
+
+    private void destroySipDelegateInternal(ISipDelegate d, int reason) {
+        SipDelegateAidlWrapper result = null;
+        for (SipDelegateAidlWrapper w : mDelegates) {
+            if (Objects.equals(d, w.getDelegateBinder())) {
+                result = w;
+                break;
+            }
+        }
+
+        if (result != null) {
+            unlinkDeathRecipient(result);
+            mDelegates.remove(result);
+            destroySipDelegate(result.getDelegate(), reason);
+        } else {
+            Log.w(LOG_TAG, "destroySipDelegateInternal, could not findSipDelegate corresponding to "
+                    + d);
+        }
+    }
+
+    private void linkDeathRecipient(SipDelegateAidlWrapper w) {
+        try {
+            w.getStateCallbackBinder().asBinder().linkToDeath(mDeathRecipient, 0);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "linkDeathRecipient, remote process already died, cleaning up.");
+            mDeathRecipient.binderDied(w.getStateCallbackBinder().asBinder());
+        }
+    }
+
+    private void unlinkDeathRecipient(SipDelegateAidlWrapper w) {
+        try {
+            w.getStateCallbackBinder().asBinder().unlinkToDeath(mDeathRecipient, 0);
+        } catch (NoSuchElementException e) {
+            // Ignore this case.
+        }
+    }
+
+    private void binderDiedInternal(IBinder who) {
+        for (SipDelegateAidlWrapper w : mDelegates) {
+            // If the binder itself was not given from the platform, just clean up all binders.
+            if (who == null || w.getStateCallbackBinder().asBinder().equals(who))  {
+                Log.w(LOG_TAG, "Binder death detected for " + w + ", calling destroy and "
+                        + "removing.");
+                mDelegates.remove(w);
+                destroySipDelegate(w.getDelegate(),
+                        SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+                return;
+            }
+        }
+        Log.w(LOG_TAG, "Binder death detected for IBinder " + who + ", but couldn't find matching "
+                + "SipDelegate");
+    }
+
+    /**
+     * @return The IInterface used by the framework.
+     * @hide
+     */
+    public ISipTransport getBinder() {
+        return mSipTransportImpl;
+    }
+
+    /**
+     * Set default Executor from ImsService.
+     * @param executor The default executor for the framework to use when executing the methods
+     * overridden by the implementation of SipTransport.
+     * @hide
+     */
+    public final void setDefaultExecutor(@NonNull Executor executor) {
+        if (mBinderExecutor == null) {
+            mBinderExecutor = executor;
+        }
+    }
+}
diff --git a/android-35/android/telephony/mbms/DownloadProgressListener.java b/android-35/android/telephony/mbms/DownloadProgressListener.java
new file mode 100644
index 0000000..4301cb1
--- /dev/null
+++ b/android-35/android/telephony/mbms/DownloadProgressListener.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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.mbms;
+
+import android.telephony.MbmsDownloadSession;
+
+/**
+ * A optional listener class used by download clients to track progress. Apps should extend this
+ * class and pass an instance into
+ * {@link MbmsDownloadSession#download(DownloadRequest)}
+ *
+ * This is optionally specified when requesting a download and will only be called while the app
+ * is running.
+ */
+public class DownloadProgressListener {
+    /**
+     * Called when the middleware wants to report progress for a file in a {@link DownloadRequest}.
+     *
+     * @param request a {@link DownloadRequest}, indicating which download is being referenced.
+     * @param fileInfo a {@link FileInfo} specifying the file to report progress on.  Note that
+     *   the request may result in many files being downloaded and the client
+     *   may not have been able to get a list of them in advance.
+     * @param currentDownloadSize is the current amount downloaded.
+     * @param fullDownloadSize is the total number of bytes that make up the downloaded content.
+     *   This may be different from the decoded final size, but is useful in gauging download
+     *   progress.
+     * @param currentDecodedSize is the number of bytes that have been decoded.
+     * @param fullDecodedSize is the total number of bytes that make up the final decoded content.
+     */
+    public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
+            int currentDownloadSize, int fullDownloadSize,
+            int currentDecodedSize, int fullDecodedSize) {
+    }
+}
diff --git a/android-35/android/telephony/mbms/DownloadRequest.java b/android-35/android/telephony/mbms/DownloadRequest.java
new file mode 100644
index 0000000..81d5be8
--- /dev/null
+++ b/android-35/android/telephony/mbms/DownloadRequest.java
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2017 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.mbms;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Base64;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Externalizable;
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
+
+/**
+ * Describes a request to download files over cell-broadcast. Instances of this class should be
+ * created by the app when requesting a download, and instances of this class will be passed back
+ * to the app when the middleware updates the status of the download.
+ */
+public final class DownloadRequest implements Parcelable {
+    // Version code used to keep token calculation consistent.
+    private static final int CURRENT_VERSION = 1;
+    private static final String LOG_TAG = "MbmsDownloadRequest";
+
+    /** @hide */
+    public static final int MAX_APP_INTENT_SIZE = 50000;
+
+    /** @hide */
+    public static final int MAX_DESTINATION_URI_SIZE = 50000;
+
+    /** @hide */
+    private static class SerializationDataContainer implements Externalizable {
+        private String fileServiceId;
+        private Uri source;
+        private Uri destination;
+        private int subscriptionId;
+        private String appIntent;
+        private int version;
+
+        public SerializationDataContainer() {}
+
+        SerializationDataContainer(DownloadRequest request) {
+            fileServiceId = request.fileServiceId;
+            source = request.sourceUri;
+            destination = request.destinationUri;
+            subscriptionId = request.subscriptionId;
+            appIntent = request.serializedResultIntentForApp;
+            version = request.version;
+        }
+
+        @Override
+        public void writeExternal(ObjectOutput objectOutput) throws IOException {
+            objectOutput.write(version);
+            objectOutput.writeUTF(fileServiceId);
+            objectOutput.writeUTF(source.toString());
+            objectOutput.writeUTF(destination.toString());
+            objectOutput.write(subscriptionId);
+            objectOutput.writeUTF(appIntent);
+        }
+
+        @Override
+        public void readExternal(ObjectInput objectInput) throws IOException {
+            version = objectInput.read();
+            fileServiceId = objectInput.readUTF();
+            source = Uri.parse(objectInput.readUTF());
+            destination = Uri.parse(objectInput.readUTF());
+            subscriptionId = objectInput.read();
+            appIntent = objectInput.readUTF();
+            // Do version checks here -- future versions may have other fields.
+        }
+    }
+
+    public static class Builder {
+        private String fileServiceId;
+        private Uri source;
+        private Uri destination;
+        private int subscriptionId;
+        private String appIntent;
+        private int version = CURRENT_VERSION;
+
+        /**
+         * Constructs a {@link Builder} from a {@link DownloadRequest}
+         * @param other The {@link DownloadRequest} from which the data for the {@link Builder}
+         *              should come.
+         * @return An instance of {@link Builder} pre-populated with data from the provided
+         *         {@link DownloadRequest}.
+         */
+        public static Builder fromDownloadRequest(DownloadRequest other) {
+            Builder result = new Builder(other.sourceUri, other.destinationUri)
+                    .setServiceId(other.fileServiceId)
+                    .setSubscriptionId(other.subscriptionId);
+            result.appIntent = other.serializedResultIntentForApp;
+            // Version of the result is going to be the current version -- as this class gets
+            // updated, new fields will be set to default values in here.
+            return result;
+        }
+
+        /**
+         * This method constructs a new instance of {@link Builder} based on the serialized data
+         * passed in.
+         * @param data A byte array, the contents of which should have been originally obtained
+         *             from {@link DownloadRequest#toByteArray()}.
+         */
+        public static Builder fromSerializedRequest(byte[] data) {
+            Builder builder;
+            try {
+                ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
+                SerializationDataContainer dataContainer =
+                        (SerializationDataContainer) stream.readObject();
+                builder = new Builder(dataContainer.source, dataContainer.destination);
+                builder.version = dataContainer.version;
+                builder.appIntent = dataContainer.appIntent;
+                builder.fileServiceId = dataContainer.fileServiceId;
+                builder.subscriptionId = dataContainer.subscriptionId;
+            } catch (IOException e) {
+                // Really should never happen
+                Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
+                throw new IllegalArgumentException(e);
+            } catch (ClassNotFoundException e) {
+                Log.e(LOG_TAG, "Got ClassNotFoundException trying to parse opaque data");
+                throw new IllegalArgumentException(e);
+            }
+            return builder;
+        }
+
+        /**
+         * Builds a new DownloadRequest.
+         * @param sourceUri the source URI for the DownloadRequest to be built. This URI should
+         *     never be null.
+         * @param destinationUri The final location for the file(s) that are to be downloaded. It
+         *     must be on the same filesystem as the temp file directory set via
+         *     {@link android.telephony.MbmsDownloadSession#setTempFileRootDirectory(File)}.
+         *     The provided path must be a directory that exists. An
+         *     {@link IllegalArgumentException} will be thrown otherwise.
+         */
+        public Builder(@NonNull Uri sourceUri, @NonNull Uri destinationUri) {
+            if (sourceUri == null || destinationUri == null) {
+                throw new IllegalArgumentException("Source and destination URIs must be non-null.");
+            }
+            source = sourceUri;
+            destination = destinationUri;
+        }
+
+        /**
+         * Sets the service from which the download request to be built will download from.
+         * @param serviceInfo
+         * @return
+         */
+        public Builder setServiceInfo(FileServiceInfo serviceInfo) {
+            fileServiceId = serviceInfo.getServiceId();
+            return this;
+        }
+
+        /**
+         * Set the service ID for the download request. For use by the middleware only.
+         * @hide
+         */
+        @SystemApi
+        public Builder setServiceId(String serviceId) {
+            fileServiceId = serviceId;
+            return this;
+        }
+
+        /**
+         * Set the subscription ID on which the file(s) should be downloaded.
+         * @param subscriptionId
+         */
+        public Builder setSubscriptionId(int subscriptionId) {
+            this.subscriptionId = subscriptionId;
+            return this;
+        }
+
+        /**
+         * Set the {@link Intent} that should be sent when the download completes or fails. This
+         * should be an intent with a explicit {@link android.content.ComponentName} targeted to a
+         * {@link android.content.BroadcastReceiver} in the app's package.
+         *
+         * The middleware should not use this method.
+         * @param intent
+         */
+        public Builder setAppIntent(Intent intent) {
+            this.appIntent = intent.toUri(0);
+            if (this.appIntent.length() > MAX_APP_INTENT_SIZE) {
+                throw new IllegalArgumentException("App intent must not exceed length " +
+                        MAX_APP_INTENT_SIZE);
+            }
+            return this;
+        }
+
+        public DownloadRequest build() {
+            return new DownloadRequest(fileServiceId, source, destination,
+                    subscriptionId, appIntent, version);
+        }
+    }
+
+    private final String fileServiceId;
+    private final Uri sourceUri;
+    private final Uri destinationUri;
+    private final int subscriptionId;
+    private final String serializedResultIntentForApp;
+    private final int version;
+
+    private DownloadRequest(String fileServiceId,
+            Uri source, Uri destination, int sub,
+            String appIntent, int version) {
+        this.fileServiceId = fileServiceId;
+        sourceUri = source;
+        subscriptionId = sub;
+        destinationUri = destination;
+        serializedResultIntentForApp = appIntent;
+        this.version = version;
+    }
+
+    private DownloadRequest(Parcel in) {
+        fileServiceId = in.readString();
+        sourceUri = in.readParcelable(getClass().getClassLoader(), android.net.Uri.class);
+        destinationUri = in.readParcelable(getClass().getClassLoader(), android.net.Uri.class);
+        subscriptionId = in.readInt();
+        serializedResultIntentForApp = in.readString();
+        version = in.readInt();
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(fileServiceId);
+        out.writeParcelable(sourceUri, flags);
+        out.writeParcelable(destinationUri, flags);
+        out.writeInt(subscriptionId);
+        out.writeString(serializedResultIntentForApp);
+        out.writeInt(version);
+    }
+
+    /**
+     * @return The ID of the file service to download from.
+     */
+    public String getFileServiceId() {
+        return fileServiceId;
+    }
+
+    /**
+     * @return The source URI to download from
+     */
+    public Uri getSourceUri() {
+        return sourceUri;
+    }
+
+    /**
+     * @return The destination {@link Uri} of the downloaded file.
+     */
+    public Uri getDestinationUri() {
+        return destinationUri;
+    }
+
+    /**
+     * @return The subscription ID on which to perform MBMS operations.
+     */
+    public int getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    /**
+     * For internal use -- returns the intent to send to the app after download completion or
+     * failure.
+     * @hide
+     */
+    public Intent getIntentForApp() {
+        try {
+            return Intent.parseUri(serializedResultIntentForApp, 0);
+        } catch (URISyntaxException e) {
+            return null;
+        }
+    }
+
+    /**
+     * This method returns a byte array that may be persisted to disk and restored to a
+     * {@link DownloadRequest}. The instance of {@link DownloadRequest} persisted by this method
+     * may be recovered via {@link Builder#fromSerializedRequest(byte[])}.
+     * @return A byte array of data to persist.
+     */
+    public byte[] toByteArray() {
+        try {
+            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+            ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
+            SerializationDataContainer container = new SerializationDataContainer(this);
+            stream.writeObject(container);
+            stream.flush();
+            return byteArrayOutputStream.toByteArray();
+        } catch (IOException e) {
+            // Really should never happen
+            Log.e(LOG_TAG, "Got IOException trying to serialize opaque data");
+            return null;
+        }
+    }
+
+    /** @hide */
+    public int getVersion() {
+        return version;
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<DownloadRequest> CREATOR =
+            new Parcelable.Creator<DownloadRequest>() {
+        public DownloadRequest createFromParcel(Parcel in) {
+            return new DownloadRequest(in);
+        }
+        public DownloadRequest[] newArray(int size) {
+            return new DownloadRequest[size];
+        }
+    };
+
+    /**
+     * Maximum permissible length for the app's destination path, when serialized via
+     * {@link Uri#toString()}.
+     */
+    public static int getMaxAppIntentSize() {
+        return MAX_APP_INTENT_SIZE;
+    }
+
+    /**
+     * Maximum permissible length for the app's download-completion intent, when serialized via
+     * {@link Intent#toUri(int)}.
+     */
+    public static int getMaxDestinationUriSize() {
+        return MAX_DESTINATION_URI_SIZE;
+    }
+
+    /**
+     * Retrieves the hash string that should be used as the filename when storing a token for
+     * this DownloadRequest.
+     * @hide
+     */
+    public String getHash() {
+        MessageDigest digest;
+        try {
+            digest = MessageDigest.getInstance("SHA-256");
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("Could not get sha256 hash object");
+        }
+        if (version >= 1) {
+            // Hash the source, destination, and the app intent
+            digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
+            digest.update(destinationUri.toString().getBytes(StandardCharsets.UTF_8));
+            if (serializedResultIntentForApp != null) {
+                digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
+            }
+        }
+        // Add updates for future versions here
+        return Base64.encodeToString(digest.digest(), Base64.URL_SAFE | Base64.NO_WRAP);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null) {
+            return false;
+        }
+        if (!(o instanceof DownloadRequest)) {
+            return false;
+        }
+        DownloadRequest request = (DownloadRequest) o;
+        return subscriptionId == request.subscriptionId &&
+                version == request.version &&
+                Objects.equals(fileServiceId, request.fileServiceId) &&
+                Objects.equals(sourceUri, request.sourceUri) &&
+                Objects.equals(destinationUri, request.destinationUri) &&
+                Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(fileServiceId, sourceUri, destinationUri,
+                subscriptionId, serializedResultIntentForApp, version);
+    }
+}
diff --git a/android-35/android/telephony/mbms/DownloadStatusListener.java b/android-35/android/telephony/mbms/DownloadStatusListener.java
new file mode 100644
index 0000000..ca77932
--- /dev/null
+++ b/android-35/android/telephony/mbms/DownloadStatusListener.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 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.mbms;
+
+import android.annotation.IntDef;
+import android.telephony.MbmsDownloadSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A optional listener class used by download clients to track progress. Apps should extend this
+ * class and pass an instance into
+ * {@link MbmsDownloadSession#download(DownloadRequest)}
+ *
+ * This is optionally specified when requesting a download and will only be called while the app
+ * is running.
+ */
+public class DownloadStatusListener {
+    /**
+     * Gives download status callbacks for a file in a {@link DownloadRequest}.
+     *
+     * @param request a {@link DownloadRequest}, indicating which download is being referenced.
+     * @param fileInfo a {@link FileInfo} specifying the file to report progress on.  Note that
+     *   the request may result in many files being downloaded and the client
+     *   may not have been able to get a list of them in advance.
+     * @param status The current status of the download.
+     */
+    public void onStatusUpdated(DownloadRequest request, FileInfo fileInfo,
+            @MbmsDownloadSession.DownloadStatus int status) {
+    }
+}
diff --git a/android-35/android/telephony/mbms/FileInfo.java b/android-35/android/telephony/mbms/FileInfo.java
new file mode 100644
index 0000000..ffd864e
--- /dev/null
+++ b/android-35/android/telephony/mbms/FileInfo.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 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.mbms;
+
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Describes a single file that is available over MBMS.
+ */
+public final class FileInfo implements Parcelable {
+
+    private final Uri uri;
+
+    private final String mimeType;
+
+    public static final @android.annotation.NonNull Parcelable.Creator<FileInfo> CREATOR =
+            new Parcelable.Creator<FileInfo>() {
+        @Override
+        public FileInfo createFromParcel(Parcel source) {
+            return new FileInfo(source);
+        }
+
+        @Override
+        public FileInfo[] newArray(int size) {
+            return new FileInfo[size];
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @SystemApi
+    public FileInfo(Uri uri, String mimeType) {
+        this.uri = uri;
+        this.mimeType = mimeType;
+    }
+
+    private FileInfo(Parcel in) {
+        uri = in.readParcelable(null, android.net.Uri.class);
+        mimeType = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(uri, flags);
+        dest.writeString(mimeType);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * @return The URI in the carrier's infrastructure which points to this file. Apps should
+     * negotiate the contents of this URI separately with the carrier.
+     */
+    public Uri getUri() {
+        return uri;
+    }
+
+    /**
+     * @return The MIME type of the file.
+     */
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        FileInfo fileInfo = (FileInfo) o;
+        return Objects.equals(uri, fileInfo.uri) &&
+                Objects.equals(mimeType, fileInfo.mimeType);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(uri, mimeType);
+    }
+}
diff --git a/android-35/android/telephony/mbms/FileServiceInfo.java b/android-35/android/telephony/mbms/FileServiceInfo.java
new file mode 100644
index 0000000..0fc3be6
--- /dev/null
+++ b/android-35/android/telephony/mbms/FileServiceInfo.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 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.mbms;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Describes a file service available from the carrier from which files can be downloaded via
+ * cell-broadcast.
+ */
+public final class FileServiceInfo extends ServiceInfo implements Parcelable {
+    private final List<FileInfo> files;
+
+    /** @hide */
+    @SystemApi
+    public FileServiceInfo(Map<Locale, String> newNames, String newClassName,
+            List<Locale> newLocales, String newServiceId, Date start, Date end,
+            List<FileInfo> newFiles) {
+        super(newNames, newClassName, newLocales, newServiceId, start, end);
+        files = new ArrayList<>(newFiles);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<FileServiceInfo> CREATOR =
+            new Parcelable.Creator<FileServiceInfo>() {
+        @Override
+        public FileServiceInfo createFromParcel(Parcel source) {
+            return new FileServiceInfo(source);
+        }
+
+        @Override
+        public FileServiceInfo[] newArray(int size) {
+            return new FileServiceInfo[size];
+        }
+    };
+
+    FileServiceInfo(Parcel in) {
+        super(in);
+        files = new ArrayList<FileInfo>();
+        in.readList(files, FileInfo.class.getClassLoader(), android.telephony.mbms.FileInfo.class);
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeList(files);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * @return A list of files available from this service. Note that this list may not be
+     * exhaustive -- the middleware may not have information on all files that are available.
+     * Consult the carrier for an authoritative and exhaustive list.
+     */
+    public List<FileInfo> getFiles() {
+        return files;
+    }
+}
diff --git a/android-35/android/telephony/mbms/GroupCall.java b/android-35/android/telephony/mbms/GroupCall.java
new file mode 100644
index 0000000..25e274e
--- /dev/null
+++ b/android-35/android/telephony/mbms/GroupCall.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2018 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.mbms;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.telephony.MbmsGroupCallSession;
+import android.telephony.mbms.vendor.IMbmsGroupCallService;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Class used to represent a single MBMS group call. After a call has been started with
+ * {@link MbmsGroupCallSession#startGroupCall},
+ * this class is used to hold information about the call and control it.
+ */
+public class GroupCall implements AutoCloseable {
+    private static final String LOG_TAG = "MbmsGroupCall";
+
+    /**
+     * The state of a group call, reported via
+     * {@link GroupCallCallback#onGroupCallStateChanged(int, int)}
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
+    public @interface GroupCallState {}
+
+    /**
+     * Indicates that the group call is in a stopped state
+     *
+     * This can be reported after network action or after calling {@link #close}.
+     */
+    public static final int STATE_STOPPED = 1;
+
+    /**
+     * Indicates that the group call is started.
+     *
+     * Data can be transmitted and received in this state.
+     */
+    public static final int STATE_STARTED = 2;
+
+    /**
+     * Indicates that the group call is stalled.
+     *
+     * This may be due to a network issue or the device being temporarily out of range.
+     */
+    public static final int STATE_STALLED = 3;
+
+    /**
+     * The reason for a call state change, reported via
+     * {@link GroupCallCallback#onGroupCallStateChanged(int, int)}
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "REASON_" },
+            value = {REASON_BY_USER_REQUEST, REASON_FREQUENCY_CONFLICT,
+                    REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE,
+                    REASON_LEFT_MBMS_BROADCAST_AREA, REASON_NONE})
+    public @interface GroupCallStateChangeReason {}
+
+    /**
+     * Indicates that the middleware does not have a reason to provide for the state change.
+     */
+    public static final int REASON_NONE = 0;
+
+    /**
+     * State changed due to a call to {@link #close()} or
+     * {@link MbmsGroupCallSession#startGroupCall}
+     */
+    public static final int REASON_BY_USER_REQUEST = 1;
+
+    // 2 is unused to match up with streaming.
+
+    /**
+     * State changed due to a frequency conflict with another requested call.
+     */
+    public static final int REASON_FREQUENCY_CONFLICT = 3;
+
+    /**
+     * State changed due to the middleware running out of memory
+     */
+    public static final int REASON_OUT_OF_MEMORY = 4;
+
+    /**
+     * State changed due to the device leaving the home carrier's LTE network.
+     */
+    public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5;
+
+    /**
+     * State changed due to the device leaving the area where this call is being broadcast.
+     */
+    public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6;
+
+    private final int mSubscriptionId;
+    private final long mTmgi;
+    private final MbmsGroupCallSession mParentSession;
+    private final InternalGroupCallCallback mCallback;
+    private IMbmsGroupCallService mService;
+
+    /**
+     * @hide
+     */
+    public GroupCall(int subscriptionId,
+            IMbmsGroupCallService service,
+            MbmsGroupCallSession session,
+            long tmgi,
+            InternalGroupCallCallback callback) {
+        mSubscriptionId = subscriptionId;
+        mParentSession = session;
+        mService = service;
+        mTmgi = tmgi;
+        mCallback = callback;
+    }
+
+    /**
+     * Retrieve the TMGI (Temporary Mobile Group Identity) corresponding to this call.
+     */
+    public long getTmgi() {
+        return mTmgi;
+    }
+
+    /**
+     * Send an update to the middleware when the SAI (Service Area Identifier) list and frequency
+     * information of the group call has * changed. Callers must obtain this information from the
+     * wireless carrier independently.
+     * @param saiList New list of SAIs that the call is available on.
+     * @param frequencyList New list of frequencies that the call is available on.
+     */
+    public void updateGroupCall(@NonNull List<Integer> saiList,
+            @NonNull List<Integer> frequencyList) {
+        if (mService == null) {
+            throw new IllegalStateException("No group call service attached");
+        }
+
+        try {
+            mService.updateGroupCall(mSubscriptionId, mTmgi, saiList, frequencyList);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService = null;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        } finally {
+            mParentSession.onGroupCallStopped(this);
+        }
+    }
+
+    /**
+     * Stop this group call. Further operations on this object will fail with an
+     * {@link IllegalStateException}.
+     *
+     * May throw an {@link IllegalStateException}
+     */
+    @Override
+    public void close() {
+        if (mService == null) {
+            throw new IllegalStateException("No group call service attached");
+        }
+
+        try {
+            mService.stopGroupCall(mSubscriptionId, mTmgi);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService = null;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        } finally {
+            mParentSession.onGroupCallStopped(this);
+        }
+    }
+
+    /** @hide */
+    public InternalGroupCallCallback getCallback() {
+        return mCallback;
+    }
+
+    private void sendErrorToApp(int errorCode, String message) {
+        mCallback.onError(errorCode, message);
+    }
+}
+
diff --git a/android-35/android/telephony/mbms/GroupCallCallback.java b/android-35/android/telephony/mbms/GroupCallCallback.java
new file mode 100644
index 0000000..603f4e6
--- /dev/null
+++ b/android-35/android/telephony/mbms/GroupCallCallback.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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.mbms;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A callback class for use when the application is in a group call. The middleware
+ * will provide updates on the status of the call via this callback.
+ */
+public interface GroupCallCallback {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+            MbmsErrors.ERROR_MIDDLEWARE_LOST,
+            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            MbmsErrors.GeneralErrors.ERROR_IN_E911,
+            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
+    @interface GroupCallError{}
+
+    /**
+     * Indicates broadcast signal strength is not available for this call.
+     *
+     * This may be due to the call no longer being available due to geography
+     * or timing (end of service)
+     */
+    int SIGNAL_STRENGTH_UNAVAILABLE = -1;
+
+    /**
+     * Called by the middleware when it has detected an error condition in this group call. The
+     * possible error codes are listed in {@link MbmsErrors}.
+     * @param errorCode The error code.
+     * @param message A human-readable message generated by the middleware for debugging purposes.
+     */
+    default void onError(@GroupCallError int errorCode, @Nullable String message) {}
+
+    /**
+     * Called to indicate this call has changed state.
+     *
+     * See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED}
+     * and {@link GroupCall#STATE_STALLED}.
+     */
+    default void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
+            @GroupCall.GroupCallStateChangeReason int reason) {}
+
+    /**
+     * Broadcast Signal Strength updated.
+     *
+     * This signal strength is the BROADCAST signal strength which,
+     * depending on technology in play and it's deployment, may be
+     * stronger or weaker than the traditional UNICAST signal
+     * strength.  It a simple int from 0-4 for valid levels or
+     * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
+     * for this call due to timing, geography or popularity.
+     */
+    default void onBroadcastSignalStrengthUpdated(
+            @IntRange(from = -1, to = 4) int signalStrength) {}
+}
diff --git a/android-35/android/telephony/mbms/InternalDownloadProgressListener.java b/android-35/android/telephony/mbms/InternalDownloadProgressListener.java
new file mode 100644
index 0000000..a413ef8
--- /dev/null
+++ b/android-35/android/telephony/mbms/InternalDownloadProgressListener.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 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.mbms;
+
+import android.os.Binder;
+import android.os.RemoteException;
+
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class InternalDownloadProgressListener extends IDownloadProgressListener.Stub {
+    private final Executor mExecutor;
+    private final DownloadProgressListener mAppListener;
+    private volatile boolean mIsStopped = false;
+
+    public InternalDownloadProgressListener(DownloadProgressListener appListener,
+            Executor executor) {
+        mAppListener = appListener;
+        mExecutor = executor;
+    }
+
+    @Override
+    public void onProgressUpdated(final DownloadRequest request, final FileInfo fileInfo,
+            final int currentDownloadSize, final int fullDownloadSize, final int currentDecodedSize,
+            final int fullDecodedSize) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppListener.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                            fullDownloadSize, currentDecodedSize, fullDecodedSize);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/android-35/android/telephony/mbms/InternalDownloadSessionCallback.java b/android-35/android/telephony/mbms/InternalDownloadSessionCallback.java
new file mode 100644
index 0000000..67539a0
--- /dev/null
+++ b/android-35/android/telephony/mbms/InternalDownloadSessionCallback.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 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.mbms;
+
+import android.os.Binder;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/** @hide */
+public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallback.Stub {
+
+    private final Executor mExecutor;
+    private final MbmsDownloadSessionCallback mAppCallback;
+    private volatile boolean mIsStopped = false;
+
+    public InternalDownloadSessionCallback(MbmsDownloadSessionCallback appCallback,
+            Executor executor) {
+        mAppCallback = appCallback;
+        mExecutor = executor;
+    }
+
+    @Override
+    public void onError(final int errorCode, final String message) {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onError(errorCode, message);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void onFileServicesUpdated(final List<FileServiceInfo> services) {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onFileServicesUpdated(services);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void onMiddlewareReady() {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onMiddlewareReady();
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/android-35/android/telephony/mbms/InternalDownloadStatusListener.java b/android-35/android/telephony/mbms/InternalDownloadStatusListener.java
new file mode 100644
index 0000000..ce96a8f
--- /dev/null
+++ b/android-35/android/telephony/mbms/InternalDownloadStatusListener.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 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.mbms;
+
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.MbmsDownloadSession;
+
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class InternalDownloadStatusListener extends IDownloadStatusListener.Stub {
+    private final Executor mExecutor;
+    private final DownloadStatusListener mAppListener;
+    private volatile boolean mIsStopped = false;
+
+    public InternalDownloadStatusListener(DownloadStatusListener appCallback, Executor executor) {
+        mAppListener = appCallback;
+        mExecutor = executor;
+    }
+
+    @Override
+    public void onStatusUpdated(final DownloadRequest request, final FileInfo fileInfo,
+            @MbmsDownloadSession.DownloadStatus final int status) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppListener.onStatusUpdated(request, fileInfo, status);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/android-35/android/telephony/mbms/InternalGroupCallCallback.java b/android-35/android/telephony/mbms/InternalGroupCallCallback.java
new file mode 100644
index 0000000..5e1f1f1
--- /dev/null
+++ b/android-35/android/telephony/mbms/InternalGroupCallCallback.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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.mbms;
+
+import android.os.Binder;
+
+import java.util.concurrent.Executor;
+
+/** @hide */
+public class InternalGroupCallCallback extends IGroupCallCallback.Stub {
+    private final GroupCallCallback mAppCallback;
+    private final Executor mExecutor;
+    private volatile boolean mIsStopped = false;
+
+    public InternalGroupCallCallback(GroupCallCallback appCallback,
+            Executor executor) {
+        mAppCallback = appCallback;
+        mExecutor = executor;
+    }
+
+    @Override
+    public void onError(final int errorCode, final String message) {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onError(errorCode, message);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void onGroupCallStateChanged(final int state, final int reason) {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onGroupCallStateChanged(state, reason);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void onBroadcastSignalStrengthUpdated(final int signalStrength) {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /** Prevents this callback from calling the app */
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/android-35/android/telephony/mbms/InternalGroupCallSessionCallback.java b/android-35/android/telephony/mbms/InternalGroupCallSessionCallback.java
new file mode 100644
index 0000000..ca4190c
--- /dev/null
+++ b/android-35/android/telephony/mbms/InternalGroupCallSessionCallback.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 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.mbms;
+
+import android.os.Binder;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/** @hide */
+public class InternalGroupCallSessionCallback extends IMbmsGroupCallSessionCallback.Stub {
+    private final Executor mExecutor;
+    private final MbmsGroupCallSessionCallback mAppCallback;
+    private volatile boolean mIsStopped = false;
+
+    public InternalGroupCallSessionCallback(MbmsGroupCallSessionCallback appCallback,
+            Executor executor) {
+        mAppCallback = appCallback;
+        mExecutor = executor;
+    }
+
+    @Override
+    public void onError(final int errorCode, final String message) {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onError(errorCode, message);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void onAvailableSaisUpdated(final List currentSais, final List availableSais) {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onAvailableSaisUpdated(currentSais, availableSais);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void onServiceInterfaceAvailable(final String interfaceName, final int index) {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onServiceInterfaceAvailable(interfaceName, index);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void onMiddlewareReady() {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onMiddlewareReady();
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /** Prevents this callback from calling the app */
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/android-35/android/telephony/mbms/InternalStreamingServiceCallback.java b/android-35/android/telephony/mbms/InternalStreamingServiceCallback.java
new file mode 100644
index 0000000..d62add1
--- /dev/null
+++ b/android-35/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 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.mbms;
+
+import android.os.Binder;
+import android.os.RemoteException;
+
+import java.util.concurrent.Executor;
+
+/** @hide */
+public class InternalStreamingServiceCallback extends IStreamingServiceCallback.Stub {
+    private final StreamingServiceCallback mAppCallback;
+    private final Executor mExecutor;
+    private volatile boolean mIsStopped = false;
+
+    public InternalStreamingServiceCallback(StreamingServiceCallback appCallback,
+            Executor executor) {
+        mAppCallback = appCallback;
+        mExecutor = executor;
+    }
+
+    @Override
+    public void onError(final int errorCode, final String message) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onError(errorCode, message);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void onStreamStateUpdated(final int state, final int reason) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onStreamStateUpdated(state, reason);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void onMediaDescriptionUpdated() throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onMediaDescriptionUpdated();
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void onBroadcastSignalStrengthUpdated(final int signalStrength) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void onStreamMethodUpdated(final int methodType) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onStreamMethodUpdated(methodType);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/android-35/android/telephony/mbms/InternalStreamingSessionCallback.java b/android-35/android/telephony/mbms/InternalStreamingSessionCallback.java
new file mode 100644
index 0000000..f4ee4dc
--- /dev/null
+++ b/android-35/android/telephony/mbms/InternalStreamingSessionCallback.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 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.mbms;
+
+import android.os.Binder;
+import android.os.RemoteException;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/** @hide */
+public class InternalStreamingSessionCallback extends IMbmsStreamingSessionCallback.Stub {
+    private final Executor mExecutor;
+    private final MbmsStreamingSessionCallback mAppCallback;
+    private volatile boolean mIsStopped = false;
+
+    public InternalStreamingSessionCallback(MbmsStreamingSessionCallback appCallback,
+            Executor executor) {
+        mAppCallback = appCallback;
+        mExecutor = executor;
+    }
+
+    @Override
+    public void onError(final int errorCode, final String message) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onError(errorCode, message);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void onStreamingServicesUpdated(final List<StreamingServiceInfo> services)
+            throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onStreamingServicesUpdated(services);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void onMiddlewareReady() throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mAppCallback.onMiddlewareReady();
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/android-35/android/telephony/mbms/MbmsDownloadReceiver.java b/android-35/android/telephony/mbms/MbmsDownloadReceiver.java
new file mode 100644
index 0000000..556d6f4
--- /dev/null
+++ b/android-35/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2017 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.mbms;
+
+import android.annotation.SystemApi;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.telephony.MbmsDownloadSession;
+import android.telephony.mbms.vendor.VendorUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * The {@link BroadcastReceiver} responsible for handling intents sent from the middleware. Apps
+ * that wish to download using MBMS APIs should declare this class in their AndroidManifest.xml as
+ * follows:
+<pre>{@code
+<receiver
+    android:name="android.telephony.mbms.MbmsDownloadReceiver"
+    android:permission="android.permission.SEND_EMBMS_INTENTS"
+    android:enabled="true"
+    android:exported="true">
+</receiver>}</pre>
+ */
+public class MbmsDownloadReceiver extends BroadcastReceiver {
+    /** @hide */
+    public static final String DOWNLOAD_TOKEN_SUFFIX = ".download_token";
+    /** @hide */
+    public static final String MBMS_FILE_PROVIDER_META_DATA_KEY = "mbms-file-provider-authority";
+
+    private static final String EMBMS_INTENT_PERMISSION = "android.permission.SEND_EMBMS_INTENTS";
+
+    /**
+     * Indicates that the requested operation completed without error.
+     * @hide
+     */
+    @SystemApi
+    public static final int RESULT_OK = 0;
+
+    /**
+     * Indicates that the intent sent had an invalid action. This will be the result if
+     * {@link Intent#getAction()} returns anything other than
+     * {@link VendorUtils#ACTION_DOWNLOAD_RESULT_INTERNAL},
+     * {@link VendorUtils#ACTION_FILE_DESCRIPTOR_REQUEST}, or
+     * {@link VendorUtils#ACTION_CLEANUP}.
+     * This is a fatal result code and no result extras should be expected.
+     * @hide
+     */
+    @SystemApi
+    public static final int RESULT_INVALID_ACTION = 1;
+
+    /**
+     * Indicates that the intent was missing some required extras.
+     * This is a fatal result code and no result extras should be expected.
+     * @hide
+     */
+    @SystemApi
+    public static final int RESULT_MALFORMED_INTENT = 2;
+
+    /**
+     * Indicates that the supplied value for {@link VendorUtils#EXTRA_TEMP_FILE_ROOT}
+     * does not match what the app has stored.
+     * This is a fatal result code and no result extras should be expected.
+     * @hide
+     */
+    @SystemApi
+    public static final int RESULT_BAD_TEMP_FILE_ROOT = 3;
+
+    /**
+     * Indicates that the manager was unable to move the completed download to its final location.
+     * This is a fatal result code and no result extras should be expected.
+     * @hide
+     */
+    @SystemApi
+    public static final int RESULT_DOWNLOAD_FINALIZATION_ERROR = 4;
+
+    /**
+     * Indicates that the manager was unable to generate one or more of the requested file
+     * descriptors.
+     * This is a non-fatal result code -- some file descriptors may still be generated, but there
+     * is no guarantee that they will be the same number as requested.
+     * @hide
+     */
+    @SystemApi
+    public static final int RESULT_TEMP_FILE_GENERATION_ERROR = 5;
+
+    /**
+     * Indicates that the manager was unable to notify the app of the completed download.
+     * This is a fatal result code and no result extras should be expected.
+     * @hide
+     */
+    @SystemApi
+    public static final int RESULT_APP_NOTIFICATION_ERROR = 6;
+
+
+    private static final String LOG_TAG = "MbmsDownloadReceiver";
+    private static final String TEMP_FILE_SUFFIX = ".embms.temp";
+    private static final String TEMP_FILE_STAGING_LOCATION = "staged_completed_files";
+
+    private static final int MAX_TEMP_FILE_RETRIES = 5;
+
+    private String mFileProviderAuthorityCache = null;
+    private String mMiddlewarePackageNameCache = null;
+
+    /** @hide */
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        verifyPermissionIntegrity(context);
+
+        if (!verifyIntentContents(context, intent)) {
+            setResultCode(RESULT_MALFORMED_INTENT);
+            return;
+        }
+        if (!Objects.equals(intent.getStringExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT),
+                MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath())) {
+            setResultCode(RESULT_BAD_TEMP_FILE_ROOT);
+            return;
+        }
+
+        if (VendorUtils.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
+            moveDownloadedFile(context, intent);
+            cleanupPostMove(context, intent);
+        } else if (VendorUtils.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
+            generateTempFiles(context, intent);
+        } else if (VendorUtils.ACTION_CLEANUP.equals(intent.getAction())) {
+            cleanupTempFiles(context, intent);
+        } else {
+            setResultCode(RESULT_INVALID_ACTION);
+        }
+    }
+
+    private boolean verifyIntentContents(Context context, Intent intent) {
+        if (VendorUtils.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
+            if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT)) {
+                Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
+                return false;
+            }
+            if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST)) {
+                Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
+                return false;
+            }
+            // We do not need to verify below extras if the result is not success.
+            if (MbmsDownloadSession.RESULT_SUCCESSFUL !=
+                    intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
+                    MbmsDownloadSession.RESULT_CANCELLED)) {
+                return true;
+            }
+            if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT)) {
+                Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
+                return false;
+            }
+            if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO)) {
+                Log.w(LOG_TAG, "Download result did not include the associated file info. " +
+                        "Ignoring.");
+                return false;
+            }
+            if (!intent.hasExtra(VendorUtils.EXTRA_FINAL_URI)) {
+                Log.w(LOG_TAG, "Download result did not include the path to the final " +
+                        "temp file. Ignoring.");
+                return false;
+            }
+            DownloadRequest request = intent.getParcelableExtra(
+                    MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST, android.telephony.mbms.DownloadRequest.class);
+            String expectedTokenFileName = request.getHash() + DOWNLOAD_TOKEN_SUFFIX;
+            File expectedTokenFile = new File(
+                    MbmsUtils.getEmbmsTempFileDirForService(context, request.getFileServiceId()),
+                    expectedTokenFileName);
+            if (!expectedTokenFile.exists()) {
+                Log.w(LOG_TAG, "Supplied download request does not match a token that we have. " +
+                        "Expected " + expectedTokenFile);
+                return false;
+            }
+        } else if (VendorUtils.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
+            if (!intent.hasExtra(VendorUtils.EXTRA_SERVICE_ID)) {
+                Log.w(LOG_TAG, "Temp file request did not include the associated service id." +
+                        " Ignoring.");
+                return false;
+            }
+            if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT)) {
+                Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
+                return false;
+            }
+        } else if (VendorUtils.ACTION_CLEANUP.equals(intent.getAction())) {
+            if (!intent.hasExtra(VendorUtils.EXTRA_SERVICE_ID)) {
+                Log.w(LOG_TAG, "Cleanup request did not include the associated service id." +
+                        " Ignoring.");
+                return false;
+            }
+            if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT)) {
+                Log.w(LOG_TAG, "Cleanup request did not include the temp file root. Ignoring.");
+                return false;
+            }
+            if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILES_IN_USE)) {
+                Log.w(LOG_TAG, "Cleanup request did not include the list of temp files in use. " +
+                        "Ignoring.");
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void moveDownloadedFile(Context context, Intent intent) {
+        DownloadRequest request = intent.getParcelableExtra(
+                MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST, android.telephony.mbms.DownloadRequest.class);
+        Intent intentForApp = request.getIntentForApp();
+        if (intentForApp == null) {
+            Log.i(LOG_TAG, "Malformed app notification intent");
+            setResultCode(RESULT_APP_NOTIFICATION_ERROR);
+            return;
+        }
+
+        int result = intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
+                MbmsDownloadSession.RESULT_CANCELLED);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT, result);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST, request);
+
+        if (result != MbmsDownloadSession.RESULT_SUCCESSFUL) {
+            Log.i(LOG_TAG, "Download request indicated a failed download. Aborting.");
+            context.sendBroadcast(intentForApp);
+            setResultCode(RESULT_OK);
+            return;
+        }
+
+        Uri finalTempFile = intent.getParcelableExtra(VendorUtils.EXTRA_FINAL_URI, android.net.Uri.class);
+        if (!verifyTempFilePath(context, request.getFileServiceId(), finalTempFile)) {
+            Log.w(LOG_TAG, "Download result specified an invalid temp file " + finalTempFile);
+            setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
+            return;
+        }
+
+        FileInfo completedFileInfo =
+                (FileInfo) intent.getParcelableExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, android.telephony.mbms.FileInfo.class);
+        Path appSpecifiedDestination = FileSystems.getDefault().getPath(
+                request.getDestinationUri().getPath());
+
+        Uri finalLocation;
+        try {
+            String relativeLocation = getFileRelativePath(request.getSourceUri().getPath(),
+                    completedFileInfo.getUri().getPath());
+            finalLocation = moveToFinalLocation(finalTempFile, appSpecifiedDestination,
+                    relativeLocation);
+        } catch (IOException e) {
+            Log.w(LOG_TAG, "Failed to move temp file to final destination");
+            setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
+            return;
+        }
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI, finalLocation);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, completedFileInfo);
+
+        context.sendBroadcast(intentForApp);
+        setResultCode(RESULT_OK);
+    }
+
+    private void cleanupPostMove(Context context, Intent intent) {
+        DownloadRequest request = intent.getParcelableExtra(
+                MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST, android.telephony.mbms.DownloadRequest.class);
+        if (request == null) {
+            Log.w(LOG_TAG, "Intent does not include a DownloadRequest. Ignoring.");
+            return;
+        }
+
+        List<Uri> tempFiles = intent.getParcelableArrayListExtra(VendorUtils.EXTRA_TEMP_LIST, android.net.Uri.class);
+        if (tempFiles == null) {
+            return;
+        }
+
+        for (Uri tempFileUri : tempFiles) {
+            if (verifyTempFilePath(context, request.getFileServiceId(), tempFileUri)) {
+                File tempFile = new File(tempFileUri.getSchemeSpecificPart());
+                if (!tempFile.delete()) {
+                    Log.w(LOG_TAG, "Failed to delete temp file at " + tempFile.getPath());
+                }
+            }
+        }
+    }
+
+    private void generateTempFiles(Context context, Intent intent) {
+        String serviceId = intent.getStringExtra(VendorUtils.EXTRA_SERVICE_ID);
+        if (serviceId == null) {
+            Log.w(LOG_TAG, "Temp file request did not include the associated service id. " +
+                    "Ignoring.");
+            setResultCode(RESULT_MALFORMED_INTENT);
+            return;
+        }
+        int fdCount = intent.getIntExtra(VendorUtils.EXTRA_FD_COUNT, 0);
+        List<Uri> pausedList = intent.getParcelableArrayListExtra(VendorUtils.EXTRA_PAUSED_LIST, android.net.Uri.class);
+
+        if (fdCount == 0 && (pausedList == null || pausedList.size() == 0)) {
+            Log.i(LOG_TAG, "No temp files actually requested. Ending.");
+            setResultCode(RESULT_OK);
+            setResultExtras(Bundle.EMPTY);
+            return;
+        }
+
+        ArrayList<UriPathPair> freshTempFiles =
+                generateFreshTempFiles(context, serviceId, fdCount);
+        ArrayList<UriPathPair> pausedFiles =
+                generateUrisForPausedFiles(context, serviceId, pausedList);
+
+        Bundle result = new Bundle();
+        result.putParcelableArrayList(VendorUtils.EXTRA_FREE_URI_LIST, freshTempFiles);
+        result.putParcelableArrayList(VendorUtils.EXTRA_PAUSED_URI_LIST, pausedFiles);
+        setResultCode(RESULT_OK);
+        setResultExtras(result);
+    }
+
+    private ArrayList<UriPathPair> generateFreshTempFiles(Context context, String serviceId,
+            int freshFdCount) {
+        File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context, serviceId);
+        if (!tempFileDir.exists()) {
+            tempFileDir.mkdirs();
+        }
+
+        // Name the files with the template "N-UUID", where N is the request ID and UUID is a
+        // random uuid.
+        ArrayList<UriPathPair> result = new ArrayList<>(freshFdCount);
+        for (int i = 0; i < freshFdCount; i++) {
+            File tempFile = generateSingleTempFile(tempFileDir);
+            if (tempFile == null) {
+                setResultCode(RESULT_TEMP_FILE_GENERATION_ERROR);
+                Log.w(LOG_TAG, "Failed to generate a temp file. Moving on.");
+                continue;
+            }
+            Uri fileUri = Uri.fromFile(tempFile);
+            Uri contentUri = MbmsTempFileProvider.getUriForFile(
+                    context, getFileProviderAuthorityCached(context), tempFile);
+            context.grantUriPermission(getMiddlewarePackageCached(context), contentUri,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+            result.add(new UriPathPair(fileUri, contentUri));
+        }
+
+        return result;
+    }
+
+    private static File generateSingleTempFile(File tempFileDir) {
+        int numTries = 0;
+        while (numTries < MAX_TEMP_FILE_RETRIES) {
+            numTries++;
+            String fileName =  UUID.randomUUID() + TEMP_FILE_SUFFIX;
+            File tempFile = new File(tempFileDir, fileName);
+            try {
+                if (tempFile.createNewFile()) {
+                    return tempFile.getCanonicalFile();
+                }
+            } catch (IOException e) {
+                continue;
+            }
+        }
+        return null;
+    }
+
+    private ArrayList<UriPathPair> generateUrisForPausedFiles(Context context,
+            String serviceId, List<Uri> pausedFiles) {
+        if (pausedFiles == null) {
+            return new ArrayList<>(0);
+        }
+        ArrayList<UriPathPair> result = new ArrayList<>(pausedFiles.size());
+
+        for (Uri fileUri : pausedFiles) {
+            if (!verifyTempFilePath(context, serviceId, fileUri)) {
+                Log.w(LOG_TAG, "Supplied file " + fileUri + " is not a valid temp file to resume");
+                setResultCode(RESULT_TEMP_FILE_GENERATION_ERROR);
+                continue;
+            }
+            File tempFile = new File(fileUri.getSchemeSpecificPart());
+            if (!tempFile.exists()) {
+                Log.w(LOG_TAG, "Supplied file " + fileUri + " does not exist.");
+                setResultCode(RESULT_TEMP_FILE_GENERATION_ERROR);
+                continue;
+            }
+            Uri contentUri = MbmsTempFileProvider.getUriForFile(
+                    context, getFileProviderAuthorityCached(context), tempFile);
+            context.grantUriPermission(getMiddlewarePackageCached(context), contentUri,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+            result.add(new UriPathPair(fileUri, contentUri));
+        }
+        return result;
+    }
+
+    private void cleanupTempFiles(Context context, Intent intent) {
+        String serviceId = intent.getStringExtra(VendorUtils.EXTRA_SERVICE_ID);
+        File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context, serviceId);
+        final List<Uri> filesInUse =
+                intent.getParcelableArrayListExtra(VendorUtils.EXTRA_TEMP_FILES_IN_USE, android.net.Uri.class);
+        File[] filesToDelete = tempFileDir.listFiles(new FileFilter() {
+            @Override
+            public boolean accept(File file) {
+                File canonicalFile;
+                try {
+                    canonicalFile = file.getCanonicalFile();
+                } catch (IOException e) {
+                    Log.w(LOG_TAG, "Got IOException canonicalizing " + file + ", not deleting.");
+                    return false;
+                }
+                // Reject all files that don't match what we think a temp file should look like
+                // e.g. download tokens
+                if (!canonicalFile.getName().endsWith(TEMP_FILE_SUFFIX)) {
+                    return false;
+                }
+                // If any of the files in use match the uri, return false to reject it from the
+                // list to delete.
+                Uri fileInUseUri = Uri.fromFile(canonicalFile);
+                return !filesInUse.contains(fileInUseUri);
+            }
+        });
+        for (File fileToDelete : filesToDelete) {
+            fileToDelete.delete();
+        }
+    }
+
+    /*
+     * Moves a tempfile located at fromPath to its final home where the app wants it
+     */
+    private static Uri moveToFinalLocation(Uri fromPath, Path appSpecifiedPath,
+            String relativeLocation) throws IOException {
+        if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
+            Log.w(LOG_TAG, "Downloaded file location uri " + fromPath +
+                    " does not have a file scheme");
+            return null;
+        }
+
+        Path fromFile = FileSystems.getDefault().getPath(fromPath.getPath());
+        Path toFile = appSpecifiedPath.resolve(relativeLocation);
+
+        if (!Files.isDirectory(toFile.getParent())) {
+            Files.createDirectories(toFile.getParent());
+        }
+        Path result = Files.move(fromFile, toFile,
+                StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
+
+        return Uri.fromFile(result.toFile());
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public static String getFileRelativePath(String sourceUriPath, String fileInfoPath) {
+        if (sourceUriPath.endsWith("*")) {
+            // This is a wildcard path. Strip the last path component and use that as the root of
+            // the relative path.
+            int lastSlash = sourceUriPath.lastIndexOf('/');
+            sourceUriPath = sourceUriPath.substring(0, lastSlash);
+        }
+        if (!fileInfoPath.startsWith(sourceUriPath)) {
+            Log.e(LOG_TAG, "File location specified in FileInfo does not match the source URI."
+                    + " source: " + sourceUriPath + " fileinfo path: " + fileInfoPath);
+            return null;
+        }
+        if (fileInfoPath.length() == sourceUriPath.length()) {
+            // This is the single-file download case. Return the name of the file so that the
+            // receiver puts the file directly into the dest directory.
+            return sourceUriPath.substring(sourceUriPath.lastIndexOf('/') + 1);
+        }
+
+        String prefixOmittedPath = fileInfoPath.substring(sourceUriPath.length());
+        if (prefixOmittedPath.startsWith("/")) {
+            prefixOmittedPath = prefixOmittedPath.substring(1);
+        }
+        return prefixOmittedPath;
+    }
+
+    private static boolean verifyTempFilePath(Context context, String serviceId,
+            Uri filePath) {
+        if (!ContentResolver.SCHEME_FILE.equals(filePath.getScheme())) {
+            Log.w(LOG_TAG, "Uri " + filePath + " does not have a file scheme");
+            return false;
+        }
+
+        String path = filePath.getSchemeSpecificPart();
+        File tempFile = new File(path);
+        if (!tempFile.exists()) {
+            Log.w(LOG_TAG, "File at " + path + " does not exist.");
+            return false;
+        }
+
+        if (!MbmsUtils.isContainedIn(
+                MbmsUtils.getEmbmsTempFileDirForService(context, serviceId), tempFile)) {
+            Log.w(LOG_TAG, "File at " + path + " is not contained in the temp file root," +
+                    " which is " + MbmsUtils.getEmbmsTempFileDirForService(context, serviceId));
+            return false;
+        }
+
+        return true;
+    }
+
+    private String getFileProviderAuthorityCached(Context context) {
+        if (mFileProviderAuthorityCache != null) {
+            return mFileProviderAuthorityCache;
+        }
+
+        mFileProviderAuthorityCache = getFileProviderAuthority(context);
+        return mFileProviderAuthorityCache;
+    }
+
+    private static String getFileProviderAuthority(Context context) {
+        ApplicationInfo appInfo;
+        try {
+            appInfo = context.getPackageManager()
+                    .getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException("Package manager couldn't find " + context.getPackageName());
+        }
+        if (appInfo.metaData == null) {
+            throw new RuntimeException("App must declare the file provider authority as metadata " +
+                    "in the manifest.");
+        }
+        String authority = appInfo.metaData.getString(MBMS_FILE_PROVIDER_META_DATA_KEY);
+        if (authority == null) {
+            throw new RuntimeException("App must declare the file provider authority as metadata " +
+                    "in the manifest.");
+        }
+        return authority;
+    }
+
+    private String getMiddlewarePackageCached(Context context) {
+        if (mMiddlewarePackageNameCache == null) {
+            mMiddlewarePackageNameCache = MbmsUtils.getMiddlewareServiceInfo(context,
+                    MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION).packageName;
+        }
+        return mMiddlewarePackageNameCache;
+    }
+
+    private void verifyPermissionIntegrity(Context context) {
+        PackageManager pm = context.getPackageManager();
+        Intent queryIntent = new Intent(context, MbmsDownloadReceiver.class);
+        List<ResolveInfo> infos = pm.queryBroadcastReceivers(queryIntent, 0);
+        if (infos.size() != 1) {
+            throw new IllegalStateException("Non-unique download receiver in your app");
+        }
+        ActivityInfo selfInfo = infos.get(0).activityInfo;
+        if (selfInfo == null) {
+            throw new IllegalStateException("Queried ResolveInfo does not contain a receiver");
+        }
+        if (MbmsUtils.getOverrideServiceName(context,
+                MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION) != null) {
+            // If an override was specified, just make sure that the permission isn't null.
+            if (selfInfo.permission == null) {
+                throw new IllegalStateException(
+                        "MbmsDownloadReceiver must require some permission");
+            }
+            return;
+        }
+        if (!Objects.equals(EMBMS_INTENT_PERMISSION, selfInfo.permission)) {
+            throw new IllegalStateException("MbmsDownloadReceiver must require the " +
+                    "SEND_EMBMS_INTENTS permission.");
+        }
+    }
+}
diff --git a/android-35/android/telephony/mbms/MbmsDownloadSessionCallback.java b/android-35/android/telephony/mbms/MbmsDownloadSessionCallback.java
new file mode 100644
index 0000000..5003b57
--- /dev/null
+++ b/android-35/android/telephony/mbms/MbmsDownloadSessionCallback.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 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.mbms;
+
+import android.annotation.IntDef;
+import android.telephony.MbmsDownloadSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * A callback class that apps should use to receive information on file downloads over
+ * cell-broadcast.
+ */
+public class MbmsDownloadSessionCallback {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+            MbmsErrors.ERROR_MIDDLEWARE_LOST,
+            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+            MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+            MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            MbmsErrors.GeneralErrors.ERROR_IN_E911,
+            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+            MbmsErrors.DownloadErrors.ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT,
+            MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST,
+            MbmsErrors.DownloadErrors.ERROR_UNKNOWN_FILE_INFO}, prefix = { "ERROR_" })
+    private @interface DownloadError{}
+
+    /**
+     * Indicates that the middleware has encountered an asynchronous error.
+     * @param errorCode Any error code listed in {@link MbmsErrors}
+     * @param message A message, intended for debugging purposes, describing the error in further
+     *                detail.
+     */
+    public void onError(@DownloadError int errorCode, String message) {
+        // default implementation empty
+    }
+
+    /**
+     * Called to indicate published File Services have changed.
+     *
+     * This will only be called after the application has requested a list of file services and
+     * specified a service class list of interest via
+     * {@link MbmsDownloadSession#requestUpdateFileServices(List)}. If there are subsequent calls to
+     * {@link MbmsDownloadSession#requestUpdateFileServices(List)},
+     * this method may not be called again if
+     * the list of service classes would remain the same.
+     *
+     * @param services The most recently updated list of available file services.
+     */
+    public void onFileServicesUpdated(List<FileServiceInfo> services) {
+        // default implementation empty
+    }
+
+    /**
+     * Called to indicate that the middleware has been initialized and is ready.
+     *
+     * Before this method is called, calling any method on an instance of
+     * {@link MbmsDownloadSession} will result in an {@link IllegalStateException}
+     * being thrown or {@link #onError(int, String)} being called with error code
+     * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
+     */
+    public void onMiddlewareReady() {
+        // default implementation empty
+    }
+}
diff --git a/android-35/android/telephony/mbms/MbmsErrors.java b/android-35/android/telephony/mbms/MbmsErrors.java
new file mode 100644
index 0000000..40f3ae8
--- /dev/null
+++ b/android-35/android/telephony/mbms/MbmsErrors.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2017 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.mbms;
+
+import android.annotation.IntDef;
+import android.telephony.MbmsStreamingSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public class MbmsErrors {
+    /**
+     * Indicates that the middleware has sent an error code that is not defined in the version of
+     * the SDK targeted by your app. This is an illegal value for the middleware to return -- it
+     * should only ever be generated by the framework.
+     */
+    public static final int UNKNOWN = -1;
+
+    /** Indicates that the operation was successful. */
+    public static final int SUCCESS = 0;
+
+    // Following errors are generated in the manager and should not be returned from the
+    // middleware
+    /**
+     * Indicates that either no MBMS middleware app is installed on the device or multiple
+     * middleware apps are installed on the device.
+     */
+    public static final int ERROR_NO_UNIQUE_MIDDLEWARE = 1;
+
+    /**
+     * Indicates that the app attempted to perform an operation on an instance of
+     * {@link android.telephony.MbmsDownloadSession} or
+     * {@link MbmsStreamingSession} without being bound to the middleware.
+     */
+    public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2;
+
+    /** Indicates that the middleware has died and the requested operation was not completed.*/
+    public static final int ERROR_MIDDLEWARE_LOST = 3;
+
+    /**
+     * Indicates errors that may be generated during initialization by the
+     * middleware. They are applicable to both streaming and file-download use-cases.
+     */
+    public static class InitializationErrors {
+        private InitializationErrors() {}
+        /**
+         * Indicates that the app tried to create more than one instance each of
+         * {@link MbmsStreamingSession} or {@link android.telephony.MbmsDownloadSession}.
+         */
+        public static final int ERROR_DUPLICATE_INITIALIZE = 101;
+        /** Indicates that the app is not authorized to access media via MBMS.*/
+        public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102;
+        /** Indicates that the middleware was unable to initialize for this app. */
+        public static final int ERROR_UNABLE_TO_INITIALIZE = 103;
+    }
+
+    /**
+     * Indicates the errors that may occur at any point and are applicable to both
+     * streaming and file-download.
+     */
+    public static class GeneralErrors {
+        private GeneralErrors() {}
+        /**
+         * Indicates that the app attempted to perform an operation before receiving notification
+         * that the middleware is ready via {@link MbmsStreamingSessionCallback#onMiddlewareReady()}
+         * or {@link MbmsDownloadSessionCallback#onMiddlewareReady()}.
+         */
+        public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201;
+        /**
+         * Indicates that the middleware ran out of memory and was unable to complete the requested
+         * operation.
+         */
+        public static final int ERROR_OUT_OF_MEMORY = 202;
+        /**
+         * Indicates that the requested operation failed due to the middleware being unavailable due
+         * to a transient condition. The app may retry the operation at a later time.
+         */
+        public static final int ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE = 203;
+        /**
+         * Indicates that the requested operation was not performed due to being in emergency
+         * callback mode.
+         */
+        public static final int ERROR_IN_E911 = 204;
+        /** Indicates that MBMS is not available due to the device being in roaming. */
+        public static final int ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE = 205;
+        /** Indicates that MBMS is not available due to a SIM read error. */
+        public static final int ERROR_UNABLE_TO_READ_SIM = 206;
+        /**
+         * Indicates that MBMS is not available due to the inserted SIM being from an unsupported
+         * carrier.
+         */
+        public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 207;
+    }
+
+    /**
+     * Indicates the errors that are applicable only to the streaming use-case
+     */
+    public static class StreamingErrors {
+        private StreamingErrors() {}
+        /** Indicates that the middleware cannot start a stream due to too many ongoing streams */
+        public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 301;
+
+        /** Indicates that the middleware was unable to start the streaming service */
+        public static final int ERROR_UNABLE_TO_START_SERVICE = 302;
+
+        /**
+         * Indicates that the app called
+         * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
+         * java.util.concurrent.Executor, StreamingServiceCallback)}
+         * more than once for the same {@link StreamingServiceInfo}.
+         */
+        public static final int ERROR_DUPLICATE_START_STREAM = 303;
+    }
+
+    /**
+     * Indicates the errors that are applicable only to the file-download use-case
+     */
+    public static class DownloadErrors {
+        private DownloadErrors() { }
+        /**
+         * Indicates that the app is not allowed to change the temp file root at this time due to
+         * outstanding download requests.
+         */
+        public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 401;
+
+        /** Indicates that the middleware has no record of the supplied {@link DownloadRequest}. */
+        public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402;
+
+        /** Indicates the the middleware has no record of the supplied {@link FileInfo} */
+        public static final int ERROR_UNKNOWN_FILE_INFO = 403;
+
+        /**
+         * Indicates that the service announcement descriptor passed via
+         * {@link android.telephony.MbmsDownloadSession#addServiceAnnouncement(byte[])}
+         * is malformed.
+         */
+        public static final int ERROR_MALFORMED_SERVICE_ANNOUNCEMENT = 404;
+    }
+
+    /**
+     * Indicates the errors that are applicable only to the group call use-case.
+     */
+    public static class GroupCallErrors {
+        private GroupCallErrors() { }
+        /** Indicates that the middleware was unable to start the group call. */
+        public static final int ERROR_UNABLE_TO_START_SERVICE = 501;
+
+        /**
+         * Indicates that the app called
+         * {@link android.telephony.MbmsGroupCallSession#startGroupCall} more than once for the
+         * same {@code tmgi}.
+         */
+        public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502;
+    }
+
+    /** @hide */
+    @IntDef(value = {
+            SUCCESS,
+            ERROR_NO_UNIQUE_MIDDLEWARE,
+            ERROR_MIDDLEWARE_NOT_BOUND,
+            ERROR_MIDDLEWARE_LOST,
+            InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+            InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+            InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+            GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            GeneralErrors.ERROR_OUT_OF_MEMORY,
+            GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            GeneralErrors.ERROR_IN_E911,
+            GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+            StreamingErrors.ERROR_CONCURRENT_SERVICE_LIMIT_REACHED,
+            StreamingErrors.ERROR_UNABLE_TO_START_SERVICE,
+            StreamingErrors.ERROR_DUPLICATE_START_STREAM,
+            DownloadErrors.ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT,
+            DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST,
+            DownloadErrors.ERROR_UNKNOWN_FILE_INFO,
+            DownloadErrors.ERROR_MALFORMED_SERVICE_ANNOUNCEMENT,
+            GroupCallErrors.ERROR_UNABLE_TO_START_SERVICE,
+            GroupCallErrors.ERROR_DUPLICATE_START_GROUP_CALL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MbmsError {
+    }
+
+    private MbmsErrors() {}
+}
diff --git a/android-35/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/android-35/android/telephony/mbms/MbmsGroupCallSessionCallback.java
new file mode 100644
index 0000000..ac7e172
--- /dev/null
+++ b/android-35/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 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.mbms;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.telephony.MbmsGroupCallSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A callback class that is used to receive information from the middleware on MBMS group-call
+ * services. An instance of this object should be passed into
+ * {@link MbmsGroupCallSession#create(Context, int, Executor, MbmsGroupCallSessionCallback)}.
+ */
+public interface MbmsGroupCallSessionCallback {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+            MbmsErrors.ERROR_MIDDLEWARE_LOST,
+            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+            MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+            MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            MbmsErrors.GeneralErrors.ERROR_IN_E911,
+            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
+    @interface GroupCallError{}
+
+    /**
+     * Called by the middleware when it has detected an error condition. The possible error codes
+     * are listed in {@link MbmsErrors}.
+     * @param errorCode The error code.
+     * @param message A human-readable message generated by the middleware for debugging purposes.
+     */
+    default void onError(@GroupCallError int errorCode, @Nullable String message) {}
+
+    /**
+     * Indicates that the list of currently available SAIs has been updated. The app may use this
+     * information to filter the list of group calls when displaying available group calls to
+     * the user by matching the SAIs with a list of group calls separately negotiated with the
+     * carrier. The app may also report the aggregate list of SAIs to the group call application
+     * server so that a network entity can determine when, and where to activate the broadcast of
+     * particular group calls.
+     * @param currentSais The available SAIs on the current cell.
+     * @param availableSais A list of lists of available SAIS in neighboring cells, where each list
+     *                      contains the available SAIs in an individual cell.
+     */
+    default void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
+            @NonNull List<List<Integer>> availableSais) {}
+
+    /**
+     * Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied
+     * via this callback may be used to establish a data-link interface with the modem.
+     *
+     * In order to establish the data-link interface, the multicast IP and port must be obtained
+     * out-of-band from the carrier. A {@link java.net.MulticastSocket} may then be constructed
+     * using a {@link java.net.NetworkInterface} with the name and interface supplied by this
+     * callback.
+     *
+     * @param interfaceName The interface name for the data link.
+     * @param index The index for the data link.
+     */
+    default void onServiceInterfaceAvailable(@NonNull String interfaceName, int index) {}
+
+    /**
+     * Called to indicate that the middleware has been initialized and is ready.
+     *
+     * Before this method is called, calling any method on an instance of
+     * {@link MbmsGroupCallSession} will result in an {@link IllegalStateException} or an error
+     * delivered via {@link #onError(int, String)} with error code
+     * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
+     */
+    default void onMiddlewareReady() {}
+}
diff --git a/android-35/android/telephony/mbms/MbmsStreamingSessionCallback.java b/android-35/android/telephony/mbms/MbmsStreamingSessionCallback.java
new file mode 100644
index 0000000..1bdb20b
--- /dev/null
+++ b/android-35/android/telephony/mbms/MbmsStreamingSessionCallback.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 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.mbms;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.telephony.MbmsStreamingSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A callback class that is used to receive information from the middleware on MBMS streaming
+ * services. An instance of this object should be passed into
+ * {@link MbmsStreamingSession#create(Context, Executor, int, MbmsStreamingSessionCallback)}.
+ */
+public class MbmsStreamingSessionCallback {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+            MbmsErrors.ERROR_MIDDLEWARE_LOST,
+            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+            MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+            MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            MbmsErrors.GeneralErrors.ERROR_IN_E911,
+            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+            MbmsErrors.StreamingErrors.ERROR_CONCURRENT_SERVICE_LIMIT_REACHED,
+            MbmsErrors.StreamingErrors.ERROR_UNABLE_TO_START_SERVICE,
+            MbmsErrors.StreamingErrors.ERROR_DUPLICATE_START_STREAM}, prefix = { "ERROR_" })
+    private @interface StreamingError{}
+
+    /**
+     * Called by the middleware when it has detected an error condition. The possible error codes
+     * are listed in {@link MbmsErrors}.
+     * @param errorCode The error code.
+     * @param message A human-readable message generated by the middleware for debugging purposes.
+     */
+    public void onError(@StreamingError int errorCode, @Nullable String message) {
+        // default implementation empty
+    }
+
+    /**
+     * Called to indicate published Streaming Services have changed.
+     *
+     * This will only be called after the application has requested
+     * a list of streaming services and specified a service class list
+     * of interest AND the results of a subsequent getStreamServices
+     * call with the same service class list would return different
+     * results.
+     *
+     * @param services The list of available services.
+     */
+    public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) {
+        // default implementation empty
+    }
+
+    /**
+     * Called to indicate that the middleware has been initialized and is ready.
+     *
+     * Before this method is called, calling any method on an instance of
+     * {@link MbmsStreamingSession} will result in an {@link IllegalStateException} or an error
+     * delivered via {@link #onError(int, String)} with error code
+     * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
+     */
+    public void onMiddlewareReady() {
+        // default implementation empty
+    }
+}
diff --git a/android-35/android/telephony/mbms/MbmsTempFileProvider.java b/android-35/android/telephony/mbms/MbmsTempFileProvider.java
new file mode 100644
index 0000000..17adede
--- /dev/null
+++ b/android-35/android/telephony/mbms/MbmsTempFileProvider.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 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.mbms;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.ProviderInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.telephony.MbmsDownloadSession;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public class MbmsTempFileProvider extends ContentProvider {
+    public static final String TEMP_FILE_ROOT_PREF_FILE_NAME = "MbmsTempFileRootPrefs";
+    public static final String TEMP_FILE_ROOT_PREF_NAME = "mbms_temp_file_root";
+
+    private String mAuthority;
+    private Context mContext;
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+            @Nullable String selection, @Nullable String[] selectionArgs,
+            @Nullable String sortOrder) {
+        throw new UnsupportedOperationException("No querying supported");
+    }
+
+    @Override
+    public String getType(@NonNull Uri uri) {
+        // EMBMS temp files can contain arbitrary content.
+        return "application/octet-stream";
+    }
+
+    @Override
+    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+        throw new UnsupportedOperationException("No inserting supported");
+    }
+
+    @Override
+    public int delete(@NonNull Uri uri, @Nullable String selection,
+            @Nullable String[] selectionArgs) {
+        throw new UnsupportedOperationException("No deleting supported");
+    }
+
+    @Override
+    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String
+            selection, @Nullable String[] selectionArgs) {
+        throw new UnsupportedOperationException("No updating supported");
+    }
+
+    @Override
+    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+        // ContentProvider has already checked granted permissions
+        final File file = getFileForUri(mContext, mAuthority, uri);
+        final int fileMode = ParcelFileDescriptor.parseMode(mode);
+        return ParcelFileDescriptor.open(file, fileMode);
+    }
+
+    @Override
+    public void attachInfo(Context context, ProviderInfo info) {
+        super.attachInfo(context, info);
+
+        // Correctness check our security
+        if (info.exported) {
+            throw new SecurityException("Provider must not be exported");
+        }
+        if (!info.grantUriPermissions) {
+            throw new SecurityException("Provider must grant uri permissions");
+        }
+
+        mAuthority = info.authority;
+        mContext = context;
+    }
+
+    public static Uri getUriForFile(Context context, String authority, File file) {
+        // Get the canonical path of the temp file
+        String filePath;
+        try {
+            filePath = file.getCanonicalPath();
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Could not get canonical path for file " + file);
+        }
+
+        // Make sure the temp file is contained in the temp file directory as configured in the
+        // manifest
+        File tempFileDir = getEmbmsTempFileDir(context);
+        if (!MbmsUtils.isContainedIn(tempFileDir, file)) {
+            throw new IllegalArgumentException("File " + file + " is not contained in the temp " +
+                    "file directory, which is " + tempFileDir);
+        }
+
+        // Get the canonical path of the temp file directory
+        String tempFileDirPath;
+        try {
+            tempFileDirPath = tempFileDir.getCanonicalPath();
+        } catch (IOException e) {
+            throw new RuntimeException(
+                    "Could not get canonical path for temp file root dir " + tempFileDir);
+        }
+
+        // Start at first char of path under temp file directory
+        String pathFragment;
+        if (tempFileDirPath.endsWith("/")) {
+            pathFragment = filePath.substring(tempFileDirPath.length());
+        } else {
+            pathFragment = filePath.substring(tempFileDirPath.length() + 1);
+        }
+
+        String encodedPath = Uri.encode(pathFragment);
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority).encodedPath(encodedPath).build();
+    }
+
+    public static File getFileForUri(Context context, String authority, Uri uri)
+            throws FileNotFoundException {
+        if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+            throw new IllegalArgumentException("Uri must have scheme content");
+        }
+        if (!Objects.equals(authority, uri.getAuthority())) {
+            throw new IllegalArgumentException("Uri does not have a matching authority: " +
+                    authority + ", " + uri.getAuthority());
+        }
+
+        String relPath = Uri.decode(uri.getEncodedPath());
+        File file;
+        File tempFileDir;
+
+        try {
+            tempFileDir = getEmbmsTempFileDir(context).getCanonicalFile();
+            file = new File(tempFileDir, relPath).getCanonicalFile();
+        } catch (IOException e) {
+            throw new FileNotFoundException("Could not resolve paths");
+        }
+
+        if (!file.getPath().startsWith(tempFileDir.getPath())) {
+            throw new SecurityException("Resolved path jumped beyond configured root");
+        }
+
+        return file;
+    }
+
+    /**
+     * Returns a File for the directory used to store temp files for this app
+     */
+    public static File getEmbmsTempFileDir(Context context) {
+        SharedPreferences prefs = context.getSharedPreferences(TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+        String storedTempFileRoot = prefs.getString(TEMP_FILE_ROOT_PREF_NAME, null);
+        try {
+            if (storedTempFileRoot != null) {
+                return new File(storedTempFileRoot).getCanonicalFile();
+            } else {
+                return new File(context.getFilesDir(),
+                        MbmsDownloadSession.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY).getCanonicalFile();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("Unable to canonicalize temp file root path " + e);
+        }
+    }
+}
diff --git a/android-35/android/telephony/mbms/MbmsUtils.java b/android-35/android/telephony/mbms/MbmsUtils.java
new file mode 100644
index 0000000..95b4d37
--- /dev/null
+++ b/android-35/android/telephony/mbms/MbmsUtils.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2017 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.mbms;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.*;
+import android.content.pm.ServiceInfo;
+import android.telephony.MbmsDownloadSession;
+import android.telephony.MbmsGroupCallSession;
+import android.telephony.MbmsStreamingSession;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class MbmsUtils {
+    private static final String LOG_TAG = "MbmsUtils";
+
+    public static boolean isContainedIn(File parent, File child) {
+        try {
+            String parentPath = parent.getCanonicalPath();
+            String childPath = child.getCanonicalPath();
+            return childPath.startsWith(parentPath);
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to resolve canonical paths: " + e);
+        }
+    }
+
+    public static ComponentName toComponentName(ComponentInfo ci) {
+        return new ComponentName(ci.packageName, ci.name);
+    }
+
+    public static ComponentName getOverrideServiceName(Context context, String serviceAction) {
+        String metaDataKey = null;
+        switch (serviceAction) {
+            case MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION:
+                metaDataKey = MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA;
+                break;
+            case MbmsStreamingSession.MBMS_STREAMING_SERVICE_ACTION:
+                metaDataKey = MbmsStreamingSession.MBMS_STREAMING_SERVICE_OVERRIDE_METADATA;
+                break;
+            case MbmsGroupCallSession.MBMS_GROUP_CALL_SERVICE_ACTION:
+                metaDataKey = MbmsGroupCallSession.MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA;
+                break;
+        }
+        if (metaDataKey == null) {
+            return null;
+        }
+
+        ApplicationInfo appInfo;
+        try {
+            appInfo = context.getPackageManager()
+                    .getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+        if (appInfo.metaData == null) {
+            return null;
+        }
+        String serviceComponent = appInfo.metaData.getString(metaDataKey);
+        if (serviceComponent == null) {
+            return null;
+        }
+        return ComponentName.unflattenFromString(serviceComponent);
+    }
+
+    public static ServiceInfo getMiddlewareServiceInfo(Context context, String serviceAction) {
+        // Query for the proper service
+        PackageManager packageManager = context.getPackageManager();
+        Intent queryIntent = new Intent();
+        queryIntent.setAction(serviceAction);
+
+        ComponentName overrideService = getOverrideServiceName(context, serviceAction);
+        List<ResolveInfo> services;
+        if (overrideService == null) {
+            services = packageManager.queryIntentServices(queryIntent,
+                    PackageManager.MATCH_SYSTEM_ONLY);
+        } else {
+            queryIntent.setComponent(overrideService);
+            services = packageManager.queryIntentServices(queryIntent,
+                    PackageManager.MATCH_ALL);
+        }
+
+        if (services == null || services.size() == 0) {
+            Log.w(LOG_TAG, "No MBMS services found, cannot get service info");
+            return null;
+        }
+
+        if (services.size() > 1) {
+            Log.w(LOG_TAG, "More than one MBMS service found, cannot get unique service");
+            return null;
+        }
+        return services.get(0).serviceInfo;
+    }
+
+    public static int startBinding(Context context, String serviceAction,
+            ServiceConnection serviceConnection) {
+        Intent bindIntent = new Intent();
+        ServiceInfo mbmsServiceInfo =
+                MbmsUtils.getMiddlewareServiceInfo(context, serviceAction);
+
+        if (mbmsServiceInfo == null) {
+            return MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE;
+        }
+
+        bindIntent.setComponent(MbmsUtils.toComponentName(mbmsServiceInfo));
+
+        context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
+        return MbmsErrors.SUCCESS;
+    }
+
+    /**
+     * Returns a File linked to the directory used to store temp files for this file service
+     */
+    public static File getEmbmsTempFileDirForService(Context context, String serviceId) {
+        // Replace all non-alphanumerics/underscores with an underscore. Some filesystems don't
+        // like special characters.
+        String sanitizedServiceId = serviceId.replaceAll("[^a-zA-Z0-9_]", "_");
+
+        File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(context);
+
+        return new File(embmsTempFileDir, sanitizedServiceId);
+    }
+}
diff --git a/android-35/android/telephony/mbms/ServiceInfo.java b/android-35/android/telephony/mbms/ServiceInfo.java
new file mode 100644
index 0000000..02424ff
--- /dev/null
+++ b/android-35/android/telephony/mbms/ServiceInfo.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2016 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.mbms;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Describes a cell-broadcast service. This class should not be instantiated directly -- use
+ * {@link StreamingServiceInfo} or {@link FileServiceInfo}
+ */
+public class ServiceInfo {
+    // arbitrary limit on the number of locale -> name pairs we support
+    final static int MAP_LIMIT = 1000;
+
+    private final Map<Locale, String> names;
+    private final String className;
+    private final List<Locale> locales;
+    private final String serviceId;
+    private final Date sessionStartTime;
+    private final Date sessionEndTime;
+
+    /** @hide */
+    public ServiceInfo(Map<Locale, String> newNames, String newClassName, List<Locale> newLocales,
+            String newServiceId, Date start, Date end) {
+        if (newNames == null || newClassName == null
+                || newLocales == null || newServiceId == null
+                || start == null || end == null) {
+            throw new IllegalArgumentException("Bad ServiceInfo construction");
+        }
+        if (newNames.size() > MAP_LIMIT) {
+            throw new RuntimeException("bad map length " + newNames.size());
+        }
+        if (newLocales.size() > MAP_LIMIT) {
+            throw new RuntimeException("bad locales length " + newLocales.size());
+        }
+
+        names = new HashMap(newNames.size());
+        names.putAll(newNames);
+        className = newClassName;
+        locales = new ArrayList(newLocales);
+        serviceId = newServiceId;
+        sessionStartTime = (Date)start.clone();
+        sessionEndTime = (Date)end.clone();
+    }
+
+    /** @hide */
+    protected ServiceInfo(Parcel in) {
+        int mapCount = in.readInt();
+        if (mapCount > MAP_LIMIT || mapCount < 0) {
+            throw new RuntimeException("bad map length" + mapCount);
+        }
+        names = new HashMap(mapCount);
+        while (mapCount-- > 0) {
+            Locale locale = (java.util.Locale) in.readSerializable(java.util.Locale.class.getClassLoader(), java.util.Locale.class);
+            String name = in.readString();
+            names.put(locale, name);
+        }
+        className = in.readString();
+        int localesCount = in.readInt();
+        if (localesCount > MAP_LIMIT || localesCount < 0) {
+            throw new RuntimeException("bad locale length " + localesCount);
+        }
+        locales = new ArrayList<Locale>(localesCount);
+        while (localesCount-- > 0) {
+            Locale l = (java.util.Locale) in.readSerializable(java.util.Locale.class.getClassLoader(), java.util.Locale.class);
+            locales.add(l);
+        }
+        serviceId = in.readString();
+        sessionStartTime = (java.util.Date) in.readSerializable(java.util.Date.class.getClassLoader(), java.util.Date.class);
+        sessionEndTime = (java.util.Date) in.readSerializable(java.util.Date.class.getClassLoader(), java.util.Date.class);
+    }
+
+    /** @hide */
+    public void writeToParcel(Parcel dest, int flags) {
+        Set<Locale> keySet = names.keySet();
+        dest.writeInt(keySet.size());
+        for (Locale l : keySet) {
+            dest.writeSerializable(l);
+            dest.writeString(names.get(l));
+        }
+        dest.writeString(className);
+        int localesCount = locales.size();
+        dest.writeInt(localesCount);
+        for (Locale l : locales) {
+            dest.writeSerializable(l);
+        }
+        dest.writeString(serviceId);
+        dest.writeSerializable(sessionStartTime);
+        dest.writeSerializable(sessionEndTime);
+    }
+
+    /**
+     * Get the user-displayable name for this cell-broadcast service corresponding to the
+     * provided {@link Locale}.
+     * @param locale The {@link Locale} in which you want the name of the service. This must be a
+     *               value from the set returned by {@link #getNamedContentLocales()} -- an
+     *               {@link java.util.NoSuchElementException} may be thrown otherwise.
+     * @return The {@link CharSequence} providing the name of the service in the given
+     *         {@link Locale}
+     */
+    public @NonNull CharSequence getNameForLocale(@NonNull Locale locale) {
+        if (!names.containsKey(locale)) {
+            throw new NoSuchElementException("Locale not supported");
+        }
+        return names.get(locale);
+    }
+
+    /**
+     * Return an unmodifiable set of the current {@link Locale}s that have a user-displayable name
+     * associated with them. The user-displayable name associated with any {@link Locale} in this
+     * set can be retrieved with {@link #getNameForLocale(Locale)}.
+     * @return An unmodifiable set of {@link Locale} objects corresponding to a user-displayable
+     * content name in that locale.
+     */
+    public @NonNull Set<Locale> getNamedContentLocales() {
+        return Collections.unmodifiableSet(names.keySet());
+    }
+
+    /**
+     * The class name for this service - used to categorize and filter
+     */
+    public String getServiceClassName() {
+        return className;
+    }
+
+    /**
+     * The languages available for this service content
+     */
+    public List<Locale> getLocales() {
+        return locales;
+    }
+
+    /**
+     * The carrier's identifier for the service.
+     */
+    public String getServiceId() {
+        return serviceId;
+    }
+
+    /**
+     * The start time indicating when this service will be available.
+     */
+    public Date getSessionStartTime() {
+        return sessionStartTime;
+    }
+
+    /**
+     * The end time indicating when this session stops being available.
+     */
+    public Date getSessionEndTime() {
+        return sessionEndTime;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null) {
+            return false;
+        }
+        if (!(o instanceof ServiceInfo)) {
+            return false;
+        }
+        ServiceInfo that = (ServiceInfo) o;
+        return Objects.equals(names, that.names) &&
+                Objects.equals(className, that.className) &&
+                Objects.equals(locales, that.locales) &&
+                Objects.equals(serviceId, that.serviceId) &&
+                Objects.equals(sessionStartTime, that.sessionStartTime) &&
+                Objects.equals(sessionEndTime, that.sessionEndTime);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(names, className, locales, serviceId, sessionStartTime, sessionEndTime);
+    }
+}
diff --git a/android-35/android/telephony/mbms/StreamingService.java b/android-35/android/telephony/mbms/StreamingService.java
new file mode 100644
index 0000000..b6239fe
--- /dev/null
+++ b/android-35/android/telephony/mbms/StreamingService.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2016 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.mbms;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.MbmsStreamingSession;
+import android.telephony.mbms.vendor.IMbmsStreamingService;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Class used to represent a single MBMS stream. After a stream has been started with
+ * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo, java.util.concurrent.Executor,
+ * StreamingServiceCallback)},
+ * this class is used to hold information about the stream and control it.
+ */
+public class StreamingService implements AutoCloseable {
+    private static final String LOG_TAG = "MbmsStreamingService";
+
+    /**
+     * The state of a stream, reported via {@link StreamingServiceCallback#onStreamStateUpdated}
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
+    public @interface StreamingState {}
+    public final static int STATE_STOPPED = 1;
+    public final static int STATE_STARTED = 2;
+    public final static int STATE_STALLED = 3;
+
+    /**
+     * The reason for a stream state change, reported via
+     * {@link StreamingServiceCallback#onStreamStateUpdated}
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "REASON_" },
+            value = {REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT,
+            REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE,
+            REASON_LEFT_MBMS_BROADCAST_AREA, REASON_NONE})
+    public @interface StreamingStateChangeReason {}
+
+    /**
+     * Indicates that the middleware does not have a reason to provide for the state change.
+     */
+    public static final int REASON_NONE = 0;
+
+    /**
+     * State changed due to a call to {@link #close()} or
+     * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
+     * java.util.concurrent.Executor, StreamingServiceCallback)}
+     */
+    public static final int REASON_BY_USER_REQUEST = 1;
+
+    /**
+     * State changed due to the streaming session ending at the carrier.
+     */
+    public static final int REASON_END_OF_SESSION = 2;
+
+    /**
+     * State changed due to a frequency conflict with another requested stream.
+     */
+    public static final int REASON_FREQUENCY_CONFLICT = 3;
+
+    /**
+     * State changed due to the middleware running out of memory
+     */
+    public static final int REASON_OUT_OF_MEMORY = 4;
+
+    /**
+     * State changed due to the device leaving the home carrier's LTE network.
+     */
+    public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5;
+
+    /**
+     * State changed due to the device leaving the where this stream is being broadcast.
+     */
+    public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6;
+
+    /**
+     * The method of transmission currently used for a stream,
+     * reported via {@link StreamingServiceCallback#onStreamMethodUpdated}
+     */
+    public final static int BROADCAST_METHOD = 1;
+    public final static int UNICAST_METHOD   = 2;
+
+    private final int mSubscriptionId;
+    private final MbmsStreamingSession mParentSession;
+    private final StreamingServiceInfo mServiceInfo;
+    private final InternalStreamingServiceCallback mCallback;
+
+    private IMbmsStreamingService mService;
+
+    /**
+     * @hide
+     */
+    public StreamingService(int subscriptionId,
+            IMbmsStreamingService service,
+            MbmsStreamingSession session,
+            StreamingServiceInfo streamingServiceInfo,
+            InternalStreamingServiceCallback callback) {
+        mSubscriptionId = subscriptionId;
+        mParentSession = session;
+        mService = service;
+        mServiceInfo = streamingServiceInfo;
+        mCallback = callback;
+    }
+
+    /**
+     * Retrieve the Uri used to play this stream.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
+     *
+     * @return The {@link Uri} to pass to the streaming client, or {@code null} if an error
+     *         occurred.
+     */
+    public @Nullable Uri getPlaybackUri() {
+        if (mService == null) {
+            throw new IllegalStateException("No streaming service attached");
+        }
+
+        try {
+            return mService.getPlaybackUri(mSubscriptionId, mServiceInfo.getServiceId());
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService = null;
+            mParentSession.onStreamingServiceStopped(this);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return null;
+        }
+    }
+
+    /**
+     * Retrieve the {@link StreamingServiceInfo} corresponding to this stream.
+     */
+    public StreamingServiceInfo getInfo() {
+        return mServiceInfo;
+    }
+
+    /**
+     * Stop streaming this service. Further operations on this object will fail with an
+     * {@link IllegalStateException}.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     */
+    @Override
+    public void close() {
+        if (mService == null) {
+            throw new IllegalStateException("No streaming service attached");
+        }
+
+        try {
+            mService.stopStreaming(mSubscriptionId, mServiceInfo.getServiceId());
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService = null;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        } finally {
+            mParentSession.onStreamingServiceStopped(this);
+        }
+    }
+
+    /** @hide */
+    public InternalStreamingServiceCallback getCallback() {
+        return mCallback;
+    }
+
+    private void sendErrorToApp(int errorCode, String message) {
+        try {
+            mCallback.onError(errorCode, message);
+        } catch (RemoteException e) {
+            // Ignore, should not happen locally.
+        }
+    }
+}
+
diff --git a/android-35/android/telephony/mbms/StreamingServiceCallback.java b/android-35/android/telephony/mbms/StreamingServiceCallback.java
new file mode 100644
index 0000000..c265db6
--- /dev/null
+++ b/android-35/android/telephony/mbms/StreamingServiceCallback.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 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.mbms;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A callback class for use when the application is actively streaming content. The middleware
+ * will provide updates on the status of the stream via this callback.
+ */
+public class StreamingServiceCallback {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+            MbmsErrors.ERROR_MIDDLEWARE_LOST,
+            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            MbmsErrors.GeneralErrors.ERROR_IN_E911,
+            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+            MbmsErrors.StreamingErrors.ERROR_CONCURRENT_SERVICE_LIMIT_REACHED,
+            MbmsErrors.StreamingErrors.ERROR_UNABLE_TO_START_SERVICE,
+            MbmsErrors.StreamingErrors.ERROR_DUPLICATE_START_STREAM}, prefix = { "ERROR_" })
+    private @interface StreamingServiceError{}
+
+    /**
+     * Indicates broadcast signal strength is not available for this service.
+     *
+     * This may be due to the service no longer being available due to geography
+     * or timing (end of service) or because lack of demand has caused the service
+     * to be delivered via unicast.
+     */
+    public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
+
+    /**
+     * Called by the middleware when it has detected an error condition in this stream. The
+     * possible error codes are listed in {@link MbmsErrors}.
+     * @param errorCode The error code.
+     * @param message A human-readable message generated by the middleware for debugging purposes.
+     */
+    public void onError(@StreamingServiceError int errorCode, @Nullable String message) {
+        // default implementation empty
+    }
+
+    /**
+     * Called to indicate this stream has changed state.
+     *
+     * See {@link StreamingService#STATE_STOPPED}, {@link StreamingService#STATE_STARTED}
+     * and {@link StreamingService#STATE_STALLED}.
+     */
+    public void onStreamStateUpdated(@StreamingService.StreamingState int state,
+            @StreamingService.StreamingStateChangeReason int reason) {
+        // default implementation empty
+    }
+
+    /**
+     * Called to indicate the mpd of a the stream has changed.
+     *
+     * Depending on the Dash Client it may need to be either reset
+     * (less drastic, but original spec didn't allow mpd to change so not
+     * always supported) or restarted.
+     *
+     * This may be called when a looping stream hits the end or
+     * when parameters have changed to account for time drift.
+     */
+    public void onMediaDescriptionUpdated() {
+        // default implementation empty
+    }
+
+    /**
+     * Broadcast Signal Strength updated.
+     *
+     * This signal strength is the BROADCAST signal strength which,
+     * depending on technology in play and it's deployment, may be
+     * stronger or weaker than the traditional UNICAST signal
+     * strength.  It a simple int from 0-4 for valid levels or
+     * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
+     * for this service due to timing, geography or popularity.
+     */
+    public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+        // default implementation empty
+    }
+
+    /**
+     * Notify of bcast/unicast method being used.
+     *
+     * This is intended to be informational.  Indicates
+     * whether we're able to use cell broadcast or have
+     * had to fallback to unicast for this stream.
+     *
+     * This must be called once at the beginning of the stream
+     * around the same time as we change to STATE_STARTED, but
+     * strict ordering is not specified.  It must be called
+     * again if we change modes, but if that doesn't happen
+     * the callback won't be used again.
+     *
+     * See {@link StreamingService#BROADCAST_METHOD} and
+     * {@link StreamingService#UNICAST_METHOD}
+     */
+    public void onStreamMethodUpdated(int methodType) {
+        // default implementation empty
+    }
+}
diff --git a/android-35/android/telephony/mbms/StreamingServiceInfo.java b/android-35/android/telephony/mbms/StreamingServiceInfo.java
new file mode 100644
index 0000000..316e865
--- /dev/null
+++ b/android-35/android/telephony/mbms/StreamingServiceInfo.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 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.mbms;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Describes a single MBMS streaming service.
+ */
+public final class StreamingServiceInfo extends ServiceInfo implements Parcelable {
+
+    /**
+     * @param names User displayable names listed by language.
+     * @param className The class name for this service - used by frontend apps to categorize and
+     *                  filter.
+     * @param locales The languages available for this service content.
+     * @param serviceId The carrier's identifier for the service.
+     * @param start The start time indicating when this service will be available.
+     * @param end The end time indicating when this session stops being available.
+     * @hide
+     */
+    @SystemApi
+    public StreamingServiceInfo(Map<Locale, String> names, String className,
+            List<Locale> locales, String serviceId, Date start, Date end) {
+        super(names, className, locales, serviceId, start, end);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<StreamingServiceInfo> CREATOR =
+            new Parcelable.Creator<StreamingServiceInfo>() {
+        @Override
+        public StreamingServiceInfo createFromParcel(Parcel source) {
+            return new StreamingServiceInfo(source);
+        }
+
+        @Override
+        public StreamingServiceInfo[] newArray(int size) {
+            return new StreamingServiceInfo[size];
+        }
+    };
+
+    private StreamingServiceInfo(Parcel in) {
+        super(in);
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/android-35/android/telephony/mbms/UriPathPair.java b/android-35/android/telephony/mbms/UriPathPair.java
new file mode 100644
index 0000000..54d9d9e
--- /dev/null
+++ b/android-35/android/telephony/mbms/UriPathPair.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 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.mbms;
+
+import android.annotation.SystemApi;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.mbms.vendor.VendorUtils;
+
+/**
+ * Wrapper for a pair of {@link Uri}s that describe a temp file used by the middleware to
+ * download files via cell-broadcast.
+ * @hide
+ */
+@SystemApi
+public final class UriPathPair implements Parcelable {
+    private final Uri mFilePathUri;
+    private final Uri mContentUri;
+
+    /** @hide */
+    public UriPathPair(Uri fileUri, Uri contentUri) {
+        if (fileUri == null || !ContentResolver.SCHEME_FILE.equals(fileUri.getScheme())) {
+            throw new IllegalArgumentException("File URI must have file scheme");
+        }
+        if (contentUri == null || !ContentResolver.SCHEME_CONTENT.equals(contentUri.getScheme())) {
+            throw new IllegalArgumentException("Content URI must have content scheme");
+        }
+
+        mFilePathUri = fileUri;
+        mContentUri = contentUri;
+    }
+
+    /** @hide */
+    private UriPathPair(Parcel in) {
+        mFilePathUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
+        mContentUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
+    }
+
+    public static final @android.annotation.NonNull Creator<UriPathPair> CREATOR = new Creator<UriPathPair>() {
+        @Override
+        public UriPathPair createFromParcel(Parcel in) {
+            return new UriPathPair(in);
+        }
+
+        @Override
+        public UriPathPair[] newArray(int size) {
+            return new UriPathPair[size];
+        }
+    };
+
+    /**
+     * Returns the file-path {@link Uri}. This has scheme {@code file} and points to the actual
+     * location on disk where the temp file resides. Use this when sending {@link Uri}s back to the
+     * app in the intents in {@link VendorUtils}.
+     * @return A {@code file} {@link Uri}.
+     */
+    public Uri getFilePathUri() {
+        return mFilePathUri;
+    }
+
+    /**
+     * Returns the content {@link Uri} that may be used with
+     * {@link ContentResolver#openFileDescriptor(Uri, String)} to obtain a
+     * {@link android.os.ParcelFileDescriptor} to a temp file to write to. This {@link Uri} will
+     * expire if the middleware process dies.
+     * @return A {@code content} {@link Uri}
+     */
+    public Uri getContentUri() {
+        return mContentUri;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mFilePathUri, flags);
+        dest.writeParcelable(mContentUri, flags);
+    }
+}
diff --git a/android-35/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/android-35/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
new file mode 100644
index 0000000..ffc1d2e
--- /dev/null
+++ b/android-35/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2017 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.mbms.vendor;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.MbmsDownloadSession;
+import android.telephony.mbms.DownloadProgressListener;
+import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.DownloadStatusListener;
+import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.FileServiceInfo;
+import android.telephony.mbms.IDownloadProgressListener;
+import android.telephony.mbms.IDownloadStatusListener;
+import android.telephony.mbms.IMbmsDownloadSessionCallback;
+import android.telephony.mbms.MbmsDownloadSessionCallback;
+import android.telephony.mbms.MbmsErrors;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class for MbmsDownloadService. The middleware should return an instance of this object from
+ * its {@link android.app.Service#onBind(Intent)} method.
+ * @hide
+ */
+@SystemApi
+public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
+    private final Map<IBinder, DownloadStatusListener> mDownloadStatusListenerBinderMap =
+            new HashMap<>();
+    private final Map<IBinder, DownloadProgressListener> mDownloadProgressListenerBinderMap =
+            new HashMap<>();
+    private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>();
+
+    private abstract static class VendorDownloadStatusListener extends DownloadStatusListener {
+        private final IDownloadStatusListener mListener;
+        public VendorDownloadStatusListener(IDownloadStatusListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void onStatusUpdated(DownloadRequest request, FileInfo fileInfo,
+                @MbmsDownloadSession.DownloadStatus int state) {
+            try {
+                mListener.onStatusUpdated(request, fileInfo, state);
+            } catch (RemoteException e) {
+                onRemoteException(e);
+            }
+        }
+
+        protected abstract void onRemoteException(RemoteException e);
+    }
+
+    private abstract static class VendorDownloadProgressListener extends DownloadProgressListener {
+        private final IDownloadProgressListener mListener;
+
+        public VendorDownloadProgressListener(IDownloadProgressListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
+                int currentDownloadSize, int fullDownloadSize, int currentDecodedSize,
+                int fullDecodedSize) {
+            try {
+                mListener.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                        fullDownloadSize, currentDecodedSize, fullDecodedSize);
+            } catch (RemoteException e) {
+                onRemoteException(e);
+            }
+        }
+
+        protected abstract void onRemoteException(RemoteException e);
+    }
+
+    /**
+     * Initialize the download service for this app and subId, registering the listener.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}, which
+     * will be intercepted and passed to the app as
+     * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
+     *
+     * May return any value from {@link MbmsErrors.InitializationErrors}
+     * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via
+     * {@link IMbmsDownloadSessionCallback#onError(int, String)}.
+     *
+     * @param callback The callback to use to communicate with the app.
+     * @param subscriptionId The subscription ID to use.
+     */
+    public int initialize(int subscriptionId, MbmsDownloadSessionCallback callback)
+            throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation -- hides the callback AIDL from the API.
+     * @hide
+     */
+    @Override
+    public final int initialize(final int subscriptionId,
+            final IMbmsDownloadSessionCallback callback) throws RemoteException {
+        if (callback == null) {
+            throw new NullPointerException("Callback must not be null");
+        }
+
+        final int uid = Binder.getCallingUid();
+
+        int result = initialize(subscriptionId, new MbmsDownloadSessionCallback() {
+            @Override
+            public void onError(int errorCode, String message) {
+                try {
+                    if (errorCode == MbmsErrors.UNKNOWN) {
+                        throw new IllegalArgumentException(
+                                "Middleware cannot send an unknown error.");
+                    }
+                    callback.onError(errorCode, message);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onFileServicesUpdated(List<FileServiceInfo> services) {
+                try {
+                    callback.onFileServicesUpdated(services);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onMiddlewareReady() {
+                try {
+                    callback.onMiddlewareReady();
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+        });
+
+        if (result == MbmsErrors.SUCCESS) {
+            callback.asBinder().linkToDeath(new DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }, 0);
+        }
+
+        return result;
+    }
+
+    /**
+     * Registers serviceClasses of interest with the appName/subId key.
+     * Starts async fetching data on streaming services of matching classes to be reported
+     * later via {@link IMbmsDownloadSessionCallback#onFileServicesUpdated(List)}
+     *
+     * Note that subsequent calls with the same uid and subId will replace
+     * the service class list.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * @param subscriptionId The subscription id to use.
+     * @param serviceClasses The service classes that the app wishes to get info on. The strings
+     *                       may contain arbitrary data as negotiated between the app and the
+     *                       carrier.
+     * @return One of {@link MbmsErrors#SUCCESS} or
+     *         {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY},
+     */
+    @Override
+    public int requestUpdateFileServices(int subscriptionId, List<String> serviceClasses)
+            throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Sets the temp file root directory for this app/subscriptionId combination. The middleware
+     * should persist {@code rootDirectoryPath} and send it back when sending intents to the
+     * app's {@link android.telephony.mbms.MbmsDownloadReceiver}.
+     *
+     * If the calling app (as identified by the calling UID) currently has any pending download
+     * requests that have not been canceled, the middleware must return
+     * {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} here.
+     *
+     * @param subscriptionId The subscription id the download is operating under.
+     * @param rootDirectoryPath The path to the app's temp file root directory.
+     * @return {@link MbmsErrors#SUCCESS},
+     *         {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} or
+     *         {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
+     */
+    @Override
+    public int setTempFileRootDirectory(int subscriptionId,
+            String rootDirectoryPath) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Called when the client application wishes to receive file information according to a
+     * service announcement descriptor received from a group call server.
+     *
+     * The service announcement descriptor is in the format of a multipart MIME file with XML parts,
+     * though no validation is performed on the contents of the {@code contents} argument --
+     * implementing middleware applications should perform their own validation and return
+     * {@link MbmsErrors.DownloadErrors#ERROR_MALFORMED_SERVICE_ANNOUNCEMENT} if the descriptor is
+     * malformed.
+     *
+     * @param subscriptionId The subscription id the service announcement applies to.
+     * @param contents The contents of the service announcement descriptor.
+     * @return {@link MbmsErrors#SUCCESS}, or
+     *         {@link MbmsErrors.DownloadErrors#ERROR_MALFORMED_SERVICE_ANNOUNCEMENT}
+     */
+    // TODO: are there any public specifications of what the file format is that I can link to?
+    @Override
+    public @MbmsErrors.MbmsError int addServiceAnnouncement(
+            int subscriptionId, @NonNull byte[] contents) {
+        throw new UnsupportedOperationException("addServiceAnnouncement not supported by"
+                + " this middleware.");
+    }
+
+    /**
+     * Issues a request to download a set of files.
+     *
+     * The middleware should expect that {@link #setTempFileRootDirectory(int, String)} has been
+     * called for this app between when the app was installed and when this method is called. If
+     * this is not the case, an {@link IllegalStateException} may be thrown.
+     *
+     * @param downloadRequest An object describing the set of files to be downloaded.
+     * @return Any error from {@link MbmsErrors.GeneralErrors}
+     *         or {@link MbmsErrors#SUCCESS}
+     */
+    @Override
+    public int download(DownloadRequest downloadRequest) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Registers a download status listener for the provided {@link DownloadRequest}.
+     *
+     * This method is called by the app when it wants to request updates on the status of
+     * the download.
+     *
+     * If the middleware is not aware of a download having been requested with the provided
+     * {@link DownloadRequest} in the past,
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+     * must be returned.
+     *
+     * @param downloadRequest The {@link DownloadRequest} that was used to initiate the download
+     *                        for which progress updates are being requested.
+     * @param listener The listener object to use.
+     */
+    public int addStatusListener(DownloadRequest downloadRequest,
+            DownloadStatusListener listener) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
+     * @hide
+     */
+    @Override
+    public final int addStatusListener(final DownloadRequest downloadRequest,
+            final IDownloadStatusListener listener) throws RemoteException {
+        final int uid = Binder.getCallingUid();
+        if (downloadRequest == null) {
+            throw new NullPointerException("Download request must not be null");
+        }
+        if (listener == null) {
+            throw new NullPointerException("Callback must not be null");
+        }
+
+        DownloadStatusListener exposedCallback = new VendorDownloadStatusListener(listener) {
+            @Override
+            protected void onRemoteException(RemoteException e) {
+                onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+            }
+        };
+
+        int result = addStatusListener(downloadRequest, exposedCallback);
+
+        if (result == MbmsErrors.SUCCESS) {
+            DeathRecipient deathRecipient = new DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+                    mDownloadStatusListenerBinderMap.remove(listener.asBinder());
+                    mDownloadCallbackDeathRecipients.remove(listener.asBinder());
+                }
+            };
+            mDownloadCallbackDeathRecipients.put(listener.asBinder(), deathRecipient);
+            listener.asBinder().linkToDeath(deathRecipient, 0);
+            mDownloadStatusListenerBinderMap.put(listener.asBinder(), exposedCallback);
+        }
+
+        return result;
+    }
+
+    /**
+     * Un-registers a download status listener for the provided {@link DownloadRequest}.
+     *
+     * This method is called by the app when it no longer wants to request status updates on the
+     * download.
+     *
+     * If the middleware is not aware of a download having been requested with the provided
+     * {@link DownloadRequest} in the past,
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+     * must be returned.
+     *
+     * @param downloadRequest The {@link DownloadRequest} that was used to register the callback
+     * @param listener The callback object that
+     *                 {@link #addStatusListener(DownloadRequest, DownloadStatusListener)}
+     *                 was called with.
+     */
+    public int removeStatusListener(DownloadRequest downloadRequest,
+            DownloadStatusListener listener) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
+     * @hide
+     */
+    public final int removeStatusListener(
+            final DownloadRequest downloadRequest, final IDownloadStatusListener listener)
+            throws RemoteException {
+        if (downloadRequest == null) {
+            throw new NullPointerException("Download request must not be null");
+        }
+        if (listener == null) {
+            throw new NullPointerException("Callback must not be null");
+        }
+
+        DeathRecipient deathRecipient =
+                mDownloadCallbackDeathRecipients.remove(listener.asBinder());
+        if (deathRecipient == null) {
+            throw new IllegalArgumentException("Unknown listener");
+        }
+
+        listener.asBinder().unlinkToDeath(deathRecipient, 0);
+
+        DownloadStatusListener exposedCallback =
+                mDownloadStatusListenerBinderMap.remove(listener.asBinder());
+        if (exposedCallback == null) {
+            throw new IllegalArgumentException("Unknown listener");
+        }
+
+        return removeStatusListener(downloadRequest, exposedCallback);
+    }
+
+    /**
+     * Registers a download progress listener for the provided {@link DownloadRequest}.
+     *
+     * This method is called by the app when it wants to request updates on the progress of
+     * the download.
+     *
+     * If the middleware is not aware of a download having been requested with the provided
+     * {@link DownloadRequest} in the past,
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+     * must be returned.
+     *
+     * @param downloadRequest The {@link DownloadRequest} that was used to initiate the download
+     *                        for which progress updates are being requested.
+     * @param listener The listener object to use.
+     */
+    public int addProgressListener(DownloadRequest downloadRequest,
+            DownloadProgressListener listener) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
+     * @hide
+     */
+    @Override
+    public final int addProgressListener(final DownloadRequest downloadRequest,
+            final IDownloadProgressListener listener) throws RemoteException {
+        final int uid = Binder.getCallingUid();
+        if (downloadRequest == null) {
+            throw new NullPointerException("Download request must not be null");
+        }
+        if (listener == null) {
+            throw new NullPointerException("Callback must not be null");
+        }
+
+        DownloadProgressListener exposedCallback = new VendorDownloadProgressListener(listener) {
+            @Override
+            protected void onRemoteException(RemoteException e) {
+                onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+            }
+        };
+
+        int result = addProgressListener(downloadRequest, exposedCallback);
+
+        if (result == MbmsErrors.SUCCESS) {
+            DeathRecipient deathRecipient = new DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+                    mDownloadProgressListenerBinderMap.remove(listener.asBinder());
+                    mDownloadCallbackDeathRecipients.remove(listener.asBinder());
+                }
+            };
+            mDownloadCallbackDeathRecipients.put(listener.asBinder(), deathRecipient);
+            listener.asBinder().linkToDeath(deathRecipient, 0);
+            mDownloadProgressListenerBinderMap.put(listener.asBinder(), exposedCallback);
+        }
+
+        return result;
+    }
+
+    /**
+     * Un-registers a download progress listener for the provided {@link DownloadRequest}.
+     *
+     * This method is called by the app when it no longer wants to request progress updates on the
+     * download.
+     *
+     * If the middleware is not aware of a download having been requested with the provided
+     * {@link DownloadRequest} in the past,
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+     * must be returned.
+     *
+     * @param downloadRequest The {@link DownloadRequest} that was used to register the callback
+     * @param listener The callback object that
+     *                 {@link #addProgressListener(DownloadRequest, DownloadProgressListener)}
+     *                 was called with.
+     */
+    public int removeProgressListener(DownloadRequest downloadRequest,
+            DownloadProgressListener listener) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
+     * @hide
+     */
+    public final int removeProgressListener(
+            final DownloadRequest downloadRequest, final IDownloadProgressListener listener)
+            throws RemoteException {
+        if (downloadRequest == null) {
+            throw new NullPointerException("Download request must not be null");
+        }
+        if (listener == null) {
+            throw new NullPointerException("Callback must not be null");
+        }
+
+        DeathRecipient deathRecipient =
+                mDownloadCallbackDeathRecipients.remove(listener.asBinder());
+        if (deathRecipient == null) {
+            throw new IllegalArgumentException("Unknown listener");
+        }
+
+        listener.asBinder().unlinkToDeath(deathRecipient, 0);
+
+        DownloadProgressListener exposedCallback =
+                mDownloadProgressListenerBinderMap.remove(listener.asBinder());
+        if (exposedCallback == null) {
+            throw new IllegalArgumentException("Unknown listener");
+        }
+
+        return removeProgressListener(downloadRequest, exposedCallback);
+    }
+
+    /**
+     * Returns a list of pending {@link DownloadRequest}s that originated from the calling
+     * application, identified by its uid. A pending request is one that was issued via
+     * {@link #download(DownloadRequest)} but not cancelled through
+     * {@link #cancelDownload(DownloadRequest)}.
+     * The middleware must return a non-null result synchronously or throw an exception
+     * inheriting from {@link RuntimeException}.
+     * @return A list, possibly empty, of {@link DownloadRequest}s
+     */
+    @Override
+    public @NonNull List<DownloadRequest> listPendingDownloads(int subscriptionId)
+            throws RemoteException {
+        return null;
+    }
+
+    /**
+     * Issues a request to cancel the specified download request.
+     *
+     * If the middleware is unable to cancel the request for whatever reason, it should return
+     * synchronously with an error. If this method returns {@link MbmsErrors#SUCCESS}, the app
+     * will no longer be expecting any more file-completed intents from the middleware for this
+     * {@link DownloadRequest}.
+     * @param downloadRequest The request to cancel
+     * @return {@link MbmsErrors#SUCCESS},
+     *         {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST},
+     *         {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
+     */
+    @Override
+    public int cancelDownload(DownloadRequest downloadRequest) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Requests information about the state of a file pending download.
+     *
+     * If the middleware has no records of the
+     * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_FILE_INFO} must be returned.
+     *
+     * @param downloadRequest The download request to query.
+     * @param fileInfo The particular file within the request to get information on.
+     * @return {@link MbmsErrors#SUCCESS} if the request was successful, an error code otherwise.
+     */
+    @Override
+    public int requestDownloadState(DownloadRequest downloadRequest, FileInfo fileInfo)
+            throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Resets the middleware's knowledge of previously-downloaded files in this download request.
+     *
+     * When this method is called, the middleware must attempt to re-download all the files
+     * specified by the {@link DownloadRequest}, even if the files have not changed on the server.
+     * In addition, current in-progress downloads must not be interrupted.
+     *
+     * If the middleware is not aware of the specified download request, return
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
+     *
+     * @param downloadRequest The request to re-download files for.
+     */
+    @Override
+    public int resetDownloadKnowledge(DownloadRequest downloadRequest)
+            throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Signals that the app wishes to dispose of the session identified by the
+     * {@code subscriptionId} argument and the caller's uid. No notification back to the
+     * app is required for this operation, and the corresponding callback provided via
+     * {@link #initialize(int, IMbmsDownloadSessionCallback)} should no longer be used
+     * after this method has been called by the app.
+     *
+     * Any download requests issued by the app should remain in effect until the app calls
+     * {@link #cancelDownload(DownloadRequest)} on another session.
+     *
+     * May throw an {@link IllegalStateException}
+     *
+     * @param subscriptionId The subscription id to use.
+     */
+    @Override
+    public void dispose(int subscriptionId) throws RemoteException {
+    }
+
+    /**
+     * Indicates that the app identified by the given UID and subscription ID has died.
+     * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}.
+     * @param subscriptionId The subscription ID the app is using.
+     */
+    public void onAppCallbackDied(int uid, int subscriptionId) {
+    }
+
+    // Following two methods exist to workaround b/124210145
+    /** @hide */
+    @SystemApi
+    @Override
+    public android.os.IBinder asBinder() {
+        return super.asBinder();
+    }
+
+    /** @hide */
+    @SystemApi
+    @Override
+    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
+            int flags) throws RemoteException {
+        return super.onTransact(code, data, reply, flags);
+    }
+}
diff --git a/android-35/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/android-35/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
new file mode 100644
index 0000000..e5b18bb
--- /dev/null
+++ b/android-35/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2018 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.mbms.vendor;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.mbms.GroupCallCallback;
+import android.telephony.mbms.IGroupCallCallback;
+import android.telephony.mbms.IMbmsGroupCallSessionCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsGroupCallSessionCallback;
+import android.telephony.mbms.vendor.IMbmsGroupCallService.Stub;
+
+import java.util.List;
+
+/**
+ * Base class for MBMS group-call services. The middleware should override this class to implement
+ * its {@link Service} for group calls
+ * Subclasses should call this class's {@link #onBind} method to obtain an {@link IBinder} if they
+ * override {@link #onBind}.
+ * @hide
+ */
+@SystemApi
+public class MbmsGroupCallServiceBase extends Service {
+    private final IBinder mInterface = new Stub() {
+        @Override
+        public int initialize(final IMbmsGroupCallSessionCallback callback,
+                final int subscriptionId) throws RemoteException {
+            if (callback == null) {
+                throw new NullPointerException("Callback must not be null");
+            }
+
+            final int uid = Binder.getCallingUid();
+
+            int result = MbmsGroupCallServiceBase.this.initialize(
+                    new MbmsGroupCallSessionCallback() {
+                        @Override
+                        public void onError(final int errorCode, final String message) {
+                            try {
+                                if (errorCode == MbmsErrors.UNKNOWN) {
+                                    throw new IllegalArgumentException(
+                                            "Middleware cannot send an unknown error.");
+                                }
+                                callback.onError(errorCode, message);
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+
+                        @Override
+                        public void onAvailableSaisUpdated(final List currentSais,
+                                final List availableSais) {
+                            try {
+                                callback.onAvailableSaisUpdated(currentSais, availableSais);
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+
+                        @Override
+                        public void onServiceInterfaceAvailable(final String interfaceName,
+                                final int index) {
+                            try {
+                                callback.onServiceInterfaceAvailable(interfaceName, index);
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+
+                        @Override
+                        public void onMiddlewareReady() {
+                            try {
+                                callback.onMiddlewareReady();
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+                    }, subscriptionId);
+
+            if (result == MbmsErrors.SUCCESS) {
+                callback.asBinder().linkToDeath(new DeathRecipient() {
+                    @Override
+                    public void binderDied() {
+                        onAppCallbackDied(uid, subscriptionId);
+                    }
+                }, 0);
+            }
+
+            return result;
+        }
+
+        @Override
+        public void stopGroupCall(int subId, long tmgi) {
+            MbmsGroupCallServiceBase.this.stopGroupCall(subId, tmgi);
+        }
+
+        @Override
+        public void updateGroupCall(int subscriptionId, long tmgi, List saiList,
+                List frequencyList) {
+            MbmsGroupCallServiceBase.this.updateGroupCall(
+                    subscriptionId, tmgi, saiList, frequencyList);
+        }
+
+        @Override
+        public int startGroupCall(final int subscriptionId, final long tmgi,
+                final List saiList,
+                final List frequencyList, final IGroupCallCallback callback)
+                throws RemoteException {
+            if (callback == null) {
+                throw new NullPointerException("Callback must not be null");
+            }
+
+            final int uid = Binder.getCallingUid();
+
+            int result = MbmsGroupCallServiceBase.this.startGroupCall(
+                    subscriptionId, tmgi, saiList, frequencyList, new GroupCallCallback() {
+                        @Override
+                        public void onError(final int errorCode, final String message) {
+                            try {
+                                if (errorCode == MbmsErrors.UNKNOWN) {
+                                    throw new IllegalArgumentException(
+                                            "Middleware cannot send an unknown error.");
+                                }
+                                callback.onError(errorCode, message);
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+
+                        public void onGroupCallStateChanged(int state, int reason) {
+                            try {
+                                callback.onGroupCallStateChanged(state, reason);
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+
+                        public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+                            try {
+                                callback.onBroadcastSignalStrengthUpdated(signalStrength);
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+                    });
+
+            if (result == MbmsErrors.SUCCESS) {
+                callback.asBinder().linkToDeath(new DeathRecipient() {
+                    @Override
+                    public void binderDied() {
+                        onAppCallbackDied(uid, subscriptionId);
+                    }
+                }, 0);
+            }
+
+            return result;
+        }
+
+        @Override
+        public void dispose(int subId) throws RemoteException {
+            MbmsGroupCallServiceBase.this.dispose(subId);
+        }
+    };
+
+    /**
+     * Initialize the group call service for this app and subscription ID, registering the callback.
+     *
+     * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which
+     * will be intercepted and passed to the app as
+     * {@link MbmsErrors.InitializationErrtrors#ERROR_UNABLE_TO_INITIALIZE}
+     *
+     * May return any value from {@link MbmsErrors.InitializationErrors}
+     * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via
+     * {@link IMbmsGroupCallSessionCallback#onError(int, String)}.
+     *
+     * @param callback The callback to use to communicate with the app.
+     * @param subscriptionId The subscription ID to use.
+     */
+    public int initialize(@NonNull MbmsGroupCallSessionCallback callback, int subscriptionId)
+            throws RemoteException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
+     * Starts a particular group call. This method may perform asynchronous work. When
+     * the call is ready for consumption, the middleware should inform the app via
+     * {@link IGroupCallCallback#onGroupCallStateChanged(int, int)}.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * @param subscriptionId The subscription id to use.
+     * @param tmgi The TMGI, an identifier for the group call.
+     * @param saiList A list of SAIs for the group call.
+     * @param frequencyList A list of frequencies for the group call.
+     * @param callback The callback object on which the app wishes to receive updates.
+     * @return Any error in {@link MbmsErrors.GeneralErrors}
+     */
+    public int startGroupCall(int subscriptionId, long tmgi, @NonNull List<Integer> saiList,
+            @NonNull List<Integer> frequencyList, @NonNull GroupCallCallback callback) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
+     * Stop the group call identified by {@code tmgi}.
+     *
+     * The callback provided via {@link #startGroupCall} should no longer be
+     * used after this method has called by the app.
+     *
+     * May throw an {@link IllegalStateException}
+     *
+     * @param subscriptionId The subscription id to use.
+     * @param tmgi The TMGI for the call to stop.
+     */
+    public void stopGroupCall(int subscriptionId, long tmgi) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
+     * Called when the app receives new SAI and frequency information for the group call identified
+     * by {@code tmgi}.
+     * @param saiList New list of SAIs that the call is available on.
+     * @param frequencyList New list of frequencies that the call is available on.
+     */
+    public void updateGroupCall(int subscriptionId, long tmgi, @NonNull List<Integer> saiList,
+            @NonNull List<Integer> frequencyList) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
+     * Signals that the app wishes to dispose of the session identified by the
+     * {@code subscriptionId} argument and the caller's uid. No notification back to the
+     * app is required for this operation, and the corresponding callback provided via
+     * {@link #initialize} should no longer be used
+     * after this method has been called by the app.
+     *
+     * May throw an {@link IllegalStateException}
+     *
+     * @param subscriptionId The subscription id to use.
+     */
+    public void dispose(int subscriptionId) throws RemoteException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
+     * Indicates that the app identified by the given UID and subscription ID has died.
+     * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}.
+     * @param subscriptionId The subscription ID the app is using.
+     */
+    public void onAppCallbackDied(int uid, int subscriptionId) {
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mInterface;
+    }
+}
diff --git a/android-35/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/android-35/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
new file mode 100644
index 0000000..e169b16
--- /dev/null
+++ b/android-35/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2017 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.mbms.vendor;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.mbms.IMbmsStreamingSessionCallback;
+import android.telephony.mbms.IStreamingServiceCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsStreamingSessionCallback;
+import android.telephony.mbms.StreamingService;
+import android.telephony.mbms.StreamingServiceCallback;
+import android.telephony.mbms.StreamingServiceInfo;
+
+import java.util.List;
+
+/**
+ * Base class for MBMS streaming services. The middleware should return an instance of this
+ * object from its {@link android.app.Service#onBind(Intent)} method.
+ * @hide
+ */
+@SystemApi
+public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
+    /**
+     * Initialize streaming service for this app and subId, registering the listener.
+     *
+     * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which
+     * will be intercepted and passed to the app as
+     * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
+     *
+     * May return any value from {@link MbmsErrors.InitializationErrors}
+     * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via
+     * {@link IMbmsStreamingSessionCallback#onError(int, String)}.
+     *
+     * @param callback The callback to use to communicate with the app.
+     * @param subscriptionId The subscription ID to use.
+     */
+    public int initialize(MbmsStreamingSessionCallback callback, int subscriptionId)
+            throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation that hides the callback AIDL from the middleware.
+     * @hide
+     */
+    @Override
+    public final int initialize(final IMbmsStreamingSessionCallback callback,
+            final int subscriptionId) throws RemoteException {
+        if (callback == null) {
+            throw new NullPointerException("Callback must not be null");
+        }
+
+        final int uid = Binder.getCallingUid();
+
+        int result = initialize(new MbmsStreamingSessionCallback() {
+            @Override
+            public void onError(final int errorCode, final String message) {
+                try {
+                    if (errorCode == MbmsErrors.UNKNOWN) {
+                        throw new IllegalArgumentException(
+                                "Middleware cannot send an unknown error.");
+                    }
+                    callback.onError(errorCode, message);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onStreamingServicesUpdated(final List<StreamingServiceInfo> services) {
+                try {
+                    callback.onStreamingServicesUpdated(services);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onMiddlewareReady() {
+                try {
+                    callback.onMiddlewareReady();
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+        }, subscriptionId);
+
+        if (result == MbmsErrors.SUCCESS) {
+            callback.asBinder().linkToDeath(new DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }, 0);
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Registers serviceClasses of interest with the appName/subId key.
+     * Starts async fetching data on streaming services of matching classes to be reported
+     * later via {@link IMbmsStreamingSessionCallback#onStreamingServicesUpdated(List)}
+     *
+     * Note that subsequent calls with the same uid and subId will replace
+     * the service class list.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * @param subscriptionId The subscription id to use.
+     * @param serviceClasses The service classes that the app wishes to get info on. The strings
+     *                       may contain arbitrary data as negotiated between the app and the
+     *                       carrier.
+     * @return {@link MbmsErrors#SUCCESS} or any of the errors in
+     * {@link MbmsErrors.GeneralErrors}
+     */
+    @Override
+    public int requestUpdateStreamingServices(int subscriptionId,
+            List<String> serviceClasses) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Starts streaming on a particular service. This method may perform asynchronous work. When
+     * the middleware is ready to send bits to the frontend, it should inform the app via
+     * {@link IStreamingServiceCallback#onStreamStateUpdated(int, int)}.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * @param subscriptionId The subscription id to use.
+     * @param serviceId The ID of the streaming service that the app has requested.
+     * @param callback The callback object on which the app wishes to receive updates.
+     * @return Any error in {@link MbmsErrors.GeneralErrors}
+     */
+    public int startStreaming(int subscriptionId, String serviceId,
+            StreamingServiceCallback callback) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation of startStreaming that hides the callback AIDL from the
+     * middleware.
+     * @hide
+     */
+    @Override
+    public int startStreaming(final int subscriptionId, String serviceId,
+            final IStreamingServiceCallback callback) throws RemoteException {
+        if (callback == null) {
+            throw new NullPointerException("Callback must not be null");
+        }
+
+        final int uid = Binder.getCallingUid();
+
+        int result = startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() {
+            @Override
+            public void onError(final int errorCode, final String message) {
+                try {
+                    if (errorCode == MbmsErrors.UNKNOWN) {
+                        throw new IllegalArgumentException(
+                                "Middleware cannot send an unknown error.");
+                    }
+                    callback.onError(errorCode, message);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onStreamStateUpdated(@StreamingService.StreamingState final int state,
+                    @StreamingService.StreamingStateChangeReason final int reason) {
+                try {
+                    callback.onStreamStateUpdated(state, reason);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onMediaDescriptionUpdated() {
+                try {
+                    callback.onMediaDescriptionUpdated();
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onBroadcastSignalStrengthUpdated(final int signalStrength) {
+                try {
+                    callback.onBroadcastSignalStrengthUpdated(signalStrength);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+
+            @Override
+            public void onStreamMethodUpdated(final int methodType) {
+                try {
+                    callback.onStreamMethodUpdated(methodType);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }
+        });
+
+        if (result == MbmsErrors.SUCCESS) {
+            callback.asBinder().linkToDeath(new DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
+            }, 0);
+        }
+
+        return result;
+    }
+
+    /**
+     * Retrieves the streaming URI for a particular service. If the middleware is not yet ready to
+     * stream the service, this method may return null.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * @param subscriptionId The subscription id to use.
+     * @param serviceId The ID of the streaming service that the app has requested.
+     * @return An opaque {@link Uri} to be passed to a video player that understands the format.
+     */
+    @Override
+    public @Nullable Uri getPlaybackUri(int subscriptionId, String serviceId)
+            throws RemoteException {
+        return null;
+    }
+
+    /**
+     * Stop streaming the stream identified by {@code serviceId}. Notification of the resulting
+     * stream state change should be reported to the app via
+     * {@link IStreamingServiceCallback#onStreamStateUpdated(int, int)}.
+     *
+     * In addition, the callback provided via
+     * {@link #startStreaming(int, String, IStreamingServiceCallback)} should no longer be
+     * used after this method has called by the app.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * @param subscriptionId The subscription id to use.
+     * @param serviceId The ID of the streaming service that the app wishes to stop.
+     */
+    @Override
+    public void stopStreaming(int subscriptionId, String serviceId)
+            throws RemoteException {
+    }
+
+    /**
+     * Signals that the app wishes to dispose of the session identified by the
+     * {@code subscriptionId} argument and the caller's uid. No notification back to the
+     * app is required for this operation, and the corresponding callback provided via
+     * {@link #initialize(IMbmsStreamingSessionCallback, int)} should no longer be used
+     * after this method has been called by the app.
+     *
+     * May throw an {@link IllegalStateException}
+     *
+     * @param subscriptionId The subscription id to use.
+     */
+    @Override
+    public void dispose(int subscriptionId) throws RemoteException {
+    }
+
+    /**
+     * Indicates that the app identified by the given UID and subscription ID has died.
+     * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}.
+     * @param subscriptionId The subscription ID the app is using.
+     */
+    public void onAppCallbackDied(int uid, int subscriptionId) {
+    }
+
+
+    // Following two methods exist to workaround b/124210145
+    /** @hide */
+    @SystemApi
+    @Override
+    public android.os.IBinder asBinder() {
+        return super.asBinder();
+    }
+
+    /** @hide */
+    @SystemApi
+    @Override
+    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
+            int flags) throws RemoteException {
+        return super.onTransact(code, data, reply, flags);
+    }
+}
diff --git a/android-35/android/telephony/mbms/vendor/VendorUtils.java b/android-35/android/telephony/mbms/vendor/VendorUtils.java
new file mode 100644
index 0000000..a43f122
--- /dev/null
+++ b/android-35/android/telephony/mbms/vendor/VendorUtils.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 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.mbms.vendor;
+
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.telephony.MbmsDownloadSession;
+import android.telephony.mbms.MbmsDownloadReceiver;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Contains constants and utility methods for MBMS Download middleware apps to communicate with
+ * frontend apps.
+ * @hide
+ */
+@SystemApi
+public class VendorUtils {
+
+    /**
+     * The MBMS middleware should send this when a download of single file has completed or
+     * failed. The only mandatory extra is
+     * {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_RESULT}
+     * and the following are required when the download has completed:
+     * {@link MbmsDownloadSession#EXTRA_MBMS_FILE_INFO}
+     * {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_REQUEST}
+     * {@link #EXTRA_TEMP_LIST}
+     * {@link #EXTRA_FINAL_URI}
+     */
+    public static final String ACTION_DOWNLOAD_RESULT_INTERNAL =
+            "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL";
+
+    /**
+     * The MBMS middleware should send this when it wishes to request {@code content://} URIs to
+     * serve as temp files for downloads or when it wishes to resume paused downloads. Mandatory
+     * extras are
+     * {@link #EXTRA_SERVICE_ID}
+     *
+     * Optional extras are
+     * {@link #EXTRA_FD_COUNT} (0 if not present)
+     * {@link #EXTRA_PAUSED_LIST} (empty if not present)
+     */
+    public static final String ACTION_FILE_DESCRIPTOR_REQUEST =
+            "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST";
+
+    /**
+     * The MBMS middleware should send this when it wishes to clean up temp  files in the app's
+     * filesystem. Mandatory extras are:
+     * {@link #EXTRA_TEMP_FILES_IN_USE}
+     */
+    public static final String ACTION_CLEANUP =
+            "android.telephony.mbms.action.CLEANUP";
+
+    /**
+     * Extra containing a {@link List} of {@link Uri}s that were used as temp files for this
+     * completed file. These {@link Uri}s should have scheme {@code file://}, and the temp
+     * files will be deleted upon receipt of the intent.
+     * May be null.
+     */
+    public static final String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST";
+
+    /**
+     * Extra containing an integer indicating the number of temp files requested.
+     */
+    public static final String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT";
+
+    /**
+     * Extra containing a list of {@link Uri}s that the middleware is requesting access to via
+     * {@link #ACTION_FILE_DESCRIPTOR_REQUEST} in order to resume downloading. These {@link Uri}s
+     * should have scheme {@code file://}.
+     */
+    public static final String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST";
+
+    /**
+     * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the
+     * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These are temp files that are meant
+     * to be used for new file downloads.
+     */
+    public static final String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST";
+
+    /**
+     * Extra containing a list of {@link android.telephony.mbms.UriPathPair}s, used in the
+     * response to {@link #ACTION_FILE_DESCRIPTOR_REQUEST}. These
+     * {@link android.telephony.mbms.UriPathPair}s contain {@code content://} URIs that provide
+     * access to previously paused downloads.
+     */
+    public static final String EXTRA_PAUSED_URI_LIST =
+            "android.telephony.mbms.extra.PAUSED_URI_LIST";
+
+    /**
+     * Extra containing a string that points to the middleware's knowledge of where the temp file
+     * root for the app is. The path should be a canonical path as returned by
+     * {@link File#getCanonicalPath()}
+     */
+    public static final String EXTRA_TEMP_FILE_ROOT =
+            "android.telephony.mbms.extra.TEMP_FILE_ROOT";
+
+    /**
+     * Extra containing a list of {@link Uri}s indicating temp files which the middleware is
+     * still using.
+     */
+    public static final String EXTRA_TEMP_FILES_IN_USE =
+            "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
+
+    /**
+     * Extra containing a single {@link Uri} indicating the path to the temp file in which the
+     * decoded downloaded file resides. Must not be null.
+     */
+    public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
+
+    /**
+     * Extra containing a String representing a service ID, used by
+     * file-descriptor requests and cleanup requests to specify which service they want to
+     * request temp files or clean up temp files for, respectively.
+     */
+    public static final String EXTRA_SERVICE_ID =
+            "android.telephony.mbms.extra.SERVICE_ID";
+
+    /**
+     * Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that
+     * the various intents from the middleware should be targeted towards.
+     * @param packageName The package name of the app.
+     * @return The component name of the receiver that the middleware should send its intents to,
+     * or null if the app didn't declare it in the manifest.
+     */
+    public static ComponentName getAppReceiverFromPackageName(Context context, String packageName) {
+        ComponentName candidate = new ComponentName(packageName,
+                MbmsDownloadReceiver.class.getCanonicalName());
+        Intent queryIntent = new Intent();
+        queryIntent.setComponent(candidate);
+        List<ResolveInfo> receivers =
+                context.getPackageManager().queryBroadcastReceivers(queryIntent, 0);
+        if (receivers != null && receivers.size() > 0) {
+            return candidate;
+        }
+        return null;
+    }
+}
diff --git a/android-35/android/telephony/satellite/AntennaDirection.java b/android-35/android/telephony/satellite/AntennaDirection.java
new file mode 100644
index 0000000..c690f98
--- /dev/null
+++ b/android-35/android/telephony/satellite/AntennaDirection.java
@@ -0,0 +1,148 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * Antenna direction is provided as X/Y/Z values corresponding to the direction of the antenna
+ * main lobe as a unit vector in CTIA coordinate system (as specified in Appendix A of Wireless
+ * device CTIA OTAn test plan). CTIA coordinate system is defined relative to device’s screen
+ * when the device is held in default portrait mode with screen facing the user:
+ *
+ * Z axis is vertical along the plane of the device with positive Z pointing up and negative z
+ * pointing towards bottom of the device
+ * Y axis is horizontal along the plane of the device with positive Y pointing towards right of
+ * the phone screen and negative Y pointing towards left
+ * X axis is orthogonal to the Y-Z plane (phone screen), pointing away from the phone screen for
+ * positive X and pointing away from back of the phone for negative X.
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public final class AntennaDirection implements Parcelable {
+    /** Antenna x axis direction. */
+    private float mX;
+
+    /** Antenna y axis direction. */
+    private float mY;
+
+    /** Antenna z axis direction. */
+    private float mZ;
+
+    /**
+     * @hide
+     */
+    public AntennaDirection(float x, float y, float z) {
+        mX = x;
+        mY = y;
+        mZ = z;
+    }
+
+    private AntennaDirection(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeFloat(mX);
+        out.writeFloat(mY);
+        out.writeFloat(mZ);
+    }
+
+    @NonNull
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final Creator<AntennaDirection> CREATOR =
+            new Creator<>() {
+                @Override
+                public AntennaDirection createFromParcel(Parcel in) {
+                    return new AntennaDirection(in);
+                }
+
+                @Override
+                public AntennaDirection[] newArray(int size) {
+                    return new AntennaDirection[size];
+                }
+            };
+
+    @Override
+    @NonNull public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("X:");
+        sb.append(mX);
+        sb.append(",");
+
+        sb.append("Y:");
+        sb.append(mY);
+        sb.append(",");
+
+        sb.append("Z:");
+        sb.append(mZ);
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        AntennaDirection that = (AntennaDirection) o;
+        return mX == that.mX
+                && mY == that.mY
+                && mZ == that.mZ;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mX, mY, mZ);
+    }
+
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public float getX() {
+        return mX;
+    }
+
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public float getY() {
+        return mY;
+    }
+
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public float getZ() {
+        return mZ;
+    }
+
+    private void readFromParcel(Parcel in) {
+        mX = in.readFloat();
+        mY = in.readFloat();
+        mZ = in.readFloat();
+    }
+}
diff --git a/android-35/android/telephony/satellite/AntennaPosition.java b/android-35/android/telephony/satellite/AntennaPosition.java
new file mode 100644
index 0000000..d6440fc
--- /dev/null
+++ b/android-35/android/telephony/satellite/AntennaPosition.java
@@ -0,0 +1,126 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * Antenna Position received from satellite modem which gives information about antenna
+ * direction to be used with satellite communication and suggested device hold positions.
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public final class AntennaPosition implements Parcelable {
+    /** Antenna direction used for satellite communication. */
+    @NonNull private AntennaDirection mAntennaDirection;
+
+    /** Enum corresponding to device hold position to be used by the end user. */
+    @SatelliteManager.DeviceHoldPosition private int mSuggestedHoldPosition;
+
+    /**
+     * @hide
+     */
+    public AntennaPosition(@NonNull AntennaDirection antennaDirection, int suggestedHoldPosition) {
+        mAntennaDirection = antennaDirection;
+        mSuggestedHoldPosition = suggestedHoldPosition;
+    }
+
+    private AntennaPosition(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeParcelable(mAntennaDirection, flags);
+        out.writeInt(mSuggestedHoldPosition);
+    }
+
+    @NonNull
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final Creator<AntennaPosition> CREATOR =
+            new Creator<>() {
+                @Override
+                public AntennaPosition createFromParcel(Parcel in) {
+                    return new AntennaPosition(in);
+                }
+
+                @Override
+                public AntennaPosition[] newArray(int size) {
+                    return new AntennaPosition[size];
+                }
+            };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        AntennaPosition that = (AntennaPosition) o;
+        return Objects.equals(mAntennaDirection, that.mAntennaDirection)
+                && mSuggestedHoldPosition == that.mSuggestedHoldPosition;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAntennaDirection, mSuggestedHoldPosition);
+    }
+
+    @Override
+    @NonNull public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("antennaDirection:");
+        sb.append(mAntennaDirection);
+        sb.append(",");
+
+        sb.append("suggestedHoldPosition:");
+        sb.append(mSuggestedHoldPosition);
+        return sb.toString();
+    }
+
+    @NonNull
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public AntennaDirection getAntennaDirection() {
+        return mAntennaDirection;
+    }
+
+    @SatelliteManager.DeviceHoldPosition
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public int getSuggestedHoldPosition() {
+        return mSuggestedHoldPosition;
+    }
+
+    private void readFromParcel(Parcel in) {
+        mAntennaDirection = in.readParcelable(AntennaDirection.class.getClassLoader(),
+                AntennaDirection.class);
+        mSuggestedHoldPosition = in.readInt();
+    }
+}
diff --git a/android-35/android/telephony/satellite/EnableRequestAttributes.java b/android-35/android/telephony/satellite/EnableRequestAttributes.java
new file mode 100644
index 0000000..bc9d230
--- /dev/null
+++ b/android-35/android/telephony/satellite/EnableRequestAttributes.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 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.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * EnableRequestAttributes is used to store the attributes of the request
+ * {@link SatelliteManager#requestEnabled(EnableRequestAttributes, Executor, Consumer)}
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public class EnableRequestAttributes {
+    /** {@code true} to enable satellite and {@code false} to disable satellite */
+    private boolean mIsEnabled;
+    /**
+     * {@code true} to enable demo mode and {@code false} to disable. When disabling satellite,
+     * {@code mIsDemoMode} is always considered as {@code false} by Telephony.
+     */
+    private boolean mIsDemoMode;
+    /**
+     * {@code true} means satellite is enabled for emergency mode, {@code false} otherwise. When
+     * disabling satellite, {@code isEmergencyMode} is always considered as {@code false} by
+     * Telephony.
+     */
+    private boolean mIsEmergencyMode;
+
+    /**
+     * Constructor from builder.
+     *
+     * @param builder Builder of {@link EnableRequestAttributes}.
+     */
+    private EnableRequestAttributes(@NonNull Builder builder) {
+        this.mIsEnabled = builder.mIsEnabled;
+        this.mIsDemoMode = builder.mIsDemoMode;
+        this.mIsEmergencyMode = builder.mIsEmergencyMode;
+    }
+
+    /**
+     * @return Whether satellite is to be enabled
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    /**
+     * @return Whether demo mode is to be enabled
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public boolean isDemoMode() {
+        return mIsDemoMode;
+    }
+
+    /**
+     * @return Whether satellite is to be enabled for emergency mode
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public boolean isEmergencyMode() {
+        return mIsEmergencyMode;
+    }
+
+    /**
+     * The builder class of {@link EnableRequestAttributes}
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final class Builder {
+        private boolean mIsEnabled;
+        private boolean mIsDemoMode = false;
+        private boolean mIsEmergencyMode = false;
+
+        @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        public Builder(boolean isEnabled) {
+            mIsEnabled = isEnabled;
+        }
+
+        /**
+         * Set demo mode
+         *
+         * @param isDemoMode {@code true} to enable demo mode and {@code false} to disable. When
+         *                   disabling satellite, {@code isDemoMode} is always considered as
+         *                   {@code false} by Telephony.
+         * @return The builder object
+         */
+        @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        @NonNull
+        public Builder setDemoMode(boolean isDemoMode) {
+            if (mIsEnabled) {
+                mIsDemoMode = isDemoMode;
+            }
+            return this;
+        }
+
+        /**
+         * Set emergency mode
+         *
+         * @param isEmergencyMode {@code true} means satellite is enabled for emergency mode,
+         *                        {@code false} otherwise. When disabling satellite,
+         *                        {@code isEmergencyMode} is always considered as {@code false} by
+         *                        Telephony.
+         * @return The builder object
+         */
+        @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        @NonNull
+        public Builder setEmergencyMode(boolean isEmergencyMode) {
+            if (mIsEnabled) {
+                mIsEmergencyMode = isEmergencyMode;
+            }
+            return this;
+        }
+
+        /**
+         * Build the {@link EnableRequestAttributes}
+         *
+         * @return The {@link EnableRequestAttributes} instance.
+         */
+        @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        @NonNull
+        public EnableRequestAttributes build() {
+            return new EnableRequestAttributes(this);
+        }
+    }
+}
diff --git a/android-35/android/telephony/satellite/NtnSignalStrength.java b/android-35/android/telephony/satellite/NtnSignalStrength.java
new file mode 100644
index 0000000..2fec423
--- /dev/null
+++ b/android-35/android/telephony/satellite/NtnSignalStrength.java
@@ -0,0 +1,152 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * NTN signal strength related information.
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public final class NtnSignalStrength implements Parcelable {
+    /** Non-terrestrial network signal strength is not available. */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int NTN_SIGNAL_STRENGTH_NONE = 0;
+    /** Non-terrestrial network signal strength is poor. */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int NTN_SIGNAL_STRENGTH_POOR = 1;
+    /** Non-terrestrial network signal strength is moderate. */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int NTN_SIGNAL_STRENGTH_MODERATE = 2;
+    /** Non-terrestrial network signal strength is good. */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int NTN_SIGNAL_STRENGTH_GOOD = 3;
+    /** Non-terrestrial network signal strength is great. */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int NTN_SIGNAL_STRENGTH_GREAT = 4;
+    @NtnSignalStrengthLevel private int mLevel;
+
+    /** @hide */
+    @IntDef(prefix = "NTN_SIGNAL_STRENGTH_", value = {
+            NTN_SIGNAL_STRENGTH_NONE,
+            NTN_SIGNAL_STRENGTH_POOR,
+            NTN_SIGNAL_STRENGTH_MODERATE,
+            NTN_SIGNAL_STRENGTH_GOOD,
+            NTN_SIGNAL_STRENGTH_GREAT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NtnSignalStrengthLevel {}
+
+    /**
+     * Create a parcelable object to inform the current non-terrestrial signal strength
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public NtnSignalStrength(@NtnSignalStrengthLevel int level) {
+        this.mLevel = level;
+    }
+
+    /**
+     * This constructor is used to create a copy of an existing NtnSignalStrength object.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public NtnSignalStrength(@Nullable NtnSignalStrength source) {
+        this.mLevel = (source == null) ? NTN_SIGNAL_STRENGTH_NONE : source.getLevel();
+    }
+
+    private NtnSignalStrength(Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * Returns notified non-terrestrial network signal strength level.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    @NtnSignalStrengthLevel public int getLevel() {
+        return mLevel;
+    }
+
+    /**
+     * @return 0
+     */
+    @Override
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * @param out  The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written.
+     *              May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+     */
+    @Override
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mLevel);
+    }
+
+    private void readFromParcel(Parcel in) {
+        mLevel = in.readInt();
+    }
+
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    @NonNull public static final Creator<NtnSignalStrength> CREATOR =
+            new Creator<NtnSignalStrength>() {
+                @Override public NtnSignalStrength createFromParcel(Parcel in) {
+                    return new NtnSignalStrength(in);
+                }
+
+                @Override public NtnSignalStrength[] newArray(int size) {
+                    return new NtnSignalStrength[size];
+                }
+            };
+
+    @Override
+    public int hashCode() {
+        return mLevel;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null || getClass() != obj.getClass()) return false;
+
+        NtnSignalStrength that = (NtnSignalStrength) obj;
+        return mLevel == that.mLevel;
+    }
+
+    @Override public String toString() {
+        return "NtnSignalStrength{"
+                + "mLevel=" + mLevel
+                + '}';
+    }
+}
diff --git a/android-35/android/telephony/satellite/NtnSignalStrengthCallback.java b/android-35/android/telephony/satellite/NtnSignalStrengthCallback.java
new file mode 100644
index 0000000..4b79590
--- /dev/null
+++ b/android-35/android/telephony/satellite/NtnSignalStrengthCallback.java
@@ -0,0 +1,39 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * A callback class for notifying satellite signal strength change.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface NtnSignalStrengthCallback {
+    /**
+     * Called when non-terrestrial network signal strength changes.
+     * @param ntnSignalStrength The new non-terrestrial network signal strength.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    void onNtnSignalStrengthChanged(@NonNull NtnSignalStrength ntnSignalStrength);
+}
diff --git a/android-35/android/telephony/satellite/PointingInfo.java b/android-35/android/telephony/satellite/PointingInfo.java
new file mode 100644
index 0000000..9440b65
--- /dev/null
+++ b/android-35/android/telephony/satellite/PointingInfo.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2022 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.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * PointingInfo is used to store the position of satellite received from satellite modem.
+ * The position of satellite is represented by azimuth and elevation angles
+ * with degrees as unit of measurement. Satellite position is based on magnetic north direction.
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public final class PointingInfo implements Parcelable {
+    /** Satellite azimuth in degrees */
+    private float mSatelliteAzimuthDegrees;
+
+    /** Satellite elevation in degrees */
+    private float mSatelliteElevationDegrees;
+
+    /**
+     * @hide
+     */
+    public PointingInfo(float satelliteAzimuthDegrees, float satelliteElevationDegrees) {
+        mSatelliteAzimuthDegrees = satelliteAzimuthDegrees;
+        mSatelliteElevationDegrees = satelliteElevationDegrees;
+    }
+
+    private PointingInfo(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeFloat(mSatelliteAzimuthDegrees);
+        out.writeFloat(mSatelliteElevationDegrees);
+    }
+
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final @android.annotation.NonNull Creator<PointingInfo> CREATOR =
+            new Creator<PointingInfo>() {
+                @Override
+                public PointingInfo createFromParcel(Parcel in) {
+                    return new PointingInfo(in);
+                }
+
+                @Override
+                public PointingInfo[] newArray(int size) {
+                    return new PointingInfo[size];
+                }
+            };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PointingInfo that = (PointingInfo) o;
+        return mSatelliteAzimuthDegrees == that.mSatelliteAzimuthDegrees
+                && mSatelliteElevationDegrees == that.mSatelliteElevationDegrees;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSatelliteAzimuthDegrees, mSatelliteElevationDegrees);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("SatelliteAzimuthDegrees:");
+        sb.append(mSatelliteAzimuthDegrees);
+        sb.append(",");
+
+        sb.append("SatelliteElevationDegrees:");
+        sb.append(mSatelliteElevationDegrees);
+        return sb.toString();
+    }
+
+    /**
+     * Returns the azimuth of the satellite, in degrees.
+     */
+    @FloatRange(from = -180, to = 180)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public float getSatelliteAzimuthDegrees() {
+        return mSatelliteAzimuthDegrees;
+    }
+
+    /**
+     * Returns the elevation of the satellite, in degrees.
+     */
+    @FloatRange(from = -90, to = 90)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public float getSatelliteElevationDegrees() {
+        return mSatelliteElevationDegrees;
+    }
+
+    private void readFromParcel(Parcel in) {
+        mSatelliteAzimuthDegrees = in.readFloat();
+        mSatelliteElevationDegrees = in.readFloat();
+    }
+}
diff --git a/android-35/android/telephony/satellite/SatelliteCapabilities.java b/android-35/android/telephony/satellite/SatelliteCapabilities.java
new file mode 100644
index 0000000..0d8f101
--- /dev/null
+++ b/android-35/android/telephony/satellite/SatelliteCapabilities.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2022 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.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * SatelliteCapabilities is used to represent the capabilities of the satellite service
+ * received from satellite modem.
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public final class SatelliteCapabilities implements Parcelable {
+    /**
+     * List of technologies supported by the satellite modem.
+     */
+    @NonNull @SatelliteManager.NTRadioTechnology private Set<Integer> mSupportedRadioTechnologies;
+
+    /**
+     * Whether UE needs to point to a satellite to send and receive data.
+     */
+    private boolean mIsPointingRequired;
+
+    /**
+     * The maximum number of bytes per datagram that can be sent over satellite.
+     */
+    private int mMaxBytesPerOutgoingDatagram;
+
+    /**
+     * Antenna Position received from satellite modem which gives information about antenna
+     * direction to be used with satellite communication and suggested device hold positions.
+     * Map key: {@link SatelliteManager.DeviceHoldPosition} value: AntennaPosition
+     */
+    @NonNull
+    private Map<Integer, AntennaPosition> mAntennaPositionMap;
+
+    /**
+     * @hide
+     */
+    public SatelliteCapabilities(@Nullable Set<Integer> supportedRadioTechnologies,
+            boolean isPointingRequired, int maxBytesPerOutgoingDatagram,
+            @NonNull Map<Integer, AntennaPosition> antennaPositionMap) {
+        mSupportedRadioTechnologies = supportedRadioTechnologies == null
+                ? new HashSet<>() : supportedRadioTechnologies;
+        mIsPointingRequired = isPointingRequired;
+        mMaxBytesPerOutgoingDatagram = maxBytesPerOutgoingDatagram;
+        mAntennaPositionMap = antennaPositionMap;
+    }
+
+    private SatelliteCapabilities(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        if (mSupportedRadioTechnologies != null && !mSupportedRadioTechnologies.isEmpty()) {
+            out.writeInt(mSupportedRadioTechnologies.size());
+            for (int technology : mSupportedRadioTechnologies) {
+                out.writeInt(technology);
+            }
+        } else {
+            out.writeInt(0);
+        }
+
+        out.writeBoolean(mIsPointingRequired);
+        out.writeInt(mMaxBytesPerOutgoingDatagram);
+
+        if (mAntennaPositionMap != null && !mAntennaPositionMap.isEmpty()) {
+            int size = mAntennaPositionMap.size();
+            out.writeInt(size);
+            for (Map.Entry<Integer, AntennaPosition> entry : mAntennaPositionMap.entrySet()) {
+                out.writeInt(entry.getKey());
+                out.writeParcelable(entry.getValue(), flags);
+            }
+        } else {
+            out.writeInt(0);
+        }
+    }
+
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    @NonNull public static final Creator<SatelliteCapabilities> CREATOR = new Creator<>() {
+        @Override
+        public SatelliteCapabilities createFromParcel(Parcel in) {
+            return new SatelliteCapabilities(in);
+        }
+
+        @Override
+        public SatelliteCapabilities[] newArray(int size) {
+            return new SatelliteCapabilities[size];
+        }
+    };
+
+    @Override
+    @NonNull public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("SupportedRadioTechnology:");
+        if (mSupportedRadioTechnologies != null && !mSupportedRadioTechnologies.isEmpty()) {
+            for (int technology : mSupportedRadioTechnologies) {
+                sb.append(technology);
+                sb.append(",");
+            }
+        } else {
+            sb.append("none,");
+        }
+
+        sb.append("isPointingRequired:");
+        sb.append(mIsPointingRequired);
+        sb.append(",");
+
+        sb.append("maxBytesPerOutgoingDatagram:");
+        sb.append(mMaxBytesPerOutgoingDatagram);
+        sb.append(",");
+
+        sb.append("antennaPositionMap:");
+        sb.append(mAntennaPositionMap);
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SatelliteCapabilities that = (SatelliteCapabilities) o;
+        return Objects.equals(mSupportedRadioTechnologies, that.mSupportedRadioTechnologies)
+                && mIsPointingRequired == that.mIsPointingRequired
+                && mMaxBytesPerOutgoingDatagram == that.mMaxBytesPerOutgoingDatagram
+                && Objects.equals(mAntennaPositionMap, that.mAntennaPositionMap);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSupportedRadioTechnologies, mIsPointingRequired,
+                mMaxBytesPerOutgoingDatagram, mAntennaPositionMap);
+    }
+
+    /**
+     * @return The list of technologies supported by the satellite modem.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    @NonNull @SatelliteManager.NTRadioTechnology public Set<Integer>
+            getSupportedRadioTechnologies() {
+        return mSupportedRadioTechnologies;
+    }
+
+    /**
+     * Get whether UE needs to point to a satellite to send and receive data.
+     *
+     * @return {@code true} if UE needs to point to a satellite to send and receive data and
+     *         {@code false} otherwise.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public boolean isPointingRequired() {
+        return mIsPointingRequired;
+    }
+
+    /**
+     * The maximum number of bytes per datagram that can be sent over satellite.
+     *
+     * @return The maximum number of bytes per datagram that can be sent over satellite.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public int getMaxBytesPerOutgoingDatagram() {
+        return mMaxBytesPerOutgoingDatagram;
+    }
+
+    /**
+     * Antenna Position received from satellite modem which gives information about antenna
+     * direction to be used with satellite communication and suggested device hold positions.
+     * @return Map key: {@link SatelliteManager.DeviceHoldPosition} value: AntennaPosition
+     */
+    @NonNull
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public Map<Integer, AntennaPosition> getAntennaPositionMap() {
+        return mAntennaPositionMap;
+    }
+
+    private void readFromParcel(Parcel in) {
+        mSupportedRadioTechnologies = new HashSet<>();
+        int numSupportedRadioTechnologies = in.readInt();
+        if (numSupportedRadioTechnologies > 0) {
+            for (int i = 0; i < numSupportedRadioTechnologies; i++) {
+                mSupportedRadioTechnologies.add(in.readInt());
+            }
+        }
+
+        mIsPointingRequired = in.readBoolean();
+        mMaxBytesPerOutgoingDatagram = in.readInt();
+
+        mAntennaPositionMap = new HashMap<>();
+        int antennaPositionMapSize = in.readInt();
+        for (int i = 0; i < antennaPositionMapSize; i++) {
+            int key = in.readInt();
+            AntennaPosition antennaPosition = in.readParcelable(
+                    AntennaPosition.class.getClassLoader(), AntennaPosition.class);
+            mAntennaPositionMap.put(key, antennaPosition);
+        }
+    }
+}
diff --git a/android-35/android/telephony/satellite/SatelliteCapabilitiesCallback.java b/android-35/android/telephony/satellite/SatelliteCapabilitiesCallback.java
new file mode 100644
index 0000000..b68dd5a
--- /dev/null
+++ b/android-35/android/telephony/satellite/SatelliteCapabilitiesCallback.java
@@ -0,0 +1,39 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * A callback class for satellite capabilities change events.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface SatelliteCapabilitiesCallback {
+    /**
+     * Called when satellite capability has changed.
+     * @param capabilities The new satellite capabilities.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    void onSatelliteCapabilitiesChanged(@NonNull SatelliteCapabilities capabilities);
+}
diff --git a/android-35/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java b/android-35/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java
new file mode 100644
index 0000000..1a87020
--- /dev/null
+++ b/android-35/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 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.satellite;
+
+import android.annotation.FlaggedApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+
+/**
+ * A callback class for monitoring satellite communication allowed state changed events.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface SatelliteCommunicationAllowedStateCallback {
+
+    /**
+     * Telephony does not guarantee that whenever there is a change in communication allowed state,
+     * this API will be called. Telephony does its best to detect the changes and notify its
+     * listeners accordingly.
+     *
+     * @param isAllowed {@code true} means satellite allow state is changed,
+     *                  {@code false} satellite allow state is not changed
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    void onSatelliteCommunicationAllowedStateChanged(boolean isAllowed);
+}
diff --git a/android-35/android/telephony/satellite/SatelliteDatagram.java b/android-35/android/telephony/satellite/SatelliteDatagram.java
new file mode 100644
index 0000000..4d67f80
--- /dev/null
+++ b/android-35/android/telephony/satellite/SatelliteDatagram.java
@@ -0,0 +1,91 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * SatelliteDatagram is used to store data that is to be sent or received over satellite.
+ * Data is stored in byte array format.
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public final class SatelliteDatagram implements Parcelable {
+    /**
+     * Datagram to be sent or received over satellite.
+     */
+    @NonNull private byte[] mData;
+
+    /**
+     * @hide
+     */
+    public SatelliteDatagram(@NonNull byte[] data) {
+        mData = data;
+    }
+
+    private SatelliteDatagram(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeByteArray(mData);
+    }
+
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    @NonNull public static final Creator<SatelliteDatagram> CREATOR =
+            new Creator<>() {
+                @Override
+                public SatelliteDatagram createFromParcel(Parcel in) {
+                    return new SatelliteDatagram(in);
+                }
+
+                @Override
+                public SatelliteDatagram[] newArray(int size) {
+                    return new SatelliteDatagram[size];
+                }
+            };
+
+    /**
+     * Get satellite datagram.
+     * @return byte array. The format of the byte array is determined by the corresponding
+     * satellite provider. Client application should be aware of how to encode the datagram based
+     * upon the satellite provider.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    @NonNull public byte[] getSatelliteDatagram() {
+        return mData;
+    }
+
+    private void readFromParcel(Parcel in) {
+        mData = in.createByteArray();
+    }
+}
diff --git a/android-35/android/telephony/satellite/SatelliteDatagramCallback.java b/android-35/android/telephony/satellite/SatelliteDatagramCallback.java
new file mode 100644
index 0000000..9aaa986
--- /dev/null
+++ b/android-35/android/telephony/satellite/SatelliteDatagramCallback.java
@@ -0,0 +1,53 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * A callback class for listening to satellite datagrams.
+ * {@link SatelliteDatagramCallback} is registered to telephony when an app which invokes
+ * {@link SatelliteManager#registerForIncomingDatagram(Executor, SatelliteDatagramCallback)},
+ * and {@link #onSatelliteDatagramReceived(long, SatelliteDatagram, int, Consumer)} will be invoked
+ * when a new datagram is received from satellite.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface SatelliteDatagramCallback {
+    /**
+     * Called when there is an incoming datagram to be received.
+     *
+     * @param datagramId An id that uniquely identifies incoming datagram.
+     * @param datagram Datagram to be received over satellite.
+     * @param pendingCount Number of datagrams yet to be received by the app.
+     * @param callback This callback will be used by datagram receiver app to inform Telephony
+     *                 that they received the datagram. If the callback is not received within
+     *                 five minutes, Telephony will resend the datagram.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    void onSatelliteDatagramReceived(long datagramId, @NonNull SatelliteDatagram datagram,
+            int pendingCount, @NonNull Consumer<Void> callback);
+}
diff --git a/android-35/android/telephony/satellite/SatelliteManager.java b/android-35/android/telephony/satellite/SatelliteManager.java
new file mode 100644
index 0000000..47f53f3
--- /dev/null
+++ b/android-35/android/telephony/satellite/SatelliteManager.java
@@ -0,0 +1,2512 @@
+/*
+ * 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.telephony.satellite;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ICancellationSignal;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.IVoidConsumer;
+import com.android.internal.telephony.flags.Flags;
+import com.android.telephony.Rlog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+/**
+ * Manages satellite operations such as provisioning, pointing, messaging, location sharing, etc.
+ * To get the object, call {@link Context#getSystemService(String)}.
+ *
+ * @hide
+ */
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SATELLITE)
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public final class SatelliteManager {
+    private static final String TAG = "SatelliteManager";
+
+    private static final ConcurrentHashMap<SatelliteDatagramCallback, ISatelliteDatagramCallback>
+            sSatelliteDatagramCallbackMap = new ConcurrentHashMap<>();
+    private static final ConcurrentHashMap<SatelliteProvisionStateCallback,
+            ISatelliteProvisionStateCallback> sSatelliteProvisionStateCallbackMap =
+            new ConcurrentHashMap<>();
+    private static final ConcurrentHashMap<SatelliteModemStateCallback,
+            ISatelliteModemStateCallback>
+            sSatelliteModemStateCallbackMap = new ConcurrentHashMap<>();
+    private static final ConcurrentHashMap<SatelliteTransmissionUpdateCallback,
+            ISatelliteTransmissionUpdateCallback> sSatelliteTransmissionUpdateCallbackMap =
+            new ConcurrentHashMap<>();
+    private static final ConcurrentHashMap<NtnSignalStrengthCallback, INtnSignalStrengthCallback>
+            sNtnSignalStrengthCallbackMap = new ConcurrentHashMap<>();
+    private static final ConcurrentHashMap<SatelliteCapabilitiesCallback,
+            ISatelliteCapabilitiesCallback>
+            sSatelliteCapabilitiesCallbackMap = new ConcurrentHashMap<>();
+    private static final ConcurrentHashMap<SatelliteSupportedStateCallback,
+            ISatelliteSupportedStateCallback> sSatelliteSupportedStateCallbackMap =
+            new ConcurrentHashMap<>();
+
+    private static final ConcurrentHashMap<SatelliteCommunicationAllowedStateCallback,
+            ISatelliteCommunicationAllowedStateCallback>
+            sSatelliteCommunicationAllowedStateCallbackMap =
+            new ConcurrentHashMap<>();
+
+    private final int mSubId;
+
+    /**
+     * Context this SatelliteManager is for.
+     */
+    @Nullable private final Context mContext;
+
+    /**
+     * Create an instance of the SatelliteManager.
+     *
+     * @param context The context the SatelliteManager belongs to.
+     * @hide
+     */
+    public SatelliteManager(@Nullable Context context) {
+        this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+    }
+
+    /**
+     * Create an instance of the SatelliteManager associated with a particular subscription.
+     *
+     * @param context The context the SatelliteManager belongs to.
+     * @param subId The subscription ID associated with the SatelliteManager.
+     */
+    private SatelliteManager(@Nullable Context context, int subId) {
+        mContext = context;
+        mSubId = subId;
+    }
+
+    /**
+     * Exception from the satellite service containing the {@link SatelliteResult} error code.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static class SatelliteException extends Exception {
+        @SatelliteResult private final int mErrorCode;
+
+        /**
+         * Create a SatelliteException with a given error code.
+         *
+         * @param errorCode The {@link SatelliteResult}.
+         */
+        @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        public SatelliteException(@SatelliteResult int errorCode) {
+            mErrorCode = errorCode;
+        }
+
+        /**
+         * Get the error code returned from the satellite service.
+         *
+         * @return The {@link SatelliteResult}.
+         */
+        @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        @SatelliteResult public int getErrorCode() {
+            return mErrorCode;
+        }
+    }
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestIsEnabled(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+
+    public static final String KEY_SATELLITE_ENABLED = "satellite_enabled";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestIsDemoModeEnabled(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+
+    public static final String KEY_DEMO_MODE_ENABLED = "demo_mode_enabled";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestIsEmergencyModeEnabled(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+    public static final String KEY_EMERGENCY_MODE_ENABLED = "emergency_mode_enabled";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestIsSupported(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+
+    public static final String KEY_SATELLITE_SUPPORTED = "satellite_supported";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestCapabilities(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+
+    public static final String KEY_SATELLITE_CAPABILITIES = "satellite_capabilities";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestIsProvisioned(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+
+    public static final String KEY_SATELLITE_PROVISIONED = "satellite_provisioned";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestIsCommunicationAllowedForCurrentLocation(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+
+    public static final String KEY_SATELLITE_COMMUNICATION_ALLOWED =
+            "satellite_communication_allowed";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestTimeForNextSatelliteVisibility(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+
+    public static final String KEY_SATELLITE_NEXT_VISIBILITY = "satellite_next_visibility";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestNtnSignalStrength(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+
+    public static final String KEY_NTN_SIGNAL_STRENGTH = "ntn_signal_strength";
+
+    /**
+     * The request was successfully processed.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_SUCCESS = 0;
+    /**
+     * A generic error which should be used only when other specific errors cannot be used.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_ERROR = 1;
+    /**
+     * Error received from the satellite server.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_SERVER_ERROR = 2;
+    /**
+     * Error received from the vendor service. This generic error code should be used
+     * only when the error cannot be mapped to other specific service error codes.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_SERVICE_ERROR = 3;
+    /**
+     * Error received from satellite modem. This generic error code should be used only when
+     * the error cannot be mapped to other specific modem error codes.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_MODEM_ERROR = 4;
+    /**
+     * Error received from the satellite network. This generic error code should be used only when
+     * the error cannot be mapped to other specific network error codes.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_NETWORK_ERROR = 5;
+    /**
+     * Telephony is not in a valid state to receive requests from clients.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_INVALID_TELEPHONY_STATE = 6;
+    /**
+     * Satellite modem is not in a valid state to receive requests from clients.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_INVALID_MODEM_STATE = 7;
+    /**
+     * Either vendor service, or modem, or Telephony framework has received a request with
+     * invalid arguments from its clients.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_INVALID_ARGUMENTS = 8;
+    /**
+     * Telephony framework failed to send a request or receive a response from the vendor service
+     * or satellite modem due to internal error.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_REQUEST_FAILED = 9;
+    /**
+     * Radio did not start or is resetting.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_RADIO_NOT_AVAILABLE = 10;
+    /**
+     * The request is not supported by either the satellite modem or the network.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_REQUEST_NOT_SUPPORTED = 11;
+    /**
+     * Satellite modem or network has no resources available to handle requests from clients.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_NO_RESOURCES = 12;
+    /**
+     * Satellite service is not provisioned yet.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_SERVICE_NOT_PROVISIONED = 13;
+    /**
+     * Satellite service provision is already in progress.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS = 14;
+    /**
+     * The ongoing request was aborted by either the satellite modem or the network.
+     * This error is also returned when framework decides to abort current send request as one
+     * of the previous send request failed.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_REQUEST_ABORTED = 15;
+    /**
+     * The device/subscriber is barred from accessing the satellite service.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_ACCESS_BARRED = 16;
+    /**
+     * Satellite modem timeout to receive ACK or response from the satellite network after
+     * sending a request to the network.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_NETWORK_TIMEOUT = 17;
+    /**
+     * Satellite network is not reachable from the modem.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_NOT_REACHABLE = 18;
+    /**
+     * The device/subscriber is not authorized to register with the satellite service provider.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_NOT_AUTHORIZED = 19;
+    /**
+     * The device does not support satellite.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_NOT_SUPPORTED = 20;
+
+    /**
+     * The current request is already in-progress.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_REQUEST_IN_PROGRESS = 21;
+
+    /**
+     * Satellite modem is currently busy due to which current request cannot be processed.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_MODEM_BUSY = 22;
+
+    /**
+     * Telephony process is not currently available or satellite is not supported.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_ILLEGAL_STATE = 23;
+
+    /**
+     * Telephony framework timeout to receive ACK or response from the satellite modem after
+     * sending a request to the modem.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_MODEM_TIMEOUT = 24;
+
+    /** @hide */
+    @IntDef(prefix = {"SATELLITE_RESULT_"}, value = {
+            SATELLITE_RESULT_SUCCESS,
+            SATELLITE_RESULT_ERROR,
+            SATELLITE_RESULT_SERVER_ERROR,
+            SATELLITE_RESULT_SERVICE_ERROR,
+            SATELLITE_RESULT_MODEM_ERROR,
+            SATELLITE_RESULT_NETWORK_ERROR,
+            SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+            SATELLITE_RESULT_INVALID_MODEM_STATE,
+            SATELLITE_RESULT_INVALID_ARGUMENTS,
+            SATELLITE_RESULT_REQUEST_FAILED,
+            SATELLITE_RESULT_RADIO_NOT_AVAILABLE,
+            SATELLITE_RESULT_REQUEST_NOT_SUPPORTED,
+            SATELLITE_RESULT_NO_RESOURCES,
+            SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
+            SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS,
+            SATELLITE_RESULT_REQUEST_ABORTED,
+            SATELLITE_RESULT_ACCESS_BARRED,
+            SATELLITE_RESULT_NETWORK_TIMEOUT,
+            SATELLITE_RESULT_NOT_REACHABLE,
+            SATELLITE_RESULT_NOT_AUTHORIZED,
+            SATELLITE_RESULT_NOT_SUPPORTED,
+            SATELLITE_RESULT_REQUEST_IN_PROGRESS,
+            SATELLITE_RESULT_MODEM_BUSY,
+            SATELLITE_RESULT_ILLEGAL_STATE,
+            SATELLITE_RESULT_MODEM_TIMEOUT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SatelliteResult {}
+
+    /**
+     * Unknown Non-Terrestrial radio technology. This generic radio technology should be used
+     * only when the radio technology cannot be mapped to other specific radio technologies.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int NT_RADIO_TECHNOLOGY_UNKNOWN = 0;
+    /**
+     * 3GPP NB-IoT (Narrowband Internet of Things) over Non-Terrestrial-Networks technology.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int NT_RADIO_TECHNOLOGY_NB_IOT_NTN = 1;
+    /**
+     * 3GPP 5G NR over Non-Terrestrial-Networks technology.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int NT_RADIO_TECHNOLOGY_NR_NTN = 2;
+    /**
+     * 3GPP eMTC (enhanced Machine-Type Communication) over Non-Terrestrial-Networks technology.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int NT_RADIO_TECHNOLOGY_EMTC_NTN = 3;
+    /**
+     * Proprietary technology.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int NT_RADIO_TECHNOLOGY_PROPRIETARY = 4;
+
+    /** @hide */
+    @IntDef(prefix = "NT_RADIO_TECHNOLOGY_", value = {
+            NT_RADIO_TECHNOLOGY_UNKNOWN,
+            NT_RADIO_TECHNOLOGY_NB_IOT_NTN,
+            NT_RADIO_TECHNOLOGY_NR_NTN,
+            NT_RADIO_TECHNOLOGY_EMTC_NTN,
+            NT_RADIO_TECHNOLOGY_PROPRIETARY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NTRadioTechnology {}
+
+    /** Suggested device hold position is unknown. */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int DEVICE_HOLD_POSITION_UNKNOWN = 0;
+    /** User is suggested to hold the device in portrait mode. */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int DEVICE_HOLD_POSITION_PORTRAIT = 1;
+    /** User is suggested to hold the device in landscape mode with left hand. */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int DEVICE_HOLD_POSITION_LANDSCAPE_LEFT = 2;
+    /** User is suggested to hold the device in landscape mode with right hand. */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int DEVICE_HOLD_POSITION_LANDSCAPE_RIGHT = 3;
+
+    /** @hide */
+    @IntDef(prefix = {"DEVICE_HOLD_POSITION_"}, value = {
+            DEVICE_HOLD_POSITION_UNKNOWN,
+            DEVICE_HOLD_POSITION_PORTRAIT,
+            DEVICE_HOLD_POSITION_LANDSCAPE_LEFT,
+            DEVICE_HOLD_POSITION_LANDSCAPE_RIGHT
+       })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DeviceHoldPosition {}
+
+    /** Display mode is unknown. */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int DISPLAY_MODE_UNKNOWN = 0;
+    /** Display mode of the device used for satellite communication for non-foldable phones. */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int DISPLAY_MODE_FIXED = 1;
+    /** Display mode of the device used for satellite communication for foldabale phones when the
+     * device is opened. */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int DISPLAY_MODE_OPENED = 2;
+    /** Display mode of the device used for satellite communication for foldabable phones when the
+     * device is closed. */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int DISPLAY_MODE_CLOSED = 3;
+
+    /** @hide */
+    @IntDef(prefix = {"ANTENNA_POSITION_"}, value = {
+            DISPLAY_MODE_UNKNOWN,
+            DISPLAY_MODE_FIXED,
+            DISPLAY_MODE_OPENED,
+            DISPLAY_MODE_CLOSED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DisplayMode {}
+
+    /**
+     * The emergency call is handed over to oem-enabled satellite SOS messaging. SOS messages are
+     * sent to SOS providers, which will then forward the messages to emergency providers.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS = 1;
+    /**
+     * The emergency call is handed over to carrier-enabled satellite T911 messaging. T911 messages
+     * are sent directly to local emergency providers.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final int EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911 = 2;
+
+    /**
+     * Request to enable or disable the satellite modem and demo mode.
+     * If satellite modem and cellular modem cannot work concurrently,
+     * then this will disable the cellular modem if satellite modem is enabled,
+     * and will re-enable the cellular modem if satellite modem is disabled.
+     *
+     * Demo mode is created to simulate the experience of sending and receiving messages over
+     * satellite. If user enters demo mode, a request should be sent to framework to enable
+     * satellite with enableDemoMode set to {code true}. Once satellite is enabled and device is
+     * aligned with the satellite, user can send a message and also receive a reply in demo mode.
+     * If enableSatellite is {@code false}, enableDemoMode has no impact on the behavior.
+     *
+     * @param attributes The attributes of the enable request.
+     * @param executor The executor on which the error code listener will be called.
+     * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void requestEnabled(@NonNull EnableRequestAttributes attributes,
+            @NonNull @CallbackExecutor Executor executor,
+            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
+        Objects.requireNonNull(attributes);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(resultListener);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> resultListener.accept(result)));
+                    }
+                };
+                telephony.requestSatelliteEnabled(mSubId, attributes.isEnabled(),
+                        attributes.isDemoMode(), attributes.isEmergencyMode(), errorCallback);
+            } else {
+                Rlog.e(TAG, "requestEnabled() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(
+                        () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "requestEnabled() exception: ", ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+        }
+    }
+
+    /**
+     * Request to get whether the satellite modem is enabled.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return a {@code boolean} with value {@code true} if the satellite modem
+     *                 is enabled and {@code false} otherwise.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteResult}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void requestIsEnabled(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_SATELLITE_ENABLED)) {
+                                boolean isSatelliteEnabled =
+                                        resultData.getBoolean(KEY_SATELLITE_ENABLED);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(isSatelliteEnabled)));
+                            } else {
+                                loge("KEY_SATELLITE_ENABLED does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestIsSatelliteEnabled(mSubId, receiver);
+            } else {
+                loge("requestIsEnabled() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestIsEnabled() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
+    /**
+     * Request to get whether the satellite service demo mode is enabled.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return a {@code boolean} with value {@code true} if demo mode is enabled
+     *                 and {@code false} otherwise.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteResult}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void requestIsDemoModeEnabled(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_DEMO_MODE_ENABLED)) {
+                                boolean isDemoModeEnabled =
+                                        resultData.getBoolean(KEY_DEMO_MODE_ENABLED);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(isDemoModeEnabled)));
+                            } else {
+                                loge("KEY_DEMO_MODE_ENABLED does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestIsDemoModeEnabled(mSubId, receiver);
+            } else {
+                loge("requestIsDemoModeEnabled() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestIsDemoModeEnabled() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
+    /**
+     * Request to get whether the satellite service is enabled for emergency mode.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return a {@code boolean} with value {@code true} if satellite is enabled
+     *                 for emergency mode and {@code false} otherwise.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteResult}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void requestIsEmergencyModeEnabled(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_EMERGENCY_MODE_ENABLED)) {
+                                boolean isEmergencyModeEnabled =
+                                        resultData.getBoolean(KEY_EMERGENCY_MODE_ENABLED);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(isEmergencyModeEnabled)));
+                            } else {
+                                loge("KEY_EMERGENCY_MODE_ENABLED does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestIsEmergencyModeEnabled(mSubId, receiver);
+            } else {
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestIsEmergencyModeEnabled() RemoteException: " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Request to get whether the satellite service is supported on the device.
+     *
+     * <p>
+     * Note: This API only checks whether the device supports the satellite feature. The result will
+     * not be affected by whether the device is provisioned.
+     * </p>
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return a {@code boolean} with value {@code true} if the satellite
+     *                 service is supported on the device and {@code false} otherwise.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteResult}.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void requestIsSupported(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_SATELLITE_SUPPORTED)) {
+                                boolean isSatelliteSupported =
+                                        resultData.getBoolean(KEY_SATELLITE_SUPPORTED);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(isSatelliteSupported)));
+                            } else {
+                                loge("KEY_SATELLITE_SUPPORTED does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestIsSatelliteSupported(mSubId, receiver);
+            } else {
+                loge("requestIsSupported() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestIsSupported() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
+    /**
+     * Request to get the {@link SatelliteCapabilities} of the satellite service.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return the {@link SatelliteCapabilities} of the satellite service.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteResult}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void requestCapabilities(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<SatelliteCapabilities, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_SATELLITE_CAPABILITIES)) {
+                                SatelliteCapabilities capabilities =
+                                        resultData.getParcelable(KEY_SATELLITE_CAPABILITIES,
+                                                SatelliteCapabilities.class);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(capabilities)));
+                            } else {
+                                loge("KEY_SATELLITE_CAPABILITIES does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestSatelliteCapabilities(mSubId, receiver);
+            } else {
+                loge("requestCapabilities() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestCapabilities() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
+    /**
+     * The default state indicating that datagram transfer is idle.
+     * This should be sent if there are no message transfer activity happening.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE = 0;
+    /**
+     * A transition state indicating that a datagram is being sent.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING = 1;
+    /**
+     * An end state indicating that datagram sending completed successfully.
+     * After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
+     * will be sent if no more messages are pending.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS = 2;
+    /**
+     * An end state indicating that datagram sending completed with a failure.
+     * After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
+     * must be sent before reporting any additional datagram transfer state changes. All pending
+     * messages will be reported as failed, to the corresponding applications.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED = 3;
+    /**
+     * A transition state indicating that a datagram is being received.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING = 4;
+    /**
+     * An end state indicating that datagram receiving completed successfully.
+     * After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
+     * will be sent if no more messages are pending.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS = 5;
+    /**
+     * An end state indicating that datagram receive operation found that there are no
+     * messages to be retrieved from the satellite.
+     * After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
+     * will be sent if no more messages are pending.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE = 6;
+    /**
+     * An end state indicating that datagram receive completed with a failure.
+     * After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
+     * will be sent if no more messages are pending.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7;
+    /**
+     * A transition state indicating that Telephony is waiting for satellite modem to connect to a
+     * satellite network before sending a datagram or polling for datagrams. If the satellite modem
+     * successfully connects to a satellite network, either
+     * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING} or
+     * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING} will be sent. Otherwise,
+     * either {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED} or
+     * {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED} will be sent.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8;
+    /**
+     * The datagram transfer state is unknown. This generic datagram transfer state should be used
+     * only when the datagram transfer state cannot be mapped to other specific datagram transfer
+     * states.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN = -1;
+
+    /** @hide */
+    @IntDef(prefix = {"SATELLITE_DATAGRAM_TRANSFER_STATE_"}, value = {
+            SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+            SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+            SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
+            SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
+            SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING,
+            SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS,
+            SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE,
+            SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED,
+            SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
+            SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SatelliteDatagramTransferState {}
+    // TODO: Split into two enums for sending and receiving states
+
+    /**
+     * Satellite modem is in idle state.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_MODEM_STATE_IDLE = 0;
+    /**
+     * Satellite modem is listening for incoming datagrams.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_MODEM_STATE_LISTENING = 1;
+    /**
+     * Satellite modem is sending and/or receiving datagrams.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2;
+    /**
+     * Satellite modem is retrying to send and/or receive datagrams.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3;
+    /**
+     * Satellite modem is powered off.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_MODEM_STATE_OFF = 4;
+    /**
+     * Satellite modem is unavailable.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5;
+    /**
+     * The satellite modem is powered on but the device is not registered to a satellite cell.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_MODEM_STATE_NOT_CONNECTED = 6;
+    /**
+     * The satellite modem is powered on and the device is registered to a satellite cell.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_MODEM_STATE_CONNECTED = 7;
+    /**
+     * The satellite modem is being powered on.
+     * @hide
+     */
+    public static final int SATELLITE_MODEM_STATE_ENABLING_SATELLITE = 8;
+    /**
+     * The satellite modem is being powered off.
+     * @hide
+     */
+    public static final int SATELLITE_MODEM_STATE_DISABLING_SATELLITE = 9;
+    /**
+     * Satellite modem state is unknown. This generic modem state should be used only when the
+     * modem state cannot be mapped to other specific modem states.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_MODEM_STATE_UNKNOWN = -1;
+
+    /** @hide */
+    @IntDef(prefix = {"SATELLITE_MODEM_STATE_"}, value = {
+            SATELLITE_MODEM_STATE_IDLE,
+            SATELLITE_MODEM_STATE_LISTENING,
+            SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING,
+            SATELLITE_MODEM_STATE_DATAGRAM_RETRYING,
+            SATELLITE_MODEM_STATE_OFF,
+            SATELLITE_MODEM_STATE_UNAVAILABLE,
+            SATELLITE_MODEM_STATE_NOT_CONNECTED,
+            SATELLITE_MODEM_STATE_CONNECTED,
+            SATELLITE_MODEM_STATE_ENABLING_SATELLITE,
+            SATELLITE_MODEM_STATE_DISABLING_SATELLITE,
+            SATELLITE_MODEM_STATE_UNKNOWN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SatelliteModemState {}
+
+    /**
+     * Datagram type is unknown. This generic datagram type should be used only when the
+     * datagram type cannot be mapped to other specific datagram types.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int DATAGRAM_TYPE_UNKNOWN = 0;
+    /**
+     * Datagram type indicating that the datagram to be sent or received is of type SOS message.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int DATAGRAM_TYPE_SOS_MESSAGE = 1;
+    /**
+     * Datagram type indicating that the datagram to be sent or received is of type
+     * location sharing.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2;
+    /**
+     * This type of datagram is used to keep the device in satellite connected state or check if
+     * there is any incoming message.
+     * @hide
+     */
+    public static final int DATAGRAM_TYPE_KEEP_ALIVE = 3;
+    /**
+     * Datagram type indicating that the datagram to be sent or received is of type SOS message and
+     * is the last message to emergency service provider indicating still needs help.
+     * @hide
+     */
+    public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP = 4;
+    /**
+     * Datagram type indicating that the datagram to be sent or received is of type SOS message and
+     * is the last message to emergency service provider indicating no more help is needed.
+     * @hide
+     */
+    public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED = 5;
+
+    /** @hide */
+    @IntDef(prefix = "DATAGRAM_TYPE_", value = {
+            DATAGRAM_TYPE_UNKNOWN,
+            DATAGRAM_TYPE_SOS_MESSAGE,
+            DATAGRAM_TYPE_LOCATION_SHARING,
+            DATAGRAM_TYPE_KEEP_ALIVE,
+            DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP,
+            DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DatagramType {}
+
+    /**
+     * Satellite communication restricted by user.
+     * @hide
+     */
+    public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER = 0;
+
+    /**
+     * Satellite communication restricted by geolocation. This can be
+     * triggered based upon geofence input provided by carrier to enable or disable satellite.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1;
+
+    /**
+     * Satellite communication restricted by entitlement server. This can be triggered based on
+     * the EntitlementStatus value received from the entitlement server to enable or disable
+     * satellite.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT = 2;
+
+    /** @hide */
+    @IntDef(prefix = "SATELLITE_COMMUNICATION_RESTRICTION_REASON_", value = {
+            SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER,
+            SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION,
+            SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SatelliteCommunicationRestrictionReason {}
+
+    /**
+     * Start receiving satellite transmission updates.
+     * This can be called by the pointing UI when the user starts pointing to the satellite.
+     * Modem should continue to report the pointing input as the device or satellite moves.
+     * Satellite transmission updates are started only on {@link #SATELLITE_RESULT_SUCCESS}.
+     * All other results indicate that this operation failed.
+     * Once satellite transmission updates begin, position and datagram transfer state updates
+     * will be sent through {@link SatelliteTransmissionUpdateCallback}.
+     *
+     * @param executor The executor on which the callback and error code listener will be called.
+     * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
+     * @param callback The callback to notify of satellite transmission updates.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    @SuppressWarnings("SamShouldBeLast")
+    public void startTransmissionUpdates(@NonNull @CallbackExecutor Executor executor,
+            @SatelliteResult @NonNull Consumer<Integer> resultListener,
+            @NonNull SatelliteTransmissionUpdateCallback callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(resultListener);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> resultListener.accept(result)));
+                    }
+                };
+                ISatelliteTransmissionUpdateCallback internalCallback =
+                        new ISatelliteTransmissionUpdateCallback.Stub() {
+
+                            @Override
+                            public void onSatellitePositionChanged(PointingInfo pointingInfo) {
+                                executor.execute(() -> Binder.withCleanCallingIdentity(
+                                        () -> callback.onSatellitePositionChanged(pointingInfo)));
+                            }
+
+                            @Override
+                            public void onSendDatagramStateChanged(int datagramType, int state,
+                                    int sendPendingCount, int errorCode) {
+                                executor.execute(() -> Binder.withCleanCallingIdentity(
+                                        () -> callback.onSendDatagramStateChanged(datagramType,
+                                                state, sendPendingCount, errorCode)));
+
+                                // For backward compatibility
+                                executor.execute(() -> Binder.withCleanCallingIdentity(
+                                        () -> callback.onSendDatagramStateChanged(
+                                                state, sendPendingCount, errorCode)));
+                            }
+
+                            @Override
+                            public void onReceiveDatagramStateChanged(int state,
+                                    int receivePendingCount, int errorCode) {
+                                executor.execute(() -> Binder.withCleanCallingIdentity(
+                                        () -> callback.onReceiveDatagramStateChanged(
+                                                state, receivePendingCount, errorCode)));
+                            }
+                        };
+                sSatelliteTransmissionUpdateCallbackMap.put(callback, internalCallback);
+                telephony.startSatelliteTransmissionUpdates(mSubId, errorCallback,
+                        internalCallback);
+            } else {
+                loge("startTransmissionUpdates() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(
+                        () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+            }
+        } catch (RemoteException ex) {
+            loge("startTransmissionUpdates() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+        }
+    }
+
+    /**
+     * Stop receiving satellite transmission updates.
+     * This can be called by the pointing UI when the user stops pointing to the satellite.
+     * Satellite transmission updates are stopped and the callback is unregistered only on
+     * {@link #SATELLITE_RESULT_SUCCESS}. All other results that this operation failed.
+     *
+     * @param callback The callback that was passed to {@link
+     * #startTransmissionUpdates(Executor, Consumer, SatelliteTransmissionUpdateCallback)}.
+     * @param executor The executor on which the error code listener will be called.
+     * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void stopTransmissionUpdates(@NonNull SatelliteTransmissionUpdateCallback callback,
+            @SuppressWarnings("ListenerLast") @NonNull @CallbackExecutor Executor executor,
+            @SuppressWarnings("ListenerLast") @SatelliteResult @NonNull
+            Consumer<Integer> resultListener) {
+        Objects.requireNonNull(callback);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(resultListener);
+
+        ISatelliteTransmissionUpdateCallback internalCallback =
+                sSatelliteTransmissionUpdateCallbackMap.remove(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (internalCallback != null) {
+                    IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                        @Override
+                        public void accept(int result) {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(
+                                    () -> resultListener.accept(result)));
+                        }
+                    };
+                    telephony.stopSatelliteTransmissionUpdates(mSubId, errorCallback,
+                            internalCallback);
+                    // TODO: Notify SmsHandler that pointing UI stopped
+                } else {
+                    loge("stopSatelliteTransmissionUpdates: No internal callback.");
+                    executor.execute(() -> Binder.withCleanCallingIdentity(
+                            () -> resultListener.accept(SATELLITE_RESULT_INVALID_ARGUMENTS)));
+                }
+            } else {
+                loge("stopTransmissionUpdates() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(
+                        () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+            }
+        } catch (RemoteException ex) {
+            loge("stopTransmissionUpdates() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+        }
+    }
+
+    /**
+     * Provision the device with a satellite provider.
+     * This is needed if the provider allows dynamic registration.
+     *
+     * @param token The token is generated by the user which is used as a unique identifier for
+     *              provisioning with satellite gateway.
+     * @param provisionData Data from the provisioning app that can be used by provisioning server
+     * @param cancellationSignal The optional signal used by the caller to cancel the provision
+     *                           request. Even when the cancellation is signaled, Telephony will
+     *                           still trigger the callback to return the result of this request.
+     * @param executor The executor on which the error code listener will be called.
+     * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void provisionService(@NonNull String token, @NonNull byte[] provisionData,
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull @CallbackExecutor Executor executor,
+            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
+        Objects.requireNonNull(token);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(resultListener);
+        Objects.requireNonNull(provisionData);
+
+        ICancellationSignal cancelRemote = null;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> resultListener.accept(result)));
+                    }
+                };
+                cancelRemote = telephony.provisionSatelliteService(mSubId, token, provisionData,
+                        errorCallback);
+            } else {
+                loge("provisionService() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(
+                        () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+            }
+        } catch (RemoteException ex) {
+            loge("provisionService() RemoteException=" + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+        }
+        if (cancellationSignal != null) {
+            cancellationSignal.setRemote(cancelRemote);
+        }
+    }
+
+    /**
+     * Deprovision the device with the satellite provider.
+     * This is needed if the provider allows dynamic registration. Once deprovisioned,
+     * {@link SatelliteProvisionStateCallback#onSatelliteProvisionStateChanged(boolean)}
+     * should report as deprovisioned.
+     * For provisioning satellite service, refer to
+     * {@link #provisionService(String, byte[], CancellationSignal, Executor, Consumer)}
+     *
+     * @param token The token of the device/subscription to be deprovisioned.
+     *              This should match with the token passed as input in
+     *              {@link #provisionService(String, byte[], CancellationSignal, Executor,
+     *              Consumer)}
+     * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void deprovisionService(@NonNull String token,
+            @NonNull @CallbackExecutor Executor executor,
+            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
+        Objects.requireNonNull(token);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(resultListener);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> resultListener.accept(result)));
+                    }
+                };
+                telephony.deprovisionSatelliteService(mSubId, token, errorCallback);
+            } else {
+                loge("deprovisionService() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(
+                        () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+            }
+        } catch (RemoteException ex) {
+            loge("deprovisionService() RemoteException ex=" + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+        }
+    }
+
+    /**
+     * Registers for the satellite provision state changed.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback to handle the satellite provision state changed event.
+     *
+     * @return The {@link SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    @SatelliteResult public int registerForProvisionStateChanged(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull SatelliteProvisionStateCallback callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ISatelliteProvisionStateCallback internalCallback =
+                        new ISatelliteProvisionStateCallback.Stub() {
+                            @Override
+                            public void onSatelliteProvisionStateChanged(boolean provisioned) {
+                                executor.execute(() -> Binder.withCleanCallingIdentity(
+                                        () -> callback.onSatelliteProvisionStateChanged(
+                                                provisioned)));
+                            }
+                        };
+                sSatelliteProvisionStateCallbackMap.put(callback, internalCallback);
+                return telephony.registerForSatelliteProvisionStateChanged(
+                        mSubId, internalCallback);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("registerForProvisionStateChanged() RemoteException: " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return SATELLITE_RESULT_REQUEST_FAILED;
+    }
+
+    /**
+     * Unregisters for the satellite provision state changed.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param callback The callback that was passed to
+     * {@link #registerForProvisionStateChanged(Executor, SatelliteProvisionStateCallback)}
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void unregisterForProvisionStateChanged(
+            @NonNull SatelliteProvisionStateCallback callback) {
+        Objects.requireNonNull(callback);
+        ISatelliteProvisionStateCallback internalCallback =
+                sSatelliteProvisionStateCallbackMap.remove(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (internalCallback != null) {
+                    telephony.unregisterForSatelliteProvisionStateChanged(mSubId, internalCallback);
+                } else {
+                    loge("unregisterForProvisionStateChanged: No internal callback.");
+                }
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("unregisterForProvisionStateChanged() RemoteException: " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Request to get whether this device is provisioned with a satellite provider.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return a {@code boolean} with value {@code true} if the device is
+     *                 provisioned with a satellite provider and {@code false} otherwise.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteResult}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void requestIsProvisioned(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_SATELLITE_PROVISIONED)) {
+                                boolean isSatelliteProvisioned =
+                                        resultData.getBoolean(KEY_SATELLITE_PROVISIONED);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(isSatelliteProvisioned)));
+                            } else {
+                                loge("KEY_SATELLITE_PROVISIONED does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestIsSatelliteProvisioned(mSubId, receiver);
+            } else {
+                loge("requestIsProvisioned() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestIsProvisioned() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
+    /**
+     * Registers for modem state changed from satellite modem.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback to handle the satellite modem state changed event.
+     *
+     * @return The {@link SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    @SatelliteResult public int registerForModemStateChanged(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull SatelliteModemStateCallback callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ISatelliteModemStateCallback internalCallback =
+                        new ISatelliteModemStateCallback.Stub() {
+                    @Override
+                    public void onSatelliteModemStateChanged(int state) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                callback.onSatelliteModemStateChanged(state)));
+                    }
+                };
+                sSatelliteModemStateCallbackMap.put(callback, internalCallback);
+                return telephony.registerForSatelliteModemStateChanged(mSubId, internalCallback);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("registerForModemStateChanged() RemoteException:" + ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return SATELLITE_RESULT_REQUEST_FAILED;
+    }
+
+    /**
+     * Unregisters for modem state changed from satellite modem.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param callback The callback that was passed to
+     * {@link #registerForModemStateChanged(Executor, SatelliteModemStateCallback)}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void unregisterForModemStateChanged(
+            @NonNull SatelliteModemStateCallback callback) {
+        Objects.requireNonNull(callback);
+        ISatelliteModemStateCallback internalCallback = sSatelliteModemStateCallbackMap.remove(
+                callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (internalCallback != null) {
+                    telephony.unregisterForModemStateChanged(mSubId, internalCallback);
+                } else {
+                    loge("unregisterForModemStateChanged: No internal callback.");
+                }
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("unregisterForModemStateChanged() RemoteException:" + ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Register to receive incoming datagrams over satellite.
+     *
+     * To poll for pending satellite datagrams, refer to
+     * {@link #pollPendingDatagrams(Executor, Consumer)}
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback to handle incoming datagrams over satellite.
+     *                 This callback with be invoked when a new datagram is received from satellite.
+     *
+     * @return The {@link SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    @SatelliteResult public int registerForIncomingDatagram(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull SatelliteDatagramCallback callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ISatelliteDatagramCallback internalCallback =
+                        new ISatelliteDatagramCallback.Stub() {
+                            @Override
+                            public void onSatelliteDatagramReceived(long datagramId,
+                                    @NonNull SatelliteDatagram datagram, int pendingCount,
+                                    @NonNull IVoidConsumer internalAck) {
+                                Consumer<Void> externalAck = new Consumer<Void>() {
+                                    @Override
+                                    public void accept(Void result) {
+                                        try {
+                                            internalAck.accept();
+                                        }  catch (RemoteException e) {
+                                              logd("onSatelliteDatagramReceived "
+                                                      + "RemoteException: " + e);
+                                        }
+                                    }
+                                };
+
+                                executor.execute(() -> Binder.withCleanCallingIdentity(
+                                        () -> callback.onSatelliteDatagramReceived(
+                                                datagramId, datagram, pendingCount, externalAck)));
+                            }
+                        };
+                sSatelliteDatagramCallbackMap.put(callback, internalCallback);
+                return telephony.registerForIncomingDatagram(mSubId, internalCallback);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("registerForIncomingDatagram() RemoteException:" + ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return SATELLITE_RESULT_REQUEST_FAILED;
+    }
+
+    /**
+     * Unregister to stop receiving incoming datagrams over satellite.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param callback The callback that was passed to
+     * {@link #registerForIncomingDatagram(Executor, SatelliteDatagramCallback)}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void unregisterForIncomingDatagram(@NonNull SatelliteDatagramCallback callback) {
+        Objects.requireNonNull(callback);
+        ISatelliteDatagramCallback internalCallback =
+                sSatelliteDatagramCallbackMap.remove(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (internalCallback != null) {
+                    telephony.unregisterForIncomingDatagram(mSubId, internalCallback);
+                } else {
+                    loge("unregisterForIncomingDatagram: No internal callback.");
+                }
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("unregisterForIncomingDatagram() RemoteException:" + ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Poll pending satellite datagrams over satellite.
+     *
+     * This method should be called when user specifies to check incoming messages over satellite.
+     * This method requests modem to check if there are any pending datagrams to be received over
+     * satellite. If there are any incoming datagrams, they will be received via
+     * {@link SatelliteDatagramCallback#onSatelliteDatagramReceived(long, SatelliteDatagram, int,
+     * Consumer)} )}
+     *
+     * @param executor The executor on which the result listener will be called.
+     * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void pollPendingDatagrams(@NonNull @CallbackExecutor Executor executor,
+            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(resultListener);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> resultListener.accept(result)));
+                    }
+                };
+                telephony.pollPendingDatagrams(mSubId, internalCallback);
+            } else {
+                loge("pollPendingDatagrams() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(
+                        () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+            }
+        } catch (RemoteException ex) {
+            loge("pollPendingDatagrams() RemoteException:" + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+        }
+    }
+
+    /**
+     * Send datagram over satellite.
+     *
+     * Gateway encodes SOS message or location sharing message into a datagram and passes it as
+     * input to this method. Datagram received here will be passed down to modem without any
+     * encoding or encryption.
+     *
+     * @param datagramType datagram type indicating whether the datagram is of type
+     *                     SOS_SMS or LOCATION_SHARING.
+     * @param datagram encoded gateway datagram which is encrypted by the caller.
+     *                 Datagram will be passed down to modem without any encoding or encryption.
+     * @param needFullScreenPointingUI If set to true, this indicates pointingUI app to open in full
+     *                                 screen mode if satellite communication needs pointingUI.
+     *                                 If this is set to false, pointingUI may be presented to the
+     *                                 user in collapsed view. Application may decide to mark this
+     *                                 flag as true when the user is sending data for the first time
+     *                                 or whenever there is a considerable idle time between
+     *                                 satellite activity. This decision should be done based upon
+     *                                 user activity and the application's ability to determine the
+     *                                 best possible UX experience for the user.
+     * @param executor The executor on which the result listener will be called.
+     * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void sendDatagram(@DatagramType int datagramType,
+            @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
+            @NonNull @CallbackExecutor Executor executor,
+            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
+        Objects.requireNonNull(datagram);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(resultListener);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> resultListener.accept(result)));
+                    }
+                };
+                telephony.sendDatagram(mSubId, datagramType, datagram,
+                        needFullScreenPointingUI, internalCallback);
+            } else {
+                loge("sendDatagram() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(
+                        () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+            }
+        } catch (RemoteException ex) {
+            loge("sendDatagram() RemoteException:" + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+        }
+    }
+
+    /**
+     * Request to get whether satellite communication is allowed for the current location.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return a {@code boolean} with value {@code true} if satellite
+     *                 communication is allowed for the current location and
+     *                 {@code false} otherwise.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteResult}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void requestIsCommunicationAllowedForCurrentLocation(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
+                                boolean isSatelliteCommunicationAllowed =
+                                        resultData.getBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(isSatelliteCommunicationAllowed)));
+                            } else {
+                                loge("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestIsCommunicationAllowedForCurrentLocation(mSubId, receiver);
+            } else {
+                loge("requestIsCommunicationAllowedForCurrentLocation() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestIsCommunicationAllowedForCurrentLocation() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
+    /**
+     * Request to get the duration in seconds after which the satellite will be visible.
+     * This will be {@link Duration#ZERO} if the satellite is currently visible.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return the time after which the satellite will be visible.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteResult}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void requestTimeForNextSatelliteVisibility(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Duration, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_SATELLITE_NEXT_VISIBILITY)) {
+                                int nextVisibilityDuration =
+                                        resultData.getInt(KEY_SATELLITE_NEXT_VISIBILITY);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(
+                                                Duration.ofSeconds(nextVisibilityDuration))));
+                            } else {
+                                loge("KEY_SATELLITE_NEXT_VISIBILITY does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestTimeForNextSatelliteVisibility(mSubId, receiver);
+            } else {
+                loge("requestTimeForNextSatelliteVisibility() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestTimeForNextSatelliteVisibility() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
+    /**
+     * Inform whether the device is aligned with the satellite for demo mode.
+     *
+     * Framework can send datagram to modem only when device is aligned with the satellite.
+     * This method helps framework to simulate the experience of sending datagram over satellite.
+     *
+     * @param isAligned {@true} Device is aligned with the satellite for demo mode
+     *                  {@false} Device is not aligned with the satellite for demo mode
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void setDeviceAlignedWithSatellite(boolean isAligned) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setDeviceAlignedWithSatellite(mSubId, isAligned);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("setDeviceAlignedWithSatellite() RemoteException:" + ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * User request to enable or disable carrier supported satellite plmn scan and attach by modem.
+     * <p>
+     * This API should be called by only settings app to pass down the user input for
+     * enabling/disabling satellite. This user input will be persisted across device reboots.
+     * <p>
+     * Satellite will be enabled only when the following conditions are met:
+     * <ul>
+     * <li>Users want to enable it.</li>
+     * <li>There is no satellite communication restriction, which is added by
+     * {@link #addAttachRestrictionForCarrier(int, int, Executor, Consumer)}</li>
+     * <li>The carrier config {@link
+     * android.telephony.CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} is set to
+     * {@code true}.</li>
+     * </ul>
+     *
+     * @param subId The subscription ID of the carrier.
+     * @param enableSatellite {@code true} to enable the satellite and {@code false} to disable.
+     * @param executor The executor on which the error code listener will be called.
+     * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalArgumentException if the subscription is invalid.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public void requestAttachEnabledForCarrier(int subId, boolean enableSatellite,
+            @NonNull @CallbackExecutor Executor executor,
+            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(resultListener);
+
+        if (enableSatellite) {
+            removeAttachRestrictionForCarrier(subId,
+                    SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener);
+        } else {
+            addAttachRestrictionForCarrier(subId,
+                    SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener);
+        }
+    }
+
+    /**
+     * Request to get whether the carrier supported satellite plmn scan and attach by modem is
+     * enabled by user.
+     *
+     * @param subId The subscription ID of the carrier.
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return a {@code boolean} with value {@code true} if the satellite
+     *                 is enabled and {@code false} otherwise.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteResult}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws IllegalArgumentException if the subscription is invalid.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public void requestIsAttachEnabledForCarrier(int subId,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        Set<Integer> restrictionReason = getAttachRestrictionReasonsForCarrier(subId);
+        executor.execute(() -> callback.onResult(
+                !restrictionReason.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER)));
+    }
+
+    /**
+     * Add a restriction reason for disallowing carrier supported satellite plmn scan and attach
+     * by modem.
+     *
+     * @param subId The subscription ID of the carrier.
+     * @param reason Reason for disallowing satellite communication.
+     * @param executor The executor on which the error code listener will be called.
+     * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalArgumentException if the subscription is invalid.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public void addAttachRestrictionForCarrier(int subId,
+            @SatelliteCommunicationRestrictionReason int reason,
+            @NonNull @CallbackExecutor Executor executor,
+            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid subscription ID");
+        }
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> resultListener.accept(result)));
+                    }
+                };
+                telephony.addAttachRestrictionForCarrier(subId, reason, errorCallback);
+            } else {
+                loge("addAttachRestrictionForCarrier() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(
+                        () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+            }
+        } catch (RemoteException ex) {
+            loge("addAttachRestrictionForCarrier() RemoteException:" + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+        }
+    }
+
+    /**
+     * Remove a restriction reason for disallowing carrier supported satellite plmn scan and attach
+     * by modem.
+     *
+     * @param subId The subscription ID of the carrier.
+     * @param reason Reason for disallowing satellite communication.
+     * @param executor The executor on which the error code listener will be called.
+     * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalArgumentException if the subscription is invalid.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public void removeAttachRestrictionForCarrier(int subId,
+            @SatelliteCommunicationRestrictionReason int reason,
+            @NonNull @CallbackExecutor Executor executor,
+            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid subscription ID");
+        }
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> resultListener.accept(result)));
+                    }
+                };
+                telephony.removeAttachRestrictionForCarrier(subId, reason, errorCallback);
+            } else {
+                loge("removeAttachRestrictionForCarrier() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(
+                        () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+            }
+        } catch (RemoteException ex) {
+            loge("removeAttachRestrictionForCarrier() RemoteException:" + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
+        }
+    }
+
+    /**
+     * Get reasons for disallowing satellite attach, as requested by
+     * {@link #addAttachRestrictionForCarrier(int, int, Executor, Consumer)}
+     *
+     * @param subId The subscription ID of the carrier.
+     * @return Set of reasons for disallowing satellite communication.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws IllegalArgumentException if the subscription is invalid.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @SatelliteCommunicationRestrictionReason
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    @NonNull public Set<Integer> getAttachRestrictionReasonsForCarrier(int subId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid subscription ID");
+        }
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                int[] receivedArray =
+                        telephony.getAttachRestrictionReasonsForCarrier(subId);
+                if (receivedArray.length == 0) {
+                    logd("receivedArray is empty, create empty set");
+                    return new HashSet<>();
+                } else {
+                    return Arrays.stream(receivedArray).boxed().collect(Collectors.toSet());
+                }
+            } else {
+                throw new IllegalStateException("Telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("getAttachRestrictionReasonsForCarrier() RemoteException: " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return new HashSet<>();
+    }
+
+    /**
+     * Request to get the signal strength of the satellite connection.
+     *
+     * <p>
+     * Note: This API is specifically designed for OEM enabled satellite connectivity only.
+     * For satellite connectivity enabled using carrier roaming, please refer to
+     * {@link android.telephony.TelephonyCallback.SignalStrengthsListener}, and
+     * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+     * </p>
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered. If the request is
+     * successful, {@link OutcomeReceiver#onResult(Object)} will return an instance of
+     * {@link NtnSignalStrength} with a value of {@link NtnSignalStrength.NtnSignalStrengthLevel}
+     * The {@link NtnSignalStrength#NTN_SIGNAL_STRENGTH_NONE} will be returned if there is no
+     * signal strength data available.
+     * If the request is not successful, {@link OutcomeReceiver#onError(Throwable)} will return a
+     * {@link SatelliteException} with the {@link SatelliteResult}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void requestNtnSignalStrength(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<NtnSignalStrength, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_NTN_SIGNAL_STRENGTH)) {
+                                NtnSignalStrength ntnSignalStrength =
+                                        resultData.getParcelable(KEY_NTN_SIGNAL_STRENGTH,
+                                                NtnSignalStrength.class);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(ntnSignalStrength)));
+                            } else {
+                                loge("KEY_NTN_SIGNAL_STRENGTH does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestNtnSignalStrength(mSubId, receiver);
+            } else {
+                loge("requestNtnSignalStrength() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestNtnSignalStrength() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
+    /**
+     * Registers for NTN signal strength changed from satellite modem.
+     * If the registration operation is not successful, a {@link SatelliteException} that contains
+     * {@link SatelliteResult} will be thrown.
+     *
+     * <p>
+     * Note: This API is specifically designed for OEM enabled satellite connectivity only.
+     * For satellite connectivity enabled using carrier roaming, please refer to
+     * {@link android.telephony.TelephonyCallback.SignalStrengthsListener}, and
+     * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
+     * </p>
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback to handle the NTN signal strength changed event.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void registerForNtnSignalStrengthChanged(@NonNull @CallbackExecutor Executor executor,
+            @NonNull NtnSignalStrengthCallback callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                INtnSignalStrengthCallback internalCallback =
+                        new INtnSignalStrengthCallback.Stub() {
+                            @Override
+                            public void onNtnSignalStrengthChanged(
+                                    NtnSignalStrength ntnSignalStrength) {
+                                executor.execute(() -> Binder.withCleanCallingIdentity(
+                                        () -> callback.onNtnSignalStrengthChanged(
+                                                ntnSignalStrength)));
+                            }
+                        };
+                telephony.registerForNtnSignalStrengthChanged(mSubId, internalCallback);
+                sNtnSignalStrengthCallbackMap.put(callback, internalCallback);
+            } else {
+                throw new IllegalStateException("Telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("registerForNtnSignalStrengthChanged() RemoteException: " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Unregisters for NTN signal strength changed from satellite modem.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * <p>
+     * Note: This API is specifically designed for OEM enabled satellite connectivity only.
+     * For satellite connectivity enabled using carrier roaming, please refer to
+     * {@link TelephonyManager#unregisterTelephonyCallback(TelephonyCallback)}..
+     * </p>
+     *
+     * @param callback The callback that was passed to.
+     * {@link #registerForNtnSignalStrengthChanged(Executor, NtnSignalStrengthCallback)}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalArgumentException if the callback is not valid or has already been
+     * unregistered.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void unregisterForNtnSignalStrengthChanged(@NonNull NtnSignalStrengthCallback callback) {
+        Objects.requireNonNull(callback);
+        INtnSignalStrengthCallback internalCallback =
+                sNtnSignalStrengthCallbackMap.remove(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (internalCallback != null) {
+                    telephony.unregisterForNtnSignalStrengthChanged(mSubId, internalCallback);
+                } else {
+                    loge("unregisterForNtnSignalStrengthChanged: No internal callback.");
+                    throw new IllegalArgumentException("callback is not valid");
+                }
+            } else {
+                throw new IllegalStateException("Telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("unregisterForNtnSignalStrengthChanged() RemoteException: " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Registers for satellite capabilities change event from the satellite service.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback to handle the satellite capabilities changed event.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    @SatelliteResult public int registerForCapabilitiesChanged(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull SatelliteCapabilitiesCallback callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ISatelliteCapabilitiesCallback internalCallback =
+                        new ISatelliteCapabilitiesCallback.Stub() {
+                            @Override
+                            public void onSatelliteCapabilitiesChanged(
+                                    SatelliteCapabilities capabilities) {
+                                executor.execute(() -> Binder.withCleanCallingIdentity(
+                                        () -> callback.onSatelliteCapabilitiesChanged(
+                                                capabilities)));
+                            }
+                        };
+                sSatelliteCapabilitiesCallbackMap.put(callback, internalCallback);
+                return telephony.registerForCapabilitiesChanged(mSubId, internalCallback);
+            } else {
+                throw new IllegalStateException("Telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("registerForCapabilitiesChanged() RemoteException: " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return SATELLITE_RESULT_REQUEST_FAILED;
+    }
+
+    /**
+     * Unregisters for satellite capabilities change event from the satellite service.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param callback The callback that was passed to.
+     * {@link #registerForCapabilitiesChanged(Executor, SatelliteCapabilitiesCallback)}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void unregisterForCapabilitiesChanged(
+            @NonNull SatelliteCapabilitiesCallback callback) {
+        Objects.requireNonNull(callback);
+        ISatelliteCapabilitiesCallback internalCallback =
+                sSatelliteCapabilitiesCallbackMap.remove(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (internalCallback != null) {
+                    telephony.unregisterForCapabilitiesChanged(mSubId, internalCallback);
+                } else {
+                    loge("unregisterForCapabilitiesChanged: No internal callback.");
+                }
+            } else {
+                throw new IllegalStateException("Telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("unregisterForCapabilitiesChanged() RemoteException: " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Get all satellite PLMNs for which attach is enable for carrier.
+     *
+     * @param subId subId The subscription ID of the carrier.
+     *
+     * @return List of plmn for carrier satellite service. If no plmn is available, empty list will
+     * be returned.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    @NonNull public List<String> getSatellitePlmnsForCarrier(int subId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid subscription ID");
+        }
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getSatellitePlmnsForCarrier(subId);
+            } else {
+                throw new IllegalStateException("Telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("getSatellitePlmnsForCarrier() RemoteException: " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return new ArrayList<>();
+    }
+
+    /**
+     * Registers for the satellite supported state changed.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback to handle the satellite supoprted state changed event.
+     *
+     * @return The {@link SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @SatelliteResult public int registerForSupportedStateChanged(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull SatelliteSupportedStateCallback callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ISatelliteSupportedStateCallback internalCallback =
+                        new ISatelliteSupportedStateCallback.Stub() {
+                            @Override
+                            public void onSatelliteSupportedStateChanged(boolean supported) {
+                                executor.execute(() -> Binder.withCleanCallingIdentity(
+                                        () -> callback.onSatelliteSupportedStateChanged(
+                                                supported)));
+                            }
+                        };
+                sSatelliteSupportedStateCallbackMap.put(callback, internalCallback);
+                return telephony.registerForSatelliteSupportedStateChanged(
+                        mSubId, internalCallback);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("registerForSupportedStateChanged() RemoteException: " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return SATELLITE_RESULT_REQUEST_FAILED;
+    }
+
+    /**
+     * Unregisters for the satellite supported state changed.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param callback The callback that was passed to
+     * {@link #registerForSupportedStateChanged(Executor, SatelliteSupportedStateCallback)}
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void unregisterForSupportedStateChanged(
+            @NonNull SatelliteSupportedStateCallback callback) {
+        Objects.requireNonNull(callback);
+        ISatelliteSupportedStateCallback internalCallback =
+                sSatelliteSupportedStateCallbackMap.remove(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (internalCallback != null) {
+                    telephony.unregisterForSatelliteSupportedStateChanged(mSubId, internalCallback);
+                } else {
+                    loge("unregisterForSupportedStateChanged: No internal callback.");
+                }
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("unregisterForSupportedStateChanged() RemoteException: " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Registers for the satellite communication allowed state changed.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback to handle satellite communication allowed state changed event.
+     * @return The {@link SatelliteResult} result of the operation.
+     * @throws SecurityException     if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @SatelliteResult
+    public int registerForCommunicationAllowedStateChanged(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull SatelliteCommunicationAllowedStateCallback callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ISatelliteCommunicationAllowedStateCallback internalCallback =
+                        new ISatelliteCommunicationAllowedStateCallback.Stub() {
+                            @Override
+                            public void onSatelliteCommunicationAllowedStateChanged(
+                                    boolean isAllowed) {
+                                executor.execute(() -> Binder.withCleanCallingIdentity(
+                                        () -> callback.onSatelliteCommunicationAllowedStateChanged(
+                                                isAllowed)));
+                            }
+                        };
+                sSatelliteCommunicationAllowedStateCallbackMap.put(callback, internalCallback);
+                return telephony.registerForCommunicationAllowedStateChanged(
+                        mSubId, internalCallback);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("registerForCommunicationAllowedStateChanged() RemoteException: " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return SATELLITE_RESULT_REQUEST_FAILED;
+    }
+
+    /**
+     * Unregisters for the satellite communication allowed state changed.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param callback The callback that was passed to
+     *                 {@link #registerForCommunicationAllowedStateChanged(Executor,
+     *                 SatelliteCommunicationAllowedStateCallback)}
+     * @throws SecurityException     if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void unregisterForCommunicationAllowedStateChanged(
+            @NonNull SatelliteCommunicationAllowedStateCallback callback) {
+        Objects.requireNonNull(callback);
+        ISatelliteCommunicationAllowedStateCallback internalCallback =
+                sSatelliteCommunicationAllowedStateCallbackMap.remove(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (internalCallback != null) {
+                    telephony.unregisterForCommunicationAllowedStateChanged(mSubId,
+                            internalCallback);
+                } else {
+                    loge("unregisterForCommunicationAllowedStateChanged: No internal callback.");
+                }
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("unregisterForCommunicationAllowedStateChanged() RemoteException: " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    @Nullable
+    private static ITelephony getITelephony() {
+        ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
+                .getTelephonyServiceManager()
+                .getTelephonyServiceRegisterer()
+                .get());
+        return binder;
+    }
+
+    private static void logd(@NonNull String log) {
+        Rlog.d(TAG, log);
+    }
+
+    private static void loge(@NonNull String log) {
+        Rlog.e(TAG, log);
+    }
+}
diff --git a/android-35/android/telephony/satellite/SatelliteModemStateCallback.java b/android-35/android/telephony/satellite/SatelliteModemStateCallback.java
new file mode 100644
index 0000000..8d33c88
--- /dev/null
+++ b/android-35/android/telephony/satellite/SatelliteModemStateCallback.java
@@ -0,0 +1,38 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * A callback class for monitoring satellite modem state change events.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface SatelliteModemStateCallback {
+    /**
+     * Called when satellite modem state changes.
+     * @param state The new satellite modem state.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state);
+}
diff --git a/android-35/android/telephony/satellite/SatelliteProvisionStateCallback.java b/android-35/android/telephony/satellite/SatelliteProvisionStateCallback.java
new file mode 100644
index 0000000..a12952b
--- /dev/null
+++ b/android-35/android/telephony/satellite/SatelliteProvisionStateCallback.java
@@ -0,0 +1,42 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * A callback class for monitoring satellite provision state change events.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface SatelliteProvisionStateCallback {
+    /**
+     * Called when satellite provision state changes.
+     *
+     * @param provisioned The new provision state. {@code true} means satellite is provisioned
+     *                    {@code false} means satellite is not provisioned.
+     *                    It is generally expected that the provisioning app retries if
+     *                    provisioning fails.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    void onSatelliteProvisionStateChanged(boolean provisioned);
+}
diff --git a/android-35/android/telephony/satellite/SatelliteSupportedStateCallback.java b/android-35/android/telephony/satellite/SatelliteSupportedStateCallback.java
new file mode 100644
index 0000000..7e19bd1
--- /dev/null
+++ b/android-35/android/telephony/satellite/SatelliteSupportedStateCallback.java
@@ -0,0 +1,40 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * A callback class for monitoring satellite supported state change events.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface SatelliteSupportedStateCallback {
+    /**
+     * Called when satellite supported state changes.
+     *
+     * @param supported The new supported state. {@code true} means satellite is supported,
+     * {@code false} means satellite is not supported.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    void onSatelliteSupportedStateChanged(boolean supported);
+}
diff --git a/android-35/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java b/android-35/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
new file mode 100644
index 0000000..d8bd662
--- /dev/null
+++ b/android-35/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
@@ -0,0 +1,78 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+/**
+ * A callback class for monitoring satellite position update and datagram transfer state change
+ * events.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public interface SatelliteTransmissionUpdateCallback {
+    /**
+     * Called when the satellite position changed.
+     *
+     * @param pointingInfo The pointing info containing the satellite location.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    void onSatellitePositionChanged(@NonNull PointingInfo pointingInfo);
+
+    /**
+     * Called when satellite datagram send state changed.
+     *
+     * @param state The new send datagram transfer state.
+     * @param sendPendingCount The number of datagrams that are currently being sent.
+     * @param errorCode If datagram transfer failed, the reason for failure.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    void onSendDatagramStateChanged(
+            @SatelliteManager.SatelliteDatagramTransferState int state, int sendPendingCount,
+            @SatelliteManager.SatelliteResult int errorCode);
+
+    /**
+     * Called when satellite datagram send state changed.
+     *
+     * @param datagramType The datagram type of currently being sent.
+     * @param state The new send datagram transfer state.
+     * @param sendPendingCount The number of datagrams that are currently being sent.
+     * @param errorCode If datagram transfer failed, the reason for failure.
+     *
+     * @hide
+     */
+    void onSendDatagramStateChanged(@SatelliteManager.DatagramType int datagramType,
+            @SatelliteManager.SatelliteDatagramTransferState int state, int sendPendingCount,
+            @SatelliteManager.SatelliteResult int errorCode);
+    /**
+     * Called when satellite datagram receive state changed.
+     *
+     * @param state The new receive datagram transfer state.
+     * @param receivePendingCount The number of datagrams that are currently pending to be received.
+     * @param errorCode If datagram transfer failed, the reason for failure.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    void onReceiveDatagramStateChanged(
+            @SatelliteManager.SatelliteDatagramTransferState int state, int receivePendingCount,
+            @SatelliteManager.SatelliteResult int errorCode);
+}
diff --git a/android-35/android/telephony/satellite/stub/SatelliteGatewayService.java b/android-35/android/telephony/satellite/stub/SatelliteGatewayService.java
new file mode 100644
index 0000000..f4514a6
--- /dev/null
+++ b/android-35/android/telephony/satellite/stub/SatelliteGatewayService.java
@@ -0,0 +1,77 @@
+/*
+ * 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.telephony.satellite.stub;
+
+import android.annotation.SdkConstant;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import com.android.telephony.Rlog;
+
+/**
+ * Main SatelliteGatewayService implementation, which binds via the Telephony SatelliteController.
+ * Services that extend SatelliteGatewayService must register the service in their AndroidManifest
+ * to be detected by the framework. The application must declare that they require the
+ * "android.permission.BIND_SATELLITE_GATEWAY_SERVICE" permission to ensure that nothing else can
+ * bind to their service except the Telephony framework. The SatelliteGatewayService definition in
+ * the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgSatelliteGatewayService"
+ *     android:permission="android.permission.BIND_SATELLITE_GATEWAY_SERVICE" >
+ *     ...
+ *     <intent-filter>
+ *         <action android:name="android.telephony.satellite.SatelliteGatewayService" />
+ *     </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the SatelliteGatewayService defined in the manifest if
+ * it is the default SatelliteGatewayService defined in the device overlay
+ * "config_satellite_gateway_service_package".
+ * @hide
+ */
+public abstract class SatelliteGatewayService extends Service {
+    private static final String TAG = "SatelliteGatewayService";
+
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.telephony.satellite.SatelliteGatewayService";
+
+    private final IBinder mBinder = new ISatelliteGateway.Stub() {};
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IBinder onBind(Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            Rlog.d(TAG, "SatelliteGatewayService bound");
+            return mBinder;
+        }
+        return null;
+    }
+
+    /**
+     * @return The binder for the ISatelliteGateway.
+     * @hide
+     */
+    public final IBinder getBinder() {
+        return mBinder;
+    }
+}
diff --git a/android-35/android/telephony/satellite/stub/SatelliteImplBase.java b/android-35/android/telephony/satellite/stub/SatelliteImplBase.java
new file mode 100644
index 0000000..a623633
--- /dev/null
+++ b/android-35/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -0,0 +1,771 @@
+/*
+ * Copyright (C) 2022 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.satellite.stub;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.IBooleanConsumer;
+import android.telephony.IIntegerConsumer;
+import android.util.Log;
+
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
+
+/**
+ * Base implementation of satellite service.
+ * Any service wishing to provide satellite services should extend this class and implement all
+ * methods that the service supports.
+ * @hide
+ */
+public class SatelliteImplBase extends SatelliteService {
+    private static final String TAG = "SatelliteImplBase";
+
+    protected final Executor mExecutor;
+
+    /**
+     * Create SatelliteImplBase using the Executor specified for methods being called from the
+     * framework.
+     * @param executor The executor for the framework to use when executing the methods overridden
+     * by the implementation of Satellite.
+     * @hide
+     */
+    public SatelliteImplBase(@NonNull Executor executor) {
+        super();
+        mExecutor = executor;
+    }
+
+    /**
+     * @return The binder for the Satellite implementation.
+     * @hide
+     */
+    public final IBinder getBinder() {
+        return mBinder;
+    }
+
+    private final IBinder mBinder = new ISatellite.Stub() {
+        @Override
+        public void setSatelliteListener(ISatelliteListener listener) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this.setSatelliteListener(listener),
+                    "setSatelliteListener");
+        }
+
+        @Override
+        public void requestSatelliteListeningEnabled(boolean enable, int timeout,
+                IIntegerConsumer resultCallback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .requestSatelliteListeningEnabled(enable, timeout, resultCallback),
+                    "requestSatelliteListeningEnabled");
+        }
+
+        @Override
+        public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
+                IIntegerConsumer resultCallback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .enableCellularModemWhileSatelliteModeIsOn(enabled, resultCallback),
+                    "enableCellularModemWhileSatelliteModeIsOn");
+        }
+
+        @Override
+        public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
+                boolean isEmergency, IIntegerConsumer resultCallback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .requestSatelliteEnabled(
+                                    enableSatellite, enableDemoMode, isEmergency, resultCallback),
+                    "requestSatelliteEnabled");
+        }
+
+        @Override
+        public void requestIsSatelliteEnabled(IIntegerConsumer resultCallback,
+                IBooleanConsumer callback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .requestIsSatelliteEnabled(resultCallback, callback),
+                    "requestIsSatelliteEnabled");
+        }
+
+        @Override
+        public void requestIsSatelliteSupported(IIntegerConsumer resultCallback,
+                IBooleanConsumer callback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .requestIsSatelliteSupported(resultCallback, callback),
+                    "requestIsSatelliteSupported");
+        }
+
+        @Override
+        public void requestSatelliteCapabilities(IIntegerConsumer resultCallback,
+                ISatelliteCapabilitiesConsumer callback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .requestSatelliteCapabilities(resultCallback, callback),
+                    "requestSatelliteCapabilities");
+        }
+
+        @Override
+        public void startSendingSatellitePointingInfo(IIntegerConsumer resultCallback)
+                throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this.startSendingSatellitePointingInfo(resultCallback),
+                    "startSendingSatellitePointingInfo");
+        }
+
+        @Override
+        public void stopSendingSatellitePointingInfo(IIntegerConsumer resultCallback)
+                throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this.stopSendingSatellitePointingInfo(resultCallback),
+                    "stopSendingSatellitePointingInfo");
+        }
+
+        @Override
+        public void provisionSatelliteService(String token, byte[] provisionData,
+                IIntegerConsumer resultCallback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .provisionSatelliteService(token, provisionData, resultCallback),
+                    "provisionSatelliteService");
+        }
+
+        @Override
+        public void deprovisionSatelliteService(String token, IIntegerConsumer resultCallback)
+                throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this.deprovisionSatelliteService(token, resultCallback),
+                    "deprovisionSatelliteService");
+        }
+
+        @Override
+        public void requestIsSatelliteProvisioned(IIntegerConsumer resultCallback,
+                IBooleanConsumer callback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .requestIsSatelliteProvisioned(resultCallback, callback),
+                    "requestIsSatelliteProvisioned");
+        }
+
+        @Override
+        public void pollPendingSatelliteDatagrams(IIntegerConsumer resultCallback)
+                throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this.pollPendingSatelliteDatagrams(resultCallback),
+                    "pollPendingSatelliteDatagrams");
+        }
+
+        @Override
+        public void sendSatelliteDatagram(SatelliteDatagram datagram, boolean isEmergency,
+                IIntegerConsumer resultCallback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .sendSatelliteDatagram(datagram, isEmergency, resultCallback),
+                    "sendDatagram");
+        }
+
+        @Override
+        public void requestSatelliteModemState(IIntegerConsumer resultCallback,
+                IIntegerConsumer callback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .requestSatelliteModemState(resultCallback, callback),
+                    "requestSatelliteModemState");
+        }
+
+        @Override
+        public void requestTimeForNextSatelliteVisibility(IIntegerConsumer resultCallback,
+                IIntegerConsumer callback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .requestTimeForNextSatelliteVisibility(resultCallback, callback),
+                    "requestTimeForNextSatelliteVisibility");
+        }
+
+        @Override
+        public void setSatellitePlmn(int simSlot, List<String> carrierPlmnList,
+                List<String> devicePlmnList, IIntegerConsumer resultCallback)
+                throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this.setSatellitePlmn(
+                            simSlot, carrierPlmnList, devicePlmnList, resultCallback),
+                    "setSatellitePlmn");
+        }
+
+        @Override
+        public void setSatelliteEnabledForCarrier(int simSlot, boolean enableSatellite,
+                IIntegerConsumer resultCallback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this.setSatelliteEnabledForCarrier(
+                            simSlot, enableSatellite, resultCallback),
+                    "setSatelliteEnabledForCarrier");
+        }
+
+        @Override
+        public void requestIsSatelliteEnabledForCarrier(int simSlot,
+                IIntegerConsumer resultCallback, IBooleanConsumer callback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .requestIsSatelliteEnabledForCarrier(simSlot, resultCallback, callback),
+                    "requestIsSatelliteEnabledForCarrier");
+        }
+
+        @Override
+        public void requestSignalStrength(IIntegerConsumer resultCallback,
+                INtnSignalStrengthConsumer callback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this.requestSignalStrength(resultCallback, callback),
+                    "requestSignalStrength");
+        }
+
+        @Override
+        public void startSendingNtnSignalStrength(IIntegerConsumer resultCallback)
+                throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this.startSendingNtnSignalStrength(resultCallback),
+                    "startSendingNtnSignalStrength");
+        }
+
+        @Override
+        public void stopSendingNtnSignalStrength(IIntegerConsumer resultCallback)
+                throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this.stopSendingNtnSignalStrength(resultCallback),
+                    "stopSendingNtnSignalStrength");
+        }
+
+        @Override
+        public void abortSendingSatelliteDatagrams(IIntegerConsumer resultCallback)
+                throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this.abortSendingSatelliteDatagrams(resultCallback),
+                    "abortSendingSatelliteDatagrams");
+        }
+
+        // Call the methods with a clean calling identity on the executor and wait indefinitely for
+        // the future to return.
+        private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+            try {
+                CompletableFuture.runAsync(
+                        () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(TAG, "SatelliteImplBase Binder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+    };
+
+    /**
+     * Register the callback interface with satellite service.
+     *
+     * @param listener The callback interface to handle satellite service indications.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     */
+    public void setSatelliteListener(@NonNull ISatelliteListener listener) {
+        // stub implementation
+    }
+
+    /**
+     * Request to enable or disable the satellite service listening mode.
+     * Listening mode allows the satellite service to listen for incoming pages.
+     *
+     * @param enable True to enable satellite listening mode and false to disable.
+     * @param timeout How long the satellite modem should wait for the next incoming page before
+     *                disabling listening mode.
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     */
+    public void requestSatelliteListeningEnabled(boolean enable, int timeout,
+            @NonNull IIntegerConsumer resultCallback) {
+        // stub implementation
+    }
+
+    /**
+     * Allow cellular modem scanning while satellite mode is on.
+     * @param enabled  {@code true} to enable cellular modem while satellite mode is on
+     * and {@code false} to disable
+     * @param resultCallback The callback to receive the error code result of the operation.
+     */
+    public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
+            @NonNull IIntegerConsumer resultCallback) {
+        // stub implementation
+    }
+
+    /**
+     * Request to enable or disable the satellite modem and demo mode. If the satellite modem is
+     * enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
+     * this may also re-enable the cellular modem.
+     *
+     * @param enableSatellite True to enable the satellite modem and false to disable.
+     * @param enableDemoMode True to enable demo mode and false to disable.
+     * @param isEmergency To specify the satellite is enabled for emergency session and false for
+     * non emergency session. Note: it is possible that a emergency session started get converted
+     * to a non emergency session and vice versa.
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     */
+    public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
+            boolean isEmergency, @NonNull IIntegerConsumer resultCallback) {
+        // stub implementation
+    }
+
+    /**
+     * Request to get whether the satellite modem is enabled.
+     *
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *                       This must only be sent when the result is not
+     *                       SatelliteResult#SATELLITE_RESULT_SUCCESS.
+     * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+     *                 receive whether the satellite modem is enabled.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     */
+    public void requestIsSatelliteEnabled(@NonNull IIntegerConsumer resultCallback,
+            @NonNull IBooleanConsumer callback) {
+        // stub implementation
+    }
+
+    /**
+     * Request to get whether the satellite service is supported on the device.
+     *
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *                       This must only be sent when the result is not
+     *                       SatelliteResult#SATELLITE_RESULT_SUCCESS.
+     * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+     *                 receive whether the satellite service is supported on the device.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     */
+    public void requestIsSatelliteSupported(@NonNull IIntegerConsumer resultCallback,
+            @NonNull IBooleanConsumer callback) {
+        // stub implementation
+    }
+
+    /**
+     * Request to get the SatelliteCapabilities of the satellite service.
+     *
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *                       This must only be sent when the result is not
+     *                       SatelliteResult#SATELLITE_RESULT_SUCCESS.
+     * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+     *                 receive the SatelliteCapabilities of the satellite service.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     */
+    public void requestSatelliteCapabilities(@NonNull IIntegerConsumer resultCallback,
+            @NonNull ISatelliteCapabilitiesConsumer callback) {
+        // stub implementation
+    }
+
+    /**
+     * User started pointing to the satellite.
+     * The satellite service should report the satellite pointing info via
+     * ISatelliteListener#onSatellitePositionChanged as the user device/satellite moves.
+     *
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     */
+    public void startSendingSatellitePointingInfo(@NonNull IIntegerConsumer resultCallback) {
+        // stub implementation
+    }
+
+    /**
+     * User stopped pointing to the satellite.
+     * The satellite service should stop reporting satellite pointing info to the framework.
+     *
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     */
+    public void stopSendingSatellitePointingInfo(@NonNull IIntegerConsumer resultCallback) {
+        // stub implementation
+    }
+
+    /**
+     * Provision the device with a satellite provider.
+     * This is needed if the provider allows dynamic registration.
+     * Once provisioned, ISatelliteListener#onSatelliteProvisionStateChanged should report true.
+     *
+     * @param token The token to be used as a unique identifier for provisioning with satellite
+     *              gateway.
+     * @param provisionData Data from the provisioning app that can be used by provisioning
+     *                      server
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED
+     *   SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT
+     */
+    public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
+            @NonNull IIntegerConsumer resultCallback) {
+        // stub implementation
+    }
+
+    /**
+     * Deprovision the device with the satellite provider.
+     * This is needed if the provider allows dynamic registration.
+     * Once deprovisioned, ISatelliteListener#onSatelliteProvisionStateChanged should report false.
+     *
+     * @param token The token of the device/subscription to be deprovisioned.
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED
+     *   SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT
+     */
+    public void deprovisionSatelliteService(@NonNull String token,
+            @NonNull IIntegerConsumer resultCallback) {
+        // stub implementation
+    }
+
+    /**
+     * Request to get whether this device is provisioned with a satellite provider.
+     *
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *                       This must only be sent when the result is not
+     *                       SatelliteResult#SATELLITE_RESULT_SUCCESS.
+     * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+     *                 receive whether this device is provisioned with a satellite provider.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     */
+    public void requestIsSatelliteProvisioned(@NonNull IIntegerConsumer resultCallback,
+            @NonNull IBooleanConsumer callback) {
+        // stub implementation
+    }
+
+    /**
+     * Poll the pending datagrams to be received over satellite.
+     * The satellite service should check if there are any pending datagrams to be received over
+     * satellite and report them via ISatelliteListener#onSatelliteDatagramsReceived.
+     *
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     *   SatelliteResult:SATELLITE_RESULT_ACCESS_BARRED
+     *   SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT
+     *   SatelliteResult:SATELLITE_RESULT_NOT_REACHABLE
+     *   SatelliteResult:SATELLITE_RESULT_NOT_AUTHORIZED
+     */
+    public void pollPendingSatelliteDatagrams(@NonNull IIntegerConsumer resultCallback) {
+        // stub implementation
+    }
+
+    /**
+     * Send datagram over satellite.
+     *
+     * @param datagram Datagram to send in byte format.
+     * @param isEmergency Whether this is an emergency datagram.
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED
+     *   SatelliteResult:SATELLITE_RESULT_ACCESS_BARRED
+     *   SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT
+     *   SatelliteResult:SATELLITE_RESULT_NOT_REACHABLE
+     *   SatelliteResult:SATELLITE_RESULT_NOT_AUTHORIZED
+     */
+    public void sendSatelliteDatagram(@NonNull SatelliteDatagram datagram, boolean isEmergency,
+            @NonNull IIntegerConsumer resultCallback) {
+        // stub implementation
+    }
+
+    /**
+     * Request the current satellite modem state.
+     * The satellite service should report the current satellite modem state via
+     * ISatelliteListener#onSatelliteModemStateChanged.
+     *
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *                       This must only be sent when the result is not
+     *                       SatelliteResult#SATELLITE_RESULT_SUCCESS.
+     * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+     *                 receive the current satellite modem state.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     */
+    public void requestSatelliteModemState(@NonNull IIntegerConsumer resultCallback,
+            @NonNull IIntegerConsumer callback) {
+        // stub implementation
+    }
+
+    /**
+     * Request to get the time after which the satellite will be visible. This is an int
+     * representing the duration in seconds after which the satellite will be visible.
+     * This will return 0 if the satellite is currently visible.
+     *
+     * @param resultCallback The callback to receive the error code result of the operation.
+     *                       This must only be sent when the result is not
+     *                       SatelliteResult#SATELLITE_RESULT_SUCCESS.
+     * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+     *                 receive the time after which the satellite will be visible.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     */
+    public void requestTimeForNextSatelliteVisibility(@NonNull IIntegerConsumer resultCallback,
+            @NonNull IIntegerConsumer callback) {
+        // stub implementation
+    }
+
+
+    /**
+     * Set the non-terrestrial PLMN with lower priority than terrestrial networks.
+     * MCC/MNC broadcast by the non-terrestrial networks may not be included in OPLMNwACT file on
+     * SIM profile. Acquisition of satellite based system is lower priority to terrestrial
+     * networks. UE shall make all attempts to acquire terrestrial service prior to camping on
+     * satellite LTE service.
+     *
+     * @param simLogicalSlotIndex Indicates the SIM logical slot index to which this API will be
+     * applied. The modem will use this information to determine the relevant carrier.
+     * @param resultCallback The callback to receive the error code result of the operation.
+     * @param carrierPlmnList The list of roaming PLMN used for connecting to satellite networks
+     *                        supported by user subscription.
+     * @param allSatellitePlmnList Modem should use the allSatellitePlmnList to identify satellite
+     *                             PLMNs that are not supported by the carrier and make sure not to
+     *                             attach to them.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:NONE
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:MODEM_ERR
+     *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     */
+    public void setSatellitePlmn(@NonNull int simLogicalSlotIndex,
+            @NonNull List<String> carrierPlmnList, @NonNull List<String> allSatellitePlmnList,
+            @NonNull IIntegerConsumer resultCallback) {
+        // stub implementation
+    }
+
+    /**
+     * Request to enable or disable carrier supported satellite plmn scan and attach by modem.
+     * Refer {@link #setSatellitePlmn} for the details of satellite PLMN scanning process.
+     *
+     * @param simLogicalSlotIndex Indicates the SIM logical slot index to which this API will be
+     * applied. The modem will use this information to determine the relevant carrier.
+     * @param satelliteEnabled {@code true} to enable satellite, {@code false} to disable satellite.
+     * @param callback {@code true} to enable satellite, {@code false} to disable satellite.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     */
+    public void setSatelliteEnabledForCarrier(@NonNull int simLogicalSlotIndex,
+            @NonNull boolean satelliteEnabled, @NonNull IIntegerConsumer callback) {
+        // stub implementation
+    }
+
+    /**
+     * Request to get whether the satellite is enabled in the cellular modem associated with a
+     * carrier.
+     *
+     * @param simLogicalSlotIndex Indicates the SIM logical slot index to which this API will be
+     * applied. The modem will use this information to determine the relevant carrier.
+     * @param resultCallback The callback to receive the error code result of the operation.
+     * @param callback {@code true} to satellite enabled, {@code false} to satellite disabled.
+     *
+     * Valid result codes returned:
+     *   SatelliteResult:SATELLITE_RESULT_SUCCESS
+     *   SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+     *   SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+     *   SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+     *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+     */
+    public void requestIsSatelliteEnabledForCarrier(@NonNull int simLogicalSlotIndex,
+            @NonNull IIntegerConsumer resultCallback, @NonNull IBooleanConsumer callback) {
+        // stub implementation
+    }
+
+    /**
+     * Request to get the signal strength of the satellite connection.
+     *
+     * @param resultCallback The {@link SatelliteError} result of the operation.
+     * @param callback The callback to handle the NTN signal strength changed event.
+     */
+    public void requestSignalStrength(@NonNull IIntegerConsumer resultCallback,
+            INtnSignalStrengthConsumer callback) {
+        // stub implementation
+    }
+
+    /**
+     * Requests to deliver signal strength changed events through the
+     * {@link ISatelliteListener#onNtnSignalStrengthChanged(NtnSignalStrength ntnSignalStrength)}
+     * callback.
+     *
+     * @param resultCallback The {@link SatelliteError} result of the operation.
+     */
+    public void startSendingNtnSignalStrength(@NonNull IIntegerConsumer resultCallback) {
+        // stub implementation
+    }
+
+    /**
+     * Requests to stop signal strength changed events
+     *
+     * @param resultCallback The {@link SatelliteError} result of the operation.
+     */
+    public void stopSendingNtnSignalStrength(@NonNull IIntegerConsumer resultCallback){
+        // stub implementation
+    }
+
+    /**
+     * Requests to abort sending satellite datagrams
+     *
+     * @param resultCallback The {@link SatelliteError} result of the operation.
+     */
+    public void abortSendingSatelliteDatagrams(@NonNull IIntegerConsumer resultCallback){
+        // stub implementation
+    }
+}
diff --git a/android-35/android/telephony/satellite/stub/SatelliteService.java b/android-35/android/telephony/satellite/stub/SatelliteService.java
new file mode 100644
index 0000000..5b96e34
--- /dev/null
+++ b/android-35/android/telephony/satellite/stub/SatelliteService.java
@@ -0,0 +1,64 @@
+/*
+ * 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.telephony.satellite.stub;
+
+import android.annotation.SdkConstant;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import com.android.telephony.Rlog;
+
+/**
+ * Main SatelliteService implementation, which binds via the Telephony SatelliteServiceController.
+ * Services that extend SatelliteService must register the service in their AndroidManifest to be
+ * detected by the framework. First, the application must declare that they use the
+ * "android.permission.BIND_SATELLITE_SERVICE" permission. Then, the SatelliteService definition in
+ * the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgSatelliteService"
+ *     android:permission="android.permission.BIND_SATELLITE_SERVICE" >
+ *     ...
+ *     <intent-filter>
+ *         <action android:name="android.telephony.satellite.SatelliteService" />
+ *     </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the SatelliteService defined in the manifest if it is
+ * the default SatelliteService defined in the device overlay "config_satellite_service_package".
+ * @hide
+ */
+public class SatelliteService extends Service {
+    private static final String TAG = "SatelliteService";
+
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE = "android.telephony.satellite.SatelliteService";
+
+    /**
+     * @hide
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            Rlog.d(TAG, "SatelliteService bound");
+            return new SatelliteImplBase(Runnable::run).getBinder();
+        }
+        return null;
+    }
+}