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 & 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>
+ * <!-- Service that delivers SMS messages received from the phone "quick response" -->
+ * <service android:name=".HeadlessSmsSendService"
+ * android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
+ * android:exported="true" >
+ * <intent-filter>
+ * <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
+ * <category android:name="android.intent.category.DEFAULT" />
+ * <data android:scheme="sms" />
+ * <data android:scheme="smsto" />
+ * <data android:scheme="mms" />
+ * <data android:scheme="mmsto" />
+ * </intent-filter>
+ * </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 & 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;
+ }
+}