Import Android SDK Platform P [4697573]

/google/data/ro/projects/android/fetch_artifact \
    --bid 4697573 \
    --target sdk_phone_armv7-win_sdk \
    sdk-repo-linux-sources-4697573.zip

AndroidVersion.ApiLevel has been modified to appear as 28

Change-Id: If80578c3c657366cc9cf75f8db13d46e2dd4e077
diff --git a/android/telephony/AccessNetworkConstants.java b/android/telephony/AccessNetworkConstants.java
index 7cd1612..cac9f2b 100644
--- a/android/telephony/AccessNetworkConstants.java
+++ b/android/telephony/AccessNetworkConstants.java
@@ -30,6 +30,9 @@
         public static final int EUTRAN = 3;
         public static final int CDMA2000 = 4;
         public static final int IWLAN = 5;
+
+        /** @hide */
+        private AccessNetworkType() {};
     }
 
     /**
@@ -42,6 +45,9 @@
         public static final int WWAN = 1;
         /** Wireless Local Area Networks (i.e. Wifi) */
         public static final int WLAN = 2;
+
+        /** @hide */
+        private TransportType() {};
     }
 
     /**
@@ -63,6 +69,9 @@
         public static final int BAND_DCS1800 = 12;
         public static final int BAND_PCS1900 = 13;
         public static final int BAND_ER900 = 14;
+
+        /** @hide */
+        private GeranBand() {};
     }
 
     /**
@@ -92,6 +101,9 @@
         /** band 23, 24 are reserved */
         public static final int BAND_25 = 25;
         public static final int BAND_26 = 26;
+
+        /** @hide */
+        private UtranBand() {};
     }
 
     /**
@@ -147,6 +159,9 @@
         public static final int BAND_66 = 66;
         public static final int BAND_68 = 68;
         public static final int BAND_70 = 70;
+
+        /** @hide */
+        private EutranBand() {};
     }
 
     /**
@@ -179,5 +194,11 @@
         public static final int BAND_19 = 20;
         public static final int BAND_20 = 21;
         public static final int BAND_21 = 22;
+
+        /** @hide */
+        private CdmaBands() {};
     }
+
+    /** @hide */
+    private AccessNetworkConstants() {};
 }
diff --git a/android/telephony/AccessNetworkUtils.java b/android/telephony/AccessNetworkUtils.java
new file mode 100644
index 0000000..5d2c225
--- /dev/null
+++ b/android/telephony/AccessNetworkUtils.java
@@ -0,0 +1,167 @@
+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.ServiceState.DuplexMode;
+
+
+/**
+ * Utilities to map between radio constants.
+ *
+ * @hide
+ */
+public class AccessNetworkUtils {
+
+    // do not instantiate
+    private AccessNetworkUtils() {}
+
+    public static final int INVALID_BAND = -1;
+
+    /**
+     * 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_68) {
+            return DUPLEX_MODE_UNKNOWN;
+        } else if (band >= EutranBand.BAND_65) {
+            return DUPLEX_MODE_FDD;
+        } else if (band >= EutranBand.BAND_47) {
+            return DUPLEX_MODE_UNKNOWN;
+        } 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 36.101 sec 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 > 67535) {
+            return INVALID_BAND;
+        } 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 > 54339) {
+            return INVALID_BAND;
+        } else if (earfcn >= 46790 /* inferred from the end range of BAND_45 */) {
+            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;
+    }
+}
diff --git a/android/telephony/CarrierConfigManager.java b/android/telephony/CarrierConfigManager.java
index 91d86c6..4683161 100644
--- a/android/telephony/CarrierConfigManager.java
+++ b/android/telephony/CarrierConfigManager.java
@@ -27,8 +27,8 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.service.carrier.CarrierService;
+import android.telephony.ims.ImsReasonInfo;
 
-import com.android.ims.ImsReasonInfo;
 import com.android.internal.telephony.ICarrierConfigLoader;
 
 /**
@@ -77,6 +77,14 @@
     public static final String
             KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
 
+   /**
+    * Boolean indicating if the "Call barring" item is visible in the Call Settings menu.
+    * true means visible. false means gone.
+    * @hide
+    */
+    public static final String KEY_CALL_BARRING_VISIBILITY_BOOL =
+            "call_barring_visibility_bool";
+
     /**
      * Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
      * events from the Sim.
@@ -146,6 +154,15 @@
     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.
      */
@@ -380,6 +397,15 @@
     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";
+
+    /**
      * 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>
@@ -503,6 +529,20 @@
     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";
+
     /** Flag specifying whether VoLTE TTY is supported. */
     public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
             = "carrier_volte_tty_supported_bool";
@@ -920,6 +960,8 @@
      * 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";
 
@@ -969,8 +1011,13 @@
             "wfc_emergency_address_carrier_app_string";
 
     /**
-     * Boolean to decide whether to use #KEY_CARRIER_NAME_STRING from CarrierConfig app.
-     * @hide
+     * Unconditionally override the carrier name string using #KEY_CARRIER_NAME_STRING.
+     *
+     * If true, then the carrier display name 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.
      */
     public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
 
@@ -978,7 +1025,6 @@
      * 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
-     * @hide
      */
     public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
 
@@ -1250,13 +1296,38 @@
 
     /**
      * The duration in seconds that platform call and message blocking is disabled after the user
-     * contacts emergency services. Platform considers values in the range 0 to 604800 (one week) as
-     * valid. See {@link android.provider.BlockedNumberContract#isBlocked(Context, String)}).
+     * 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-eanble 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.
+     * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED
+     * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE
+     * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE
+     * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN
+     *
+     * <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.
+     * @hide
+     */
+    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.
@@ -1345,6 +1416,12 @@
      */
     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.
+     * @hide
+     */
+    public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL =
+            "carrier_allow_deflect_ims_call_bool";
 
     /**
      * Flag indicating whether the carrier always wants to play an "on-hold" tone when a call has
@@ -1381,6 +1458,14 @@
     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}.
      *
@@ -1390,7 +1475,7 @@
             "allow_video_calling_fallback_bool";
 
     /**
-     * Defines operator-specific {@link com.android.ims.ImsReasonInfo} mappings.
+     * Defines operator-specific {@link ImsReasonInfo} mappings.
      *
      * Format: "ORIGINAL_CODE|MESSAGE|NEW_CODE"
      * Where {@code ORIGINAL_CODE} corresponds to a {@link ImsReasonInfo#getCode()} code,
@@ -1530,6 +1615,14 @@
             "notify_international_call_on_wfc_bool";
 
     /**
+     * Flag specifying whether to show an alert dialog for video call charges.
+     * By default this value is {@code false}.
+     * @hide
+     */
+    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
@@ -1659,13 +1752,6 @@
             "roaming_operator_string_array";
 
     /**
-     * Controls whether Assisted Dialing is enabled and the preference is shown. This feature
-     * transforms numbers when the user is roaming.
-     */
-    public static final String KEY_ASSISTED_DIALING_ENABLED_BOOL =
-            "assisted_dialing_enabled_bool";
-
-    /**
      * URL from which the proto containing the public key of the Carrier used for
      * IMSI encryption will be downloaded.
      * @hide
@@ -1777,22 +1863,74 @@
             "check_pricing_with_carrier_data_roaming_bool";
 
     /**
-     * List of thresholds of RSRP for determining the display level of LTE signal bar.
+     * 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";
 
+    /**
+     * 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 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";
+
+    /**
+     * A list of 4 LTE 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 SignalStrength#MAX_WCDMA_RSCP and SignalStrength#MIN_WDCMA_RSCP. 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";
+
+    /**
+     * 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 only supports the value "rscp"
+     * @hide
+     */
+    public static final String KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING =
+            "wcdma_default_signal_strength_measurement_string";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
     static {
         sDefaults = new PersistableBundle();
         sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
         sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
         sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false);
         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);
@@ -1802,6 +1940,7 @@
         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.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);
@@ -1815,11 +1954,15 @@
         sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_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_VOLTE_TTY_SUPPORTED_BOOL, false);
         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.putString(KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_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_INSTANT_LETTERING_INVALID_CHARS_STRING, "");
         sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, "");
         sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING, "");
@@ -1833,6 +1976,7 @@
         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_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);
@@ -1945,6 +2089,7 @@
         sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
         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, false);
 
         // MMS defaults
         sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
@@ -2033,6 +2178,7 @@
         sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_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);
@@ -2050,6 +2196,7 @@
         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_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false);
         sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
                 null);
         sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
@@ -2062,7 +2209,6 @@
                 false);
         sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
-        sDefaults.putBoolean(KEY_ASSISTED_DIALING_ENABLED_BOOL, true);
         sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
         sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
@@ -2075,19 +2221,27 @@
         sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
         sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
                 new int[] {
-                        -140, /* SIGNAL_STRENGTH_NONE_OR_UNKNOWN */
                         -128, /* SIGNAL_STRENGTH_POOR */
                         -118, /* SIGNAL_STRENGTH_MODERATE */
                         -108, /* SIGNAL_STRENGTH_GOOD */
                         -98,  /* SIGNAL_STRENGTH_GREAT */
-                        -44
                 });
+        sDefaults.putIntArray(KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY,
+                new int[] {
+                        -115,  /* SIGNAL_STRENGTH_POOR */
+                        -105, /* SIGNAL_STRENGTH_MODERATE */
+                        -95, /* SIGNAL_STRENGTH_GOOD */
+                        -85  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "");
     }
 
     /**
      * 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.
+     * 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}
@@ -2114,7 +2268,9 @@
     }
 
     /**
-     * Gets the configuration values for the default subscription.
+     * 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}
@@ -2143,6 +2299,9 @@
      * <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.
      * </p>
      *
      * @param bundle the configuration bundle to be checked.
diff --git a/android/telephony/CellIdentity.java b/android/telephony/CellIdentity.java
index e092d52..890a6ea 100644
--- a/android/telephony/CellIdentity.java
+++ b/android/telephony/CellIdentity.java
@@ -18,11 +18,14 @@
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
+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;
 
 /**
  * CellIdentity represents the identity of a unique cell. This is the base class for
@@ -68,6 +71,9 @@
      */
     public static final int TYPE_TDSCDMA        = 5;
 
+    /** @hide */
+    public static final int INVALID_CHANNEL_NUMBER = -1;
+
     // Log tag
     /** @hide */
     protected final String mTag;
@@ -81,8 +87,16 @@
     /** @hide */
     protected final String mMncStr;
 
+    // long alpha Operator Name String or Enhanced Operator Name String
     /** @hide */
-    protected CellIdentity(String tag, int type, String mcc, String mnc) {
+    protected final String mAlphaLong;
+    // short alpha Operator Name String or Enhanced Operator Name String
+    /** @hide */
+    protected final String mAlphaShort;
+
+    /** @hide */
+    protected CellIdentity(String tag, int type, String mcc, String mnc, String alphal,
+                           String alphas) {
         mTag = tag;
         mType = type;
 
@@ -110,6 +124,8 @@
             mMncStr = null;
             log("invalid MNC format: " + mnc);
         }
+        mAlphaLong = alphal;
+        mAlphaShort = alphas;
     }
 
     /** Implement the Parcelable interface */
@@ -125,6 +141,50 @@
     public @Type int getType() { return mType; }
 
     /**
+     * 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;
+    }
+
+    /**
+     * @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;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CellIdentity)) {
+            return false;
+        }
+
+        CellIdentity o = (CellIdentity) other;
+        return 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
@@ -134,6 +194,8 @@
         dest.writeInt(type);
         dest.writeString(mMccStr);
         dest.writeString(mMncStr);
+        dest.writeString(mAlphaLong);
+        dest.writeString(mAlphaShort);
     }
 
     /**
@@ -141,7 +203,8 @@
      * @hide
      */
     protected CellIdentity(String tag, int type, Parcel source) {
-        this(tag, type, source.readString(), source.readString());
+        this(tag, type, source.readString(), source.readString(),
+                source.readString(), source.readString());
     }
 
     /** Implement the Parcelable interface */
diff --git a/android/telephony/CellIdentityCdma.java b/android/telephony/CellIdentityCdma.java
index 2e1d1dc..58a2c45 100644
--- a/android/telephony/CellIdentityCdma.java
+++ b/android/telephony/CellIdentityCdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -48,23 +49,17 @@
      * to +90 degrees).
      */
     private final int mLatitude;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
 
     /**
      * @hide
      */
     public CellIdentityCdma() {
-        super(TAG, TYPE_CDMA, null, null);
+        super(TAG, TYPE_CDMA, null, null, null, null);
         mNetworkId = Integer.MAX_VALUE;
         mSystemId = Integer.MAX_VALUE;
         mBasestationId = Integer.MAX_VALUE;
         mLongitude = Integer.MAX_VALUE;
         mLatitude = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
 
     /**
@@ -99,14 +94,16 @@
      */
     public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat, String alphal,
                              String alphas) {
-        super(TAG, TYPE_CDMA, null, null);
+        super(TAG, TYPE_CDMA, null, null, alphal, alphas);
         mNetworkId = nid;
         mSystemId = sid;
         mBasestationId = bid;
-        mLongitude = lon;
-        mLatitude = lat;
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
+        if (!isNullIsland(lat, lon)) {
+            mLongitude = lon;
+            mLatitude = lat;
+        } else {
+            mLongitude = mLatitude = Integer.MAX_VALUE;
+        }
     }
 
     private CellIdentityCdma(CellIdentityCdma cid) {
@@ -119,6 +116,18 @@
     }
 
     /**
+     * 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, Integer.MAX_VALUE if unknown
      */
     public int getNetworkId() {
@@ -161,26 +170,10 @@
         return mLatitude;
     }
 
-    /**
-     * @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.
-     */
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @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.
-     */
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
     @Override
     public int hashCode() {
         return Objects.hash(mNetworkId, mSystemId, mBasestationId, mLatitude, mLongitude,
-                mAlphaLong, mAlphaShort);
+                super.hashCode());
     }
 
     @Override
@@ -200,8 +193,7 @@
                 && mBasestationId == o.mBasestationId
                 && mLatitude == o.mLatitude
                 && mLongitude == o.mLongitude
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -227,8 +219,6 @@
         dest.writeInt(mBasestationId);
         dest.writeInt(mLongitude);
         dest.writeInt(mLatitude);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -239,8 +229,6 @@
         mBasestationId = in.readInt();
         mLongitude = in.readInt();
         mLatitude = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
 
         if (DBG) log(toString());
     }
diff --git a/android/telephony/CellIdentityGsm.java b/android/telephony/CellIdentityGsm.java
index f948f81..c697880 100644
--- a/android/telephony/CellIdentityGsm.java
+++ b/android/telephony/CellIdentityGsm.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -36,22 +37,16 @@
     private final int mArfcn;
     // 6-bit Base Station Identity Code
     private final int mBsic;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
 
     /**
      * @hide
      */
     public CellIdentityGsm() {
-        super(TAG, TYPE_GSM, null, null);
+        super(TAG, TYPE_GSM, null, null, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mArfcn = Integer.MAX_VALUE;
         mBsic = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
     /**
      * public constructor
@@ -97,16 +92,13 @@
      */
     public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr,
                             String mncStr, String alphal, String alphas) {
-        super(TAG, TYPE_GSM, mccStr, mncStr);
+        super(TAG, TYPE_GSM, mccStr, mncStr, alphal, alphas);
         mLac = lac;
         mCid = cid;
         mArfcn = arfcn;
         // In RIL BSIC is a UINT8, so 0xFF is the 'INVALID' designator
         // for inbound parcels
         mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
-
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
     }
 
     private CellIdentityGsm(CellIdentityGsm cid) {
@@ -120,7 +112,7 @@
 
     /**
      * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
-     * @deprecated Use {@link #getMccStr} instead.
+     * @deprecated Use {@link #getMccString} instead.
      */
     @Deprecated
     public int getMcc() {
@@ -129,7 +121,7 @@
 
     /**
      * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
-     * @deprecated Use {@link #getMncStr} instead.
+     * @deprecated Use {@link #getMncString} instead.
      */
     @Deprecated
     public int getMnc() {
@@ -176,35 +168,24 @@
     /**
      * @return Mobile Country Code in string format, null if unknown
      */
-    public String getMccStr() {
+    public String getMccString() {
         return mMccStr;
     }
 
     /**
      * @return Mobile Network Code in string format, null if unknown
      */
-    public String getMncStr() {
+    public String getMncString() {
         return mMncStr;
     }
 
-    /**
-     * @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.
-     */
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mArfcn;
     }
 
     /**
-     * @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.
-     */
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
-
-    /**
      * @deprecated Primary Scrambling Code is not applicable to GSM.
      * @return Integer.MAX_VALUE, undefined for GSM
      */
@@ -215,7 +196,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mAlphaLong, mAlphaShort);
+        return Objects.hash(mLac, mCid, super.hashCode());
     }
 
     @Override
@@ -235,8 +216,7 @@
                 && mBsic == o.mBsic
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -262,8 +242,6 @@
         dest.writeInt(mCid);
         dest.writeInt(mArfcn);
         dest.writeInt(mBsic);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -273,8 +251,6 @@
         mCid = in.readInt();
         mArfcn = in.readInt();
         mBsic = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
 
         if (DBG) log(toString());
     }
diff --git a/android/telephony/CellIdentityLte.java b/android/telephony/CellIdentityLte.java
index 7f20c8a..177fced 100644
--- a/android/telephony/CellIdentityLte.java
+++ b/android/telephony/CellIdentityLte.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -36,22 +37,19 @@
     private final int mTac;
     // 18-bit Absolute RF Channel Number
     private final int mEarfcn;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
+    // cell bandwidth, in kHz
+    private final int mBandwidth;
 
     /**
      * @hide
      */
     public CellIdentityLte() {
-        super(TAG, TYPE_LTE, null, null);
+        super(TAG, TYPE_LTE, null, null, null, null);
         mCi = Integer.MAX_VALUE;
         mPci = Integer.MAX_VALUE;
         mTac = Integer.MAX_VALUE;
         mEarfcn = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
+        mBandwidth = Integer.MAX_VALUE;
     }
 
     /**
@@ -65,7 +63,8 @@
      * @hide
      */
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
-        this(ci, pci, tac, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc), null, null);
+        this(ci, pci, tac, Integer.MAX_VALUE, Integer.MAX_VALUE, String.valueOf(mcc),
+                String.valueOf(mnc), null, null);
     }
 
     /**
@@ -80,7 +79,8 @@
      * @hide
      */
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
-        this(ci, pci, tac, earfcn, String.valueOf(mcc), String.valueOf(mnc), null, null);
+        this(ci, pci, tac, earfcn, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc),
+                null, null);
     }
 
     /**
@@ -89,6 +89,7 @@
      * @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
@@ -96,19 +97,18 @@
      *
      * @hide
      */
-    public CellIdentityLte(int ci, int pci, int tac, int earfcn, String mccStr,
-                            String mncStr, String alphal, String alphas) {
-        super(TAG, TYPE_LTE, mccStr, mncStr);
+    public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr,
+            String mncStr, String alphal, String alphas) {
+        super(TAG, TYPE_LTE, mccStr, mncStr, alphal, alphas);
         mCi = ci;
         mPci = pci;
         mTac = tac;
         mEarfcn = earfcn;
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
+        mBandwidth = bandwidth;
     }
 
     private CellIdentityLte(CellIdentityLte cid) {
-        this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mMccStr,
+        this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort);
     }
 
@@ -118,7 +118,7 @@
 
     /**
      * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
-     * @deprecated Use {@link #getMccStr} instead.
+     * @deprecated Use {@link #getMccString} instead.
      */
     @Deprecated
     public int getMcc() {
@@ -127,7 +127,7 @@
 
     /**
      * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
-     * @deprecated Use {@link #getMncStr} instead.
+     * @deprecated Use {@link #getMncString} instead.
      */
     @Deprecated
     public int getMnc() {
@@ -163,16 +163,23 @@
     }
 
     /**
+     * @return Cell bandwidth in kHz, Integer.MAX_VALUE if unknown
+     */
+    public int getBandwidth() {
+        return mBandwidth;
+    }
+
+    /**
      * @return Mobile Country Code in string format, null if unknown
      */
-    public String getMccStr() {
+    public String getMccString() {
         return mMccStr;
     }
 
     /**
      * @return Mobile Network Code in string format, null if unknown
      */
-    public String getMncStr() {
+    public String getMncString() {
         return mMncStr;
     }
 
@@ -183,25 +190,15 @@
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
 
-    /**
-     * @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.
-     */
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @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.
-     */
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mEarfcn;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mCi, mPci, mTac, mAlphaLong, mAlphaShort);
+        return Objects.hash(mCi, mPci, mTac, super.hashCode());
     }
 
     @Override
@@ -219,10 +216,10 @@
                 && mPci == o.mPci
                 && mTac == o.mTac
                 && mEarfcn == o.mEarfcn
+                && mBandwidth == o.mBandwidth
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -232,6 +229,7 @@
         .append(" mPci=").append(mPci)
         .append(" mTac=").append(mTac)
         .append(" mEarfcn=").append(mEarfcn)
+        .append(" mBandwidth=").append(mBandwidth)
         .append(" mMcc=").append(mMccStr)
         .append(" mMnc=").append(mMncStr)
         .append(" mAlphaLong=").append(mAlphaLong)
@@ -248,8 +246,7 @@
         dest.writeInt(mPci);
         dest.writeInt(mTac);
         dest.writeInt(mEarfcn);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
+        dest.writeInt(mBandwidth);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -259,8 +256,7 @@
         mPci = in.readInt();
         mTac = in.readInt();
         mEarfcn = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
+        mBandwidth = in.readInt();
 
         if (DBG) log(toString());
     }
diff --git a/android/telephony/CellIdentityTdscdma.java b/android/telephony/CellIdentityTdscdma.java
index 001d19f..18ab6d4 100644
--- a/android/telephony/CellIdentityTdscdma.java
+++ b/android/telephony/CellIdentityTdscdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -39,7 +40,7 @@
      * @hide
      */
     public CellIdentityTdscdma() {
-        super(TAG, TYPE_TDSCDMA, null, null);
+        super(TAG, TYPE_TDSCDMA, null, null, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mCpid = Integer.MAX_VALUE;
@@ -55,7 +56,7 @@
      * @hide
      */
     public CellIdentityTdscdma(int mcc, int mnc, int lac, int cid, int cpid) {
-        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid);
+        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid, null, null);
     }
 
     /**
@@ -65,17 +66,38 @@
      * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
      * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
      *
+     * FIXME: This is a temporary constructor to facilitate migration.
      * @hide
      */
     public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid) {
-        super(TAG, TYPE_TDSCDMA, mcc, mnc);
+        super(TAG, TYPE_TDSCDMA, mcc, mnc, null, null);
+        mLac = lac;
+        mCid = cid;
+        mCpid = cpid;
+    }
+
+    /**
+     * @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, INT_MAX if unknown
+     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     * @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 CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid,
+            String alphal, String alphas) {
+        super(TAG, TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
         mLac = lac;
         mCid = cid;
         mCpid = cpid;
     }
 
     private CellIdentityTdscdma(CellIdentityTdscdma cid) {
-        this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid, cid.mCpid);
+        this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid,
+                cid.mCpid, cid.mAlphaLong, cid.mAlphaShort);
     }
 
     CellIdentityTdscdma copy() {
@@ -86,7 +108,7 @@
      * Get Mobile Country Code in string format
      * @return Mobile Country Code in string format, null if unknown
      */
-    public String getMccStr() {
+    public String getMccString() {
         return mMccStr;
     }
 
@@ -94,7 +116,7 @@
      * Get Mobile Network Code in string format
      * @return Mobile Network Code in string format, null if unknown
      */
-    public String getMncStr() {
+    public String getMncString() {
         return mMncStr;
     }
 
@@ -121,7 +143,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid);
+        return Objects.hash(mLac, mCid, mCpid, super.hashCode());
     }
 
     @Override
@@ -139,7 +161,8 @@
                 && TextUtils.equals(mMncStr, o.mMncStr)
                 && mLac == o.mLac
                 && mCid == o.mCid
-                && mCpid == o.mCpid;
+                && mCpid == o.mCpid
+                && super.equals(other);
     }
 
     @Override
@@ -150,6 +173,8 @@
         .append(" mLac=").append(mLac)
         .append(" mCid=").append(mCid)
         .append(" mCpid=").append(mCpid)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
         .append("}").toString();
     }
 
diff --git a/android/telephony/CellIdentityWcdma.java b/android/telephony/CellIdentityWcdma.java
index 1aa1715..984483e 100644
--- a/android/telephony/CellIdentityWcdma.java
+++ b/android/telephony/CellIdentityWcdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -36,22 +37,16 @@
     private final int mPsc;
     // 16-bit UMTS Absolute RF Channel Number
     private final int mUarfcn;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
 
     /**
      * @hide
      */
     public CellIdentityWcdma() {
-        super(TAG, TYPE_TDSCDMA, null, null);
+        super(TAG, TYPE_TDSCDMA, null, null, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mPsc = Integer.MAX_VALUE;
         mUarfcn = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
     /**
      * public constructor
@@ -98,13 +93,11 @@
      */
     public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
                               String mccStr, String mncStr, String alphal, String alphas) {
-        super(TAG, TYPE_WCDMA, mccStr, mncStr);
+        super(TAG, TYPE_WCDMA, mccStr, mncStr, alphal, alphas);
         mLac = lac;
         mCid = cid;
         mPsc = psc;
         mUarfcn = uarfcn;
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
     }
 
     private CellIdentityWcdma(CellIdentityWcdma cid) {
@@ -118,7 +111,7 @@
 
     /**
      * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
-     * @deprecated Use {@link #getMccStr} instead.
+     * @deprecated Use {@link #getMccString} instead.
      */
     @Deprecated
     public int getMcc() {
@@ -127,7 +120,7 @@
 
     /**
      * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
-     * @deprecated Use {@link #getMncStr} instead.
+     * @deprecated Use {@link #getMncString} instead.
      */
     @Deprecated
     public int getMnc() {
@@ -160,14 +153,14 @@
     /**
      * @return Mobile Country Code in string version, null if unknown
      */
-    public String getMccStr() {
+    public String getMccString() {
         return mMccStr;
     }
 
     /**
      * @return Mobile Network Code in string version, null if unknown
      */
-    public String getMncStr() {
+    public String getMncString() {
         return mMncStr;
     }
 
@@ -178,25 +171,9 @@
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
 
-    /**
-     * @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.
-     */
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @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.
-     */
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mPsc, mAlphaLong, mAlphaShort);
+        return Objects.hash(mLac, mCid, mPsc, super.hashCode());
     }
 
     /**
@@ -206,6 +183,12 @@
         return mUarfcn;
     }
 
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mUarfcn;
+    }
+
     @Override
     public boolean equals(Object other) {
         if (this == other) {
@@ -223,8 +206,7 @@
                 && mUarfcn == o.mUarfcn
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -250,8 +232,6 @@
         dest.writeInt(mCid);
         dest.writeInt(mPsc);
         dest.writeInt(mUarfcn);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -261,8 +241,6 @@
         mCid = in.readInt();
         mPsc = in.readInt();
         mUarfcn = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
         if (DBG) log(toString());
     }
 
diff --git a/android/telephony/CellInfo.java b/android/telephony/CellInfo.java
index b5e4eef..9232ed7 100644
--- a/android/telephony/CellInfo.java
+++ b/android/telephony/CellInfo.java
@@ -16,8 +16,11 @@
 
 package android.telephony;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Immutable cell information from a point in time.
@@ -47,6 +50,34 @@
     /** @hide */
     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;
+
+    private int mCellConnectionStatus = CONNECTION_NONE;
+
     // True if device is mRegistered to the mobile network
     private boolean mRegistered;
 
@@ -69,6 +100,7 @@
         this.mRegistered = ci.mRegistered;
         this.mTimeStampType = ci.mTimeStampType;
         this.mTimeStamp = ci.mTimeStamp;
+        this.mCellConnectionStatus = ci.mCellConnectionStatus;
     }
 
     /** True if this cell is registered to the mobile network */
@@ -90,6 +122,25 @@
     }
 
     /**
+     * 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;
+    }
+
+    /**
      * Where time stamp gets recorded.
      * @return one of TIMESTAMP_TYPE_XXXX
      *
@@ -111,7 +162,7 @@
     public int hashCode() {
         int primeNum = 31;
         return ((mRegistered ? 0 : 1) * primeNum) + ((int)(mTimeStamp / 1000) * primeNum)
-                + (mTimeStampType * primeNum);
+                + (mTimeStampType * primeNum) + (mCellConnectionStatus * primeNum);
     }
 
     @Override
@@ -125,7 +176,9 @@
         try {
             CellInfo o = (CellInfo) other;
             return mRegistered == o.mRegistered
-                    && mTimeStamp == o.mTimeStamp && mTimeStampType == o.mTimeStampType;
+                    && mTimeStamp == o.mTimeStamp
+                    && mTimeStampType == o.mTimeStampType
+                    && mCellConnectionStatus == o.mCellConnectionStatus;
         } catch (ClassCastException e) {
             return false;
         }
@@ -155,6 +208,7 @@
         timeStampType = timeStampTypeToString(mTimeStampType);
         sb.append(" mTimeStampType=").append(timeStampType);
         sb.append(" mTimeStamp=").append(mTimeStamp).append("ns");
+        sb.append(" mCellConnectionStatus=").append(mCellConnectionStatus);
 
         return sb.toString();
     }
@@ -181,6 +235,7 @@
         dest.writeInt(mRegistered ? 1 : 0);
         dest.writeInt(mTimeStampType);
         dest.writeLong(mTimeStamp);
+        dest.writeInt(mCellConnectionStatus);
     }
 
     /**
@@ -192,6 +247,7 @@
         mRegistered = (in.readInt() == 1) ? true : false;
         mTimeStampType = in.readInt();
         mTimeStamp = in.readLong();
+        mCellConnectionStatus = in.readInt();
     }
 
     /** Implement the Parcelable interface */
diff --git a/android/telephony/CellSignalStrengthCdma.java b/android/telephony/CellSignalStrengthCdma.java
index 0474362..183f96d 100644
--- a/android/telephony/CellSignalStrengthCdma.java
+++ b/android/telephony/CellSignalStrengthCdma.java
@@ -20,6 +20,8 @@
 import android.os.Parcelable;
 import android.telephony.Rlog;
 
+import java.util.Objects;
+
 /**
  * Signal strength related information.
  */
@@ -34,58 +36,49 @@
     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
 
-    /**
-     * Empty constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthCdma() {
         setDefaultValues();
     }
 
     /**
-     * Constructor
+     * 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 Integer.MAX_VALUE rather than left as -1, which is
+     * a departure from SignalStrength, which is stuck with the values it currently reports.
+     *
+     * @param cdmaDbm negative of the CDMA signal strength value or -1 if invalid.
+     * @param cdmaEcio negative of the CDMA pilot/noise ratio or -1 if invalid.
+     * @param evdoDbm negative of the EvDO signal strength value or -1 if invalid.
+     * @param evdoEcio negative of the EvDO pilot/noise ratio or -1 if invalid.
+     * @param evdoSnr an SNR value 0..8 or -1 if invalid.
      * @hide
      */
     public CellSignalStrengthCdma(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio,
             int evdoSnr) {
-        initialize(cdmaDbm, cdmaEcio, evdoDbm, evdoEcio, evdoSnr);
+        // The values here were lifted from SignalStrength.validateInput()
+        // FIXME: Combine all checking and setting logic between this and SignalStrength.
+        mCdmaDbm = ((cdmaDbm > 0) && (cdmaDbm < 120))  ? -cdmaDbm : Integer.MAX_VALUE;
+        mCdmaEcio = ((cdmaEcio > 0) && (cdmaEcio < 160)) ? -cdmaEcio : Integer.MAX_VALUE;
+
+        mEvdoDbm = ((evdoDbm > 0) && (evdoDbm < 120)) ? -evdoDbm : Integer.MAX_VALUE;
+        mEvdoEcio = ((evdoEcio > 0) && (evdoEcio < 160)) ? -evdoEcio : Integer.MAX_VALUE;
+        mEvdoSnr = ((evdoSnr > 0) && (evdoSnr <= 8)) ? evdoSnr : Integer.MAX_VALUE;
     }
 
-    /**
-     * Copy constructors
-     *
-     * @param s Source SignalStrength
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthCdma(CellSignalStrengthCdma s) {
         copyFrom(s);
     }
 
-    /**
-     * Initialize all the values
-     *
-     * @param cdmaDbm
-     * @param cdmaEcio
-     * @param evdoDbm
-     * @param evdoEcio
-     * @param evdoSnr
-     *
-     * @hide
-     */
-    public void initialize(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio, int evdoSnr) {
-        mCdmaDbm = cdmaDbm;
-        mCdmaEcio = cdmaEcio;
-        mEvdoDbm = evdoDbm;
-        mEvdoEcio = evdoEcio;
-        mEvdoSnr = evdoSnr;
-    }
-
-    /**
-     * @hide
-     */
+    /** @hide */
     protected void copyFrom(CellSignalStrengthCdma s) {
         mCdmaDbm = s.mCdmaDbm;
         mCdmaEcio = s.mCdmaEcio;
@@ -94,9 +87,7 @@
         mEvdoSnr = s.mEvdoSnr;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @Override
     public CellSignalStrengthCdma copy() {
         return new CellSignalStrengthCdma(this);
@@ -293,9 +284,7 @@
 
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return ((mCdmaDbm * primeNum) + (mCdmaEcio * primeNum)
-                + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum));
+        return Objects.hash(mCdmaDbm, mCdmaEcio, mEvdoDbm, mEvdoEcio, mEvdoSnr);
     }
 
     @Override
@@ -336,13 +325,10 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         if (DBG) log("writeToParcel(Parcel, int): " + toString());
-        // Need to multiply CdmaDbm, CdmaEcio, EvdoDbm and EvdoEcio by -1
-        // to ensure consistency when reading values written here
-        // unless the value is invalid
-        dest.writeInt(mCdmaDbm * (mCdmaDbm != Integer.MAX_VALUE ? -1 : 1));
-        dest.writeInt(mCdmaEcio * (mCdmaEcio != Integer.MAX_VALUE ? -1 : 1));
-        dest.writeInt(mEvdoDbm * (mEvdoDbm != Integer.MAX_VALUE ? -1 : 1));
-        dest.writeInt(mEvdoEcio * (mEvdoEcio != Integer.MAX_VALUE ? -1 : 1));
+        dest.writeInt(mCdmaDbm);
+        dest.writeInt(mCdmaEcio);
+        dest.writeInt(mEvdoDbm);
+        dest.writeInt(mEvdoEcio);
         dest.writeInt(mEvdoSnr);
     }
 
@@ -355,13 +341,9 @@
         // the parcel as positive values.
         // Need to convert into negative values unless the value is invalid
         mCdmaDbm = in.readInt();
-        if (mCdmaDbm != Integer.MAX_VALUE) mCdmaDbm *= -1;
         mCdmaEcio = in.readInt();
-        if (mCdmaEcio != Integer.MAX_VALUE) mCdmaEcio *= -1;
         mEvdoDbm = in.readInt();
-        if (mEvdoDbm != Integer.MAX_VALUE) mEvdoDbm *= -1;
         mEvdoEcio = in.readInt();
-        if (mEvdoEcio != Integer.MAX_VALUE) mEvdoEcio *= -1;
         mEvdoSnr = in.readInt();
         if (DBG) log("CellSignalStrengthCdma(Parcel): " + toString());
     }
diff --git a/android/telephony/CellSignalStrengthGsm.java b/android/telephony/CellSignalStrengthGsm.java
index 4137853..8687cd1 100644
--- a/android/telephony/CellSignalStrengthGsm.java
+++ b/android/telephony/CellSignalStrengthGsm.java
@@ -20,6 +20,8 @@
 import android.os.Parcelable;
 import android.telephony.Rlog;
 
+import java.util.Objects;
+
 /**
  * GSM signal strength related information.
  */
@@ -32,80 +34,40 @@
     private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
     private static final int GSM_SIGNAL_STRENGTH_MODERATE = 5;
 
-    private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
+    private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
     private int mBitErrorRate;   // bit error rate (0-7, 99) as defined in TS 27.007 8.5
-    private int mTimingAdvance;
+    private int mTimingAdvance; // range from 0-219 or Integer.MAX_VALUE if unknown
 
-    /**
-     * Empty constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthGsm() {
         setDefaultValues();
     }
 
-    /**
-     * Constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthGsm(int ss, int ber) {
-        initialize(ss, ber);
+        this(ss, ber, Integer.MAX_VALUE);
     }
 
-    /**
-     * Copy constructors
-     *
-     * @param s Source SignalStrength
-     *
-     * @hide
-     */
-    public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
-        copyFrom(s);
-    }
-
-    /**
-     * Initialize all the values
-     *
-     * @param ss SignalStrength as ASU value
-     * @param ber is Bit Error Rate
-     *
-     * @hide
-     */
-    public void initialize(int ss, int ber) {
-        mSignalStrength = ss;
-        mBitErrorRate = ber;
-        mTimingAdvance = Integer.MAX_VALUE;
-    }
-
-    /**
-     * Initialize all the values
-     *
-     * @param ss SignalStrength as ASU value
-     * @param ber is Bit Error Rate
-     * @param ta timing advance
-     *
-     * @hide
-     */
-    public void initialize(int ss, int ber, int ta) {
+    /** @hide */
+    public CellSignalStrengthGsm(int ss, int ber, int ta) {
         mSignalStrength = ss;
         mBitErrorRate = ber;
         mTimingAdvance = ta;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
+    public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
+        copyFrom(s);
+    }
+
+    /** @hide */
     protected void copyFrom(CellSignalStrengthGsm s) {
         mSignalStrength = s.mSignalStrength;
         mBitErrorRate = s.mBitErrorRate;
         mTimingAdvance = s.mTimingAdvance;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @Override
     public CellSignalStrengthGsm copy() {
         return new CellSignalStrengthGsm(this);
@@ -185,8 +147,7 @@
 
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return (mSignalStrength * primeNum) + (mBitErrorRate * primeNum);
+        return Objects.hash(mSignalStrength, mBitErrorRate, mTimingAdvance);
     }
 
     @Override
diff --git a/android/telephony/CellSignalStrengthLte.java b/android/telephony/CellSignalStrengthLte.java
index 0d07a40..7e86966 100644
--- a/android/telephony/CellSignalStrengthLte.java
+++ b/android/telephony/CellSignalStrengthLte.java
@@ -20,6 +20,8 @@
 import android.os.Parcelable;
 import android.telephony.Rlog;
 
+import java.util.Objects;
+
 /**
  * LTE signal strength related information.
  */
@@ -35,50 +37,15 @@
     private int mCqi;
     private int mTimingAdvance;
 
-    /**
-     * Empty constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthLte() {
         setDefaultValues();
     }
 
-    /**
-     * Constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthLte(int signalStrength, int rsrp, int rsrq, int rssnr, int cqi,
             int timingAdvance) {
-        initialize(signalStrength, rsrp, rsrq, rssnr, cqi, timingAdvance);
-    }
-
-    /**
-     * Copy constructors
-     *
-     * @param s Source SignalStrength
-     *
-     * @hide
-     */
-    public CellSignalStrengthLte(CellSignalStrengthLte s) {
-        copyFrom(s);
-    }
-
-    /**
-     * Initialize all the values
-     *
-     * @param lteSignalStrength
-     * @param rsrp
-     * @param rsrq
-     * @param rssnr
-     * @param cqi
-     *
-     * @hide
-     */
-    public void initialize(int lteSignalStrength, int rsrp, int rsrq, int rssnr, int cqi,
-            int timingAdvance) {
-        mSignalStrength = lteSignalStrength;
+        mSignalStrength = signalStrength;
         mRsrp = rsrp;
         mRsrq = rsrq;
         mRssnr = rssnr;
@@ -86,25 +53,12 @@
         mTimingAdvance = timingAdvance;
     }
 
-    /**
-     * Initialize from the SignalStrength structure.
-     *
-     * @param ss
-     *
-     * @hide
-     */
-    public void initialize(SignalStrength ss, int timingAdvance) {
-        mSignalStrength = ss.getLteSignalStrength();
-        mRsrp = ss.getLteRsrp();
-        mRsrq = ss.getLteRsrq();
-        mRssnr = ss.getLteRssnr();
-        mCqi = ss.getLteCqi();
-        mTimingAdvance = timingAdvance;
+    /** @hide */
+    public CellSignalStrengthLte(CellSignalStrengthLte s) {
+        copyFrom(s);
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     protected void copyFrom(CellSignalStrengthLte s) {
         mSignalStrength = s.mSignalStrength;
         mRsrp = s.mRsrp;
@@ -114,9 +68,7 @@
         mTimingAdvance = s.mTimingAdvance;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @Override
     public CellSignalStrengthLte copy() {
         return new CellSignalStrengthLte(this);
@@ -220,7 +172,7 @@
     }
 
     /**
-     * Get the timing advance value for LTE, as a value between 0..63.
+     * Get the timing advance value for LTE, as a value in range of 0..1282.
      * Integer.MAX_VALUE 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.
@@ -231,10 +183,7 @@
 
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return (mSignalStrength * primeNum) + (mRsrp * primeNum)
-                + (mRsrq * primeNum) + (mRssnr * primeNum) + (mCqi * primeNum)
-                + (mTimingAdvance * primeNum);
+        return Objects.hash(mSignalStrength, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance);
     }
 
     @Override
diff --git a/android/telephony/CellSignalStrengthWcdma.java b/android/telephony/CellSignalStrengthWcdma.java
index b94b01d..dd32a96 100644
--- a/android/telephony/CellSignalStrengthWcdma.java
+++ b/android/telephony/CellSignalStrengthWcdma.java
@@ -20,6 +20,8 @@
 import android.os.Parcelable;
 import android.telephony.Rlog;
 
+import java.util.Objects;
+
 /**
  * Wcdma signal strength related information.
  */
@@ -32,62 +34,32 @@
     private static final int WCDMA_SIGNAL_STRENGTH_GOOD = 8;
     private static final int WCDMA_SIGNAL_STRENGTH_MODERATE = 5;
 
-    private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
-    private int mBitErrorRate;   // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+    private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
+    private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
 
-    /**
-     * Empty constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthWcdma() {
         setDefaultValues();
     }
 
-    /**
-     * Constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthWcdma(int ss, int ber) {
-        initialize(ss, ber);
-    }
-
-    /**
-     * Copy constructors
-     *
-     * @param s Source SignalStrength
-     *
-     * @hide
-     */
-    public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
-        copyFrom(s);
-    }
-
-    /**
-     * Initialize all the values
-     *
-     * @param ss SignalStrength as ASU value
-     * @param ber is Bit Error Rate
-     *
-     * @hide
-     */
-    public void initialize(int ss, int ber) {
         mSignalStrength = ss;
         mBitErrorRate = ber;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
+    public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
+        copyFrom(s);
+    }
+
+    /** @hide */
     protected void copyFrom(CellSignalStrengthWcdma s) {
         mSignalStrength = s.mSignalStrength;
         mBitErrorRate = s.mBitErrorRate;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @Override
     public CellSignalStrengthWcdma copy() {
         return new CellSignalStrengthWcdma(this);
@@ -156,8 +128,7 @@
 
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return (mSignalStrength * primeNum) + (mBitErrorRate * primeNum);
+        return Objects.hash(mSignalStrength, mBitErrorRate);
     }
 
     @Override
diff --git a/android/telephony/DataConnectionRealTimeInfo.java b/android/telephony/DataConnectionRealTimeInfo.java
index f71f58d..fc4e17a 100644
--- a/android/telephony/DataConnectionRealTimeInfo.java
+++ b/android/telephony/DataConnectionRealTimeInfo.java
@@ -28,10 +28,14 @@
 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       = 1;
-    public static final int DC_POWER_STATE_MEDIUM    = 2;
-    public static final int DC_POWER_STATE_HIGH      = 3;
-    public static final int DC_POWER_STATE_UNKNOWN   = Integer.MAX_VALUE;
+    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]
 
diff --git a/android/telephony/DataSpecificRegistrationStates.java b/android/telephony/DataSpecificRegistrationStates.java
new file mode 100644
index 0000000..97e3037
--- /dev/null
+++ b/android/telephony/DataSpecificRegistrationStates.java
@@ -0,0 +1,72 @@
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to data network registration.
+ * @hide
+ */
+public class DataSpecificRegistrationStates implements Parcelable{
+    /**
+     * The maximum number of simultaneous Data Calls that
+     * must be established using setupDataCall().
+     */
+    public final int maxDataCalls;
+
+    DataSpecificRegistrationStates(int maxDataCalls) {
+        this.maxDataCalls = maxDataCalls;
+    }
+
+    private DataSpecificRegistrationStates(Parcel source) {
+        maxDataCalls = source.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(maxDataCalls);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "DataSpecificRegistrationStates {" + " mMaxDataCalls=" + maxDataCalls + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(maxDataCalls);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof DataSpecificRegistrationStates)) {
+            return false;
+        }
+
+        DataSpecificRegistrationStates other = (DataSpecificRegistrationStates) o;
+        return this.maxDataCalls == other.maxDataCalls;
+    }
+
+    public static final Parcelable.Creator<DataSpecificRegistrationStates> CREATOR =
+            new Parcelable.Creator<DataSpecificRegistrationStates>() {
+                @Override
+                public DataSpecificRegistrationStates createFromParcel(Parcel source) {
+                    return new DataSpecificRegistrationStates(source);
+                }
+
+                @Override
+                public DataSpecificRegistrationStates[] newArray(int size) {
+                    return new DataSpecificRegistrationStates[size];
+                }
+            };
+}
\ No newline at end of file
diff --git a/android/telephony/LocationAccessPolicy.java b/android/telephony/LocationAccessPolicy.java
new file mode 100644
index 0000000..6db8e82
--- /dev/null
+++ b/android/telephony/LocationAccessPolicy.java
@@ -0,0 +1,130 @@
+/*
+ * 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.Manifest;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.location.LocationManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Helper for performing location access checks.
+ * @hide
+ */
+public final class LocationAccessPolicy {
+    private static final String LOG_TAG = LocationAccessPolicy.class.getSimpleName();
+
+    /**
+     * API to determine if the caller has permissions to get cell location.
+     *
+     * @param pkgName Package name of the application requesting access
+     * @param uid The uid of the package
+     * @param pid The pid of the package
+     * @param throwOnDeniedPermission Whether to throw if the location permission is denied.
+     * @return boolean true or false if permissions is granted
+     */
+    public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
+            int uid, int pid, boolean throwOnDeniedPermission) throws SecurityException {
+        Trace.beginSection("TelephonyLohcationCheck");
+        try {
+            // Always allow the phone process to access location. This avoid breaking legacy code
+            // that rely on public-facing APIs to access cell location, and it doesn't create a
+            // info leak risk because the cell location is stored in the phone process anyway.
+            if (uid == Process.PHONE_UID) {
+                return true;
+            }
+
+            // We always require the location permission and also require the
+            // location mode to be on for non-legacy apps. Legacy apps are
+            // required to be in the foreground to at least mitigate the case
+            // where a legacy app the user is not using tracks their location.
+            // Granting ACCESS_FINE_LOCATION to an app automatically grants it
+            // ACCESS_COARSE_LOCATION.
+            if (throwOnDeniedPermission) {
+                context.enforcePermission(Manifest.permission.ACCESS_COARSE_LOCATION,
+                        pid, uid, "canAccessCellLocation");
+            } else if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION,
+                    pid, uid) == PackageManager.PERMISSION_DENIED) {
+                return false;
+            }
+            final int opCode = AppOpsManager.permissionToOpCode(
+                    Manifest.permission.ACCESS_COARSE_LOCATION);
+            if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
+                    .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
+                return false;
+            }
+            if (!isLocationModeEnabled(context, UserHandle.getUserId(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);
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
+        LocationManager locationManager = context.getSystemService(LocationManager.class);
+        if (locationManager == null) {
+            Log.w(LOG_TAG, "Couldn't get location manager, denying location access");
+            return false;
+        }
+        return locationManager.isLocationEnabledForUser(UserHandle.of(userId));
+    }
+
+    private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
+        return context.checkCallingOrSelfPermission(
+                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private static boolean isCurrentProfile(@NonNull Context context, int uid) {
+        long token = Binder.clearCallingIdentity();
+        try {
+            final int currentUser = ActivityManager.getCurrentUser();
+            final int callingUserId = UserHandle.getUserId(uid);
+            if (callingUserId == currentUser) {
+                return true;
+            } else {
+                List<UserInfo> userProfiles = context.getSystemService(
+                        UserManager.class).getProfiles(currentUser);
+                for (UserInfo user : userProfiles) {
+                    if (user.id == callingUserId) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+}
diff --git a/android/telephony/MbmsDownloadSession.java b/android/telephony/MbmsDownloadSession.java
index 059a2d0..38a6593 100644
--- a/android/telephony/MbmsDownloadSession.java
+++ b/android/telephony/MbmsDownloadSession.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -30,15 +32,16 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
-import android.telephony.mbms.DownloadStateCallback;
-import android.telephony.mbms.FileInfo;
+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.InternalDownloadStateCallback;
-import android.telephony.mbms.MbmsDownloadSessionCallback;
+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;
@@ -53,11 +56,10 @@
 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;
 
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
 /**
  * This class provides functionality for file download over MBMS.
  */
@@ -107,11 +109,8 @@
     /**
      * {@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 temp file root set
-     * via {@link #setTempFileRootDirectory(File)}.
-     * While you may use this file in-place, it is highly encouraged that you move
-     * this file to a different location after receiving the download completion intent, as this
-     * file resides within the temp file directory.
+     * 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}.
@@ -134,6 +133,14 @@
      */
     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.
      */
@@ -220,6 +227,8 @@
      */
     public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
 
+    private static final String DESTINATION_SANITY_CHECK_FILE_NAME = "destinationSanityCheckFile";
+
     private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
 
     private final Context mContext;
@@ -233,26 +242,25 @@
 
     private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
     private final InternalDownloadSessionCallback mInternalCallback;
-    private final Map<DownloadStateCallback, InternalDownloadStateCallback>
-            mInternalDownloadCallbacks = new HashMap<>();
+    private final Map<DownloadStatusListener, InternalDownloadStatusListener>
+            mInternalDownloadStatusListeners = new HashMap<>();
+    private final Map<DownloadProgressListener, InternalDownloadProgressListener>
+            mInternalDownloadProgressListeners = new HashMap<>();
 
-    private MbmsDownloadSession(Context context, MbmsDownloadSessionCallback callback,
-            int subscriptionId, Handler handler) {
+    private MbmsDownloadSession(Context context, Executor executor, int subscriptionId,
+            MbmsDownloadSessionCallback callback) {
         mContext = context;
         mSubscriptionId = subscriptionId;
-        if (handler == null) {
-            handler = new Handler(Looper.getMainLooper());
-        }
-        mInternalCallback = new InternalDownloadSessionCallback(callback, handler);
+        mInternalCallback = new InternalDownloadSessionCallback(callback, executor);
     }
 
     /**
      * Create a new {@link MbmsDownloadSession} using the system default data subscription ID.
-     * See {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)}
+     * See {@link #create(Context, Executor, int, MbmsDownloadSessionCallback)}
      */
     public static MbmsDownloadSession create(@NonNull Context context,
-            @NonNull MbmsDownloadSessionCallback callback, @NonNull Handler handler) {
-        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+            @NonNull Executor executor, @NonNull MbmsDownloadSessionCallback callback) {
+        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
     }
 
     /**
@@ -279,24 +287,24 @@
      * {@link MbmsDownloadSession} that you received before calling this method again.
      *
      * @param context The instance of {@link Context} to use
-     * @param callback A callback to get asynchronous error messages and file service updates.
+     * @param executor The executor on which you wish to execute callbacks.
      * @param subscriptionId The data subscription ID to use
-     * @param handler The {@link Handler} on which callbacks should be enqueued.
+     * @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,
-            final @NonNull MbmsDownloadSessionCallback callback,
-            int subscriptionId, @NonNull Handler handler) {
+            @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, callback, subscriptionId, handler);
+                new MbmsDownloadSession(context, executor, subscriptionId, callback);
         final int result = session.bindAndInitialize();
         if (result != MbmsErrors.SUCCESS) {
             sIsInitialized.set(false);
-            handler.post(new Runnable() {
+            executor.execute(new Runnable() {
                 @Override
                 public void run() {
                     callback.onError(result, null);
@@ -329,6 +337,12 @@
                             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);
@@ -380,6 +394,11 @@
         }
         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);
             }
@@ -435,8 +454,14 @@
 
         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);
@@ -499,13 +524,19 @@
      * {@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.
-     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
-     * and some other error code otherwise.
      */
-    public int download(@NonNull DownloadRequest request) {
+    public void download(@NonNull DownloadRequest request) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
             throw new IllegalStateException("Middleware not yet bound");
@@ -521,16 +552,25 @@
             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);
             }
-            return result;
         } catch (RemoteException e) {
             mService.set(null);
             sIsInitialized.set(false);
-            return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
         }
     }
 
@@ -558,111 +598,232 @@
     }
 
     /**
-     * Registers a callback for a {@link DownloadRequest} previously requested via
+     * 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 DownloadStateCallback} will be enqueued.
+     * 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}.
+     *
      * @param request The {@link DownloadRequest} that you want updates on.
-     * @param callback The callback that should be called when the middleware has information to
-     *                 share on the download.
-     * @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on.
-     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
-     * and some other error code otherwise.
+     * @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 int registerStateCallback(@NonNull DownloadRequest request,
-            @NonNull DownloadStateCallback callback, @NonNull Handler handler) {
+    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");
         }
 
-        InternalDownloadStateCallback internalCallback =
-                new InternalDownloadStateCallback(callback, handler);
+        InternalDownloadStatusListener internalListener =
+                new InternalDownloadStatusListener(listener, executor);
 
         try {
-            int result = downloadService.registerStateCallback(request, internalCallback,
-                    callback.getCallbackFilterFlags());
+            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.");
                 }
-                return result;
+                sendErrorToApp(result, null);
+                return;
             }
         } catch (RemoteException e) {
             mService.set(null);
             sIsInitialized.set(false);
-            return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return;
         }
-        mInternalDownloadCallbacks.put(callback, internalCallback);
-        return MbmsErrors.SUCCESS;
+        mInternalDownloadStatusListeners.put(listener, internalListener);
     }
 
     /**
-     * Un-register a callback previously registered via
-     * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback, Handler)}. After
-     * this method is called, no further callbacks will be enqueued on the {@link Handler}
+     * 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 callback The callback provided during registration.
-     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
-     * and some other error code otherwise.
+     * @param listener The listener provided during registration.
      */
-    public int unregisterStateCallback(@NonNull DownloadRequest request,
-            @NonNull DownloadStateCallback callback) {
+    public void removeStatusListener(@NonNull DownloadRequest request,
+            @NonNull DownloadStatusListener listener) {
         try {
             IMbmsDownloadService downloadService = mService.get();
             if (downloadService == null) {
                 throw new IllegalStateException("Middleware not yet bound");
             }
 
-            InternalDownloadStateCallback internalCallback =
-                    mInternalDownloadCallbacks.get(callback);
-            if (internalCallback == null) {
-                throw new IllegalArgumentException("Provided callback was never registered");
+            InternalDownloadStatusListener internalListener =
+                    mInternalDownloadStatusListeners.get(listener);
+            if (internalListener == null) {
+                throw new IllegalArgumentException("Provided listener was never registered");
             }
 
             try {
-                int result = downloadService.unregisterStateCallback(request, internalCallback);
+                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.");
                     }
-                    return result;
+                    sendErrorToApp(result, null);
+                    return;
                 }
             } catch (RemoteException e) {
                 mService.set(null);
                 sIsInitialized.set(false);
-                return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+                sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+                return;
             }
         } finally {
-            InternalDownloadStateCallback internalCallback =
-                    mInternalDownloadCallbacks.remove(callback);
+            InternalDownloadStatusListener internalCallback =
+                    mInternalDownloadStatusListeners.remove(listener);
             if (internalCallback != null) {
                 internalCallback.stop();
             }
         }
-        return MbmsErrors.SUCCESS;
+    }
+
+    /**
+     * 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}.
+     *
+     * @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 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 downloadRequest The download request that you wish to cancel.
-     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
-     * and some other error code otherwise.
      */
-    public int cancelDownload(@NonNull DownloadRequest downloadRequest) {
+    public void cancelDownload(@NonNull DownloadRequest downloadRequest) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
             throw new IllegalStateException("Middleware not yet bound");
@@ -670,46 +831,65 @@
 
         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) {
-                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
-                    throw new IllegalArgumentException("Unknown download request.");
-                }
+                sendErrorToApp(result, null);
             } else {
                 deleteDownloadRequestToken(downloadRequest);
             }
-            return result;
         } catch (RemoteException e) {
             mService.set(null);
             sIsInitialized.set(false);
-            return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
         }
     }
 
     /**
-     * Gets information about the status of a file pending download.
+     * Requests information about the state of a file pending download.
      *
-     * If there was a problem communicating with the middleware or if it has no records of the
+     * 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},
-     * {@link #STATUS_UNKNOWN} will be returned.
+     * 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.
-     * @return The status of the download.
      */
-    @DownloadStatus
-    public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo) {
+    public void requestDownloadState(DownloadRequest downloadRequest, FileInfo fileInfo) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
             throw new IllegalStateException("Middleware not yet bound");
         }
 
         try {
-            return downloadService.getDownloadStatus(downloadRequest, fileInfo);
+            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);
-            return STATUS_UNKNOWN;
         }
     }
 
@@ -741,6 +921,11 @@
 
         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.");
@@ -762,7 +947,7 @@
      * instance of {@link MbmsDownloadSessionCallback}, but callbacks that have already been
      * enqueued will still be delivered.
      *
-     * It is safe to call {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)} to
+     * It is safe to call {@link #create(Context, Executor, int, MbmsDownloadSessionCallback)} to
      * obtain another instance of {@link MbmsDownloadSession} immediately after this method
      * returns.
      *
@@ -799,11 +984,11 @@
         try {
             if (!token.createNewFile()) {
                 throw new RuntimeException("Failed to create download token for request "
-                        + 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);
+                    + " due to IOException " + e + ". Attempted to write to " + token.getPath());
         }
     }
 
@@ -818,6 +1003,36 @@
         }
     }
 
+    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());
@@ -827,10 +1042,6 @@
     }
 
     private void sendErrorToApp(int errorCode, String message) {
-        try {
-            mInternalCallback.onError(errorCode, message);
-        } catch (RemoteException e) {
-            // Ignore, should not happen locally.
-        }
+        mInternalCallback.onError(errorCode, message);
     }
 }
diff --git a/android/telephony/MbmsStreamingSession.java b/android/telephony/MbmsStreamingSession.java
index fb2ff7b..cd465d2 100644
--- a/android/telephony/MbmsStreamingSession.java
+++ b/android/telephony/MbmsStreamingSession.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
@@ -24,12 +26,10 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.ServiceConnection;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
-import android.telephony.mbms.InternalStreamingSessionCallback;
 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;
@@ -42,11 +42,10 @@
 
 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;
 
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
 /**
  * This class provides functionality for streaming media over MBMS.
  */
@@ -89,14 +88,11 @@
     private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
 
     /** @hide */
-    private MbmsStreamingSession(Context context, MbmsStreamingSessionCallback callback,
-                    int subscriptionId, Handler handler) {
+    private MbmsStreamingSession(Context context, Executor executor, int subscriptionId,
+            MbmsStreamingSessionCallback callback) {
         mContext = context;
         mSubscriptionId = subscriptionId;
-        if (handler == null) {
-            handler = new Handler(Looper.getMainLooper());
-        }
-        mInternalCallback = new InternalStreamingSessionCallback(callback, handler);
+        mInternalCallback = new InternalStreamingSessionCallback(callback, executor);
     }
 
     /**
@@ -117,25 +113,25 @@
      * {@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.
-     * @param subscriptionId The subscription ID to use.
-     * @param handler The handler you wish to receive callbacks on.
      * @return An instance of {@link MbmsStreamingSession}, or null if an error occurred.
      */
     public static @Nullable MbmsStreamingSession create(@NonNull Context context,
-            final @NonNull MbmsStreamingSessionCallback callback, int subscriptionId,
-            @NonNull Handler handler) {
+            @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, callback,
-                subscriptionId, handler);
+        MbmsStreamingSession session = new MbmsStreamingSession(context, executor,
+                subscriptionId, callback);
 
         final int result = session.bindAndInitialize();
         if (result != MbmsErrors.SUCCESS) {
             sIsInitialized.set(false);
-            handler.post(new Runnable() {
+            executor.execute(new Runnable() {
                 @Override
                 public void run() {
                     callback.onError(result, null);
@@ -148,22 +144,22 @@
 
     /**
      * Create a new {@link MbmsStreamingSession} using the system default data subscription ID.
-     * See {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)}.
+     * See {@link #create(Context, Executor, int, MbmsStreamingSessionCallback)}.
      */
     public static MbmsStreamingSession create(@NonNull Context context,
-            @NonNull MbmsStreamingSessionCallback callback, @NonNull Handler handler) {
-        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+            @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#stopStreaming()} had been called on them. After this method returns,
+     * {@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, MbmsStreamingSessionCallback, int, Handler)} to
+     * It is safe to call {@link #create(Context, Executor, int, MbmsStreamingSessionCallback)} to
      * obtain another instance of {@link MbmsStreamingSession} immediately after this method
      * returns.
      *
@@ -212,6 +208,11 @@
         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);
             }
@@ -237,20 +238,20 @@
      * {@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.
-     * @param handler A handler that calls to {@code callback} should be called on.
      * @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,
-            StreamingServiceCallback callback, @NonNull Handler handler) {
+            @NonNull Executor executor, StreamingServiceCallback callback) {
         IMbmsStreamingService streamingService = mService.get();
         if (streamingService == null) {
             throw new IllegalStateException("Middleware not yet bound");
         }
 
         InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
-                callback, handler);
+                callback, executor);
 
         StreamingService serviceForApp = new StreamingService(
                 mSubscriptionId, streamingService, this, serviceInfo, serviceCallback);
@@ -259,6 +260,11 @@
         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;
@@ -305,6 +311,12 @@
                             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);
diff --git a/android/telephony/ModemActivityInfo.java b/android/telephony/ModemActivityInfo.java
index 03ce2d8..521adef 100644
--- a/android/telephony/ModemActivityInfo.java
+++ b/android/telephony/ModemActivityInfo.java
@@ -36,12 +36,12 @@
      */
     public static final int TX_POWER_LEVELS = 5;
 
-    private final long mTimestamp;
-    private final int mSleepTimeMs;
-    private final int mIdleTimeMs;
-    private final int [] mTxTimeMs = new int[TX_POWER_LEVELS];
-    private final int mRxTimeMs;
-    private final int mEnergyUsed;
+    private long mTimestamp;
+    private int mSleepTimeMs;
+    private int mIdleTimeMs;
+    private int [] mTxTimeMs = new int[TX_POWER_LEVELS];
+    private int mRxTimeMs;
+    private int mEnergyUsed;
 
     public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
                         int[] txTimeMs, int rxTimeMs, int energyUsed) {
@@ -110,6 +110,10 @@
         return mTimestamp;
     }
 
+    public void setTimestamp(long timestamp) {
+        mTimestamp = timestamp;
+    }
+
     /**
      * @return tx time in ms. It's an array of tx times
      * with each index...
@@ -118,6 +122,10 @@
         return mTxTimeMs;
     }
 
+    public void setTxTimeMillis(int[] txTimeMs) {
+        mTxTimeMs = txTimeMs;
+    }
+
     /**
      * @return sleep time in ms.
      */
@@ -125,6 +133,10 @@
         return mSleepTimeMs;
     }
 
+    public void setSleepTimeMillis(int sleepTimeMillis) {
+        mSleepTimeMs = sleepTimeMillis;
+    }
+
     /**
      * @return idle time in ms.
      */
@@ -132,6 +144,10 @@
         return mIdleTimeMs;
     }
 
+    public void setIdleTimeMillis(int idleTimeMillis) {
+        mIdleTimeMs = idleTimeMillis;
+    }
+
     /**
      * @return rx time in ms.
      */
@@ -139,6 +155,10 @@
         return mRxTimeMs;
     }
 
+    public void setRxTimeMillis(int rxTimeMillis) {
+        mRxTimeMs = rxTimeMillis;
+    }
+
     /**
      * product of current(mA), voltage(V) and time(ms)
      * @return energy used
@@ -147,6 +167,10 @@
         return mEnergyUsed;
     }
 
+    public void setEnergyUsed(int energyUsed) {
+        mEnergyUsed = energyUsed;
+    }
+
     /**
      * @return if the record is valid
      */
diff --git a/android/telephony/NetworkRegistrationState.java b/android/telephony/NetworkRegistrationState.java
index e051069..bba779d 100644
--- a/android/telephony/NetworkRegistrationState.java
+++ b/android/telephony/NetworkRegistrationState.java
@@ -105,6 +105,11 @@
     @Nullable
     private final CellIdentity mCellIdentity;
 
+    @Nullable
+    private VoiceSpecificRegistrationStates mVoiceSpecificStates;
+
+    @Nullable
+    private DataSpecificRegistrationStates mDataSpecificStates;
 
     /**
      * @param transportType Transport type. Must be {@link AccessNetworkConstants.TransportType}
@@ -128,6 +133,34 @@
         mEmergencyOnly = emergencyOnly;
     }
 
+    /**
+     * Constructor for voice network registration states.
+     * @hide
+     */
+    public NetworkRegistrationState(int transportType, int domain, int regState,
+            int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
+            int[] availableServices, @Nullable CellIdentity cellIdentity, boolean cssSupported,
+            int roamingIndicator, int systemIsInPrl, int defaultRoamingIndicator) {
+        this(transportType, domain, regState, accessNetworkTechnology,
+                reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+
+        mVoiceSpecificStates = new VoiceSpecificRegistrationStates(cssSupported, roamingIndicator,
+                systemIsInPrl, defaultRoamingIndicator);
+    }
+
+    /**
+     * Constructor for data network registration states.
+     * @hide
+     */
+    public NetworkRegistrationState(int transportType, int domain, int regState,
+            int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
+            int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls) {
+        this(transportType, domain, regState, accessNetworkTechnology,
+                reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+
+        mDataSpecificStates = new DataSpecificRegistrationStates(maxDataCalls);
+    }
+
     protected NetworkRegistrationState(Parcel source) {
         mTransportType = source.readInt();
         mDomain = source.readInt();
@@ -137,6 +170,10 @@
         mEmergencyOnly = source.readBoolean();
         mAvailableServices = source.createIntArray();
         mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader());
+        mVoiceSpecificStates = source.readParcelable(
+                VoiceSpecificRegistrationStates.class.getClassLoader());
+        mDataSpecificStates = source.readParcelable(
+                DataSpecificRegistrationStates.class.getClassLoader());
     }
 
     /**
@@ -173,6 +210,36 @@
         return mAccessNetworkTechnology;
     }
 
+    /**
+     * @return Reason for denial from network.
+     */
+    public int getReasonForDenial() {
+        return mReasonForDenial;
+    }
+
+    /**
+     * @return The cell information.
+     */
+    public CellIdentity getCellIdentity() {
+        return mCellIdentity;
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public VoiceSpecificRegistrationStates getVoiceSpecificStates() {
+        return mVoiceSpecificStates;
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public DataSpecificRegistrationStates getDataSpecificStates() {
+        return mDataSpecificStates;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -202,13 +269,16 @@
                 .append(" emergencyEnabled=").append(mEmergencyOnly)
                 .append(" supportedServices=").append(mAvailableServices)
                 .append(" cellIdentity=").append(mCellIdentity)
+                .append(" voiceSpecificStates=").append(mVoiceSpecificStates)
+                .append(" dataSpecificStates=").append(mDataSpecificStates)
                 .append("}").toString();
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mTransportType, mDomain, mRegState, mAccessNetworkTechnology,
-                mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity);
+                mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity,
+                mVoiceSpecificStates, mDataSpecificStates);
     }
 
     @Override
@@ -228,7 +298,9 @@
                 && mEmergencyOnly == other.mEmergencyOnly
                 && (mAvailableServices == other.mAvailableServices
                     || Arrays.equals(mAvailableServices, other.mAvailableServices))
-                && mCellIdentity == other.mCellIdentity;
+                && equals(mCellIdentity, other.mCellIdentity)
+                && equals(mVoiceSpecificStates, other.mVoiceSpecificStates)
+                && equals(mDataSpecificStates, other.mDataSpecificStates);
     }
 
     @Override
@@ -241,6 +313,8 @@
         dest.writeBoolean(mEmergencyOnly);
         dest.writeIntArray(mAvailableServices);
         dest.writeParcelable(mCellIdentity, 0);
+        dest.writeParcelable(mVoiceSpecificStates, 0);
+        dest.writeParcelable(mDataSpecificStates, 0);
     }
 
     public static final Parcelable.Creator<NetworkRegistrationState> CREATOR =
@@ -255,4 +329,14 @@
             return new NetworkRegistrationState[size];
         }
     };
+
+    private static boolean equals(Object o1, Object o2) {
+        if (o1 == o2) {
+            return true;
+        } else if (o1 == null) {
+            return false;
+        } else {
+            return o1.equals(o2);
+        }
+    }
 }
diff --git a/android/telephony/NetworkScan.java b/android/telephony/NetworkScan.java
index a277212..073c313 100644
--- a/android/telephony/NetworkScan.java
+++ b/android/telephony/NetworkScan.java
@@ -29,9 +29,9 @@
 
 /**
  * The caller of
- * {@link TelephonyManager#requestNetworkScan(NetworkScanRequest, NetworkScanCallback)}
+ * {@link TelephonyManager#requestNetworkScan(NetworkScanRequest, Executor, NetworkScanCallback)}
  * will receive an instance of {@link NetworkScan}, which contains a callback method
- * {@link #stop()} for stopping the in-progress scan.
+ * {@link #stopScan()} for stopping the in-progress scan.
  */
 public class NetworkScan {
 
@@ -106,16 +106,26 @@
      * 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 stop() throws RemoteException {
+    public void stopScan() {
+        ITelephony telephony = getITelephony();
+        if (telephony == null) {
+            Rlog.e(TAG, "Failed to get the ITelephony instance.");
+        }
         try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null) {
-                telephony.stopNetworkScan(mSubId, mScanId);
-            } else {
-                throw new RemoteException("Failed to get the ITelephony instance.");
-            }
+            telephony.stopNetworkScan(mSubId, mScanId);
         } catch (RemoteException ex) {
             Rlog.e(TAG, "stopNetworkScan  RemoteException", ex);
+        } catch (RuntimeException ex) {
+            Rlog.e(TAG, "stopNetworkScan  RuntimeException", ex);
+        }
+    }
+
+    /** @deprecated Use {@link #stopScan()} */
+    @Deprecated
+    public void stop() throws RemoteException {
+        try {
+            stopScan();
+        } catch (RuntimeException ex) {
             throw new RemoteException("Failed to stop the network scan with id " + mScanId);
         }
     }
diff --git a/android/telephony/NetworkService.java b/android/telephony/NetworkService.java
index 6b3584c..35682a7 100644
--- a/android/telephony/NetworkService.java
+++ b/android/telephony/NetworkService.java
@@ -28,6 +28,8 @@
 import android.os.RemoteException;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -38,7 +40,7 @@
  * follow the following format:
  * ...
  * <service android:name=".xxxNetworkService"
- *     android:permission="android.permission.BIND_NETWORK_SERVICE" >
+ *     android:permission="android.permission.BIND_TELEPHONY_NETWORK_SERVICE" >
  *     <intent-filter>
  *         <action android:name="android.telephony.NetworkService" />
  *     </intent-filter>
@@ -53,11 +55,13 @@
     public static final String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
     public static final String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
 
-    private static final int NETWORK_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE       = 1;
-    private static final int NETWORK_SERVICE_GET_REGISTRATION_STATE                    = 2;
-    private static final int NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE                 = 3;
-    private static final int NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE               = 4;
-    private static final int NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED          = 5;
+    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_STATE                          = 4;
+    private static final int NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE                       = 5;
+    private static final int NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE                     = 6;
+    private static final int NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED                = 7;
 
 
     private final HandlerThread mHandlerThread;
@@ -66,7 +70,11 @@
 
     private final SparseArray<NetworkServiceProvider> mServiceMap = new SparseArray<>();
 
-    private final SparseArray<INetworkServiceWrapper> mBinderMap = new SparseArray<>();
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public final INetworkServiceWrapper mBinder = new INetworkServiceWrapper();
 
     /**
      * The abstract class of the actual network service implementation. The network service provider
@@ -147,37 +155,50 @@
         public void handleMessage(Message message) {
             final int slotId = message.arg1;
             final INetworkServiceCallback callback = (INetworkServiceCallback) message.obj;
-            NetworkServiceProvider service;
 
-            synchronized (mServiceMap) {
-                service = mServiceMap.get(slotId);
-            }
+            NetworkServiceProvider serviceProvider = mServiceMap.get(slotId);
 
             switch (message.what) {
-                case NETWORK_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE:
-                    service = createNetworkServiceProvider(message.arg1);
-                    if (service != null) {
-                        mServiceMap.put(slotId, service);
+                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(slotId, createNetworkServiceProvider(slotId));
                     }
                     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.onDestroy();
+                        mServiceMap.remove(slotId);
+                    }
+                    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.onDestroy();
+                        }
+                    }
+                    mServiceMap.clear();
+                    break;
                 case NETWORK_SERVICE_GET_REGISTRATION_STATE:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     int domainId = message.arg2;
-                    service.getNetworkRegistrationState(domainId,
+                    serviceProvider.getNetworkRegistrationState(domainId,
                             new NetworkServiceCallback(callback));
 
                     break;
                 case NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE:
-                    if (service == null) break;
-                    service.registerForStateChanged(callback);
+                    if (serviceProvider == null) break;
+                    serviceProvider.registerForStateChanged(callback);
                     break;
                 case NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE:
-                    if (service == null) break;
-                    service.unregisterForStateChanged(callback);
+                    if (serviceProvider == null) break;
+                    serviceProvider.unregisterForStateChanged(callback);
                     break;
                 case NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED:
-                    if (service == null) break;
-                    service.notifyStateChangedToCallbacks();
+                    if (serviceProvider == null) break;
+                    serviceProvider.notifyStateChangedToCallbacks();
                     break;
                 default:
                     break;
@@ -212,47 +233,14 @@
             return null;
         }
 
-        int slotId = intent.getIntExtra(
-                NETWORK_SERVICE_EXTRA_SLOT_ID, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-
-        if (!SubscriptionManager.isValidSlotIndex(slotId)) {
-            loge("Invalid slot id " + slotId);
-            return null;
-        }
-
-        log("onBind: slot id=" + slotId);
-
-        INetworkServiceWrapper binder = mBinderMap.get(slotId);
-        if (binder == null) {
-            Message msg = mHandler.obtainMessage(
-                    NETWORK_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE);
-            msg.arg1 = slotId;
-            msg.sendToTarget();
-
-            binder = new INetworkServiceWrapper(slotId);
-            mBinderMap.put(slotId, binder);
-        }
-
-        return binder;
+        return mBinder;
     }
 
     /** @hide */
     @Override
     public boolean onUnbind(Intent intent) {
-        int slotId = intent.getIntExtra(NETWORK_SERVICE_EXTRA_SLOT_ID,
-                SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-        if (mBinderMap.get(slotId) != null) {
-            NetworkServiceProvider serviceImpl;
-            synchronized (mServiceMap) {
-                serviceImpl = mServiceMap.get(slotId);
-            }
-            // We assume only one component might bind to the service. So if onUnbind is ever
-            // called, we destroy the serviceImpl.
-            if (serviceImpl != null) {
-                serviceImpl.onDestroy();
-            }
-            mBinderMap.remove(slotId);
-        }
+        mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS, 0,
+                0, null).sendToTarget();
 
         return false;
     }
@@ -260,16 +248,6 @@
     /** @hide */
     @Override
     public void onDestroy() {
-        synchronized (mServiceMap) {
-            for (int i = 0; i < mServiceMap.size(); i++) {
-                NetworkServiceProvider serviceImpl = mServiceMap.get(i);
-                if (serviceImpl != null) {
-                    serviceImpl.onDestroy();
-                }
-            }
-            mServiceMap.clear();
-        }
-
         mHandlerThread.quit();
     }
 
@@ -279,27 +257,36 @@
      */
     private class INetworkServiceWrapper extends INetworkService.Stub {
 
-        private final int mSlotId;
-
-        INetworkServiceWrapper(int slotId) {
-            mSlotId = slotId;
+        @Override
+        public void createNetworkServiceProvider(int slotId) {
+            mHandler.obtainMessage(NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER, slotId,
+                    0, null).sendToTarget();
         }
 
         @Override
-        public void getNetworkRegistrationState(int domain, INetworkServiceCallback callback) {
-            mHandler.obtainMessage(NETWORK_SERVICE_GET_REGISTRATION_STATE, mSlotId,
+        public void removeNetworkServiceProvider(int slotId) {
+            mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER, slotId,
+                    0, null).sendToTarget();
+        }
+
+        @Override
+        public void getNetworkRegistrationState(
+                int slotId, int domain, INetworkServiceCallback callback) {
+            mHandler.obtainMessage(NETWORK_SERVICE_GET_REGISTRATION_STATE, slotId,
                     domain, callback).sendToTarget();
         }
 
         @Override
-        public void registerForNetworkRegistrationStateChanged(INetworkServiceCallback callback) {
-            mHandler.obtainMessage(NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE, mSlotId,
+        public void registerForNetworkRegistrationStateChanged(
+                int slotId, INetworkServiceCallback callback) {
+            mHandler.obtainMessage(NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE, slotId,
                     0, callback).sendToTarget();
         }
 
         @Override
-        public void unregisterForNetworkRegistrationStateChanged(INetworkServiceCallback callback) {
-            mHandler.obtainMessage(NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE, mSlotId,
+        public void unregisterForNetworkRegistrationStateChanged(
+                int slotId,INetworkServiceCallback callback) {
+            mHandler.obtainMessage(NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE, slotId,
                     0, callback).sendToTarget();
         }
     }
@@ -311,4 +298,4 @@
     private final void loge(String s) {
         Rlog.e(TAG, s);
     }
-}
\ No newline at end of file
+}
diff --git a/android/telephony/NetworkServiceCallback.java b/android/telephony/NetworkServiceCallback.java
index 92ebf36..dbad02f 100644
--- a/android/telephony/NetworkServiceCallback.java
+++ b/android/telephony/NetworkServiceCallback.java
@@ -83,6 +83,8 @@
             } catch (RemoteException e) {
                 Rlog.e(mTag, "Failed to onGetNetworkRegistrationStateComplete on the remote");
             }
+        } else {
+            Rlog.e(mTag, "Weak reference of callback is null.");
         }
     }
 }
\ No newline at end of file
diff --git a/android/telephony/PhoneNumberUtils.java b/android/telephony/PhoneNumberUtils.java
index ff67116..fadfc91 100644
--- a/android/telephony/PhoneNumberUtils.java
+++ b/android/telephony/PhoneNumberUtils.java
@@ -22,6 +22,7 @@
 import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
 import com.android.i18n.phonenumbers.ShortNumberInfo;
 
+import android.annotation.IntDef;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -42,6 +43,8 @@
 
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -49,8 +52,31 @@
 /**
  * Various utilities for dealing with phone number strings.
  */
-public class PhoneNumberUtils
-{
+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
      *
@@ -77,22 +103,6 @@
     public static final int TOA_International = 0x91;
     public static final int TOA_Unknown = 0x81;
 
-    /*
-     * 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;
-
     static final String LOG_TAG = "PhoneNumberUtils";
     private static final boolean DBG = false;
 
@@ -844,7 +854,7 @@
      *
      */
     public static String calledPartyBCDToString(
-            byte[] bytes, int offset, int length, int bcdExtType) {
+            byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
         boolean prependPlus = false;
         StringBuilder ret = new StringBuilder(1 + length * 2);
 
@@ -944,7 +954,8 @@
     }
 
     private static void internalCalledPartyBCDFragmentToString(
-            StringBuilder sb, byte [] bytes, int offset, int length, int bcdExtType) {
+            StringBuilder sb, byte [] bytes, int offset, int length,
+            @BcdExtendType int bcdExtType) {
         for (int i = offset ; i < length + offset ; i++) {
             byte b;
             char c;
@@ -999,7 +1010,7 @@
      * TOA byte. For example: SIM ADN extension fields
      */
     public static String calledPartyBCDFragmentToString(
-            byte[] bytes, int offset, int length, int bcdExtType) {
+            byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
         StringBuilder ret = new StringBuilder(length * 2);
         internalCalledPartyBCDFragmentToString(ret, bytes, offset, length, bcdExtType);
         return ret.toString();
@@ -1009,7 +1020,7 @@
      * Returns the correspond character for given {@code b} based on {@code bcdExtType}, or 0 on
      * invalid code.
      */
-    private static char bcdToChar(byte b, int bcdExtType) {
+    private static char bcdToChar(byte b, @BcdExtendType int bcdExtType) {
         if (b < 0xa) {
             return (char) ('0' + b);
         }
@@ -1027,7 +1038,7 @@
         return extended.charAt(b - 0xa);
     }
 
-    private static int charToBCD(char c, int bcdExtType) {
+    private static int charToBCD(char c, @BcdExtendType int bcdExtType) {
         if ('0' <= c && c <= '9') {
             return c - '0';
         }
@@ -1134,7 +1145,7 @@
      *
      * @return BCD byte array
      */
-    public static byte[] numberToCalledPartyBCD(String number, int bcdExtType) {
+    public static byte[] numberToCalledPartyBCD(String number, @BcdExtendType int bcdExtType) {
         return numberToCalledPartyBCDHelper(number, false, bcdExtType);
     }
 
@@ -1143,7 +1154,7 @@
      * the return array.
      */
     private static byte[] numberToCalledPartyBCDHelper(
-            String number, boolean includeLength, int bcdExtType) {
+            String number, boolean includeLength, @BcdExtendType int bcdExtType) {
         int numberLenReal = number.length();
         int numberLenEffective = numberLenReal;
         boolean hasPlus = number.indexOf('+') != -1;
diff --git a/android/telephony/PhoneStateListener.java b/android/telephony/PhoneStateListener.java
index 0ee870a..0ff2982 100644
--- a/android/telephony/PhoneStateListener.java
+++ b/android/telephony/PhoneStateListener.java
@@ -61,9 +61,6 @@
     /**
      * Listen for changes to the network signal strength (cellular).
      * {@more}
-     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
-     * READ_PHONE_STATE}
-     * <p>
      *
      * @see #onSignalStrengthChanged
      *
@@ -76,7 +73,8 @@
      * Listen for changes to the message-waiting indicator.
      * {@more}
      * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
-     * 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.
@@ -89,7 +87,9 @@
      * Listen for changes to the call-forwarding indicator.
      * {@more}
      * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
-     * READ_PHONE_STATE}
+     * READ_PHONE_STATE} or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
      * @see #onCallForwardingIndicatorChanged
      */
     public static final int LISTEN_CALL_FORWARDING_INDICATOR                = 0x00000008;
@@ -372,7 +372,7 @@
                         break;
                     case LISTEN_PHYSICAL_CHANNEL_CONFIGURATION:
                         PhoneStateListener.this.onPhysicalChannelConfigurationChanged(
-                            (List<PhysicalChannelConfig>)msg.obj);
+                                (List<PhysicalChannelConfig>)msg.obj);
                         break;
                 }
             }
@@ -430,8 +430,9 @@
      * Callback invoked when device call state changes.
      * @param state call state
      * @param phoneNumber call phone number. If application does not have
-     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission, an empty
-     * string will be passed as an argument.
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission or carrier
+     * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be
+     * passed as an argument.
      *
      * @see TelephonyManager#CALL_STATE_IDLE
      * @see TelephonyManager#CALL_STATE_RINGING
@@ -700,6 +701,10 @@
         public void onCarrierNetworkChange(boolean active) {
             send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
         }
+
+        public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
+            send(LISTEN_PHYSICAL_CHANNEL_CONFIGURATION, 0, 0, configs);
+        }
     }
 
     IPhoneStateListener callback = new IPhoneStateListenerStub(this);
diff --git a/android/telephony/PhysicalChannelConfig.java b/android/telephony/PhysicalChannelConfig.java
index 651d68d..ce444dd 100644
--- a/android/telephony/PhysicalChannelConfig.java
+++ b/android/telephony/PhysicalChannelConfig.java
@@ -27,8 +27,9 @@
  */
 public final class PhysicalChannelConfig implements Parcelable {
 
+    // TODO(b/72993578) consolidate these enums in a central location.
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({CONNECTION_PRIMARY_SERVING, CONNECTION_SECONDARY_SERVING})
+    @IntDef({CONNECTION_PRIMARY_SERVING, CONNECTION_SECONDARY_SERVING, CONNECTION_UNKNOWN})
     public @interface ConnectionStatus {}
 
     /**
@@ -41,6 +42,9 @@
      */
     public static final int CONNECTION_SECONDARY_SERVING = 2;
 
+    /** Connection status is unknown. */
+    public static final int CONNECTION_UNKNOWN = Integer.MAX_VALUE;
+
     /**
      * Connection status of the cell.
      *
@@ -86,6 +90,7 @@
      *
      * @see #CONNECTION_PRIMARY_SERVING
      * @see #CONNECTION_SECONDARY_SERVING
+     * @see #CONNECTION_UNKNOWN
      *
      * @return Connection status of the cell
      */
diff --git a/android/telephony/ServiceState.java b/android/telephony/ServiceState.java
index 77706e8..e971d08 100644
--- a/android/telephony/ServiceState.java
+++ b/android/telephony/ServiceState.java
@@ -18,15 +18,17 @@
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -36,6 +38,7 @@
  *
  * <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
@@ -71,46 +74,25 @@
      */
     public static final int STATE_POWER_OFF = 3;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({DUPLEX_MODE_UNKNOWN, DUPLEX_MODE_FDD, DUPLEX_MODE_TDD})
+    public @interface DuplexMode {}
+
     /**
-     * RIL level registration state values from ril.h
-     * ((const char **)response)[0] is registration state 0-6,
-     *              0 - Not registered, MT is not currently searching
-     *                  a new operator to register
-     *              1 - Registered, home network
-     *              2 - Not registered, but MT is currently searching
-     *                  a new operator to register
-     *              3 - Registration denied
-     *              4 - Unknown
-     *              5 - Registered, roaming
-     *             10 - Same as 0, but indicates that emergency calls
-     *                  are enabled.
-     *             12 - Same as 2, but indicates that emergency calls
-     *                  are enabled.
-     *             13 - Same as 3, but indicates that emergency calls
-     *                  are enabled.
-     *             14 - Same as 4, but indicates that emergency calls
-     *                  are enabled.
-     * @hide
+     * Duplex mode for the phone is unknown.
      */
-    public static final int RIL_REG_STATE_NOT_REG = 0;
-    /** @hide */
-    public static final int RIL_REG_STATE_HOME = 1;
-    /** @hide */
-    public static final int RIL_REG_STATE_SEARCHING = 2;
-    /** @hide */
-    public static final int RIL_REG_STATE_DENIED = 3;
-    /** @hide */
-    public static final int RIL_REG_STATE_UNKNOWN = 4;
-    /** @hide */
-    public static final int RIL_REG_STATE_ROAMING = 5;
-    /** @hide */
-    public static final int RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED = 10;
-    /** @hide */
-    public static final int RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED = 12;
-    /** @hide */
-    public static final int RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED = 13;
-    /** @hide */
-    public static final int RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED = 14;
+    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;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -196,7 +178,6 @@
 
     /**
      * Number of radio technologies for GSM, UMTS and CDMA.
-     * @hide
      */
     private static final int NEXT_RIL_RADIO_TECHNOLOGY = 20;
 
@@ -210,22 +191,6 @@
                     | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_B - 1))
                     | (1 << (RIL_RADIO_TECHNOLOGY_EHRPD - 1));
 
-    /**
-     * Available registration states for GSM, UMTS and CDMA.
-     */
-    /** @hide */
-    public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_NOT_SEARCHING = 0;
-    /** @hide */
-    public static final int REGISTRATION_STATE_HOME_NETWORK = 1;
-    /** @hide */
-    public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_SEARCHING = 2;
-    /** @hide */
-    public static final int REGISTRATION_STATE_REGISTRATION_DENIED = 3;
-    /** @hide */
-    public static final int REGISTRATION_STATE_UNKNOWN = 4;
-    /** @hide */
-    public static final int REGISTRATION_STATE_ROAMING = 5;
-
     private int mVoiceRegState = STATE_OUT_OF_SERVICE;
     private int mDataRegState = STATE_OUT_OF_SERVICE;
 
@@ -255,7 +220,7 @@
     public static final int ROAMING_TYPE_INTERNATIONAL = 3;
 
     /**
-     * Unknown ID. Could be returned by {@link #getNetworkId()} or {@link #getSystemId()}
+     * Unknown ID. Could be returned by {@link #getCdmaNetworkId()} or {@link #getCdmaSystemId()}
      */
     public static final int UNKNOWN_ID = -1;
 
@@ -286,6 +251,9 @@
 
     private boolean mIsUsingCarrierAggregation;
 
+    private int mChannelNumber;
+    private int[] mCellBandwidths = new int[0];
+
     /* EARFCN stands for E-UTRA Absolute Radio Frequency Channel Number,
      * Reference: 3GPP TS 36.104 5.4.3 */
     private int mLteEarfcnRsrpBoost = 0;
@@ -371,6 +339,8 @@
         mIsEmergencyOnly = s.mIsEmergencyOnly;
         mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration;
         mIsUsingCarrierAggregation = s.mIsUsingCarrierAggregation;
+        mChannelNumber = s.mChannelNumber;
+        mCellBandwidths = Arrays.copyOf(s.mCellBandwidths, s.mCellBandwidths.length);
         mLteEarfcnRsrpBoost = s.mLteEarfcnRsrpBoost;
         mNetworkRegistrationStates = new ArrayList<>(s.mNetworkRegistrationStates);
     }
@@ -405,6 +375,8 @@
         mLteEarfcnRsrpBoost = in.readInt();
         mNetworkRegistrationStates = new ArrayList<>();
         in.readList(mNetworkRegistrationStates, NetworkRegistrationState.class.getClassLoader());
+        mChannelNumber = in.readInt();
+        mCellBandwidths = in.createIntArray();
     }
 
     public void writeToParcel(Parcel out, int flags) {
@@ -433,6 +405,8 @@
         out.writeInt(mIsUsingCarrierAggregation ? 1 : 0);
         out.writeInt(mLteEarfcnRsrpBoost);
         out.writeList(mNetworkRegistrationStates);
+        out.writeInt(mChannelNumber);
+        out.writeIntArray(mCellBandwidths);
     }
 
     public int describeContents() {
@@ -486,6 +460,46 @@
     }
 
     /**
+     * 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() {
+        // only support LTE duplex mode
+        if (!isLte(mRilDataRadioTechnology)) {
+            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 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
      * (note: not just decoding from TS 27.007 7.2)
      *
@@ -713,6 +727,8 @@
                 + (mDataRegState * 37)
                 + mVoiceRoamingType
                 + mDataRoamingType
+                + mChannelNumber
+                + Arrays.hashCode(mCellBandwidths)
                 + (mIsManualNetworkSelection ? 1 : 0)
                 + ((null == mVoiceOperatorAlphaLong) ? 0 : mVoiceOperatorAlphaLong.hashCode())
                 + ((null == mVoiceOperatorAlphaShort) ? 0 : mVoiceOperatorAlphaShort.hashCode())
@@ -745,6 +761,8 @@
                 && mIsManualNetworkSelection == s.mIsManualNetworkSelection
                 && mVoiceRoamingType == s.mVoiceRoamingType
                 && mDataRoamingType == s.mDataRoamingType
+                && mChannelNumber == s.mChannelNumber
+                && Arrays.equals(mCellBandwidths, s.mCellBandwidths)
                 && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong)
                 && equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort)
                 && equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric)
@@ -874,6 +892,9 @@
             .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(", mVoiceRoamingType=").append(getRoamingLogString(mVoiceRoamingType))
             .append(", mDataRoamingType=").append(getRoamingLogString(mDataRoamingType))
             .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
@@ -905,6 +926,8 @@
         mDataRegState = state;
         mVoiceRoamingType = ROAMING_TYPE_NOT_ROAMING;
         mDataRoamingType = ROAMING_TYPE_NOT_ROAMING;
+        mChannelNumber = -1;
+        mCellBandwidths = new int[0];
         mVoiceOperatorAlphaLong = null;
         mVoiceOperatorAlphaShort = null;
         mVoiceOperatorNumeric = null;
@@ -953,6 +976,16 @@
         if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRegState=" + mDataRegState);
     }
 
+    /** @hide */
+    public void setCellBandwidths(int[] bandwidths) {
+        mCellBandwidths = bandwidths;
+    }
+
+    /** @hide */
+    public void setChannelNumber(int channelNumber) {
+        mChannelNumber = channelNumber;
+    }
+
     public void setRoaming(boolean roaming) {
         mVoiceRoamingType = (roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING);
         mDataRoamingType = mVoiceRoamingType;
@@ -1101,6 +1134,8 @@
         mIsDataRoamingFromRegistration = m.getBoolean("isDataRoamingFromRegistration");
         mIsUsingCarrierAggregation = m.getBoolean("isUsingCarrierAggregation");
         mLteEarfcnRsrpBoost = m.getInt("LteEarfcnRsrpBoost");
+        mChannelNumber = m.getInt("ChannelNumber");
+        mCellBandwidths = m.getIntArray("CellBandwidths");
     }
 
     /**
@@ -1132,6 +1167,8 @@
         m.putBoolean("isDataRoamingFromRegistration", mIsDataRoamingFromRegistration);
         m.putBoolean("isUsingCarrierAggregation", mIsUsingCarrierAggregation);
         m.putInt("LteEarfcnRsrpBoost", mLteEarfcnRsrpBoost);
+        m.putInt("ChannelNumber", mChannelNumber);
+        m.putIntArray("CellBandwidths", mCellBandwidths);
     }
 
     /** @hide */
@@ -1182,7 +1219,8 @@
     }
 
     /** @hide */
-    public void setSystemAndNetworkId(int systemId, int networkId) {
+    @TestApi
+    public void setCdmaSystemAndNetworkId(int systemId, int networkId) {
         this.mSystemId = systemId;
         this.mNetworkId = networkId;
     }
@@ -1251,6 +1289,84 @@
     }
 
     /** @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_IWLAN:
+                return AccessNetworkType.IWLAN;
+            case RIL_RADIO_TECHNOLOGY_UNKNOWN:
+            default:
+                return AccessNetworkType.UNKNOWN;
+        }
+    }
+
+    /** @hide */
+    public static int networkTypeToRilRadioTechnology(int networkType) {
+        switch(networkType) {
+            case TelephonyManager.NETWORK_TYPE_GPRS:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_GPRS;
+            case TelephonyManager.NETWORK_TYPE_EDGE:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_EDGE;
+            case TelephonyManager.NETWORK_TYPE_UMTS:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_UMTS;
+            case TelephonyManager.NETWORK_TYPE_HSDPA:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA;
+            case TelephonyManager.NETWORK_TYPE_HSUPA:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA;
+            case TelephonyManager.NETWORK_TYPE_HSPA:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+            case TelephonyManager.NETWORK_TYPE_CDMA:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_IS95A;
+            case TelephonyManager.NETWORK_TYPE_1xRTT:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT;
+            case TelephonyManager.NETWORK_TYPE_EVDO_0:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0;
+            case TelephonyManager.NETWORK_TYPE_EVDO_A:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A;
+            case TelephonyManager.NETWORK_TYPE_EVDO_B:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B;
+            case TelephonyManager.NETWORK_TYPE_EHRPD:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD;
+            case TelephonyManager.NETWORK_TYPE_LTE:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+            case TelephonyManager.NETWORK_TYPE_HSPAP:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP;
+            case TelephonyManager.NETWORK_TYPE_GSM:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_GSM;
+            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA;
+            case TelephonyManager.NETWORK_TYPE_IWLAN:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+            case TelephonyManager.NETWORK_TYPE_LTE_CA:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA;
+            default:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
+        }
+    }
+
+
+    /** @hide */
     public int getDataNetworkType() {
         return rilRadioTechnologyToNetworkType(mRilDataRadioTechnology);
     }
@@ -1270,7 +1386,7 @@
      * within a wireless system. (Defined in 3GPP2 C.S0023 3.4.8)
      * @return The CDMA NID or {@link #UNKNOWN_ID} if not available.
      */
-    public int getNetworkId() {
+    public int getCdmaNetworkId() {
         return this.mNetworkId;
     }
 
@@ -1279,7 +1395,7 @@
      * system. (Defined in 3GPP2 C.S0023 3.4.8)
      * @return The CDMA SID or {@link #UNKNOWN_ID} if not available.
      */
-    public int getSystemId() {
+    public int getCdmaSystemId() {
         return this.mSystemId;
     }
 
@@ -1416,7 +1532,9 @@
      */
     @SystemApi
     public List<NetworkRegistrationState> getNetworkRegistrationStates() {
-        return mNetworkRegistrationStates;
+        synchronized (mNetworkRegistrationStates) {
+            return new ArrayList<>(mNetworkRegistrationStates);
+        }
     }
 
     /**
@@ -1429,11 +1547,15 @@
     @SystemApi
     public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
         List<NetworkRegistrationState> list = new ArrayList<>();
-        for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
-            if (networkRegistrationState.getTransportType() == transportType) {
-                list.add(networkRegistrationState);
+
+        synchronized (mNetworkRegistrationStates) {
+            for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+                if (networkRegistrationState.getTransportType() == transportType) {
+                    list.add(networkRegistrationState);
+                }
             }
         }
+
         return list;
     }
 
@@ -1447,12 +1569,36 @@
      */
     @SystemApi
     public NetworkRegistrationState getNetworkRegistrationStates(int transportType, int domain) {
-        for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
-            if (networkRegistrationState.getTransportType() == transportType
-                    && networkRegistrationState.getDomain() == domain) {
-                return networkRegistrationState;
+        synchronized (mNetworkRegistrationStates) {
+            for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+                if (networkRegistrationState.getTransportType() == transportType
+                        && networkRegistrationState.getDomain() == domain) {
+                    return networkRegistrationState;
+                }
             }
         }
+
         return null;
     }
+
+    /**
+     * @hide
+     */
+    public void addNetworkRegistrationState(NetworkRegistrationState regState) {
+        if (regState == null) return;
+
+        synchronized (mNetworkRegistrationStates) {
+            for (int i = 0; i < mNetworkRegistrationStates.size(); i++) {
+                NetworkRegistrationState curRegState = mNetworkRegistrationStates.get(i);
+                if (curRegState.getTransportType() == regState.getTransportType()
+                        && curRegState.getDomain() == regState.getDomain()) {
+                    mNetworkRegistrationStates.remove(i);
+                    break;
+                }
+            }
+
+            mNetworkRegistrationStates.add(regState);
+        }
+    }
+
 }
diff --git a/android/telephony/SignalStrength.java b/android/telephony/SignalStrength.java
index fc2ef27..4e56396 100644
--- a/android/telephony/SignalStrength.java
+++ b/android/telephony/SignalStrength.java
@@ -25,6 +25,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Contains phone signal strength related information.
@@ -35,15 +36,20 @@
     private static final boolean DBG = false;
 
     /** @hide */
-    public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
+    public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN
+            = TelephonyProtoEnums.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; // = 0
     /** @hide */
-    public static final int SIGNAL_STRENGTH_POOR = 1;
+    public static final int SIGNAL_STRENGTH_POOR
+            = TelephonyProtoEnums.SIGNAL_STRENGTH_POOR; // = 1
     /** @hide */
-    public static final int SIGNAL_STRENGTH_MODERATE = 2;
+    public static final int SIGNAL_STRENGTH_MODERATE
+            = TelephonyProtoEnums.SIGNAL_STRENGTH_MODERATE; // = 2
     /** @hide */
-    public static final int SIGNAL_STRENGTH_GOOD = 3;
+    public static final int SIGNAL_STRENGTH_GOOD
+            = TelephonyProtoEnums.SIGNAL_STRENGTH_GOOD; // = 3
     /** @hide */
-    public static final int SIGNAL_STRENGTH_GREAT = 4;
+    public static final int SIGNAL_STRENGTH_GREAT
+            = TelephonyProtoEnums.SIGNAL_STRENGTH_GREAT; // = 4
     /** @hide */
     public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
     /** @hide */
@@ -57,7 +63,16 @@
      */
     public static final int INVALID = Integer.MAX_VALUE;
 
-    private static final int LTE_RSRP_THRESHOLDS_NUM = 6;
+    private static final int LTE_RSRP_THRESHOLDS_NUM = 4;
+    private static final int MAX_LTE_RSRP = -44;
+    private static final int MIN_LTE_RSRP = -140;
+
+    private static final int WCDMA_RSCP_THRESHOLDS_NUM = 4;
+    private static final int MAX_WCDMA_RSCP = -24;
+    private static final int MIN_WCDMA_RSCP = -120;
+
+    /* The type of signal measurement */
+    private static final String MEASUMENT_TYPE_RSCP = "rscp";
 
     /** Parameters reported by the Radio */
     private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
@@ -72,7 +87,10 @@
     private int mLteRsrq;
     private int mLteRssnr;
     private int mLteCqi;
-    private int mTdScdmaRscp;
+    private int mTdScdmaRscp; // Valid values are -24...-120dBm or INVALID if unknown
+    private int mWcdmaSignalStrength;
+    private int mWcdmaRscpAsu;  // the WCDMA RSCP in ASU as reported from the HAL
+    private int mWcdmaRscp;     // the WCDMA RSCP in dBm
 
     /** Parameters from the framework */
     private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating
@@ -81,9 +99,17 @@
                             // onSignalStrengthResult.
     private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
 
-    // The threshold of LTE RSRP for determining the display level of LTE signal bar.
+    // The threshold of LTE RSRP for determining the display level of LTE signal bar. Note that the
+    // min and max are fixed at MIN_LTE_RSRP (-140) and MAX_LTE_RSRP (-44).
     private int mLteRsrpThresholds[] = new int[LTE_RSRP_THRESHOLDS_NUM];
 
+    // The type of default measurement for determining the display level of WCDMA signal bar.
+    private String mWcdmaDefaultSignalMeasurement;
+
+    // The threshold of WCDMA RSCP for determining the display level of WCDMA signal bar. Note that
+    // the min and max are fixed at MIN_WCDMA_RSCP (-120) and MAX_WCDMA_RSCP (-24).
+    private int mWcdmaRscpThresholds[] = new int[WCDMA_RSCP_THRESHOLDS_NUM];
+
     /**
      * Create a new SignalStrength from a intent notifier Bundle
      *
@@ -133,10 +159,15 @@
         mLteRssnr = INVALID;
         mLteCqi = INVALID;
         mTdScdmaRscp = INVALID;
+        mWcdmaSignalStrength = 99;
+        mWcdmaRscp = INVALID;
+        mWcdmaRscpAsu = 255;
         mLteRsrpBoost = 0;
         mIsGsm = gsmFlag;
         mUseOnlyRsrpForLteLevel = false;
+        mWcdmaDefaultSignalMeasurement = "";
         setLteRsrpThresholds(getDefaultLteRsrpThresholds());
+        setWcdmaRscpThresholds(getDefaultWcdmaRscpThresholds());
     }
 
     /**
@@ -149,9 +180,10 @@
             int cdmaDbm, int cdmaEcio,
             int evdoDbm, int evdoEcio, int evdoSnr,
             int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            int tdScdmaRscp,
+            int tdScdmaRscp, int wcdmaSignalStrength, int wcdmaRscpAsu,
             // values Added by config
-            int lteRsrpBoost, boolean gsmFlag, boolean lteLevelBaseOnRsrp) {
+            int lteRsrpBoost, boolean gsmFlag, boolean lteLevelBaseOnRsrp,
+            String wcdmaDefaultMeasurement) {
         mGsmSignalStrength = gsmSignalStrength;
         mGsmBitErrorRate = gsmBitErrorRate;
         mCdmaDbm = cdmaDbm;
@@ -165,15 +197,20 @@
         mLteRssnr = lteRssnr;
         mLteCqi = lteCqi;
         mTdScdmaRscp = INVALID;
+        mWcdmaSignalStrength = wcdmaSignalStrength;
+        mWcdmaRscpAsu = wcdmaRscpAsu;
+        mWcdmaRscp = wcdmaRscpAsu - 120;
         mLteRsrpBoost = lteRsrpBoost;
         mIsGsm = gsmFlag;
         mUseOnlyRsrpForLteLevel = lteLevelBaseOnRsrp;
+        mWcdmaDefaultSignalMeasurement = wcdmaDefaultMeasurement;
         setLteRsrpThresholds(getDefaultLteRsrpThresholds());
+        setWcdmaRscpThresholds(getDefaultWcdmaRscpThresholds());
         if (DBG) log("initialize: " + toString());
     }
 
     /**
-     * Constructor for only values provided by Radio HAL
+     * Constructor for only values provided by Radio HAL V1.0
      *
      * @hide
      */
@@ -184,7 +221,23 @@
             int tdScdmaRscp) {
         this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
                 evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, tdScdmaRscp, 0, true, false);
+                lteRsrq, lteRssnr, lteCqi, tdScdmaRscp, 99, INVALID, 0, true, false, "");
+    }
+
+    /**
+     * Constructor for only values provided by Radio HAL V1.2
+     *
+     * @hide
+     */
+    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+            int cdmaDbm, int cdmaEcio,
+            int evdoDbm, int evdoEcio, int evdoSnr,
+            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
+            int tdScdmaRscp, int wcdmaSignalStrength, int wcdmaRscp) {
+        this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
+                evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
+                lteRsrq, lteRssnr, lteCqi, tdScdmaRscp, wcdmaSignalStrength, wcdmaRscp, 0, true,
+                false, "");
     }
 
     /**
@@ -215,10 +268,15 @@
         mLteRssnr = s.mLteRssnr;
         mLteCqi = s.mLteCqi;
         mTdScdmaRscp = s.mTdScdmaRscp;
+        mWcdmaSignalStrength = s.mWcdmaSignalStrength;
+        mWcdmaRscpAsu = s.mWcdmaRscpAsu;
+        mWcdmaRscp = s.mWcdmaRscp;
         mLteRsrpBoost = s.mLteRsrpBoost;
         mIsGsm = s.mIsGsm;
         mUseOnlyRsrpForLteLevel = s.mUseOnlyRsrpForLteLevel;
+        mWcdmaDefaultSignalMeasurement = s.mWcdmaDefaultSignalMeasurement;
         setLteRsrpThresholds(s.mLteRsrpThresholds);
+        setWcdmaRscpThresholds(s.mWcdmaRscpThresholds);
     }
 
     /**
@@ -242,10 +300,15 @@
         mLteRssnr = in.readInt();
         mLteCqi = in.readInt();
         mTdScdmaRscp = in.readInt();
+        mWcdmaSignalStrength = in.readInt();
+        mWcdmaRscpAsu = in.readInt();
+        mWcdmaRscp = in.readInt();
         mLteRsrpBoost = in.readInt();
         mIsGsm = in.readBoolean();
         mUseOnlyRsrpForLteLevel = in.readBoolean();
+        mWcdmaDefaultSignalMeasurement = in.readString();
         in.readIntArray(mLteRsrpThresholds);
+        in.readIntArray(mWcdmaRscpThresholds);
     }
 
     /**
@@ -265,10 +328,15 @@
         out.writeInt(mLteRssnr);
         out.writeInt(mLteCqi);
         out.writeInt(mTdScdmaRscp);
+        out.writeInt(mWcdmaSignalStrength);
+        out.writeInt(mWcdmaRscpAsu);
+        out.writeInt(mWcdmaRscp);
         out.writeInt(mLteRsrpBoost);
         out.writeBoolean(mIsGsm);
         out.writeBoolean(mUseOnlyRsrpForLteLevel);
+        out.writeString(mWcdmaDefaultSignalMeasurement);
         out.writeIntArray(mLteRsrpThresholds);
+        out.writeIntArray(mWcdmaRscpThresholds);
     }
 
     /**
@@ -308,24 +376,34 @@
         if (DBG) log("Signal before validate=" + this);
         // TS 27.007 8.5
         mGsmSignalStrength = mGsmSignalStrength >= 0 ? mGsmSignalStrength : 99;
+        mWcdmaSignalStrength = (mWcdmaSignalStrength >= 0) ? mWcdmaSignalStrength : 99;
+        mLteSignalStrength = (mLteSignalStrength >= 0) ? mLteSignalStrength : 99;
         // BER no change;
 
+        // WCDMA RSCP valid values are -120 through -24 as defined in TS 27.007 8.69
+        // but are reported in ASU which is 0 through 96, so we do the conversion here
+        mWcdmaRscpAsu =
+                ((mWcdmaRscpAsu - 120 >= MIN_WCDMA_RSCP) && (mWcdmaRscpAsu - 120 <= MAX_WCDMA_RSCP))
+                ? mWcdmaRscpAsu : 255;
+        mWcdmaRscp = ((mWcdmaRscp >= MIN_WCDMA_RSCP) && (mWcdmaRscp <= MAX_WCDMA_RSCP))
+                ? mWcdmaRscp : INVALID;
+
         mCdmaDbm = mCdmaDbm > 0 ? -mCdmaDbm : -120;
-        mCdmaEcio = (mCdmaEcio > 0) ? -mCdmaEcio : -160;
+        mCdmaEcio = (mCdmaEcio >= 0) ? -mCdmaEcio : -160;
 
         mEvdoDbm = (mEvdoDbm > 0) ? -mEvdoDbm : -120;
-        mEvdoEcio = (mEvdoEcio >= 0) ? -mEvdoEcio : -1;
-        mEvdoSnr = ((mEvdoSnr > 0) && (mEvdoSnr <= 8)) ? mEvdoSnr : -1;
+        mEvdoEcio = (mEvdoEcio >= 0) ? -mEvdoEcio : -160;
+        mEvdoSnr = ((mEvdoSnr >= 0) && (mEvdoSnr <= 8)) ? mEvdoSnr : -1;
 
         // TS 36.214 Physical Layer Section 5.1.3, TS 36.331 RRC
-        mLteSignalStrength = (mLteSignalStrength >= 0) ? mLteSignalStrength : 99;
-        mLteRsrp = ((mLteRsrp >= 44) && (mLteRsrp <= 140)) ? -mLteRsrp : SignalStrength.INVALID;
+        mLteRsrp = ((-mLteRsrp >= MIN_LTE_RSRP) && (-mLteRsrp <= MAX_LTE_RSRP)) ? -mLteRsrp
+                                : SignalStrength.INVALID;
         mLteRsrq = ((mLteRsrq >= 3) && (mLteRsrq <= 20)) ? -mLteRsrq : SignalStrength.INVALID;
         mLteRssnr = ((mLteRssnr >= -200) && (mLteRssnr <= 300)) ? mLteRssnr
                 : SignalStrength.INVALID;
 
-        mTdScdmaRscp = ((mTdScdmaRscp >= 25) && (mTdScdmaRscp <= 120))
-                ? -mTdScdmaRscp : SignalStrength.INVALID;
+        mTdScdmaRscp = ((mTdScdmaRscp >= 0) && (mTdScdmaRscp <= 96))
+                ? (mTdScdmaRscp - 120) : SignalStrength.INVALID;
         // Cqi no change
         if (DBG) log("Signal after validate=" + this);
     }
@@ -363,6 +441,16 @@
     }
 
     /**
+     * @param defaultMeasurement sets the type of WCDMA default signal measurement
+     *
+     * Used by phone to determine default measurement type for calculation WCDMA signal level.
+     * @hide
+     */
+    public void setWcdmaDefaultSignalMeasurement(String defaultMeasurement) {
+        mWcdmaDefaultSignalMeasurement = defaultMeasurement;
+    }
+
+    /**
      * @param lteRsrpBoost - signal strength offset
      *
      * Used by phone to set the lte signal strength offset which will be
@@ -406,6 +494,23 @@
     }
 
     /**
+     * Sets the threshold array for determining the display level of WCDMA signal bar.
+     *
+     * @param wcdmaRscpThresholds int array for determining the display level.
+     *
+     * @hide
+     */
+    public void setWcdmaRscpThresholds(int[] wcdmaRscpThresholds) {
+        if ((wcdmaRscpThresholds == null)
+                || (wcdmaRscpThresholds.length != WCDMA_RSCP_THRESHOLDS_NUM)) {
+            Log.wtf(LOG_TAG, "setWcdmaRscpThresholds - wcdmaRscpThresholds is invalid.");
+            return;
+        }
+        System.arraycopy(wcdmaRscpThresholds, 0, mWcdmaRscpThresholds, 0,
+                WCDMA_RSCP_THRESHOLDS_NUM);
+    }
+
+    /**
      * Get the CDMA RSSI value in dBm
      */
     public int getCdmaDbm() {
@@ -496,6 +601,8 @@
                 asuLevel = getLteAsuLevel();
             } else if (mTdScdmaRscp != SignalStrength.INVALID) {
                 asuLevel = getTdScdmaAsuLevel();
+            } else if (mWcdmaRscp != SignalStrength.INVALID) {
+                asuLevel = getWcdmaAsuLevel();
             } else {
                 asuLevel = getGsmAsuLevel();
             }
@@ -529,7 +636,11 @@
             dBm = getLteDbm();
             if (dBm == INVALID) {
                 if (getTdScdmaLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                    dBm = getGsmDbm();
+                    if (getWcdmaDbm() == INVALID) {
+                        dBm = getGsmDbm();
+                    } else {
+                        dBm = getWcdmaDbm();
+                    }
                 } else {
                     dBm = getTdScdmaDbm();
                 }
@@ -735,24 +846,29 @@
      */
     public int getLteLevel() {
         /*
-         * TS 36.214 Physical Layer Section 5.1.3 TS 36.331 RRC RSSI = received
-         * signal + noise RSRP = reference signal dBm RSRQ = quality of signal
-         * dB= Number of Resource blocksxRSRP/RSSI SNR = gain=signal/noise ratio
-         * = -10log P1/P2 dB
+         * TS 36.214 Physical Layer Section 5.1.3
+         * TS 36.331 RRC
+         *
+         * RSSI = received signal + noise
+         * RSRP = reference signal dBm
+         * RSRQ = quality of signal dB = Number of Resource blocks*RSRP/RSSI
+         * SNR = gain = signal/noise ratio = -10log P1/P2 dB
          */
         int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, rsrpIconLevel = -1, snrIconLevel = -1;
 
-        if (mLteRsrp > mLteRsrpThresholds[5]) {
-            rsrpIconLevel = -1;
-        } else if (mLteRsrp >= (mLteRsrpThresholds[4] - mLteRsrpBoost)) {
-            rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
+        if (mLteRsrp > MAX_LTE_RSRP || mLteRsrp < MIN_LTE_RSRP) {
+            if (mLteRsrp != INVALID) {
+                Log.wtf(LOG_TAG, "getLteLevel - invalid lte rsrp: mLteRsrp=" + mLteRsrp);
+            }
         } else if (mLteRsrp >= (mLteRsrpThresholds[3] - mLteRsrpBoost)) {
-            rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
+            rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
         } else if (mLteRsrp >= (mLteRsrpThresholds[2] - mLteRsrpBoost)) {
-            rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+            rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
         } else if (mLteRsrp >= (mLteRsrpThresholds[1] - mLteRsrpBoost)) {
+            rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+        } else if (mLteRsrp >= (mLteRsrpThresholds[0] - mLteRsrpBoost)) {
             rsrpIconLevel = SIGNAL_STRENGTH_POOR;
-        } else if (mLteRsrp >= mLteRsrpThresholds[0]) {
+        } else {
             rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         }
 
@@ -796,17 +912,21 @@
         if (rsrpIconLevel != -1) return rsrpIconLevel;
 
         /* Valid values are (0-63, 99) as defined in TS 36.331 */
+        // TODO the range here is probably supposed to be (0..31, 99). It's unclear if anyone relies
+        // on the current incorrect range check, so this will be fixed in a future release with more
+        // soak time
         if (mLteSignalStrength > 63) rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         else if (mLteSignalStrength >= 12) rssiIconLevel = SIGNAL_STRENGTH_GREAT;
         else if (mLteSignalStrength >= 8) rssiIconLevel = SIGNAL_STRENGTH_GOOD;
         else if (mLteSignalStrength >= 5) rssiIconLevel = SIGNAL_STRENGTH_MODERATE;
         else if (mLteSignalStrength >= 0) rssiIconLevel = SIGNAL_STRENGTH_POOR;
 
-        if (DBG) log("getLTELevel - rssi:" + mLteSignalStrength + " rssiIconLevel:"
+        if (DBG) log("getLteLevel - rssi:" + mLteSignalStrength + " rssiIconLevel:"
                 + rssiIconLevel);
         return rssiIconLevel;
 
     }
+
     /**
      * 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
@@ -899,6 +1019,105 @@
         return tdScdmaAsuLevel;
     }
 
+    /**
+     * Gets WCDMA RSCP as a dbm value between -120 and -24, as defined in TS 27.007 8.69.
+     *
+     * @hide
+     */
+    public int getWcdmaRscp() {
+        return mWcdmaRscp;
+    }
+
+    /**
+     * Get the WCDMA signal level as an ASU value between 0-96, 255 is unknown
+     *
+     * @hide
+     */
+    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
+         */
+        final int wcdmaDbm = getWcdmaDbm();
+        int wcdmaAsuLevel = 255;
+        // validateInput will always give a valid range between -120 to -24 as per ril.h. so RSCP
+        // outside range is already set to INVALID
+        if (wcdmaDbm == SignalStrength.INVALID) wcdmaAsuLevel =  255;
+        else wcdmaAsuLevel = wcdmaDbm + 120;
+        if (DBG) log("Wcdma Asu level: " + wcdmaAsuLevel);
+        return wcdmaAsuLevel;
+    }
+
+    /**
+     * Gets WCDMA signal strength as a dbm value between -120 and -24, as defined in TS 27.007 8.69.
+     *
+     * @hide
+     */
+    public int getWcdmaDbm() {
+        return mWcdmaRscp;
+    }
+
+    /**
+     * Get WCDMA as level 0..4
+     *
+     * @hide
+     */
+    public int getWcdmaLevel() {
+        int level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+        if (mWcdmaDefaultSignalMeasurement == null) {
+            Log.wtf(LOG_TAG, "getWcdmaLevel - WCDMA default signal measurement is invalid.");
+            return level;
+        }
+
+        switch (mWcdmaDefaultSignalMeasurement) {
+            case MEASUMENT_TYPE_RSCP:
+                // RSCP valid values are (-120 through -24) as defined in TS 27.007 8.69
+                if (mWcdmaRscp < MIN_WCDMA_RSCP || mWcdmaRscp > MAX_WCDMA_RSCP) {
+                    if (mWcdmaRscp != INVALID) {
+                        Log.wtf(LOG_TAG, "getWcdmaLevel - invalid WCDMA RSCP: mWcdmaRscp="
+                                + mWcdmaRscp);
+                    }
+                } else if (mWcdmaRscp >= mWcdmaRscpThresholds[3]) {
+                    level = SIGNAL_STRENGTH_GREAT;
+                } else if (mWcdmaRscp >= mWcdmaRscpThresholds[2]) {
+                    level = SIGNAL_STRENGTH_GOOD;
+                } else if (mWcdmaRscp >= mWcdmaRscpThresholds[1]) {
+                    level = SIGNAL_STRENGTH_MODERATE;
+                } else if (mWcdmaRscp >= mWcdmaRscpThresholds[0]) {
+                    level = SIGNAL_STRENGTH_POOR;
+                }
+                if (DBG) log("getWcdmaLevel=" + level + " WcdmaRscp=" + mWcdmaRscp);
+                break;
+
+            default:
+                // RSSI valid values are (0..31) as defined in TS 27.007 8.5
+                if (mWcdmaSignalStrength < 0 || mWcdmaSignalStrength > 31) {
+                    if (mWcdmaSignalStrength != 99) {
+                        Log.wtf(LOG_TAG, "getWcdmaLevel - invalid WCDMA RSSI: mWcdmaSignalStrength="
+                                + mWcdmaSignalStrength);
+                    }
+                } else if (mWcdmaSignalStrength >= 18) {
+                    level = SIGNAL_STRENGTH_GREAT;
+                } else if (mWcdmaSignalStrength >= 13) {
+                    level = SIGNAL_STRENGTH_GOOD;
+                } else if (mWcdmaSignalStrength >= 8) {
+                    level = SIGNAL_STRENGTH_MODERATE;
+                } else if (mWcdmaSignalStrength >= 3) {
+                    level = SIGNAL_STRENGTH_POOR;
+                }
+                if (DBG) log("getWcdmaLevel=" + level + " WcdmaSignalStrength=" +
+                        mWcdmaSignalStrength);
+                break;
+
+        }
+        return level;
+    }
+
    /**
      * @return hash code
      */
@@ -911,8 +1130,11 @@
                 + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
                 + (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
                 + (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
-                + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (mIsGsm ? 1 : 0)
-                + (mUseOnlyRsrpForLteLevel ? 1 : 0) + (Arrays.hashCode(mLteRsrpThresholds)));
+                + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum)
+                + (mWcdmaSignalStrength * primeNum) + (mWcdmaRscpAsu * primeNum)
+                + (mWcdmaRscp * primeNum) + (mIsGsm ? 1 : 0) + (mUseOnlyRsrpForLteLevel ? 1 : 0)
+                + (Objects.hashCode(mWcdmaDefaultSignalMeasurement))
+                + (Arrays.hashCode(mLteRsrpThresholds)) + (Arrays.hashCode(mWcdmaRscpThresholds)));
     }
 
     /**
@@ -946,9 +1168,14 @@
                 && mLteCqi == s.mLteCqi
                 && mLteRsrpBoost == s.mLteRsrpBoost
                 && mTdScdmaRscp == s.mTdScdmaRscp
+                && mWcdmaSignalStrength == s.mWcdmaSignalStrength
+                && mWcdmaRscpAsu == s.mWcdmaRscpAsu
+                && mWcdmaRscp == s.mWcdmaRscp
                 && mIsGsm == s.mIsGsm
                 && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel
-                && Arrays.equals(mLteRsrpThresholds, s.mLteRsrpThresholds));
+                && Objects.equals(mWcdmaDefaultSignalMeasurement, s.mWcdmaDefaultSignalMeasurement)
+                && Arrays.equals(mLteRsrpThresholds, s.mLteRsrpThresholds)
+                && Arrays.equals(mWcdmaRscpThresholds, s.mWcdmaRscpThresholds));
     }
 
     /**
@@ -971,10 +1198,15 @@
                 + " " + mLteCqi
                 + " " + mLteRsrpBoost
                 + " " + mTdScdmaRscp
+                + " " + mWcdmaSignalStrength
+                + " " + mWcdmaRscpAsu
+                + " " + mWcdmaRscp
                 + " " + (mIsGsm ? "gsm|lte" : "cdma")
                 + " " + (mUseOnlyRsrpForLteLevel ? "use_only_rsrp_for_lte_level" :
                          "use_rsrp_and_rssnr_for_lte_level")
-                + " " + (Arrays.toString(mLteRsrpThresholds)));
+                + " " + mWcdmaDefaultSignalMeasurement
+                + " " + (Arrays.toString(mLteRsrpThresholds))
+                + " " + (Arrays.toString(mWcdmaRscpThresholds)));
     }
 
     /** Returns the signal strength related to GSM. */
@@ -983,7 +1215,10 @@
         if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
             level = getTdScdmaLevel();
             if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                level = getGsmLevel();
+                level = getWcdmaLevel();
+                if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+                    level = getGsmLevel();
+                }
             }
         }
         return level;
@@ -1028,12 +1263,20 @@
         mLteCqi = m.getInt("LteCqi");
         mLteRsrpBoost = m.getInt("LteRsrpBoost");
         mTdScdmaRscp = m.getInt("TdScdma");
+        mWcdmaSignalStrength = m.getInt("WcdmaSignalStrength");
+        mWcdmaRscpAsu = m.getInt("WcdmaRscpAsu");
+        mWcdmaRscp = m.getInt("WcdmaRscp");
         mIsGsm = m.getBoolean("IsGsm");
         mUseOnlyRsrpForLteLevel = m.getBoolean("UseOnlyRsrpForLteLevel");
+        mWcdmaDefaultSignalMeasurement = m.getString("WcdmaDefaultSignalMeasurement");
         ArrayList<Integer> lteRsrpThresholds = m.getIntegerArrayList("lteRsrpThresholds");
         for (int i = 0; i < lteRsrpThresholds.size(); i++) {
             mLteRsrpThresholds[i] = lteRsrpThresholds.get(i);
         }
+        ArrayList<Integer> wcdmaRscpThresholds = m.getIntegerArrayList("wcdmaRscpThresholds");
+        for (int i = 0; i < wcdmaRscpThresholds.size(); i++) {
+            mWcdmaRscpThresholds[i] = wcdmaRscpThresholds.get(i);
+        }
     }
 
     /**
@@ -1057,13 +1300,22 @@
         m.putInt("LteCqi", mLteCqi);
         m.putInt("LteRsrpBoost", mLteRsrpBoost);
         m.putInt("TdScdma", mTdScdmaRscp);
+        m.putInt("WcdmaSignalStrength", mWcdmaSignalStrength);
+        m.putInt("WcdmaRscpAsu", mWcdmaRscpAsu);
+        m.putInt("WcdmaRscp", mWcdmaRscp);
         m.putBoolean("IsGsm", mIsGsm);
         m.putBoolean("UseOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
+        m.putString("WcdmaDefaultSignalMeasurement", mWcdmaDefaultSignalMeasurement);
         ArrayList<Integer> lteRsrpThresholds = new ArrayList<Integer>();
         for (int value : mLteRsrpThresholds) {
             lteRsrpThresholds.add(value);
         }
         m.putIntegerArrayList("lteRsrpThresholds", lteRsrpThresholds);
+        ArrayList<Integer> wcdmaRscpThresholds = new ArrayList<Integer>();
+        for (int value : mWcdmaRscpThresholds) {
+            wcdmaRscpThresholds.add(value);
+        }
+        m.putIntegerArrayList("wcdmaRscpThresholds", wcdmaRscpThresholds);
     }
 
     /**
@@ -1077,6 +1329,16 @@
     }
 
     /**
+     * Gets the default threshold array for determining the display level of WCDMA signal bar.
+     *
+     * @return int array for determining the display level.
+     */
+    private int[] getDefaultWcdmaRscpThresholds() {
+        return CarrierConfigManager.getDefaultConfig().getIntArray(
+                CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY);
+    }
+
+    /**
      * log
      */
     private static void log(String s) {
diff --git a/android/telephony/SmsManager.java b/android/telephony/SmsManager.java
index 5d88cf0..38bc640 100644
--- a/android/telephony/SmsManager.java
+++ b/android/telephony/SmsManager.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressAutoDoc;
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
@@ -343,15 +344,16 @@
      * applications. Intended for internal carrier use only.
      * </p>
      *
-     * <p>Requires Permission:
-     * {@link android.Manifest.permission#SEND_SMS} and
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
-     * privileges.
-     * </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}).
      *
      * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
      */
     @SystemApi
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
             android.Manifest.permission.MODIFY_PHONE_STATE,
             android.Manifest.permission.SEND_SMS
@@ -395,6 +397,112 @@
     }
 
     /**
+     * Send a text based SMS with messaging options.
+     *
+     * @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>
+     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+     *  the extra "errorCode" containing a radio technology specific value,
+     *  generally only useful for troubleshooting.<br>
+     *  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").
+     * @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}
+     */
+    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,
+            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) {
+            throw new IllegalArgumentException("Invalid priority");
+        }
+
+        if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+            throw new IllegalArgumentException("Invalid validity period");
+        }
+
+        try {
+             ISms iccISms = getISmsServiceOrThrow();
+            if (iccISms != null) {
+                iccISms.sendTextForSubscriberWithOptions(getSubscriptionId(),
+                        ActivityThread.currentPackageName(), destinationAddress, scAddress, text,
+                        sentIntent, deliveryIntent, persistMessage,  priority, expectMore,
+                        validityPeriod);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Send a text based SMS without writing it into the SMS Provider.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
+     * privileges.
+     * </p>
+     *
+     * @see #sendTextMessage(String, String, String, PendingIntent,
+     * PendingIntent, int, boolean, int)
+     * @hide
+     */
+    public void sendTextMessageWithoutPersisting(
+            String destinationAddress, String scAddress, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent, int priority,
+            boolean expectMore, int validityPeriod) {
+        sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+                false /* persistMessage */, priority, expectMore, validityPeriod);
+    }
+
+    /**
+     *
      * Inject an SMS PDU into the android application framework.
      *
      * <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
@@ -552,6 +660,140 @@
     }
 
     /**
+     * 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>
+     *
+     * @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>
+     *   For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
+     *   the extra "errorCode" containing a radio technology specific value,
+     *   generally only useful for troubleshooting.<br>
+     *   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 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}
+     */
+    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*/);
+    }
+
+    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) {
+            throw new IllegalArgumentException("Invalid priority");
+        }
+
+        if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+            throw new IllegalArgumentException("Invalid validity period");
+        }
+
+        if (parts.size() > 1) {
+            try {
+                 ISms iccISms = getISmsServiceOrThrow();
+                if (iccISms != null) {
+                    iccISms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(),
+                            ActivityThread.currentPackageName(), destinationAddress, scAddress,
+                            parts, sentIntents, deliveryIntents, persistMessage, priority,
+                            expectMore, validityPeriod);
+                }
+            } catch (RemoteException ex) {
+                // ignore it
+            }
+        } 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 multi-part text based SMS without writing it into the SMS Provider.
+     *
+     * <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, int, boolean, int)
+     * @hide
+     **/
+    public void sendMultipartTextMessageWithoutPersisting(
+            String destinationAddress, String scAddress, List<String> parts,
+            List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
+            int priority, boolean expectMore, int validityPeriod) {
+        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+                deliveryIntents, false /* 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
@@ -1014,7 +1256,7 @@
      *   <code>getAllMessagesFromIcc</code>
      * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
      */
-    private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
+    private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
         ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
         if (records != null) {
             int count = records.size();
@@ -1022,7 +1264,8 @@
                 SmsRawData data = records.get(i);
                 // List contains all records, including "free" records (null)
                 if (data != null) {
-                    SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
+                    SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes(),
+                            getSubscriptionId());
                     if (sms != null) {
                         messages.add(sms);
                     }
@@ -1134,7 +1377,11 @@
 
     // SMS send failure result codes
 
-    /** No error. {@hide}*/
+    /**
+     * No error.
+     * @hide
+     */
+    @SystemApi
     static public final int RESULT_ERROR_NONE    = 0;
     /** Generic failure cause */
     static public final int RESULT_ERROR_GENERIC_FAILURE    = 1;
@@ -1146,12 +1393,113 @@
     static public final int RESULT_ERROR_NO_SERVICE         = 4;
     /** Failed because we reached the sending queue limit. */
     static public final int RESULT_ERROR_LIMIT_EXCEEDED     = 5;
-    /** Failed because FDN is enabled. {@hide} */
+    /**
+     * Failed because FDN is enabled.
+     * @hide
+     */
+    @SystemApi
     static public final int RESULT_ERROR_FDN_CHECK_FAILURE  = 6;
     /** Failed because user denied the sending of this short code. */
     static public final int RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 7;
     /** Failed because the user has denied this app ever send premium short codes. */
     static public final int RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 8;
+    /**
+     * Failed because the radio was not available
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_RADIO_NOT_AVAILABLE = 9;
+    /**
+     * Failed because of network rejection
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_NETWORK_REJECT = 10;
+    /**
+     * Failed because of invalid arguments
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_INVALID_ARGUMENTS = 11;
+    /**
+     * Failed because of an invalid state
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_INVALID_STATE = 12;
+    /**
+     * Failed because there is no memory
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_NO_MEMORY = 13;
+    /**
+     * Failed because the sms format is not valid
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_INVALID_SMS_FORMAT = 14;
+    /**
+     * Failed because of a system error
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_SYSTEM_ERROR = 15;
+    /**
+     * Failed because of a modem error
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_MODEM_ERROR = 16;
+    /**
+     * Failed because of a network error
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_NETWORK_ERROR = 17;
+    /**
+     * Failed because of an encoding error
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_ENCODING_ERROR = 18;
+    /**
+     * Failed because of an invalid smsc address
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_INVALID_SMSC_ADDRESS = 19;
+    /**
+     * Failed because the operation is not allowed
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_OPERATION_NOT_ALLOWED = 20;
+    /**
+     * Failed because of an internal error
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_INTERNAL_ERROR = 21;
+    /**
+     * Failed because there are no resources
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_NO_RESOURCES = 22;
+    /**
+     * Failed because the operation was cancelled
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_CANCELLED = 23;
+    /**
+     * Failed because the request is not supported
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_REQUEST_NOT_SUPPORTED = 24;
+
 
     static private final String PHONE_PACKAGE_NAME = "com.android.phone";
 
diff --git a/android/telephony/SmsMessage.java b/android/telephony/SmsMessage.java
index a6dbc06..57f89e3 100644
--- a/android/telephony/SmsMessage.java
+++ b/android/telephony/SmsMessage.java
@@ -18,8 +18,8 @@
 
 import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
 
+import android.annotation.Nullable;
 import android.annotation.StringDef;
-import android.app.PendingIntent;
 import android.content.res.Resources;
 import android.os.Binder;
 import android.text.TextUtils;
@@ -207,7 +207,10 @@
      */
     public static SmsMessage createFromPdu(byte[] pdu, String format) {
         SmsMessageBase wrappedMessage;
-
+        if (pdu == null) {
+            Rlog.i(LOG_TAG, "createFromPdu(): pdu is null");
+            return null;
+        }
         if (SmsConstants.FORMAT_3GPP2.equals(format)) {
             wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
         } else if (SmsConstants.FORMAT_3GPP.equals(format)) {
@@ -277,6 +280,31 @@
     }
 
     /**
+     * Create an SmsMessage from an SMS EF record.
+     *
+     * @param index Index of SMS record. This should be index in ArrayList
+     *              returned by SmsManager.getAllMessagesFromSim + 1.
+     * @param data Record data.
+     * @param subId Subscription Id of the SMS
+     * @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;
+    }
+
+    /**
      * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
      * length in bytes (not hex chars) less the SMSC header
      *
@@ -544,8 +572,16 @@
 
     /**
      * Returns the originating address (sender) of this SMS message in String
-     * form or null if unavailable
+     * 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();
     }
@@ -828,6 +864,7 @@
          int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(subId);
          return (PHONE_TYPE_CDMA == activePhone);
    }
+
     /**
      * Decide if the carrier supports long SMS.
      * {@hide}
diff --git a/android/telephony/SubscriptionInfo.java b/android/telephony/SubscriptionInfo.java
index 38408fe..936505c 100644
--- a/android/telephony/SubscriptionInfo.java
+++ b/android/telephony/SubscriptionInfo.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -34,6 +35,8 @@
 import android.util.DisplayMetrics;
 
 import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A Parcelable class for Subscription Information.
@@ -332,12 +335,7 @@
         return this.mCountryIso;
     }
 
-    /**
-     * @return whether the subscription is an embedded one.
-     * @hide
-     *
-     * TODO(b/35851809): Make this public.
-     */
+    /** @return whether the subscription is an eUICC one. */
     public boolean isEmbedded() {
         return this.mIsEmbedded;
     }
@@ -351,9 +349,9 @@
      * @return whether the app is authorized to manage this subscription per its metadata.
      * @throws UnsupportedOperationException if this subscription is not embedded.
      * @hide
-     *
-     * TODO(b/35851809): Make this public.
+     * @deprecated - Do not use.
      */
+    @Deprecated
     public boolean canManageSubscription(Context context) {
         return canManageSubscription(context, context.getPackageName());
     }
@@ -367,7 +365,9 @@
      * @return whether the app is authorized to manage this subscription per its metadata.
      * @throws UnsupportedOperationException if this subscription is not embedded.
      * @hide
+     * @deprecated - Do not use.
      */
+    @Deprecated
     public boolean canManageSubscription(Context context, String packageName) {
         if (!isEmbedded()) {
             throw new UnsupportedOperationException("Not an embedded subscription");
@@ -395,14 +395,14 @@
      * @return the {@link UiccAccessRule}s dictating who is authorized to manage this subscription.
      * @throws UnsupportedOperationException if this subscription is not embedded.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
-    public @Nullable UiccAccessRule[] getAccessRules() {
+    @SystemApi
+    public @Nullable List<UiccAccessRule> getAccessRules() {
         if (!isEmbedded()) {
             throw new UnsupportedOperationException("Not an embedded subscription");
         }
-        return mAccessRules;
+        if (mAccessRules == null) return null;
+        return Arrays.asList(mAccessRules);
     }
 
     /**
diff --git a/android/telephony/SubscriptionManager.java b/android/telephony/SubscriptionManager.java
index debf43d..754fe68 100644
--- a/android/telephony/SubscriptionManager.java
+++ b/android/telephony/SubscriptionManager.java
@@ -25,11 +25,13 @@
 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.BroadcastOptions;
 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;
@@ -41,7 +43,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
 import android.util.DisplayMetrics;
 
 import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -58,9 +59,6 @@
 /**
  * SubscriptionManager is the application interface to SubscriptionController
  * and provides information about the current Telephony Subscriptions.
- * <p>
- * All SDK public methods require android.Manifest.permission.READ_PHONE_STATE unless otherwise
- * specified.
  */
 @SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
 public class SubscriptionManager {
@@ -507,7 +505,7 @@
     public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
 
     private final Context mContext;
-    private INetworkPolicyManager mNetworkPolicy;
+    private volatile INetworkPolicyManager mNetworkPolicy;
 
     /**
      * A listener class for monitoring changes to {@link SubscriptionInfo} records.
@@ -612,9 +610,9 @@
      *                 onSubscriptionsChanged overridden.
      */
     public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
-        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         if (DBG) {
-            logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
+            logd("register OnSubscriptionsChangedListener pkgName=" + pkgName
                     + " listener=" + listener);
         }
         try {
@@ -623,7 +621,7 @@
             ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
                     "telephony.registry"));
             if (tr != null) {
-                tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
+                tr.addOnSubscriptionsChangedListener(pkgName, listener.callback);
             }
         } catch (RemoteException ex) {
             // Should not happen
@@ -659,9 +657,15 @@
     /**
      * 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.
      */
+    @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)) {
@@ -715,9 +719,16 @@
 
     /**
      * 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
      */
+    @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)) {
@@ -769,6 +780,11 @@
      * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted
      * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
      *
+     * <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 {@link SubscriptionInfo} records available on the device.
      * <ul>
      * <li>
@@ -785,6 +801,8 @@
      * </li>
      * </ul>
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
 
@@ -822,10 +840,13 @@
      * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
      * then by {@link SubscriptionInfo#getSubscriptionId}.
      * </ul>
-     * @hide
      *
-     * TODO(b/35851809): Make this a SystemApi.
+     * <p>
+     * Permissions android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required
+     * for #getAvailableSubscriptionInfoList to be invoked.
+     * @hide
      */
+    @SystemApi
     public List<SubscriptionInfo> getAvailableSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
 
@@ -863,9 +884,6 @@
      * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
      * then by {@link SubscriptionInfo#getSubscriptionId}.
      * </ul>
-     * @hide
-     *
-     * TODO(b/35851809): Make this public.
      */
     public List<SubscriptionInfo> getAccessibleSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
@@ -891,9 +909,8 @@
      *
      * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
+    @SystemApi
     public void requestEmbeddedSubscriptionInfoListRefresh() {
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
@@ -928,10 +945,18 @@
     }
 
     /**
+     *
+     * 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, the count will include
+     * only those subscriptions accessible to the caller.
+     *
      * @return the current number of active subscriptions. There is no guarantee the value
      * returned by this method will be the same as the length of the list returned by
      * {@link #getActiveSubscriptionInfoList}.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public int getActiveSubscriptionInfoCount() {
         int result = 0;
 
@@ -1769,7 +1794,7 @@
             @DurationMillisLong long timeoutMillis) {
         try {
             final int overrideValue = overrideUnmetered ? OVERRIDE_UNMETERED : 0;
-            mNetworkPolicy.setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue,
+            getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue,
                     timeoutMillis, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1803,7 +1828,7 @@
             @DurationMillisLong long timeoutMillis) {
         try {
             final int overrideValue = overrideCongested ? OVERRIDE_CONGESTED : 0;
-            mNetworkPolicy.setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue,
+            getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue,
                     timeoutMillis, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1892,4 +1917,53 @@
         options.setTemporaryAppWhitelistDuration(TimeUnit.MINUTES.toMillis(1));
         mContext.sendBroadcast(intent, null, options.toBundle());
     }
+
+    /**
+     * 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
+     * {@code SubscriptionInfo#isEmbedded} returns true).
+     *
+     * @param info The subscription to check.
+     * @return whether the app is authorized to manage this subscription per its metadata.
+     * @throws IllegalArgumentException if this subscription is not embedded.
+     */
+    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).
+     *
+     * @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.
+     * @throws IllegalArgumentException if this subscription is not embedded.
+     * @hide
+     */
+    public boolean canManageSubscription(SubscriptionInfo info, String packageName) {
+        if (!info.isEmbedded()) {
+            throw new IllegalArgumentException("Not an embedded subscription");
+        }
+        if (info.getAccessRules() == null) {
+            return false;
+        }
+        PackageManager packageManager = mContext.getPackageManager();
+        PackageInfo packageInfo;
+        try {
+            packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalArgumentException("Unknown package: " + packageName, e);
+        }
+        for (UiccAccessRule rule : info.getAccessRules()) {
+            if (rule.getCarrierPrivilegeStatus(packageInfo)
+                    == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/android/telephony/SubscriptionPlan.java b/android/telephony/SubscriptionPlan.java
index 9411652..4ffb70b 100644
--- a/android/telephony/SubscriptionPlan.java
+++ b/android/telephony/SubscriptionPlan.java
@@ -34,6 +34,7 @@
 import java.time.Period;
 import java.time.ZonedDateTime;
 import java.util.Iterator;
+import java.util.Objects;
 
 /**
  * Description of a billing relationship plan between a carrier and a specific
@@ -124,6 +125,27 @@
                 .append("}").toString();
     }
 
+    @Override
+    public int hashCode() {
+        return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior,
+                dataUsageBytes, dataUsageTime);
+    }
+
+    @Override
+    public boolean equals(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;
+        }
+        return false;
+    }
+
     public static final Parcelable.Creator<SubscriptionPlan> CREATOR = new Parcelable.Creator<SubscriptionPlan>() {
         @Override
         public SubscriptionPlan createFromParcel(Parcel source) {
diff --git a/android/telephony/TelephonyManager.java b/android/telephony/TelephonyManager.java
index 0a6d960..e15d35b 100644
--- a/android/telephony/TelephonyManager.java
+++ b/android/telephony/TelephonyManager.java
@@ -23,9 +23,11 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+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.ActivityThread;
 import android.app.PendingIntent;
@@ -34,10 +36,12 @@
 import android.net.ConnectivityManager;
 import android.net.NetworkStats;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.BatteryStats;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -48,12 +52,13 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.VisualVoicemailService.VisualVoicemailTask;
-import android.telephony.ims.feature.ImsFeature;
+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.stub.ImsRegistrationImplBase;
 import android.util.Log;
 
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRcsFeature;
-import com.android.ims.internal.IImsRegistration;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.ITelecomService;
@@ -72,6 +77,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.Executor;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -107,6 +113,13 @@
             BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY;
 
     /**
+     * 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
@@ -203,6 +216,10 @@
         return ActivityThread.currentOpPackageName();
     }
 
+    private boolean isSystemProcess() {
+        return Process.myUid() == Process.SYSTEM_UID;
+    }
+
     /**
      * Returns the multi SIM variant
      * Returns DSDS for Dual SIM Dual Standby
@@ -903,6 +920,61 @@
     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";
+
+    /**
+     * 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 */
 
     /**
@@ -996,6 +1068,13 @@
     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>
@@ -1023,7 +1102,7 @@
 
     /**
      * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
-     * the updated carrier id {@link TelephonyManager#getAndroidCarrierIdForSubscription()} of
+     * the updated carrier id {@link TelephonyManager#getSimCarrierId()} of
      * the current subscription.
      * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
      * the carrier cannot be identified.
@@ -1033,7 +1112,7 @@
     /**
      * An string extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which
      * indicates the updated carrier name of the current subscription.
-     * {@see TelephonyManager#getSubscriptionCarrierName()}
+     * {@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).
      */
@@ -1055,7 +1134,11 @@
      * 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: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getDeviceSoftwareVersion() {
         return getDeviceSoftwareVersion(getSlotIndex());
@@ -1087,10 +1170,14 @@
      * 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>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
      * MEID for CDMA.
      */
     @Deprecated
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getDeviceId() {
         try {
@@ -1109,12 +1196,16 @@
      * 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>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @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 // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getDeviceId(int slotIndex) {
         // FIXME this assumes phoneId == slotIndex
@@ -1133,7 +1224,11 @@
     /**
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getImei() {
         return getImei(getSlotIndex());
@@ -1143,8 +1238,12 @@
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @param slotIndex of which IMEI is returned
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getImei(int slotIndex) {
         ITelephony telephony = getITelephony();
@@ -1161,7 +1260,11 @@
 
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getMeid() {
         return getMeid(getSlotIndex());
@@ -1170,8 +1273,12 @@
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @param slotIndex of which MEID is returned
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getMeid(int slotIndex) {
         ITelephony telephony = getITelephony();
@@ -1188,10 +1295,11 @@
 
     /**
      * Returns the Network Access Identifier (NAI). Return null if NAI is not available.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getNai() {
         return getNaiBySubscriberId(getSubId());
@@ -1688,10 +1796,17 @@
      * 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)
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @WorkerThread
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public PersistableBundle getCarrierConfig() {
@@ -1776,47 +1891,52 @@
     /*
      * When adding a network type to the list below, make sure to add the correct icon to
      * MobileSignalController.mapIconSets().
+     * Do not add negative types.
      */
     /** Network type is unknown */
-    public static final int NETWORK_TYPE_UNKNOWN = 0;
+    public static final int NETWORK_TYPE_UNKNOWN = TelephonyProtoEnums.NETWORK_TYPE_UNKNOWN; // = 0.
     /** Current network is GPRS */
-    public static final int NETWORK_TYPE_GPRS = 1;
+    public static final int NETWORK_TYPE_GPRS = TelephonyProtoEnums.NETWORK_TYPE_GPRS; // = 1.
     /** Current network is EDGE */
-    public static final int NETWORK_TYPE_EDGE = 2;
+    public static final int NETWORK_TYPE_EDGE = TelephonyProtoEnums.NETWORK_TYPE_EDGE; // = 2.
     /** Current network is UMTS */
-    public static final int NETWORK_TYPE_UMTS = 3;
+    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 = 4;
+    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 = 5;
+    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 = 6;
+    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 = 7;
+    public static final int NETWORK_TYPE_1xRTT = TelephonyProtoEnums.NETWORK_TYPE_1XRTT; // = 7.
     /** Current network is HSDPA */
-    public static final int NETWORK_TYPE_HSDPA = 8;
+    public static final int NETWORK_TYPE_HSDPA = TelephonyProtoEnums.NETWORK_TYPE_HSDPA; // = 8.
     /** Current network is HSUPA */
-    public static final int NETWORK_TYPE_HSUPA = 9;
+    public static final int NETWORK_TYPE_HSUPA = TelephonyProtoEnums.NETWORK_TYPE_HSUPA; // = 9.
     /** Current network is HSPA */
-    public static final int NETWORK_TYPE_HSPA = 10;
+    public static final int NETWORK_TYPE_HSPA = TelephonyProtoEnums.NETWORK_TYPE_HSPA; // = 10.
     /** Current network is iDen */
-    public static final int NETWORK_TYPE_IDEN = 11;
+    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 = 12;
+    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 = 13;
+    public static final int NETWORK_TYPE_LTE = TelephonyProtoEnums.NETWORK_TYPE_LTE; // = 13.
     /** Current network is eHRPD */
-    public static final int NETWORK_TYPE_EHRPD = 14;
+    public static final int NETWORK_TYPE_EHRPD = TelephonyProtoEnums.NETWORK_TYPE_EHRPD; // = 14.
     /** Current network is HSPA+ */
-    public static final int NETWORK_TYPE_HSPAP = 15;
+    public static final int NETWORK_TYPE_HSPAP = TelephonyProtoEnums.NETWORK_TYPE_HSPAP; // = 15.
     /** Current network is GSM */
-    public static final int NETWORK_TYPE_GSM = 16;
+    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 = 17;
+    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 = 18;
+    public static final int NETWORK_TYPE_IWLAN = TelephonyProtoEnums.NETWORK_TYPE_IWLAN; // = 18.
     /** Current network is LTE_CA {@hide} */
-    public static final int NETWORK_TYPE_LTE_CA = 19;
+    public static final int NETWORK_TYPE_LTE_CA = TelephonyProtoEnums.NETWORK_TYPE_LTE_CA; // = 19.
+
+    /** Max network type number. Update as new types are added. Don't add negative types. {@hide} */
+    public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_LTE_CA;
     /**
      * @return the NETWORK_TYPE_xxxx for current data connection.
      */
@@ -1890,6 +2010,9 @@
      * 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#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @return the network type
      *
      * @see #NETWORK_TYPE_UNKNOWN
@@ -1909,6 +2032,7 @@
      * @see #NETWORK_TYPE_EHRPD
      * @see #NETWORK_TYPE_HSPAP
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public int getDataNetworkType() {
         return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
@@ -1943,7 +2067,11 @@
 
     /**
      * Returns the NETWORK_TYPE_xxxx for voice
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public int getVoiceNetworkType() {
         return getVoiceNetworkType(getSubId());
@@ -2529,7 +2657,11 @@
     /**
      * Returns the serial number of the SIM, if applicable. 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}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getSimSerialNumber() {
          return getSimSerialNumber(getSubId());
@@ -2654,7 +2786,11 @@
     /**
      * Returns the unique subscriber ID, for example, the IMSI 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}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getSubscriberId() {
         return getSubscriberId(getSubId());
@@ -2697,18 +2833,17 @@
      * @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. The system will return a null when no key was
-     *         found, and the carrier does not require a key. The system will throw the following
-     *         exceptions:
-     *         1. IllegalArgumentException when an invalid key is sent.
-     *         2. RuntimeException if the key is required but not found; and also if there was an
-     *         internal exception.
+     *         found, and the carrier does not require a key. The system will throw
+     *         IllegalArgumentException when an invalid key is sent or when key is required but
+     *         not found.
      * @hide
      */
     public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
             if (info == null) {
-                throw new RuntimeException("IMSI error: Subscriber Info is 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) {
@@ -2716,20 +2851,18 @@
             }
             ImsiEncryptionInfo imsiEncryptionInfo = info.getCarrierInfoForImsiEncryption(
                     subId, keyType, mContext.getOpPackageName());
-            if (imsiEncryptionInfo  == null
-                    && isImsiEncryptionRequired(subId, keyType)) {
+            if (imsiEncryptionInfo == null && isImsiEncryptionRequired(subId, keyType)) {
                 Rlog.e(TAG, "IMSI error: key is required but not found");
-                throw new RuntimeException("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);
-            throw new RuntimeException("IMSI error: Remote Exception");
         } catch (NullPointerException ex) {
             // This could happen before phone restarts due to crashing
             Rlog.e(TAG, "getCarrierInfoForImsiEncryption NullPointerException" + ex);
-            throw new RuntimeException("IMSI error: Null Pointer exception");
         }
+        return null;
     }
 
     /**
@@ -2745,17 +2878,19 @@
         try {
             IPhoneSubInfo info = getSubscriberInfo();
             if (info == null) {
-                throw new RuntimeException("IMSI error: Subscriber Info is null");
+                Rlog.e(TAG, "IMSI error: Subscriber Info is null");
+                if (!isSystemProcess()) {
+                    throw new RuntimeException("IMSI error: Subscriber Info is null");
+                }
+                return;
             }
             int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
             info.resetCarrierKeysForImsiEncryption(subId, mContext.getOpPackageName());
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex);
-            throw new RuntimeException("IMSI error: Remote Exception");
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing
-            Rlog.e(TAG, "getCarrierInfoForImsiEncryption NullPointerException" + ex);
-            throw new RuntimeException("IMSI error: Null Pointer exception");
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
         }
     }
 
@@ -2822,7 +2957,11 @@
     /**
      * 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}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getGroupIdLevel1() {
         try {
@@ -2863,9 +3002,15 @@
     /**
      * Returns the phone number string for line 1, for example, the MSISDN
      * for a GSM phone. Return null if it is unavailable.
-     * <p>
-     * The default SMS app can also use this.
+     *
+     * <p>Requires Permission:
+     *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE},
+     *     {@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}).
      */
+    @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,
@@ -2920,8 +3065,7 @@
      * 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 #hasCarrierPrivileges
+     * <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
@@ -2937,8 +3081,7 @@
      * 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 #hasCarrierPrivileges
+     * <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
@@ -3057,7 +3200,11 @@
 
     /**
      * 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}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getVoiceMailNumber() {
         return getVoiceMailNumber(getSubId());
@@ -3118,8 +3265,7 @@
     /**
      * Sets the voice mail number.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param alphaTag The alpha tag to display.
      * @param number The voicemail number.
@@ -3131,8 +3277,7 @@
     /**
      * Sets the voicemail number for the given subscriber.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      * @param alphaTag The alpha tag to display.
@@ -3153,9 +3298,9 @@
     /**
      * 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, or
-     * has permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
-     * @see #hasCarrierPrivileges
+     * <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
@@ -3218,11 +3363,15 @@
      * 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
      */
     @Nullable
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getVisualVoicemailPackageName() {
         try {
@@ -3465,15 +3614,14 @@
       * Sets the voice activation state
       *
       * <p>Requires Permission:
-      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-      * Or the calling app has carrier privileges.
+      * {@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
-      * @see #hasCarrierPrivileges
       * @hide
       */
     @SystemApi
@@ -3486,8 +3634,8 @@
      * Sets the voice activation state for the given subscriber.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@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.
@@ -3495,7 +3643,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATING
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -3513,8 +3660,8 @@
      * Sets the data activation state
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@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
@@ -3522,7 +3669,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @SystemApi
@@ -3535,8 +3681,8 @@
      * Sets the data activation state for the given subscriber.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@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.
@@ -3545,7 +3691,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -3563,15 +3708,14 @@
      * Returns the voice activation state
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@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
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @SystemApi
@@ -3584,8 +3728,8 @@
      * 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 the calling app has carrier privileges.
+     * {@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.
      *
@@ -3594,7 +3738,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATING
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -3613,8 +3756,8 @@
      * Returns the data activation state
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@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
@@ -3622,7 +3765,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @SystemApi
@@ -3635,8 +3777,8 @@
      * 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 the calling app has carrier privileges.
+     * {@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.
      *
@@ -3646,7 +3788,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -3694,7 +3835,11 @@
     /**
      * 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}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getVoiceMailAlphaTag() {
         return getVoiceMailAlphaTag(getSubId());
@@ -3723,27 +3868,29 @@
     }
 
     /**
-     * Send the special dialer code. The IPC caller must be the current default dialer or has
-     * carrier privileges.
-     * @see #hasCarrierPrivileges
+     * 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 IllegalStateException if telephony service is unavailable.
      */
     public void sendDialerSpecialCode(String inputCode) {
         try {
             final ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                if (!isSystemProcess()) {
+                    throw new RuntimeException("Telephony service unavailable");
+                }
+                return;
+            }
             telephony.sendDialerSpecialCode(mContext.getOpPackageName(), inputCode);
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
-            ex.rethrowFromSystemServer();
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing
-            throw new IllegalStateException("Telephony service unavailable");
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
         }
     }
 
@@ -3997,6 +4144,9 @@
      * To unregister a listener, pass the listener object and set the
      * events argument to
      * {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0).
+     * 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.
      *
      * @param listener The {@link PhoneStateListener} object to register
      *                 (or unregister)
@@ -4250,8 +4400,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@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.
@@ -4268,8 +4418,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@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).
@@ -4285,8 +4435,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@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.
@@ -4311,8 +4461,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHC command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@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 retruned by a successful
      *            iccOpenLogicalChannel.
@@ -4328,8 +4478,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHC command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@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 retruned by a successful
@@ -4354,8 +4504,8 @@
      * Input parameters equivalent to TS 27.007 AT+CGLA command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@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.
@@ -4381,8 +4531,8 @@
      * Input parameters equivalent to TS 27.007 AT+CGLA command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@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
@@ -4417,8 +4567,8 @@
      * Input parameters equivalent to TS 27.007 AT+CSIM command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@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.
@@ -4442,8 +4592,8 @@
      * Input parameters equivalent to TS 27.007 AT+CSIM command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@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.
@@ -4462,7 +4612,7 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.iccTransmitApduBasicChannel(subId, cla,
+                return telephony.iccTransmitApduBasicChannel(subId, getOpPackageName(), cla,
                     instruction, p1, p2, p3, data);
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
@@ -4474,8 +4624,8 @@
      * 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 the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@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
@@ -4494,8 +4644,8 @@
      * 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 the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@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
@@ -4523,8 +4673,8 @@
      * Send ENVELOPE to the SIM and return the response.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@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
@@ -4541,8 +4691,8 @@
      * Send ENVELOPE to the SIM and return the response.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@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
@@ -4567,10 +4717,10 @@
     /**
      * 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 the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <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.
@@ -4593,10 +4743,10 @@
     /**
      * 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 the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <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.
@@ -4620,10 +4770,10 @@
     /**
      * 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 the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <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.
@@ -4647,10 +4797,10 @@
      * 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 the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <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 resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
      * @return true on success; false on any failure.
@@ -4745,7 +4895,7 @@
     }
 
     /**
-     * Sets the telephony property with the value specified.
+     * Sets a per-phone telephony property with the value specified.
      *
      * @hide
      */
@@ -4789,12 +4939,24 @@
             return;
         }
 
-        Rlog.d(TAG, "setTelephonyProperty: success phoneId=" + phoneId +
-                " property=" + property + " value: " + value + " propVal=" + propVal);
         SystemProperties.set(property, propVal);
     }
 
     /**
+     * Sets a global telephony property with the value specified.
+     *
+     * @hide
+     */
+    public static void setTelephonyProperty(String property, String value) {
+        if (value == null) {
+            value = "";
+        }
+        Rlog.d(TAG, "setTelephonyProperty: success" + " property=" +
+                property + " value: " + value);
+        SystemProperties.set(property, value);
+    }
+
+    /**
      * 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
@@ -4853,10 +5015,10 @@
         String v = android.provider.Settings.Global.getString(cr, name);
 
         if (index == Integer.MAX_VALUE) {
-            throw new RuntimeException("putIntAtIndex index == MAX_VALUE index=" + index);
+            throw new IllegalArgumentException("putIntAtIndex index == MAX_VALUE index=" + index);
         }
         if (index < 0) {
-            throw new RuntimeException("putIntAtIndex index < 0 index=" + index);
+            throw new IllegalArgumentException("putIntAtIndex index < 0 index=" + index);
         }
         if (v != null) {
             valArray = v.split(",");
@@ -4883,7 +5045,7 @@
     }
 
     /**
-     * Gets the telephony property.
+     * Gets a per-phone telephony property.
      *
      * @hide
      */
@@ -4899,6 +5061,19 @@
         return propVal == null ? defaultVal : propVal;
     }
 
+    /**
+     * Gets a global telephony property.
+     *
+     * See also getTelephonyProperty(phoneId, property, defaultVal). Most telephony properties are
+     * per-phone.
+     *
+     * @hide
+     */
+    public static String getTelephonyProperty(String property, String defaultVal) {
+        String propVal = SystemProperties.get(property);
+        return propVal == null ? defaultVal : propVal;
+    }
+
     /** @hide */
     public int getSimCount() {
         // FIXME Need to get it from Telephony Dev Controller when that gets implemented!
@@ -4951,28 +5126,6 @@
         }
     }
 
-    /**
-     * Returns the response of ISIM Authetification through RIL.
-     * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
-     * @return the response of ISIM Authetification, or null if not available
-     * @hide
-     * @deprecated
-     * @see getIccAuthentication with appType=PhoneConstants.APPTYPE_ISIM
-     */
-    public String getIsimChallengeResponse(String nonce){
-        try {
-            IPhoneSubInfo info = getSubscriberInfo();
-            if (info == null)
-                return null;
-            return info.getIsimChallengeResponse(nonce);
-        } catch (RemoteException ex) {
-            return null;
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing
-            return null;
-        }
-    }
-
     // ICC SIM Application Types
     /** UICC application type is SIM */
     public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM;
@@ -4995,8 +5148,8 @@
      * Returns the response of authentication for the default subscription.
      * Returns null if the authentication hasn't been successful
      *
-     * <p>Requires that the calling app has carrier privileges or READ_PRIVILEGED_PHONE_STATE
-     * permission.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param appType the icc application type, like {@link #APPTYPE_USIM}
      * @param authType the authentication type, {@link #AUTHTYPE_EAP_AKA} or
@@ -5004,9 +5157,10 @@
      * @param data authentication challenge data, base64 encoded.
      * See 3GPP TS 31.102 7.1.2 for more details.
      * @return the response of authentication, or null if not available
-     *
-     * @see #hasCarrierPrivileges
      */
+    // 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.
     public String getIccAuthentication(int appType, int authType, String data) {
         return getIccAuthentication(getSubId(), appType, authType, data);
     }
@@ -5015,7 +5169,7 @@
      * Returns the response of USIM Authentication for specified subId.
      * Returns null if the authentication hasn't been successful
      *
-     * <p>Requires that the calling app has carrier privileges.
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId subscription ID used for authentication
      * @param appType the icc application type, like {@link #APPTYPE_USIM}
@@ -5024,8 +5178,6 @@
      * @param data authentication challenge data, base64 encoded.
      * See 3GPP TS 31.102 7.1.2 for more details.
      * @return the response of authentication, or null if not available
-     *
-     * @see #hasCarrierPrivileges
      * @hide
      */
     public String getIccAuthentication(int subId, int appType, int authType, String data) {
@@ -5046,8 +5198,12 @@
      * 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
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String[] getForbiddenPlmns() {
       return getForbiddenPlmns(getSubId(), APPTYPE_USIM);
@@ -5068,7 +5224,7 @@
             ITelephony telephony = getITelephony();
             if (telephony == null)
                 return null;
-            return telephony.getForbiddenPlmns(subId, appType);
+            return telephony.getForbiddenPlmns(subId, appType, mContext.getOpPackageName());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -5094,57 +5250,60 @@
         }
     }
 
-    /** @hide */
-    @IntDef({ImsFeature.EMERGENCY_MMTEL, ImsFeature.MMTEL, ImsFeature.RCS})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Feature {}
-
     /**
-     * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id and MMTel
-     * feature or {@link null} if the service is not available. If an MMTelFeature is available, the
-     * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
-     * @param slotIndex The SIM slot that we are requesting the {@link IImsMMTelFeature} for.
-     * @param callback Listener that will send updates to ImsManager when there are updates to
-     * ImsServiceController.
-     * @return {@link IImsMMTelFeature} interface for the feature specified or {@code null} if
-     * it is unavailable.
+     * Enables IMS for the framework. This will trigger IMS registration and ImsFeature capability
+     * status updates, if not already enabled.
      * @hide
      */
-    public @Nullable IImsMMTelFeature getImsMMTelFeatureAndListen(int slotIndex,
-            IImsServiceFeatureCallback callback) {
+    public void enableIms(int slotId) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getMMTelFeatureAndListen(slotIndex, callback);
+                telephony.enableIms(slotId);
             }
         } catch (RemoteException e) {
-            Rlog.e(TAG, "getImsMMTelFeatureAndListen, RemoteException: "
+            Rlog.e(TAG, "enableIms, RemoteException: "
                     + e.getMessage());
         }
-        return null;
     }
 
     /**
-     * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id and MMTel
-     * feature for emergency calling or {@link null} if the service is not available. If an
-     * MMTelFeature is available, the {@link IImsServiceFeatureCallback} callback is registered as a
-     * listener for feature updates.
-     * @param slotIndex The SIM slot that we are requesting the {@link IImsMMTelFeature} for.
+     * 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());
+        }
+    }
+
+    /**
+     * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id and MMTel
+     * feature or {@link null} if the service is not available. If an MMTelFeature is available, the
+     * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
+     * @param slotIndex The SIM slot that we are requesting the {@link IImsMmTelFeature} for.
      * @param callback Listener that will send updates to ImsManager when there are updates to
      * ImsServiceController.
-     * @return {@link IImsMMTelFeature} interface for the feature specified or {@code null} if
+     * @return {@link IImsMmTelFeature} interface for the feature specified or {@code null} if
      * it is unavailable.
      * @hide
      */
-    public @Nullable IImsMMTelFeature getImsEmergencyMMTelFeatureAndListen(int slotIndex,
+    public @Nullable IImsMmTelFeature getImsMmTelFeatureAndListen(int slotIndex,
             IImsServiceFeatureCallback callback) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getEmergencyMMTelFeatureAndListen(slotIndex, callback);
+                return telephony.getMmTelFeatureAndListen(slotIndex, callback);
             }
         } catch (RemoteException e) {
-            Rlog.e(TAG, "getImsEmergencyMMTelFeatureAndListen, RemoteException: "
+            Rlog.e(TAG, "getImsMmTelFeatureAndListen, RemoteException: "
                     + e.getMessage());
         }
         return null;
@@ -5196,6 +5355,42 @@
     }
 
     /**
+     * @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
+     */
+    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;
+    }
+
+    /**
+     * @return true if the IMS resolver is busy resolving a binding and should not be considered
+     * available, false if the IMS resolver is idle.
+     * @hide
+     */
+    public boolean isResolvingImsBinding() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isResolvingImsBinding();
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "isResolvingImsBinding, RemoteException: " + e.getMessage());
+        }
+        return false;
+    }
+
+    /**
      * Set IMS registration state
      *
      * @param Registration state
@@ -5213,10 +5408,10 @@
     /**
      * Get the preferred network type.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <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 the preferred network type, defined in RILConstants.java.
      * @hide
@@ -5236,11 +5431,12 @@
 
     /**
      * Sets the network selection mode to automatic.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setNetworkSelectionModeAutomatic() {
         try {
@@ -5256,15 +5452,14 @@
     }
 
     /**
-     * Perform a radio scan and return the list of avialble networks.
+     * Perform a radio scan and return the list of available networks.
      *
      * The return value is a list of the OperatorInfo of the networks found. Note that this
      * scan can take a long time (sometimes minutes) to happen.
      *
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @hide
      * TODO: Add an overload that takes no args.
@@ -5288,33 +5483,49 @@
      * 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 the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * <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 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. For example AsyncTask.SERIAL_EXECUTOR.
      * @param callback Returns network scan results or errors.
      * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public NetworkScan requestNetworkScan(
-            NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
+            NetworkScanRequest request, Executor executor,
+            TelephonyScanManager.NetworkScanCallback callback) {
         synchronized (this) {
             if (mTelephonyScanManager == null) {
                 mTelephonyScanManager = new TelephonyScanManager();
             }
         }
-        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, callback);
+        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback);
+    }
+
+    /**
+     * @deprecated
+     * Use {@link
+     * #requestNetworkScan(NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    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>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * <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
@@ -5322,6 +5533,7 @@
      * normal network selection next time.
      * @return true on success; false on any failure.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setNetworkSelectionModeManual(String operatorNumeric, boolean persistSelection) {
         try {
@@ -5341,10 +5553,10 @@
     /**
      * Set the preferred network type.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <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 id of the subscription to set the preferred network type for.
      * @param networkType the preferred network type, defined in RILConstants.java.
@@ -5368,9 +5580,7 @@
     /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
-     * <p>
-     * Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return true on success; false on any failure.
      */
@@ -5381,9 +5591,7 @@
     /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
-     * <p>
-     * Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return true on success; false on any failure.
      * @hide
@@ -5473,8 +5681,7 @@
      * 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 #hasCarrierPrivileges
+     * <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.
@@ -5491,8 +5698,7 @@
      * 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 #hasCarrierPrivileges
+     * <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.
@@ -6146,39 +6352,44 @@
      * 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.
+     * {@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.
      *
-     * @see #hasCarrierPrivileges
-     * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
      */
-    @Deprecated
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setDataEnabled(boolean enable) {
-        setUserMobileDataEnabled(enable);
+        setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
     }
 
     /**
      * @hide
-     * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
+     * @deprecated use {@link #setDataEnabled(boolean)} instead.
     */
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setDataEnabled(int subId, boolean enable) {
-        setUserMobileDataEnabled(subId, enable);
+        try {
+            Log.d(TAG, "setDataEnabled: enabled=" + enable);
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.setUserDataEnabled(subId, enable);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
+        }
     }
 
     /**
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
+     * @deprecated use {@link #isDataEnabled()} instead.
      * @hide
      */
     @SystemApi
     @Deprecated
     public boolean getDataEnabled() {
-        return isUserMobileDataEnabled();
+        return isDataEnabled();
     }
 
     /**
@@ -6191,30 +6402,35 @@
      * <p>Requires one of the following permissions:
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
-     * calling app has carrier privileges.
+     * 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.
-     *
-     * @see #hasCarrierPrivileges
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
      */
-    @Deprecated
     public boolean isDataEnabled() {
-        return isUserMobileDataEnabled();
+        return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
     }
 
     /**
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
+     * @deprecated use {@link #isDataEnabled()} instead.
      * @hide
      */
     @Deprecated
     @SystemApi
     public boolean getDataEnabled(int subId) {
-        return isUserMobileDataEnabled(subId);
+        boolean retVal = false;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                retVal = telephony.isUserDataEnabled(subId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
+        } catch (NullPointerException e) {
+        }
+        return retVal;
     }
 
     /** @hide */
@@ -6327,84 +6543,106 @@
         return false;
     }
 
-   /**
-    * Returns the IMS Registration Status
-    * @hide
-    */
-   public boolean isImsRegistered() {
-       try {
-           ITelephony telephony = getITelephony();
-           if (telephony == null)
-               return false;
-           return telephony.isImsRegistered();
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
-           return false;
-       }
-   }
-
     /**
-     * Returns the IMS Registration Status for a particular Subscription ID
+     * 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.
-     *
      * @hide
      */
     public boolean isImsRegistered(int subId) {
-       try {
-           return getITelephony().isImsRegisteredForSubscriber(subId);
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
-           return false;
-       }
-    }
-
-    /**
-     * Returns the Status of Volte
-     * @hide
-     */
-    public boolean isVolteAvailable() {
-       try {
-           return getITelephony().isVolteAvailable();
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
-           return false;
-       }
-   }
-
-    /**
-     * Returns the Status of video telephony (VT)
-     * @hide
-     */
-    public boolean isVideoTelephonyAvailable() {
         try {
-            return getITelephony().isVideoTelephonyAvailable();
-        } catch (RemoteException ex) {
-            return false;
-        } catch (NullPointerException ex) {
+            return getITelephony().isImsRegistered(subId);
+        } catch (RemoteException | NullPointerException ex) {
             return false;
         }
     }
 
     /**
-     * Returns the Status of Wi-Fi Calling
+     * 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
+     */
+    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()
+     * @hide
+     */
+    public boolean isVolteAvailable() {
+        try {
+            return getITelephony().isVolteAvailable(getSubId());
+        } 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.
+     * @hide
+     */
+    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.
      * @hide
      */
     public boolean isWifiCallingAvailable() {
        try {
-           return getITelephony().isWifiCallingAvailable();
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
+           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_NONE} if we are not registered or the
+     *  result is unavailable.
+     *  @hide
+     */
+    public @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegTechnologyForMmTel() {
+        try {
+            return getITelephony().getImsRegTechnologyForMmTel(getSubId());
+        } catch (RemoteException ex) {
+            return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+        }
+    }
+
    /**
     * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
     *
@@ -6586,11 +6824,48 @@
      * @hide
      */
     public void setBasebandVersionForPhone(int phoneId, String version) {
+        setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_BASEBAND_VERSION, version);
+    }
+
+    /**
+     * Get baseband version for the default phone.
+     *
+     * @return baseband version.
+     * @hide
+     */
+    public String getBasebandVersion() {
+        int phoneId = getPhoneId();
+        return getBasebandVersionForPhone(phoneId);
+    }
+
+    /**
+     * Get baseband version for the default phone using the legacy approach.
+     * This change was added in P, to ensure backward compatiblity.
+     *
+     * @return baseband version.
+     * @hide
+     */
+    private String getBasebandVersionLegacy(int phoneId) {
         if (SubscriptionManager.isValidPhoneId(phoneId)) {
             String prop = TelephonyProperties.PROPERTY_BASEBAND_VERSION +
                     ((phoneId == 0) ? "" : Integer.toString(phoneId));
-            SystemProperties.set(prop, version);
+            return SystemProperties.get(prop);
         }
+        return null;
+    }
+
+    /**
+     * Get baseband version by phone id.
+     *
+     * @return baseband version.
+     * @hide
+     */
+    public String getBasebandVersionForPhone(int phoneId) {
+        String version = getBasebandVersionLegacy(phoneId);
+        if (version != null && !version.isEmpty()) {
+            setBasebandVersionForPhone(phoneId, version);
+        }
+        return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_BASEBAND_VERSION, "");
     }
 
     /**
@@ -6911,7 +7186,11 @@
 
     /**
      * Returns the current {@link ServiceState} information.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public ServiceState getServiceState() {
         return getServiceStateForSubscriber(getSubId());
@@ -6957,14 +7236,14 @@
     /**
      * Sets the per-account voicemail ringtone.
      *
-     * <p>Requires that the calling app is the default dialer, or has carrier privileges, or has
-     * permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * <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.
-     * @see #hasCarrierPrivileges
      *
      * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
      * instead.
@@ -7002,14 +7281,14 @@
     /**
      * 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, or has
-     * permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * <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.
-     * @see #hasCarrierPrivileges
      *
      * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
      * instead.
@@ -7030,8 +7309,8 @@
     /**
      * 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. android carrier id. The Android carrier ID is an
-     * Android platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
+     * 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/carrier_list.textpb">here</a>
      *
      * <p>Apps which have carrier-specific configurations or business logic can use the carrier id
@@ -7039,45 +7318,39 @@
      *
      * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
      * subscription is unavailable or the carrier cannot be identified.
-     * @throws IllegalStateException if telephony service is unavailable.
      */
-    public int getAndroidCarrierIdForSubscription() {
+    public int getSimCarrierId() {
         try {
             ITelephony service = getITelephony();
-            return service.getSubscriptionCarrierId(getSubId());
+            if (service != null) {
+                return service.getSubscriptionCarrierId(getSubId());
+            }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
-            ex.rethrowAsRuntimeException();
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing.
-            throw new IllegalStateException("Telephony service unavailable");
         }
         return UNKNOWN_CARRIER_ID;
     }
 
     /**
-     * Returns carrier name of the current subscription.
-     * <p>Carrier name is a user-facing name of carrier id
-     * {@link #getAndroidCarrierIdForSubscription()}, usually the brand name of the subsidiary
+     * Returns carrier id name of the current subscription.
+     * <p>Carrier id name is a user-facing name of carrier id
+     * {@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 #getAndroidCarrierIdForSubscription()} instead.
+     * 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 IllegalStateException if telephony service is unavailable.
      */
-    public CharSequence getAndroidCarrierNameForSubscription() {
+    public CharSequence getSimCarrierIdName() {
         try {
             ITelephony service = getITelephony();
-            return service.getSubscriptionCarrierName(getSubId());
+            if (service != null) {
+                return service.getSubscriptionCarrierName(getSubId());
+            }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
-            ex.rethrowAsRuntimeException();
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing.
-            throw new IllegalStateException("Telephony service unavailable");
         }
         return null;
     }
@@ -7430,60 +7703,12 @@
     }
 
     /**
-     * Turns mobile data on or off.
-     * If the {@link TelephonyManager} object has been created with
-     * {@link #createForSubscriptionId}, this API applies to the given subId.
-     * Otherwise, it 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.
-     *
-     * @param enable Whether to enable mobile data.
-     *
-     * @see #hasCarrierPrivileges
-     */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void setUserMobileDataEnabled(boolean enable) {
-        setUserMobileDataEnabled(
-                getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
-    }
-
-    /**
-     * 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 ACCESS_NETWORK_STATE},
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
-     * calling app has carrier privileges.
-     *
-     * <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.
-     *
-     * @see #hasCarrierPrivileges
-     */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.ACCESS_NETWORK_STATE,
-            android.Manifest.permission.MODIFY_PHONE_STATE
-    })
-    public boolean isUserMobileDataEnabled() {
-        return isUserMobileDataEnabled(
-                getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
-    }
-
-    /**
      * @hide
-     * Unlike isUserMobileDataEnabled, this API also evaluates carrierDataEnabled,
-     * policyDataEnabled etc to give a final decision.
+     * It's similar to isDataEnabled, but unlike isDataEnabled, this API also evaluates
+     * carrierDataEnabled, policyDataEnabled etc to give a final decision of whether mobile data is
+     * capable of using.
      */
-    public boolean isMobileDataEnabled() {
+    public boolean isDataCapable() {
         boolean retVal = false;
         try {
             int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
@@ -7498,31 +7723,142 @@
     }
 
     /**
-     * Utility class of {@link #isUserMobileDataEnabled()};
+     * In this mode, modem will not send specified indications when screen is off.
+     * @hide
      */
-    private boolean isUserMobileDataEnabled(int subId) {
-        boolean retVal = false;
+    public static final int INDICATION_UPDATE_MODE_NORMAL                   = 1;
+
+    /**
+     * In this mode, modem will still send specified indications when screen is off.
+     * @hide
+     */
+    public static final int INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF        = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "INDICATION_UPDATE_MODE_" }, value = {
+            INDICATION_UPDATE_MODE_NORMAL,
+            INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IndicationUpdateMode{}
+
+    /**
+     * 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;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "INDICATION_FILTER_" }, value = {
+            INDICATION_FILTER_SIGNAL_STRENGTH,
+            INDICATION_FILTER_FULL_NETWORK_STATE,
+            INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED,
+            INDICATION_FILTER_LINK_CAPACITY_ESTIMATE,
+            INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IndicationFilters{}
+
+    /**
+     * Sets radio indication update mode. This can be used to control the behavior of indication
+     * update from modem to Android frameworks. For example, by default several indication updates
+     * are turned off when screen is off, but in some special cases (e.g. carkit is connected but
+     * screen is off) we want to turn on those indications even when the screen is off.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     *
+     * @param filters Indication filters. Should be a bitmask of INDICATION_FILTER_XXX.
+     * @see #INDICATION_FILTER_SIGNAL_STRENGTH
+     * @see #INDICATION_FILTER_FULL_NETWORK_STATE
+     * @see #INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED
+     * @param updateMode The voice activation state
+     * @see #INDICATION_UPDATE_MODE_NORMAL
+     * @see #INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setRadioIndicationUpdateMode(@IndicationFilters int filters,
+                                             @IndicationUpdateMode int updateMode) {
         try {
             ITelephony telephony = getITelephony();
-            if (telephony != null)
-                retVal = telephony.isUserDataEnabled(subId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
-        } catch (NullPointerException e) {
+            if (telephony != null) {
+                telephony.setRadioIndicationUpdateMode(getSubId(), filters, updateMode);
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
         }
-        return retVal;
     }
 
-    /** Utility method of {@link #setUserMobileDataEnabled(boolean)} */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    private void setUserMobileDataEnabled(int subId, boolean enable) {
+    /**
+     * 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}
+     *
+     * @hide
+     */
+    @TestApi
+    public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
+            String gid2, String plmn, String spn) {
         try {
-            Log.d(TAG, "setUserMobileDataEnabled: enabled=" + enable);
             ITelephony telephony = getITelephony();
-            if (telephony != null)
-                telephony.setUserDataEnabled(subId, enable);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
+            if (telephony != null) {
+                telephony.setCarrierTestOverride(
+                        getSubId(), mccmnc, imsi, iccid, gid1, gid2, plmn, spn);
+            }
+        } 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
+     */
+    @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;
+    }
 }
diff --git a/android/telephony/TelephonyScanManager.java b/android/telephony/TelephonyScanManager.java
index c182e34..96ff332 100644
--- a/android/telephony/TelephonyScanManager.java
+++ b/android/telephony/TelephonyScanManager.java
@@ -33,6 +33,7 @@
 import android.util.SparseArray;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 import com.android.internal.telephony.ITelephony;
 
@@ -55,8 +56,10 @@
 
     /**
      * The caller of
-     * {@link TelephonyManager#requestNetworkScan(NetworkScanRequest, NetworkScanCallback)} should
-     * implement and provide this callback so that the scan results or errors can be returned.
+     * {@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. */
@@ -83,10 +86,13 @@
 
     private static class NetworkScanInfo {
         private final NetworkScanRequest mRequest;
+        private final Executor mExecutor;
         private final NetworkScanCallback mCallback;
 
-        NetworkScanInfo(NetworkScanRequest request, NetworkScanCallback callback) {
+        NetworkScanInfo(
+                NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
             mRequest = request;
+            mExecutor = executor;
             mCallback = callback;
         }
     }
@@ -112,10 +118,15 @@
                         "Failed to find NetworkScanInfo with id " + message.arg2);
                 }
                 NetworkScanCallback callback = nsi.mCallback;
+                Executor executor = nsi.mExecutor;
                 if (callback == null) {
                     throw new RuntimeException(
                         "Failed to find NetworkScanCallback with id " + message.arg2);
                 }
+                if (executor == null) {
+                    throw new RuntimeException(
+                        "Failed to find Executor with id " + message.arg2);
+                }
 
                 switch (message.what) {
                     case CALLBACK_SCAN_RESULTS:
@@ -126,21 +137,31 @@
                             for (int i = 0; i < parcelables.length; i++) {
                                 ci[i] = (CellInfo) parcelables[i];
                             }
-                            callback.onResults((List<CellInfo>) Arrays.asList(ci));
+                            executor.execute(() ->{
+                                Rlog.d(TAG, "onResults: " + ci.toString());
+                                callback.onResults((List<CellInfo>) Arrays.asList(ci));
+                            });
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onResults", e);
                         }
                         break;
                     case CALLBACK_SCAN_ERROR:
                         try {
-                            callback.onError(message.arg1);
+                            final int errorCode = message.arg1;
+                            executor.execute(() -> {
+                                Rlog.d(TAG, "onError: " + errorCode);
+                                callback.onError(errorCode);
+                            });
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onError", e);
                         }
                         break;
                     case CALLBACK_SCAN_COMPLETE:
                         try {
-                            callback.onComplete();
+                            executor.execute(() -> {
+                                Rlog.d(TAG, "onComplete");
+                                callback.onComplete();
+                            });
                             mScanInfo.remove(message.arg2);
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onComplete", e);
@@ -171,12 +192,12 @@
      * @hide
      */
     public NetworkScan requestNetworkScan(int subId,
-            NetworkScanRequest request, NetworkScanCallback callback) {
+            NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 int scanId = telephony.requestNetworkScan(subId, request, mMessenger, new Binder());
-                saveScanInfo(scanId, request, callback);
+                saveScanInfo(scanId, request, executor, callback);
                 return new NetworkScan(scanId, subId);
             }
         } catch (RemoteException ex) {
@@ -187,9 +208,10 @@
         return null;
     }
 
-    private void saveScanInfo(int id, NetworkScanRequest request, NetworkScanCallback callback) {
+    private void saveScanInfo(
+            int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
         synchronized (mScanInfo) {
-            mScanInfo.put(id, new NetworkScanInfo(request, callback));
+            mScanInfo.put(id, new NetworkScanInfo(request, executor, callback));
         }
     }
 
diff --git a/android/telephony/UiccAccessRule.java b/android/telephony/UiccAccessRule.java
index 3937201..d8836dc 100644
--- a/android/telephony/UiccAccessRule.java
+++ b/android/telephony/UiccAccessRule.java
@@ -16,6 +16,7 @@
 package android.telephony;
 
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.content.pm.PackageInfo;
 import android.content.pm.Signature;
 import android.os.Parcel;
@@ -39,9 +40,8 @@
  * specification.
  *
  * @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
  */
+@SystemApi
 public final class UiccAccessRule implements Parcelable {
     private static final String TAG = "UiccAccessRule";
 
@@ -157,6 +157,13 @@
     }
 
     /**
+     * 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
@@ -221,6 +228,15 @@
     }
 
     @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;
+    }
+
+    @Override
     public String toString() {
         return "cert: " + IccUtils.bytesToHexString(mCertificateHash) + " pkg: " +
                 mPackageName + " access: " + mAccessType;
diff --git a/android/telephony/UiccSlotInfo.java b/android/telephony/UiccSlotInfo.java
index 0b3cbad..125161d 100644
--- a/android/telephony/UiccSlotInfo.java
+++ b/android/telephony/UiccSlotInfo.java
@@ -55,10 +55,12 @@
     /** Card state restricted. */
     public static final int CARD_STATE_INFO_RESTRICTED = 4;
 
-    public final boolean isActive;
-    public final boolean isEuicc;
-    public final String cardId;
-    public final @CardStateInfo int cardStateInfo;
+    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;
 
     public static final Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
         @Override
@@ -73,18 +75,22 @@
     };
 
     private UiccSlotInfo(Parcel in) {
-        isActive = in.readByte() != 0;
-        isEuicc = in.readByte() != 0;
-        cardId = in.readString();
-        cardStateInfo = in.readInt();
+        mIsActive = in.readByte() != 0;
+        mIsEuicc = in.readByte() != 0;
+        mCardId = in.readString();
+        mCardStateInfo = in.readInt();
+        mLogicalSlotIdx = in.readInt();
+        mIsExtendedApduSupported = in.readByte() != 0;
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeByte((byte) (isActive ? 1 : 0));
-        dest.writeByte((byte) (isEuicc ? 1 : 0));
-        dest.writeString(cardId);
-        dest.writeInt(cardStateInfo);
+        dest.writeByte((byte) (mIsActive ? 1 : 0));
+        dest.writeByte((byte) (mIsEuicc ? 1 : 0));
+        dest.writeString(mCardId);
+        dest.writeInt(mCardStateInfo);
+        dest.writeInt(mLogicalSlotIdx);
+        dest.writeByte((byte) (mIsExtendedApduSupported ? 1 : 0));
     }
 
     @Override
@@ -93,28 +99,41 @@
     }
 
     public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
-            @CardStateInfo int cardStateInfo) {
-        this.isActive = isActive;
-        this.isEuicc = isEuicc;
-        this.cardId = cardId;
-        this.cardStateInfo = cardStateInfo;
+            @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;
     }
 
     public boolean getIsActive() {
-        return isActive;
+        return mIsActive;
     }
 
     public boolean getIsEuicc() {
-        return isEuicc;
+        return mIsEuicc;
     }
 
     public String getCardId() {
-        return cardId;
+        return mCardId;
     }
 
     @CardStateInfo
     public int getCardStateInfo() {
-        return cardStateInfo;
+        return mCardStateInfo;
+    }
+
+    public int getLogicalSlotIdx() {
+        return mLogicalSlotIdx;
+    }
+
+    /**
+     * @return {@code true} if this slot supports extended APDU from ATR, {@code false} otherwise.
+     */
+    public boolean getIsExtendedApduSupported() {
+        return mIsExtendedApduSupported;
     }
 
     @Override
@@ -127,32 +146,40 @@
         }
 
         UiccSlotInfo that = (UiccSlotInfo) obj;
-        return (isActive == that.isActive)
-                && (isEuicc == that.isEuicc)
-                && (cardId == that.cardId)
-                && (cardStateInfo == that.cardStateInfo);
+        return (mIsActive == that.mIsActive)
+                && (mIsEuicc == that.mIsEuicc)
+                && (mCardId == that.mCardId)
+                && (mCardStateInfo == that.mCardStateInfo)
+                && (mLogicalSlotIdx == that.mLogicalSlotIdx)
+                && (mIsExtendedApduSupported == that.mIsExtendedApduSupported);
     }
 
     @Override
     public int hashCode() {
         int result = 1;
-        result = 31 * result + (isActive ? 1 : 0);
-        result = 31 * result + (isEuicc ? 1 : 0);
-        result = 31 * result + Objects.hashCode(cardId);
-        result = 31 * result + cardStateInfo;
+        result = 31 * result + (mIsActive ? 1 : 0);
+        result = 31 * result + (mIsEuicc ? 1 : 0);
+        result = 31 * result + Objects.hashCode(mCardId);
+        result = 31 * result + mCardStateInfo;
+        result = 31 * result + mLogicalSlotIdx;
+        result = 31 * result + (mIsExtendedApduSupported ? 1 : 0);
         return result;
     }
 
     @Override
     public String toString() {
-        return "UiccSlotInfo (isActive="
-                + isActive
-                + ", isEuicc="
-                + isEuicc
-                + ", cardId="
-                + cardId
+        return "UiccSlotInfo (mIsActive="
+                + mIsActive
+                + ", mIsEuicc="
+                + mIsEuicc
+                + ", mCardId="
+                + mCardId
                 + ", cardState="
-                + cardStateInfo
+                + mCardStateInfo
+                + ", phoneId="
+                + mLogicalSlotIdx
+                + ", mIsExtendedApduSupported="
+                + mIsExtendedApduSupported
                 + ")";
     }
 }
diff --git a/android/telephony/VoiceSpecificRegistrationStates.java b/android/telephony/VoiceSpecificRegistrationStates.java
new file mode 100644
index 0000000..871ee4d
--- /dev/null
+++ b/android/telephony/VoiceSpecificRegistrationStates.java
@@ -0,0 +1,114 @@
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to voice network registration.
+ * @hide
+ */
+public class VoiceSpecificRegistrationStates 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;
+
+    VoiceSpecificRegistrationStates(boolean cssSupported, int roamingIndicator, int systemIsInPrl,
+            int defaultRoamingIndicator) {
+        this.cssSupported = cssSupported;
+        this.roamingIndicator = roamingIndicator;
+        this.systemIsInPrl = systemIsInPrl;
+        this.defaultRoamingIndicator = defaultRoamingIndicator;
+    }
+
+    private VoiceSpecificRegistrationStates(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 "VoiceSpecificRegistrationStates {"
+                + " 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 VoiceSpecificRegistrationStates)) {
+            return false;
+        }
+
+        VoiceSpecificRegistrationStates other = (VoiceSpecificRegistrationStates) o;
+        return this.cssSupported == other.cssSupported
+                && this.roamingIndicator == other.roamingIndicator
+                && this.systemIsInPrl == other.systemIsInPrl
+                && this.defaultRoamingIndicator == other.defaultRoamingIndicator;
+    }
+
+
+    public static final Parcelable.Creator<VoiceSpecificRegistrationStates> CREATOR =
+            new Parcelable.Creator<VoiceSpecificRegistrationStates>() {
+                @Override
+                public VoiceSpecificRegistrationStates createFromParcel(Parcel source) {
+                    return new VoiceSpecificRegistrationStates(source);
+                }
+
+                @Override
+                public VoiceSpecificRegistrationStates[] newArray(int size) {
+                    return new VoiceSpecificRegistrationStates[size];
+                }
+            };
+}
\ No newline at end of file
diff --git a/android/telephony/data/ApnSetting.java b/android/telephony/data/ApnSetting.java
index 73a05af..145ed7e 100644
--- a/android/telephony/data/ApnSetting.java
+++ b/android/telephony/data/ApnSetting.java
@@ -17,10 +17,10 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.StringDef;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.hardware.radio.V1_0.ApnTypes;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.Telephony;
@@ -28,17 +28,15 @@
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.URL;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -46,25 +44,184 @@
  */
 public class ApnSetting implements Parcelable {
 
-    static final String LOG_TAG = "ApnSetting";
+    private static final String LOG_TAG = "ApnSetting";
     private static final boolean VDBG = false;
 
+    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;
+    private static final int NOT_IN_MAP_INT = -1;
+    private static final int NO_PORT_SPECIFIED = -1;
+
+    /** All APN types except IA. */
+    private static final int TYPE_ALL_BUT_IA = ApnTypes.ALL & (~ApnTypes.IA);
+
+    /** APN type for default data traffic and HiPri 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;
+
+    /** @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
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApnType {}
+
+    // Possible values for authentication types.
+    /** No authentication type. */
+    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(prefix = { "AUTH_TYPE_" }, value = {
+        AUTH_TYPE_NONE,
+        AUTH_TYPE_PAP,
+        AUTH_TYPE_CHAP,
+        AUTH_TYPE_PAP_OR_CHAP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AuthType {}
+
+    // Possible values for protocol.
+    /** Protocol type for IP. */
+    public static final int PROTOCOL_IP = 0;
+    /** Protocol type for IPV6. */
+    public static final int PROTOCOL_IPV6 = 1;
+    /** Protocol type for IPV4V6. */
+    public static final int PROTOCOL_IPV4V6 = 2;
+    /** Protocol type for PPP. */
+    public static final int PROTOCOL_PPP = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "PROTOCOL_" }, value = {
+        PROTOCOL_IP,
+        PROTOCOL_IPV6,
+        PROTOCOL_IPV4V6,
+        PROTOCOL_PPP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProtocolType {}
+
+    // Possible values for MVNO type.
+    /** 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_SPN,
+        MVNO_TYPE_IMSI,
+        MVNO_TYPE_GID,
+        MVNO_TYPE_ICCID,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MvnoType {}
+
+    static {
+        APN_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
+        APN_TYPE_STRING_MAP.put("*", TYPE_ALL_BUT_IA);
+        APN_TYPE_STRING_MAP.put("default", TYPE_DEFAULT);
+        APN_TYPE_STRING_MAP.put("mms", TYPE_MMS);
+        APN_TYPE_STRING_MAP.put("supl", TYPE_SUPL);
+        APN_TYPE_STRING_MAP.put("dun", TYPE_DUN);
+        APN_TYPE_STRING_MAP.put("hipri", TYPE_HIPRI);
+        APN_TYPE_STRING_MAP.put("fota", TYPE_FOTA);
+        APN_TYPE_STRING_MAP.put("ims", TYPE_IMS);
+        APN_TYPE_STRING_MAP.put("cbs", TYPE_CBS);
+        APN_TYPE_STRING_MAP.put("ia", TYPE_IA);
+        APN_TYPE_STRING_MAP.put("emergency", TYPE_EMERGENCY);
+        APN_TYPE_INT_MAP = new ArrayMap<Integer, String>();
+        APN_TYPE_INT_MAP.put(TYPE_DEFAULT, "default");
+        APN_TYPE_INT_MAP.put(TYPE_MMS, "mms");
+        APN_TYPE_INT_MAP.put(TYPE_SUPL, "supl");
+        APN_TYPE_INT_MAP.put(TYPE_DUN, "dun");
+        APN_TYPE_INT_MAP.put(TYPE_HIPRI, "hipri");
+        APN_TYPE_INT_MAP.put(TYPE_FOTA, "fota");
+        APN_TYPE_INT_MAP.put(TYPE_IMS, "ims");
+        APN_TYPE_INT_MAP.put(TYPE_CBS, "cbs");
+        APN_TYPE_INT_MAP.put(TYPE_IA, "ia");
+        APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, "emergency");
+
+        PROTOCOL_STRING_MAP = new ArrayMap<String, Integer>();
+        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_INT_MAP = new ArrayMap<Integer, String>();
+        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");
+
+        MVNO_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
+        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<Integer, String>();
+        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 InetAddress mProxy;
-    private final int mPort;
-    private final URL mMmsc;
-    private final InetAddress mMmsProxy;
-    private final int mMmsPort;
+    private final InetAddress mProxyAddress;
+    private final int mProxyPort;
+    private final Uri mMmsc;
+    private final InetAddress mMmsProxyAddress;
+    private final int mMmsProxyPort;
     private final String mUser;
     private final String mPassword;
     private final int mAuthType;
-    private final List<String> mTypes;
-    private final int mTypesBitmap;
+    private final int mApnTypeBitmask;
     private final int mId;
     private final String mOperatorNumeric;
-    private final String mProtocol;
-    private final String mRoamingProtocol;
+    private final int mProtocol;
+    private final int mRoamingProtocol;
     private final int mMtu;
 
     private final boolean mCarrierEnabled;
@@ -78,22 +235,12 @@
     private final int mWaitTime;
     private final int mMaxConnsTime;
 
-    private final String mMvnoType;
+    private final int mMvnoType;
     private final String mMvnoMatchData;
 
     private boolean mPermanentFailed = false;
 
     /**
-     * Returns the types bitmap of the APN.
-     *
-     * @return types bitmap of the APN
-     * @hide
-     */
-    public int getTypesBitmap() {
-        return mTypesBitmap;
-    }
-
-    /**
      * Returns the MTU size of the mobile interface to which the APN connected.
      *
      * @return the MTU size of the APN
@@ -211,8 +358,8 @@
      *
      * @return proxy address.
      */
-    public InetAddress getProxy() {
-        return mProxy;
+    public InetAddress getProxyAddress() {
+        return mProxyAddress;
     }
 
     /**
@@ -220,15 +367,15 @@
      *
      * @return proxy port
      */
-    public int getPort() {
-        return mPort;
+    public int getProxyPort() {
+        return mProxyPort;
     }
     /**
-     * Returns the MMSC URL of the APN.
+     * Returns the MMSC Uri of the APN.
      *
-     * @return MMSC URL.
+     * @return MMSC Uri.
      */
-    public URL getMmsc() {
+    public Uri getMmsc() {
         return mMmsc;
     }
 
@@ -237,8 +384,8 @@
      *
      * @return MMS proxy address.
      */
-    public InetAddress getMmsProxy() {
-        return mMmsProxy;
+    public InetAddress getMmsProxyAddress() {
+        return mMmsProxyAddress;
     }
 
     /**
@@ -246,8 +393,8 @@
      *
      * @return MMS proxy port
      */
-    public int getMmsPort() {
-        return mMmsPort;
+    public int getMmsProxyPort() {
+        return mMmsProxyPort;
     }
 
     /**
@@ -268,21 +415,9 @@
         return mPassword;
     }
 
-    /** @hide */
-    @IntDef({
-            AUTH_TYPE_NONE,
-            AUTH_TYPE_PAP,
-            AUTH_TYPE_CHAP,
-            AUTH_TYPE_PAP_OR_CHAP,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AuthType {}
-
     /**
      * Returns the authentication type of the APN.
      *
-     * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
-     *
      * @return authentication type
      */
     @AuthType
@@ -290,32 +425,20 @@
         return mAuthType;
     }
 
-    /** @hide */
-    @StringDef({
-            TYPE_DEFAULT,
-            TYPE_MMS,
-            TYPE_SUPL,
-            TYPE_DUN,
-            TYPE_HIPRI,
-            TYPE_FOTA,
-            TYPE_IMS,
-            TYPE_CBS,
-            TYPE_IA,
-            TYPE_EMERGENCY
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ApnType {}
-
     /**
-     * Returns the list of APN types of the APN.
+     * Returns the bitmask of APN types.
      *
-     * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+     * <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.
      *
-     * @return the list of APN types
+     * <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
      */
-    @ApnType
-    public List<String> getTypes() {
-        return mTypes;
+    public @ApnType int getApnTypeBitmask() {
+        return mApnTypeBitmask;
     }
 
     /**
@@ -328,7 +451,7 @@
     }
 
     /**
-     * Returns the numeric operator ID for the APN. Usually
+     * 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}.
      *
@@ -338,37 +461,29 @@
         return mOperatorNumeric;
     }
 
-    /** @hide */
-    @StringDef({
-            PROTOCOL_IP,
-            PROTOCOL_IPV6,
-            PROTOCOL_IPV4V6,
-            PROTOCOL_PPP,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ProtocolType {}
-
     /**
      * Returns the protocol to use to connect to this APN.
      *
-     * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
-     * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+     * <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 String getProtocol() {
+    public int getProtocol() {
         return mProtocol;
     }
 
     /**
-     * Returns the protocol to use to connect to this APN when roaming.
+     * Returns the protocol to use to connect to this APN while the device is roaming.
      *
-     * The syntax is the same as {@link android.provider.Telephony.Carriers#PROTOCOL}.
+     * <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
      */
-    public String getRoamingProtocol() {
+    @ProtocolType
+    public int getRoamingProtocol() {
         return mRoamingProtocol;
     }
 
@@ -398,41 +513,29 @@
         return mNetworkTypeBitmask;
     }
 
-    /** @hide */
-    @StringDef({
-            MVNO_TYPE_SPN,
-            MVNO_TYPE_IMSI,
-            MVNO_TYPE_GID,
-            MVNO_TYPE_ICCID,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface MvnoType {}
-
     /**
      * Returns the MVNO match type for this APN.
      *
-     * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
-     *
+     * @see Builder#setMvnoType(int)
      * @return the MVNO match type
      */
     @MvnoType
-    public String getMvnoType() {
+    public int getMvnoType() {
         return mMvnoType;
     }
 
     private ApnSetting(Builder builder) {
         this.mEntryName = builder.mEntryName;
         this.mApnName = builder.mApnName;
-        this.mProxy = builder.mProxy;
-        this.mPort = builder.mPort;
+        this.mProxyAddress = builder.mProxyAddress;
+        this.mProxyPort = builder.mProxyPort;
         this.mMmsc = builder.mMmsc;
-        this.mMmsProxy = builder.mMmsProxy;
-        this.mMmsPort = builder.mMmsPort;
+        this.mMmsProxyAddress = builder.mMmsProxyAddress;
+        this.mMmsProxyPort = builder.mMmsProxyPort;
         this.mUser = builder.mUser;
         this.mPassword = builder.mPassword;
         this.mAuthType = builder.mAuthType;
-        this.mTypes = (builder.mTypes == null ? new ArrayList<String>() : builder.mTypes);
-        this.mTypesBitmap = builder.mTypesBitmap;
+        this.mApnTypeBitmask = builder.mApnTypeBitmask;
         this.mId = builder.mId;
         this.mOperatorNumeric = builder.mOperatorNumeric;
         this.mProtocol = builder.mProtocol;
@@ -451,25 +554,25 @@
 
     /** @hide */
     public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName,
-            String apnName, InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy,
-            int mmsPort, String user, String password, int authType, List<String> types,
-            String protocol, String roamingProtocol, boolean carrierEnabled,
+            String apnName, InetAddress proxy, int port, Uri mmsc, InetAddress mmsProxy,
+            int mmsPort, String user, String password, int authType, int mApnTypeBitmask,
+            int protocol, int roamingProtocol, boolean carrierEnabled,
             int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
-            int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
+            int waitTime, int maxConnsTime, int mtu, int mvnoType, String mvnoMatchData) {
         return new Builder()
                 .setId(id)
                 .setOperatorNumeric(operatorNumeric)
                 .setEntryName(entryName)
                 .setApnName(apnName)
-                .setProxy(proxy)
-                .setPort(port)
+                .setProxyAddress(proxy)
+                .setProxyPort(port)
                 .setMmsc(mmsc)
-                .setMmsProxy(mmsProxy)
-                .setMmsPort(mmsPort)
+                .setMmsProxyAddress(mmsProxy)
+                .setMmsProxyPort(mmsPort)
                 .setUser(user)
                 .setPassword(password)
                 .setAuthType(authType)
-                .setTypes(types)
+                .setApnTypeBitmask(mApnTypeBitmask)
                 .setProtocol(protocol)
                 .setRoamingProtocol(roamingProtocol)
                 .setCarrierEnabled(carrierEnabled)
@@ -487,7 +590,7 @@
 
     /** @hide */
     public static ApnSetting makeApnSetting(Cursor cursor) {
-        String[] types = parseTypes(
+        final int apnTypesBitmask = parseTypes(
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
         int networkTypeBitmask = cursor.getInt(
                 cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
@@ -507,7 +610,7 @@
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
                 portFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT))),
-                URLFromString(cursor.getString(
+                UriFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
                 inetAddressFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
@@ -516,10 +619,12 @@
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
-                Arrays.asList(types),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
-                cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.ROAMING_PROTOCOL)),
+                apnTypesBitmask,
+                nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)))),
+                nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.ROAMING_PROTOCOL)))),
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.CARRIER_ENABLED)) == 1,
                 networkTypeBitmask,
@@ -531,8 +636,9 @@
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MAX_CONNS_TIME)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
-                cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.MVNO_TYPE)),
+                nullToNotInMapInt(MVNO_TYPE_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.MVNO_TYPE)))),
                 cursor.getString(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MVNO_MATCH_DATA)));
     }
@@ -540,8 +646,8 @@
     /** @hide */
     public static ApnSetting makeApnSetting(ApnSetting apn) {
         return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName,
-                apn.mProxy, apn.mPort, apn.mMmsc, apn.mMmsProxy, apn.mMmsPort, apn.mUser,
-                apn.mPassword, apn.mAuthType, apn.mTypes, apn.mProtocol, apn.mRoamingProtocol,
+                apn.mProxyAddress, apn.mProxyPort, apn.mMmsc, apn.mMmsProxyAddress, apn.mMmsProxyPort, apn.mUser,
+                apn.mPassword, apn.mAuthType, apn.mApnTypeBitmask, apn.mProtocol, apn.mRoamingProtocol,
                 apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId,
                 apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
                 apn.mMvnoType, apn.mMvnoMatchData);
@@ -555,18 +661,14 @@
                 .append(", ").append(mId)
                 .append(", ").append(mOperatorNumeric)
                 .append(", ").append(mApnName)
-                .append(", ").append(inetAddressToString(mProxy))
-                .append(", ").append(URLToString(mMmsc))
-                .append(", ").append(inetAddressToString(mMmsProxy))
-                .append(", ").append(portToString(mMmsPort))
-                .append(", ").append(portToString(mPort))
+                .append(", ").append(inetAddressToString(mProxyAddress))
+                .append(", ").append(UriToString(mMmsc))
+                .append(", ").append(inetAddressToString(mMmsProxyAddress))
+                .append(", ").append(portToString(mMmsProxyPort))
+                .append(", ").append(portToString(mProxyPort))
                 .append(", ").append(mAuthType).append(", ");
-        for (int i = 0; i < mTypes.size(); i++) {
-            sb.append(mTypes.get(i));
-            if (i < mTypes.size() - 1) {
-                sb.append(" | ");
-            }
-        }
+        final String[] types = deParseTypes(mApnTypeBitmask).split(",");
+        sb.append(TextUtils.join(" | ", types)).append(", ");
         sb.append(", ").append(mProtocol);
         sb.append(", ").append(mRoamingProtocol);
         sb.append(", ").append(mCarrierEnabled);
@@ -588,56 +690,37 @@
      * @hide
      */
     public boolean hasMvnoParams() {
-        return !TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData);
+        return (mMvnoType != NOT_IN_MAP_INT) && !TextUtils.isEmpty(mMvnoMatchData);
     }
 
     /** @hide */
-    public boolean canHandleType(String type) {
-        if (!mCarrierEnabled) return false;
-        boolean wildcardable = true;
-        if (TYPE_IA.equalsIgnoreCase(type)) wildcardable = false;
-        for (String t : mTypes) {
-            // DEFAULT handles all, and HIPRI is handled by DEFAULT
-            if (t.equalsIgnoreCase(type)
-                    || (wildcardable && t.equalsIgnoreCase(TYPE_ALL))
-                    || (t.equalsIgnoreCase(TYPE_DEFAULT)
-                    && type.equalsIgnoreCase(TYPE_HIPRI))) {
-                return true;
-            }
-        }
-        return false;
+    public boolean canHandleType(@ApnType int type) {
+        return mCarrierEnabled && ((mApnTypeBitmask & type) == 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 + ": ");
-            for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
-                apnType1.append(first.mTypes.get(index1));
-                apnType1.append(",");
-            }
+            apnType1.append(deParseTypes(first.mApnTypeBitmask));
 
             StringBuilder apnType2 = new StringBuilder(second.mApnName + ": ");
-            for (int index1 = 0; index1 < second.mTypes.size(); index1++) {
-                apnType2.append(second.mTypes.get(index1));
-                apnType2.append(",");
-            }
+            apnType2.append(deParseTypes(second.mApnTypeBitmask));
+
             Rlog.d(LOG_TAG, "APN1: is " + apnType1);
             Rlog.d(LOG_TAG, "APN2: is " + apnType2);
         }
 
-        for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
-            for (int index2 = 0; index2 < second.mTypes.size(); index2++) {
-                if (first.mTypes.get(index1).equals(ApnSetting.TYPE_ALL)
-                        || second.mTypes.get(index2).equals(ApnSetting.TYPE_ALL)
-                        || first.mTypes.get(index1).equals(second.mTypes.get(index2))) {
-                    if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
-                    return true;
-                }
+        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");
+        if (VDBG) {
+            Rlog.d(LOG_TAG, "typeSameAny: return false");
+        }
         return false;
     }
 
@@ -655,16 +738,15 @@
                 && Objects.equals(mId, other.mId)
                 && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
                 && Objects.equals(mApnName, other.mApnName)
-                && Objects.equals(mProxy, other.mProxy)
+                && Objects.equals(mProxyAddress, other.mProxyAddress)
                 && Objects.equals(mMmsc, other.mMmsc)
-                && Objects.equals(mMmsProxy, other.mMmsProxy)
-                && Objects.equals(mMmsPort, other.mMmsPort)
-                && Objects.equals(mPort,other.mPort)
+                && 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(mTypes, other.mTypes)
-                && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+                && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
                 && Objects.equals(mProtocol, other.mProtocol)
                 && Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
@@ -701,16 +783,15 @@
         return mEntryName.equals(other.mEntryName)
                 && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
                 && Objects.equals(mApnName, other.mApnName)
-                && Objects.equals(mProxy, other.mProxy)
+                && Objects.equals(mProxyAddress, other.mProxyAddress)
                 && Objects.equals(mMmsc, other.mMmsc)
-                && Objects.equals(mMmsProxy, other.mMmsProxy)
-                && Objects.equals(mMmsPort, other.mMmsPort)
-                && Objects.equals(mPort, other.mPort)
+                && 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(mTypes, other.mTypes)
-                && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+                && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
                 && (isDataRoaming || Objects.equals(mProtocol,other.mProtocol))
                 && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol))
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
@@ -736,17 +817,17 @@
                 && !other.canHandleType(TYPE_DUN)
                 && Objects.equals(this.mApnName, other.mApnName)
                 && !typeSameAny(this, other)
-                && xorEqualsInetAddress(this.mProxy, other.mProxy)
-                && xorEqualsPort(this.mPort, other.mPort)
+                && xorEquals(this.mProxyAddress, other.mProxyAddress)
+                && xorEqualsPort(this.mProxyPort, other.mProxyPort)
                 && xorEquals(this.mProtocol, other.mProtocol)
                 && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
                 && Objects.equals(this.mProfileId, other.mProfileId)
                 && Objects.equals(this.mMvnoType, other.mMvnoType)
                 && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
-                && xorEqualsURL(this.mMmsc, other.mMmsc)
-                && xorEqualsInetAddress(this.mMmsProxy, other.mMmsProxy)
-                && xorEqualsPort(this.mMmsPort, other.mMmsPort))
+                && xorEquals(this.mMmsc, other.mMmsc)
+                && xorEquals(this.mMmsProxyAddress, other.mMmsProxyAddress)
+                && xorEqualsPort(this.mMmsProxyPort, other.mMmsProxyPort))
                 && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask);
     }
 
@@ -757,42 +838,23 @@
                 || TextUtils.isEmpty(second));
     }
 
-    // Equal or one is not specified.
-    private boolean xorEqualsInetAddress(InetAddress first, InetAddress second) {
-        return first == null || second == null || first.equals(second);
-    }
-
-    // Equal or one is not specified.
-    private boolean xorEqualsURL(URL first, URL second) {
+    // Equal or one is not null.
+    private boolean xorEquals(Object first, Object second) {
         return first == null || second == null || first.equals(second);
     }
 
     // Equal or one is not specified.
     private boolean xorEqualsPort(int first, int second) {
-        return first == -1 || second == -1 || Objects.equals(first, second);
+        return first == NO_PORT_SPECIFIED || second == NO_PORT_SPECIFIED
+            || Objects.equals(first, second);
     }
 
-    // Helper function to convert APN string into a 32-bit bitmask.
-    private static int getApnBitmask(String apn) {
-        switch (apn) {
-            case TYPE_DEFAULT: return ApnTypes.DEFAULT;
-            case TYPE_MMS: return ApnTypes.MMS;
-            case TYPE_SUPL: return ApnTypes.SUPL;
-            case TYPE_DUN: return ApnTypes.DUN;
-            case TYPE_HIPRI: return ApnTypes.HIPRI;
-            case TYPE_FOTA: return ApnTypes.FOTA;
-            case TYPE_IMS: return ApnTypes.IMS;
-            case TYPE_CBS: return ApnTypes.CBS;
-            case TYPE_IA: return ApnTypes.IA;
-            case TYPE_EMERGENCY: return ApnTypes.EMERGENCY;
-            case TYPE_ALL: return ApnTypes.ALL;
-            default: return ApnTypes.NONE;
-        }
-    }
-
-    private String deParseTypes(List<String> types) {
-        if (types == null) {
-            return null;
+    private String deParseTypes(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);
     }
@@ -808,21 +870,25 @@
         apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric));
         apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName));
         apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName));
-        apnValue.put(Telephony.Carriers.PROXY, mProxy == null ? "" : inetAddressToString(mProxy));
-        apnValue.put(Telephony.Carriers.PORT, portToString(mPort));
-        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : URLToString(mMmsc));
-        apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort));
-        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxy == null
-                ? "" : inetAddressToString(mMmsProxy));
+        apnValue.put(Telephony.Carriers.PROXY, mProxyAddress == null ? ""
+            : inetAddressToString(mProxyAddress));
+        apnValue.put(Telephony.Carriers.PORT, portToString(mProxyPort));
+        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : UriToString(mMmsc));
+        apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsProxyPort));
+        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxyAddress == null
+                ? "" : inetAddressToString(mMmsProxyAddress));
         apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser));
         apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword));
         apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
-        String apnType = deParseTypes(mTypes);
+        String apnType = deParseTypes(mApnTypeBitmask);
         apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType));
-        apnValue.put(Telephony.Carriers.PROTOCOL, nullToEmpty(mProtocol));
-        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, nullToEmpty(mRoamingProtocol));
+        apnValue.put(Telephony.Carriers.PROTOCOL,
+            nullToEmpty(PROTOCOL_INT_MAP.get(mProtocol)));
+        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL,
+            nullToEmpty(PROTOCOL_INT_MAP.get(mRoamingProtocol)));
         apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
-        apnValue.put(Telephony.Carriers.MVNO_TYPE, nullToEmpty(mMvnoType));
+        apnValue.put(Telephony.Carriers.MVNO_TYPE,
+            nullToEmpty(MVNO_TYPE_INT_MAP.get(mMvnoType)));
         apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
 
         return apnValue;
@@ -830,32 +896,31 @@
 
     /**
      * @param types comma delimited list of APN types
-     * @return array of APN types
+     * @return bitmask of APN types
      * @hide
      */
-    public static String[] parseTypes(String types) {
-        String[] result;
-        // If unset, set to DEFAULT.
+    public static int parseTypes(String types) {
+        // If unset, set to ALL.
         if (TextUtils.isEmpty(types)) {
-            result = new String[1];
-            result[0] = TYPE_ALL;
+            return TYPE_ALL_BUT_IA;
         } else {
-            result = types.split(",");
-        }
-        return result;
-    }
-
-    private static URL URLFromString(String url) {
-        try {
-            return TextUtils.isEmpty(url) ? null : new URL(url);
-        } catch (MalformedURLException e) {
-            Log.e(LOG_TAG, "Can't parse URL from string.");
-            return null;
+            int result = 0;
+            for (String str : types.split(",")) {
+                Integer type = APN_TYPE_STRING_MAP.get(str);
+                if (type != null) {
+                    result |= type;
+                }
+            }
+            return result;
         }
     }
 
-    private static String URLToString(URL url) {
-        return url == null ? "" : url.toString();
+    private static Uri UriFromString(String uri) {
+        return TextUtils.isEmpty(uri) ? null : Uri.parse(uri);
+    }
+
+    private static String UriToString(Uri uri) {
+        return uri == null ? "" : uri.toString();
     }
 
     private static InetAddress inetAddressFromString(String inetAddress) {
@@ -887,7 +952,7 @@
     }
 
     private static int portFromString(String strPort) {
-        int port = -1;
+        int port = NO_PORT_SPECIFIED;
         if (!TextUtils.isEmpty(strPort)) {
             try {
                 port = Integer.parseInt(strPort);
@@ -899,7 +964,7 @@
     }
 
     private static String portToString(int port) {
-        return port == -1 ? "" : Integer.toString(port);
+        return port == NO_PORT_SPECIFIED ? "" : Integer.toString(port);
     }
 
     // Implement Parcelable.
@@ -916,19 +981,19 @@
         dest.writeString(mOperatorNumeric);
         dest.writeString(mEntryName);
         dest.writeString(mApnName);
-        dest.writeValue(mProxy);
-        dest.writeInt(mPort);
+        dest.writeValue(mProxyAddress);
+        dest.writeInt(mProxyPort);
         dest.writeValue(mMmsc);
-        dest.writeValue(mMmsProxy);
-        dest.writeInt(mMmsPort);
+        dest.writeValue(mMmsProxyAddress);
+        dest.writeInt(mMmsProxyPort);
         dest.writeString(mUser);
         dest.writeString(mPassword);
         dest.writeInt(mAuthType);
-        dest.writeStringArray(mTypes.toArray(new String[0]));
-        dest.writeString(mProtocol);
-        dest.writeString(mRoamingProtocol);
+        dest.writeInt(mApnTypeBitmask);
+        dest.writeInt(mProtocol);
+        dest.writeInt(mRoamingProtocol);
         dest.writeInt(mCarrierEnabled ? 1: 0);
-        dest.writeString(mMvnoType);
+        dest.writeInt(mMvnoType);
         dest.writeInt(mNetworkTypeBitmask);
     }
 
@@ -939,23 +1004,23 @@
         final String apnName = in.readString();
         final InetAddress proxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
         final int port = in.readInt();
-        final URL mmsc = (URL)in.readValue(URL.class.getClassLoader());
+        final Uri mmsc = (Uri)in.readValue(Uri.class.getClassLoader());
         final InetAddress mmsProxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
         final int mmsPort = in.readInt();
         final String user = in.readString();
         final String password = in.readString();
         final int authType = in.readInt();
-        final List<String> types = Arrays.asList(in.readStringArray());
-        final String protocol = in.readString();
-        final String roamingProtocol = in.readString();
+        final int apnTypesBitmask = in.readInt();
+        final int protocol = in.readInt();
+        final int roamingProtocol = in.readInt();
         final boolean carrierEnabled = in.readInt() > 0;
-        final String mvnoType = in.readString();
+        final int mvnoType = in.readInt();
         final int networkTypeBitmask = in.readInt();
 
         return makeApnSetting(id, operatorNumeric, entryName, apnName,
-                proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, types, protocol,
-                roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
-                0, 0, 0, 0, mvnoType, null);
+            proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, apnTypesBitmask,
+            protocol, roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
+            0, 0, 0, 0, mvnoType, null);
     }
 
     public static final Parcelable.Creator<ApnSetting> CREATOR =
@@ -971,89 +1036,26 @@
                 }
             };
 
-    /**
-     * 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/>
-     * ALL is a special type to indicate that this APN entry can
-     * service all data connections.
-     */
-    public static final String TYPE_ALL = "*";
-    /** APN type for default data traffic */
-    public static final String TYPE_DEFAULT = "default";
-    /** APN type for MMS traffic */
-    public static final String TYPE_MMS = "mms";
-    /** APN type for SUPL assisted GPS */
-    public static final String TYPE_SUPL = "supl";
-    /** APN type for DUN traffic */
-    public static final String TYPE_DUN = "dun";
-    /** APN type for HiPri traffic */
-    public static final String TYPE_HIPRI = "hipri";
-    /** APN type for FOTA */
-    public static final String TYPE_FOTA = "fota";
-    /** APN type for IMS */
-    public static final String TYPE_IMS = "ims";
-    /** APN type for CBS */
-    public static final String TYPE_CBS = "cbs";
-    /** APN type for IA Initial Attach APN */
-    public static final String TYPE_IA = "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 String TYPE_EMERGENCY = "emergency";
-    /**
-     * Array of all APN types
-     *
-     * @hide
-     */
-    public static final String[] ALL_TYPES = {
-            TYPE_DEFAULT,
-            TYPE_MMS,
-            TYPE_SUPL,
-            TYPE_DUN,
-            TYPE_HIPRI,
-            TYPE_FOTA,
-            TYPE_IMS,
-            TYPE_CBS,
-            TYPE_IA,
-            TYPE_EMERGENCY
-    };
-
-    // Possible values for authentication types.
-    public static final int AUTH_TYPE_NONE = 0;
-    public static final int AUTH_TYPE_PAP = 1;
-    public static final int AUTH_TYPE_CHAP = 2;
-    public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
-
-    // Possible values for protocol.
-    public static final String PROTOCOL_IP = "IP";
-    public static final String PROTOCOL_IPV6 = "IPV6";
-    public static final String PROTOCOL_IPV4V6 = "IPV4V6";
-    public static final String PROTOCOL_PPP = "PPP";
-
-    // Possible values for MVNO type.
-    public static final String MVNO_TYPE_SPN = "spn";
-    public static final String MVNO_TYPE_IMSI = "imsi";
-    public static final String MVNO_TYPE_GID = "gid";
-    public static final String MVNO_TYPE_ICCID = "iccid";
+    private static int nullToNotInMapInt(Integer value) {
+        return value == null ? NOT_IN_MAP_INT : value;
+    }
 
     public static class Builder{
         private String mEntryName;
         private String mApnName;
-        private InetAddress mProxy;
-        private int mPort = -1;
-        private URL mMmsc;
-        private InetAddress mMmsProxy;
-        private int mMmsPort = -1;
+        private InetAddress mProxyAddress;
+        private int mProxyPort = NO_PORT_SPECIFIED;
+        private Uri mMmsc;
+        private InetAddress mMmsProxyAddress;
+        private int mMmsProxyPort = NO_PORT_SPECIFIED;
         private String mUser;
         private String mPassword;
         private int mAuthType;
-        private List<String> mTypes;
-        private int mTypesBitmap;
+        private int mApnTypeBitmask;
         private int mId;
         private String mOperatorNumeric;
-        private String mProtocol;
-        private String mRoamingProtocol;
+        private int mProtocol = NOT_IN_MAP_INT;
+        private int mRoamingProtocol = NOT_IN_MAP_INT;
         private int mMtu;
         private int mNetworkTypeBitmask;
         private boolean mCarrierEnabled;
@@ -1062,7 +1064,7 @@
         private int mMaxConns;
         private int mWaitTime;
         private int mMaxConnsTime;
-        private String mMvnoType;
+        private int mMvnoType = NOT_IN_MAP_INT;
         private String mMvnoMatchData;
 
         /**
@@ -1182,8 +1184,8 @@
          *
          * @param proxy the proxy address to set for the APN
          */
-        public Builder setProxy(InetAddress proxy) {
-            this.mProxy = proxy;
+        public Builder setProxyAddress(InetAddress proxy) {
+            this.mProxyAddress = proxy;
             return this;
         }
 
@@ -1192,17 +1194,17 @@
          *
          * @param port the proxy port to set for the APN
          */
-        public Builder setPort(int port) {
-            this.mPort = port;
+        public Builder setProxyPort(int port) {
+            this.mProxyPort = port;
             return this;
         }
 
         /**
-         * Sets the MMSC URL of the APN.
+         * Sets the MMSC Uri of the APN.
          *
-         * @param mmsc the MMSC URL to set for the APN
+         * @param mmsc the MMSC Uri to set for the APN
          */
-        public Builder setMmsc(URL mmsc) {
+        public Builder setMmsc(Uri mmsc) {
             this.mMmsc = mmsc;
             return this;
         }
@@ -1212,8 +1214,8 @@
          *
          * @param mmsProxy the MMS proxy address to set for the APN
          */
-        public Builder setMmsProxy(InetAddress mmsProxy) {
-            this.mMmsProxy = mmsProxy;
+        public Builder setMmsProxyAddress(InetAddress mmsProxy) {
+            this.mMmsProxyAddress = mmsProxy;
             return this;
         }
 
@@ -1222,8 +1224,8 @@
          *
          * @param mmsPort the MMS proxy port to set for the APN
          */
-        public Builder setMmsPort(int mmsPort) {
-            this.mMmsPort = mmsPort;
+        public Builder setMmsProxyPort(int mmsPort) {
+            this.mMmsProxyPort = mmsPort;
             return this;
         }
 
@@ -1251,8 +1253,6 @@
         /**
          * Sets the authentication type of the APN.
          *
-         * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
-         *
          * @param authType the authentication type to set for the APN
          */
         public Builder setAuthType(@AuthType int authType) {
@@ -1261,25 +1261,25 @@
         }
 
         /**
-         * Sets the list of APN types of the APN.
+         * Sets the bitmask of APN types.
          *
-         * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+         * <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.
          *
-         * @param types the list of APN types to set for the APN
+         * <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
          */
-        public Builder setTypes(@ApnType List<String> types) {
-            this.mTypes = types;
-            int apnBitmap = 0;
-            for (int i = 0; i < mTypes.size(); i++) {
-                mTypes.set(i, mTypes.get(i).toLowerCase());
-                apnBitmap |= getApnBitmask(mTypes.get(i));
-            }
-            this.mTypesBitmap = apnBitmap;
+        public Builder setApnTypeBitmask(@ApnType int apnTypeBitmask) {
+            this.mApnTypeBitmask = apnTypeBitmask;
             return this;
         }
 
         /**
-         * Set the numeric operator ID for the APN.
+         * 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
          */
@@ -1291,22 +1291,23 @@
         /**
          * Sets the protocol to use to connect to this APN.
          *
-         * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
-         * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+         * <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
          */
-        public Builder setProtocol(@ProtocolType String protocol) {
+        public Builder setProtocol(@ProtocolType int protocol) {
             this.mProtocol = protocol;
             return this;
         }
 
         /**
-         * Sets the protocol to use to connect to this APN when roaming.
+         * 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
          */
-        public Builder setRoamingProtocol(String roamingProtocol) {
+        public Builder setRoamingProtocol(@ProtocolType  int roamingProtocol) {
             this.mRoamingProtocol = roamingProtocol;
             return this;
         }
@@ -1334,16 +1335,25 @@
         /**
          * Sets the MVNO match type for this APN.
          *
-         * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
-         *
          * @param mvnoType the MVNO match type to set for this APN
          */
-        public Builder setMvnoType(@MvnoType String mvnoType) {
+        public Builder setMvnoType(@MvnoType int mvnoType) {
             this.mMvnoType = mvnoType;
             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 & ApnTypes.ALL) == 0 || TextUtils.isEmpty(mApnName)
+                || TextUtils.isEmpty(mEntryName)) {
+                return null;
+            }
             return new ApnSetting(this);
         }
     }
diff --git a/android/telephony/data/DataCallResponse.java b/android/telephony/data/DataCallResponse.java
index ef3a183..25f5133 100644
--- a/android/telephony/data/DataCallResponse.java
+++ b/android/telephony/data/DataCallResponse.java
@@ -27,6 +27,7 @@
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Description of the response of a setup data call connection request.
@@ -220,17 +221,8 @@
 
     @Override
     public int hashCode() {
-        return mStatus * 31
-                + mSuggestedRetryTime * 37
-                + mCid * 41
-                + mActive * 43
-                + mType.hashCode() * 47
-                + mIfname.hashCode() * 53
-                + mAddresses.hashCode() * 59
-                + mDnses.hashCode() * 61
-                + mGateways.hashCode() * 67
-                + mPcscfs.hashCode() * 71
-                + mMtu * 73;
+        return Objects.hash(mStatus, mSuggestedRetryTime, mCid, mActive, mType, mIfname, mAddresses,
+                mDnses, mGateways, mPcscfs, mMtu);
     }
 
     @Override
diff --git a/android/telephony/data/DataProfile.java b/android/telephony/data/DataProfile.java
index 41c1430..e8597b2 100644
--- a/android/telephony/data/DataProfile.java
+++ b/android/telephony/data/DataProfile.java
@@ -17,6 +17,7 @@
 package android.telephony.data;
 
 import android.annotation.SystemApi;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -230,8 +231,10 @@
 
     @Override
     public String toString() {
-        return "DataProfile=" + mProfileId + "/" + mApn + "/" + mProtocol + "/" + mAuthType
-                + "/" + mUserName + "/" + mPassword + "/" + mType + "/" + mMaxConnsTime
+        return "DataProfile=" + mProfileId + "/" + mProtocol + "/" + mAuthType
+                + "/" + (Build.IS_USER ? "***/***/***" :
+                         (mApn + "/" + mUserName + "/" + mPassword))
+                + "/" + mType + "/" + mMaxConnsTime
                 + "/" + mMaxConns + "/" + mWaitTime + "/" + mEnabled + "/"
                 + mSupportedApnTypesBitmap + "/" + mRoamingProtocol + "/" + mBearerBitmap + "/"
                 + mMtu + "/" + mMvnoType + "/" + mMvnoMatchData + "/" + mModemCognitive;
diff --git a/android/telephony/data/DataService.java b/android/telephony/data/DataService.java
index fa19ea0..e8c1cb1 100644
--- a/android/telephony/data/DataService.java
+++ b/android/telephony/data/DataService.java
@@ -18,6 +18,8 @@
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
@@ -30,9 +32,10 @@
 import android.os.RemoteException;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.Rlog;
-import android.telephony.SubscriptionManager;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -86,15 +89,17 @@
     /** The reason of the data request is IWLAN handover */
     public static final int REQUEST_REASON_HANDOVER = 3;
 
-    private static final int DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE          = 1;
-    private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL                      = 2;
-    private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL                 = 3;
-    private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN               = 4;
-    private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE                     = 5;
-    private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST                   = 6;
-    private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED      = 7;
-    private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED    = 8;
-    private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED            = 9;
+    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_GET_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 final HandlerThread mHandlerThread;
 
@@ -102,7 +107,9 @@
 
     private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>();
 
-    private final SparseArray<IDataServiceWrapper> mBinderMap = new SparseArray<>();
+    /** @hide */
+    @VisibleForTesting
+    public final IDataServiceWrapper mBinder = new IDataServiceWrapper();
 
     /**
      * The abstract class of the actual data service implementation. The data service provider
@@ -136,19 +143,21 @@
          * 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 AccessNetworkConstants.AccessNetworkType}.
+         *        Must be one of {@link 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}.
+         *        {@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.
+         *        link properties of the existing data connection, otherwise null.
+         * @param callback The result callback for this request. Null if the client does not care
+         *        about the result.
          */
         public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
                                   boolean allowRoaming, @SetupDataReason int reason,
-                                  LinkProperties linkProperties, DataServiceCallback callback) {
+                                  @Nullable LinkProperties linkProperties,
+                                  @Nullable DataServiceCallback callback) {
             // The default implementation is to return unsupported.
             callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
         }
@@ -159,13 +168,15 @@
          * 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)}.
+         *        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.
+         *        {@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,
-                                       DataServiceCallback callback) {
+                                       @Nullable DataServiceCallback callback) {
             // The default implementation is to return unsupported.
             callback.onDeactivateDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
         }
@@ -175,10 +186,11 @@
          *
          * @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.
+         * @param callback The result callback for this request. Null if the client does not care
+         *        about the result.
          */
         public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
-                                        DataServiceCallback callback) {
+                                        @Nullable DataServiceCallback callback) {
             // The default implementation is to return unsupported.
             callback.onSetInitialAttachApnComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
         }
@@ -190,10 +202,11 @@
          *
          * @param dps A list of data profiles.
          * @param isRoaming True if the device is data roaming.
-         * @param callback The result callback for this request.
+         * @param callback The result callback for this request. Null if the client does not care
+         *        about the result.
          */
         public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
-                                   DataServiceCallback callback) {
+                                   @Nullable DataServiceCallback callback) {
             // The default implementation is to return unsupported.
             callback.onSetDataProfileComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
         }
@@ -203,7 +216,7 @@
          *
          * @param callback The result callback for this request.
          */
-        public void getDataCallList(DataServiceCallback callback) {
+        public void getDataCallList(@NonNull DataServiceCallback callback) {
             // The default implementation is to return unsupported.
             callback.onGetDataCallListComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
         }
@@ -321,70 +334,89 @@
         public void handleMessage(Message message) {
             IDataServiceCallback callback;
             final int slotId = message.arg1;
-            DataServiceProvider service;
-
-            synchronized (mServiceMap) {
-                service = mServiceMap.get(slotId);
-            }
+            DataServiceProvider serviceProvider = mServiceMap.get(slotId);
 
             switch (message.what) {
-                case DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE:
-                    service = createDataServiceProvider(message.arg1);
-                    if (service != null) {
-                        mServiceMap.put(slotId, service);
+                case DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER:
+                    serviceProvider = createDataServiceProvider(message.arg1);
+                    if (serviceProvider != null) {
+                        mServiceMap.put(slotId, serviceProvider);
                     }
                     break;
+                case DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER:
+                    if (serviceProvider != null) {
+                        serviceProvider.onDestroy();
+                        mServiceMap.remove(slotId);
+                    }
+                    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.onDestroy();
+                        }
+                    }
+                    mServiceMap.clear();
+                    break;
                 case DATA_SERVICE_REQUEST_SETUP_DATA_CALL:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj;
-                    service.setupDataCall(setupDataCallRequest.accessNetworkType,
+                    serviceProvider.setupDataCall(setupDataCallRequest.accessNetworkType,
                             setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
                             setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
                             setupDataCallRequest.linkProperties,
-                            new DataServiceCallback(setupDataCallRequest.callback));
+                            (setupDataCallRequest.callback != null)
+                                    ? new DataServiceCallback(setupDataCallRequest.callback)
+                                    : null);
 
                     break;
                 case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     DeactivateDataCallRequest deactivateDataCallRequest =
                             (DeactivateDataCallRequest) message.obj;
-                    service.deactivateDataCall(deactivateDataCallRequest.cid,
+                    serviceProvider.deactivateDataCall(deactivateDataCallRequest.cid,
                             deactivateDataCallRequest.reason,
-                            new DataServiceCallback(deactivateDataCallRequest.callback));
+                            (deactivateDataCallRequest.callback != null)
+                                    ? new DataServiceCallback(deactivateDataCallRequest.callback)
+                                    : null);
                     break;
                 case DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     SetInitialAttachApnRequest setInitialAttachApnRequest =
                             (SetInitialAttachApnRequest) message.obj;
-                    service.setInitialAttachApn(setInitialAttachApnRequest.dataProfile,
+                    serviceProvider.setInitialAttachApn(setInitialAttachApnRequest.dataProfile,
                             setInitialAttachApnRequest.isRoaming,
-                            new DataServiceCallback(setInitialAttachApnRequest.callback));
+                            (setInitialAttachApnRequest.callback != null)
+                                    ? new DataServiceCallback(setInitialAttachApnRequest.callback)
+                                    : null);
                     break;
                 case DATA_SERVICE_REQUEST_SET_DATA_PROFILE:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     SetDataProfileRequest setDataProfileRequest =
                             (SetDataProfileRequest) message.obj;
-                    service.setDataProfile(setDataProfileRequest.dps,
+                    serviceProvider.setDataProfile(setDataProfileRequest.dps,
                             setDataProfileRequest.isRoaming,
-                            new DataServiceCallback(setDataProfileRequest.callback));
+                            (setDataProfileRequest.callback != null)
+                                    ? new DataServiceCallback(setDataProfileRequest.callback)
+                                    : null);
                     break;
                 case DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
 
-                    service.getDataCallList(new DataServiceCallback(
+                    serviceProvider.getDataCallList(new DataServiceCallback(
                             (IDataServiceCallback) message.obj));
                     break;
                 case DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED:
-                    if (service == null) break;
-                    service.registerForDataCallListChanged((IDataServiceCallback) message.obj);
+                    if (serviceProvider == null) break;
+                    serviceProvider.registerForDataCallListChanged((IDataServiceCallback) message.obj);
                     break;
                 case DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     callback = (IDataServiceCallback) message.obj;
-                    service.unregisterForDataCallListChanged(callback);
+                    serviceProvider.unregisterForDataCallListChanged(callback);
                     break;
                 case DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     DataCallListChangedIndication indication =
                             (DataCallListChangedIndication) message.obj;
                     try {
@@ -423,67 +455,19 @@
             loge("Unexpected intent " + intent);
             return null;
         }
-
-        int slotId = intent.getIntExtra(
-                DATA_SERVICE_EXTRA_SLOT_ID, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-
-        if (!SubscriptionManager.isValidSlotIndex(slotId)) {
-            loge("Invalid slot id " + slotId);
-            return null;
-        }
-
-        log("onBind: slot id=" + slotId);
-
-        IDataServiceWrapper binder = mBinderMap.get(slotId);
-        if (binder == null) {
-            Message msg = mHandler.obtainMessage(DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE);
-            msg.arg1 = slotId;
-            msg.sendToTarget();
-
-            binder = new IDataServiceWrapper(slotId);
-            mBinderMap.put(slotId, binder);
-        }
-
-        return binder;
+        return mBinder;
     }
 
     /** @hide */
     @Override
     public boolean onUnbind(Intent intent) {
-        int slotId = intent.getIntExtra(DATA_SERVICE_EXTRA_SLOT_ID,
-                SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-        if (mBinderMap.get(slotId) != null) {
-            DataServiceProvider serviceImpl;
-            synchronized (mServiceMap) {
-                serviceImpl = mServiceMap.get(slotId);
-            }
-            if (serviceImpl != null) {
-                serviceImpl.onDestroy();
-            }
-            mBinderMap.remove(slotId);
-        }
-
-        // If all clients unbinds, quit the handler thread
-        if (mBinderMap.size() == 0) {
-            mHandlerThread.quit();
-        }
-
+        mHandler.obtainMessage(DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS).sendToTarget();
         return false;
     }
 
     /** @hide */
     @Override
     public void onDestroy() {
-        synchronized (mServiceMap) {
-            for (int i = 0; i < mServiceMap.size(); i++) {
-                DataServiceProvider serviceImpl = mServiceMap.get(i);
-                if (serviceImpl != null) {
-                    serviceImpl.onDestroy();
-                }
-            }
-            mServiceMap.clear();
-        }
-
         mHandlerThread.quit();
     }
 
@@ -491,68 +475,78 @@
      * A wrapper around IDataService that forwards calls to implementations of {@link DataService}.
      */
     private class IDataServiceWrapper extends IDataService.Stub {
-
-        private final int mSlotId;
-
-        IDataServiceWrapper(int slotId) {
-            mSlotId = slotId;
+        @Override
+        public void createDataServiceProvider(int slotId) {
+            mHandler.obtainMessage(DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER, slotId, 0)
+                    .sendToTarget();
         }
 
         @Override
-        public void setupDataCall(int accessNetworkType, DataProfile dataProfile,
+        public void removeDataServiceProvider(int slotId) {
+            mHandler.obtainMessage(DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER, slotId, 0)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void setupDataCall(int slotId, int accessNetworkType, DataProfile dataProfile,
                                   boolean isRoaming, boolean allowRoaming, int reason,
                                   LinkProperties linkProperties, IDataServiceCallback callback) {
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, mSlotId, 0,
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotId, 0,
                     new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
                             allowRoaming, reason, linkProperties, callback))
                     .sendToTarget();
         }
 
         @Override
-        public void deactivateDataCall(int cid, int reason, IDataServiceCallback callback) {
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, mSlotId, 0,
+        public void deactivateDataCall(int slotId, int cid, int reason,
+                                       IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, slotId, 0,
                     new DeactivateDataCallRequest(cid, reason, callback))
                     .sendToTarget();
         }
 
         @Override
-        public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+        public void setInitialAttachApn(int slotId, DataProfile dataProfile, boolean isRoaming,
                                         IDataServiceCallback callback) {
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, mSlotId, 0,
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, slotId, 0,
                     new SetInitialAttachApnRequest(dataProfile, isRoaming, callback))
                     .sendToTarget();
         }
 
         @Override
-        public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
+        public void setDataProfile(int slotId, List<DataProfile> dps, boolean isRoaming,
                                    IDataServiceCallback callback) {
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, mSlotId, 0,
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, slotId, 0,
                     new SetDataProfileRequest(dps, isRoaming, callback)).sendToTarget();
         }
 
         @Override
-        public void getDataCallList(IDataServiceCallback callback) {
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, mSlotId, 0,
+        public void getDataCallList(int slotId, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("getDataCallList: callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, slotId, 0,
                     callback).sendToTarget();
         }
 
         @Override
-        public void registerForDataCallListChanged(IDataServiceCallback callback) {
+        public void registerForDataCallListChanged(int slotId, IDataServiceCallback callback) {
             if (callback == null) {
-                loge("Callback is null");
+                loge("registerForDataCallListChanged: callback is null");
                 return;
             }
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, mSlotId,
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, slotId,
                     0, callback).sendToTarget();
         }
 
         @Override
-        public void unregisterForDataCallListChanged(IDataServiceCallback callback) {
+        public void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback) {
             if (callback == null) {
-                loge("Callback is null");
+                loge("unregisterForDataCallListChanged: callback is null");
                 return;
             }
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, mSlotId,
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, slotId,
                     0, callback).sendToTarget();
         }
     }
diff --git a/android/telephony/data/DataServiceCallback.java b/android/telephony/data/DataServiceCallback.java
index b6a81f9..4af31b5 100644
--- a/android/telephony/data/DataServiceCallback.java
+++ b/android/telephony/data/DataServiceCallback.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
+import android.net.LinkProperties;
 import android.os.RemoteException;
 import android.telephony.Rlog;
 import android.telephony.data.DataService.DataServiceProvider;
@@ -37,7 +38,7 @@
 @SystemApi
 public class DataServiceCallback {
 
-    private static final String mTag = DataServiceCallback.class.getSimpleName();
+    private static final String TAG = DataServiceCallback.class.getSimpleName();
 
     /**
      * Result of data requests
@@ -46,7 +47,7 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
             RESULT_ERROR_ILLEGAL_STATE})
-    public @interface Result {}
+    public @interface ResultCode {}
 
     /** Request is completed successfully */
     public static final int RESULT_SUCCESS              = 0;
@@ -68,35 +69,35 @@
 
     /**
      * Called to indicate result for the request {@link DataServiceProvider#setupDataCall(int,
-     * DataProfile, boolean, boolean, boolean, DataServiceCallback)}.
+     * DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)} .
      *
-     * @param result The result code. Must be one of the {@link Result}.
+     * @param result The result code. Must be one of the {@link ResultCode}.
      * @param response Setup data call response.
      */
-    public void onSetupDataCallComplete(@Result int result, DataCallResponse response) {
+    public void onSetupDataCallComplete(@ResultCode int result, DataCallResponse response) {
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
             try {
                 callback.onSetupDataCallComplete(result, response);
             } catch (RemoteException e) {
-                Rlog.e(mTag, "Failed to onSetupDataCallComplete on the remote");
+                Rlog.e(TAG, "Failed to onSetupDataCallComplete on the remote");
             }
         }
     }
 
     /**
      * Called to indicate result for the request {@link DataServiceProvider#deactivateDataCall(int,
-     * boolean, boolean, DataServiceCallback)}.
+     * int, DataServiceCallback)}
      *
-     * @param result The result code. Must be one of the {@link Result}.
+     * @param result The result code. Must be one of the {@link ResultCode}.
      */
-    public void onDeactivateDataCallComplete(@Result int result) {
+    public void onDeactivateDataCallComplete(@ResultCode int result) {
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
             try {
                 callback.onDeactivateDataCallComplete(result);
             } catch (RemoteException e) {
-                Rlog.e(mTag, "Failed to onDeactivateDataCallComplete on the remote");
+                Rlog.e(TAG, "Failed to onDeactivateDataCallComplete on the remote");
             }
         }
     }
@@ -105,15 +106,15 @@
      * Called to indicate result for the request {@link DataServiceProvider#setInitialAttachApn(
      * DataProfile, boolean, DataServiceCallback)}.
      *
-     * @param result The result code. Must be one of the {@link Result}.
+     * @param result The result code. Must be one of the {@link ResultCode}.
      */
-    public void onSetInitialAttachApnComplete(@Result int result) {
+    public void onSetInitialAttachApnComplete(@ResultCode int result) {
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
             try {
                 callback.onSetInitialAttachApnComplete(result);
             } catch (RemoteException e) {
-                Rlog.e(mTag, "Failed to onSetInitialAttachApnComplete on the remote");
+                Rlog.e(TAG, "Failed to onSetInitialAttachApnComplete on the remote");
             }
         }
     }
@@ -122,16 +123,16 @@
      * Called to indicate result for the request {@link DataServiceProvider#setDataProfile(List,
      * boolean, DataServiceCallback)}.
      *
-     * @param result The result code. Must be one of the {@link Result}.
+     * @param result The result code. Must be one of the {@link ResultCode}.
      */
     @SystemApi
-    public void onSetDataProfileComplete(@Result int result) {
+    public void onSetDataProfileComplete(@ResultCode int result) {
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
             try {
                 callback.onSetDataProfileComplete(result);
             } catch (RemoteException e) {
-                Rlog.e(mTag, "Failed to onSetDataProfileComplete on the remote");
+                Rlog.e(TAG, "Failed to onSetDataProfileComplete on the remote");
             }
         }
     }
@@ -140,16 +141,17 @@
      * Called to indicate result for the request {@link DataServiceProvider#getDataCallList(
      * DataServiceCallback)}.
      *
-     * @param result The result code. Must be one of the {@link Result}.
+     * @param result The result code. Must be one of the {@link ResultCode}.
      * @param dataCallList List of the current active data connection.
      */
-    public void onGetDataCallListComplete(@Result int result, List<DataCallResponse> dataCallList) {
+    public void onGetDataCallListComplete(@ResultCode int result,
+                                          List<DataCallResponse> dataCallList) {
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
             try {
                 callback.onGetDataCallListComplete(result, dataCallList);
             } catch (RemoteException e) {
-                Rlog.e(mTag, "Failed to onGetDataCallListComplete on the remote");
+                Rlog.e(TAG, "Failed to onGetDataCallListComplete on the remote");
             }
         }
     }
@@ -165,7 +167,7 @@
             try {
                 callback.onDataCallListChanged(dataCallList);
             } catch (RemoteException e) {
-                Rlog.e(mTag, "Failed to onDataCallListChanged on the remote");
+                Rlog.e(TAG, "Failed to onDataCallListChanged on the remote");
             }
         }
     }
diff --git a/android/telephony/euicc/DownloadableSubscription.java b/android/telephony/euicc/DownloadableSubscription.java
index 01041c8..edf3b08 100644
--- a/android/telephony/euicc/DownloadableSubscription.java
+++ b/android/telephony/euicc/DownloadableSubscription.java
@@ -16,17 +16,24 @@
 package android.telephony.euicc;
 
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.UiccAccessRule;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 import com.android.internal.util.Preconditions;
 
 /**
- * Information about a subscription which is available for download.
+ * Information about a subscription which is downloadable to an eUICC using
+ * {@link EuiccManager#downloadSubscription(DownloadableSubscription, boolean, PendingIntent).
  *
- * TODO(b/35851809): Make this public.
- * @hide
+ * <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 {
 
@@ -46,11 +53,12 @@
     /**
      * 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
-     *
-     * TODO(b/35851809): Make this a SystemApi.
+     * @deprecated - Do not use. This will be private. Use getEncodedActivationCode() instead.
      */
     @Nullable
+    @Deprecated
     public final String encodedActivationCode;
 
     @Nullable private String confirmationCode;
@@ -58,8 +66,16 @@
     // see getCarrierName and setCarrierName
     @Nullable
     private String carrierName;
+
     // see getAccessRules and setAccessRules
-    private UiccAccessRule[] accessRules;
+    @Nullable
+    private List<UiccAccessRule> accessRules;
+
+    /** Gets the activation code. */
+    @Nullable
+    public String getEncodedActivationCode() {
+        return encodedActivationCode;
+    }
 
     /** @hide */
     private DownloadableSubscription(String encodedActivationCode) {
@@ -70,13 +86,73 @@
         encodedActivationCode = in.readString();
         confirmationCode = in.readString();
         carrierName = in.readString();
-        accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
+        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;
+    }
+
+    /** @hide */
+    @SystemApi
+    public static final class Builder {
+        @Nullable private String encodedActivationCode;
+        @Nullable private String confirmationCode;
+        @Nullable private String carrierName;
+        List<UiccAccessRule> accessRules;
+
+        public Builder() {}
+
+        public Builder(DownloadableSubscription baseSubscription) {
+            encodedActivationCode = baseSubscription.getEncodedActivationCode();
+            confirmationCode = baseSubscription.getConfirmationCode();
+            carrierName = baseSubscription.getCarrierName();
+            accessRules = baseSubscription.getAccessRules();
+        }
+
+        public DownloadableSubscription build() {
+            return new DownloadableSubscription(encodedActivationCode, confirmationCode,
+                    carrierName, accessRules);
+        }
+
+        public Builder setEncodedActivationCode(String value) {
+            encodedActivationCode = value;
+            return this;
+        }
+
+        public Builder setConfirmationCode(String value) {
+            confirmationCode = value;
+            return this;
+        }
+
+        public Builder setCarrierName(String value) {
+            carrierName = value;
+            return this;
+        }
+
+        public Builder setAccessRules(List<UiccAccessRule> value) {
+            accessRules = value;
+            return this;
+        }
     }
 
     /**
      * Create a DownloadableSubscription for the given activation code.
      *
-     * @param encodedActivationCode the activation code to use. Must not be null.
+     * <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}.
      */
@@ -87,13 +163,19 @@
 
     /**
      * 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() {
@@ -103,9 +185,9 @@
     /**
      * Set the user-visible carrier name.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
+     * @deprecated - Do not use.
      */
+    @Deprecated
     public void setCarrierName(String carrierName) {
         this.carrierName = carrierName;
     }
@@ -117,44 +199,51 @@
      * those created with {@link #forActivationCode}). May be populated with
      * {@link EuiccManager#getDownloadableSubscriptionMetadata}.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
+    @SystemApi
     @Nullable
     public String getCarrierName() {
         return carrierName;
     }
 
     /**
-     * Returns the {@link UiccAccessRule}s dictating access to this subscription.
+     * 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
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
-    public UiccAccessRule[] getAccessRules() {
+    @SystemApi
+    public List<UiccAccessRule> getAccessRules() {
         return accessRules;
     }
 
     /**
      * Set the {@link UiccAccessRule}s dictating access to this subscription.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
+     * @deprecated - Do not use.
      */
-    public void setAccessRules(UiccAccessRule[] accessRules) {
+    @Deprecated
+    public void setAccessRules(List<UiccAccessRule> accessRules) {
         this.accessRules = accessRules;
     }
 
+    /**
+     * @hide
+     * @deprecated - Do not use.
+     */
+    @Deprecated
+    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.writeTypedArray(accessRules, flags);
+        dest.writeTypedList(accessRules);
     }
 
     @Override
diff --git a/android/telephony/euicc/EuiccCardManager.java b/android/telephony/euicc/EuiccCardManager.java
index 88bae33..38f9745 100644
--- a/android/telephony/euicc/EuiccCardManager.java
+++ b/android/telephony/euicc/EuiccCardManager.java
@@ -17,6 +17,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -49,14 +50,14 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import android.annotation.CallbackExecutor;
+import java.util.concurrent.Executor;
 
 /**
  * EuiccCardManager is the application interface to an eSIM card.
- *
  * @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
  */
+@SystemApi
 public class EuiccCardManager {
     private static final String TAG = "EuiccCardManager";
 
@@ -68,6 +69,7 @@
             CANCEL_REASON_TIMEOUT,
             CANCEL_REASON_PPR_NOT_ALLOWED
     })
+    /** @hide */
     public @interface CancelReason {}
 
     /**
@@ -96,6 +98,7 @@
             RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES,
             RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS
     })
+    /** @hide */
     public @interface ResetOption {}
 
     /** Deletes all operational profiles. */
@@ -110,6 +113,12 @@
     /** 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;
+
     /**
      * Callback to receive the result of an eUICC card API.
      *
@@ -140,18 +149,20 @@
     }
 
     /**
-     * Gets all the profiles on eUicc.
+     * Requests all the profiles on eUicc.
      *
      * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invode.
      * @param callback The callback to get the result code and all the profiles.
      */
-    public void getAllProfiles(String cardId, ResultCallback<EuiccProfileInfo[]> callback) {
+    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) {
-                            callback.onComplete(resultCode, profiles);
+                            executor.execute(() -> callback.onComplete(resultCode, profiles));
                         }
                     });
         } catch (RemoteException e) {
@@ -161,19 +172,21 @@
     }
 
     /**
-     * Gets the profile of the given iccid.
+     * 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 invode.
      * @param callback The callback to get the result code and profile.
      */
-    public void getProfile(String cardId, String iccid, ResultCallback<EuiccProfileInfo> callback) {
+    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) {
-                            callback.onComplete(resultCode, profile);
+                            executor.execute(() -> callback.onComplete(resultCode, profile));
                         }
                     });
         } catch (RemoteException e) {
@@ -188,16 +201,17 @@
      * @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 invode.
      * @param callback The callback to get the result code.
      */
     public void disableProfile(String cardId, String iccid, boolean refresh,
-            ResultCallback<Void> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
         try {
             getIEuiccCardController().disableProfile(mContext.getOpPackageName(), cardId, iccid,
                     refresh, new IDisableProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            callback.onComplete(resultCode, null);
+                            executor.execute(() -> callback.onComplete(resultCode, null));
                         }
                     });
         } catch (RemoteException e) {
@@ -213,16 +227,17 @@
      * @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 invode.
      * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
      */
     public void switchToProfile(String cardId, String iccid, boolean refresh,
-            ResultCallback<EuiccProfileInfo> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<EuiccProfileInfo> callback) {
         try {
             getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid,
                     refresh, new ISwitchToProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo profile) {
-                            callback.onComplete(resultCode, profile);
+                            executor.execute(() -> callback.onComplete(resultCode, profile));
                         }
                     });
         } catch (RemoteException e) {
@@ -237,16 +252,17 @@
      * @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 invode.
      * @param callback The callback to get the result code.
      */
     public void setNickname(String cardId, String iccid, String nickname,
-            ResultCallback<Void> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
         try {
             getIEuiccCardController().setNickname(mContext.getOpPackageName(), cardId, iccid,
                     nickname, new ISetNicknameCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            callback.onComplete(resultCode, null);
+                            executor.execute(() -> callback.onComplete(resultCode, null));
                         }
                     });
         } catch (RemoteException e) {
@@ -260,15 +276,17 @@
      *
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile.
+     * @param executor The executor through which the callback should be invode.
      * @param callback The callback to get the result code.
      */
-    public void deleteProfile(String cardId, String iccid, ResultCallback<Void> callback) {
+    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) {
-                            callback.onComplete(resultCode, null);
+                            executor.execute(() -> callback.onComplete(resultCode, null));
                         }
                     });
         } catch (RemoteException e) {
@@ -283,15 +301,17 @@
      * @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 invode.
      * @param callback The callback to get the result code.
      */
-    public void resetMemory(String cardId, @ResetOption int options, ResultCallback<Void> callback) {
+    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) {
-                            callback.onComplete(resultCode, null);
+                            executor.execute(() -> callback.onComplete(resultCode, null));
                         }
                     });
         } catch (RemoteException e) {
@@ -301,18 +321,20 @@
     }
 
     /**
-     * Gets the default SM-DP+ address from eUICC.
+     * 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 invode.
      * @param callback The callback to get the result code and the default SM-DP+ address.
      */
-    public void getDefaultSmdpAddress(String cardId, ResultCallback<String> callback) {
+    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) {
-                            callback.onComplete(resultCode, address);
+                            executor.execute(() -> callback.onComplete(resultCode, address));
                         }
                     });
         } catch (RemoteException e) {
@@ -322,18 +344,20 @@
     }
 
     /**
-     * Gets the SM-DS address from eUICC.
+     * Requests the SM-DS address from eUICC.
      *
      * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invode.
      * @param callback The callback to get the result code and the SM-DS address.
      */
-    public void getSmdsAddress(String cardId, ResultCallback<String> callback) {
+    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) {
-                            callback.onComplete(resultCode, address);
+                            executor.execute(() -> callback.onComplete(resultCode, address));
                         }
                     });
         } catch (RemoteException e) {
@@ -347,16 +371,18 @@
      *
      * @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 invode.
      * @param callback The callback to get the result code.
      */
-    public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress, ResultCallback<Void> callback) {
+    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) {
-                            callback.onComplete(resultCode, null);
+                            executor.execute(() -> callback.onComplete(resultCode, null));
                         }
                     });
         } catch (RemoteException e) {
@@ -366,18 +392,20 @@
     }
 
     /**
-     * Gets Rules Authorisation Table.
+     * Requests Rules Authorisation Table.
      *
      * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invode.
      * @param callback the callback to get the result code and the rule authorisation table.
      */
-    public void getRulesAuthTable(String cardId, ResultCallback<EuiccRulesAuthTable> callback) {
+    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) {
-                            callback.onComplete(resultCode, rat);
+                            executor.execute(() -> callback.onComplete(resultCode, rat));
                         }
                     });
         } catch (RemoteException e) {
@@ -387,18 +415,20 @@
     }
 
     /**
-     * Gets the eUICC challenge for new profile downloading.
+     * 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 invode.
      * @param callback the callback to get the result code and the challenge.
      */
-    public void getEuiccChallenge(String cardId, ResultCallback<byte[]> callback) {
+    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) {
-                            callback.onComplete(resultCode, challenge);
+                            executor.execute(() -> callback.onComplete(resultCode, challenge));
                         }
                     });
         } catch (RemoteException e) {
@@ -408,18 +438,20 @@
     }
 
     /**
-     * Gets the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading.
+     * 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 invode.
      * @param callback the callback to get the result code and the info1.
      */
-    public void getEuiccInfo1(String cardId, ResultCallback<byte[]> callback) {
+    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) {
-                            callback.onComplete(resultCode, info);
+                            executor.execute(() -> callback.onComplete(resultCode, info));
                         }
                     });
         } catch (RemoteException e) {
@@ -432,15 +464,17 @@
      * 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 invode.
      * @param callback the callback to get the result code and the info2.
      */
-    public void getEuiccInfo2(String cardId, ResultCallback<byte[]> callback) {
+    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) {
-                            callback.onComplete(resultCode, info);
+                            executor.execute(() -> callback.onComplete(resultCode, info));
                         }
                     });
         } catch (RemoteException e) {
@@ -463,12 +497,13 @@
      *     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 invode.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+.
      */
     public void authenticateServer(String cardId, String matchingId, byte[] serverSigned1,
             byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate,
-            ResultCallback<byte[]> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) {
         try {
             getIEuiccCardController().authenticateServer(
                     mContext.getOpPackageName(),
@@ -481,7 +516,7 @@
                     new IAuthenticateServerCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            callback.onComplete(resultCode, response);
+                            executor.execute(() -> callback.onComplete(resultCode, response));
                         }
                     });
         } catch (RemoteException e) {
@@ -502,11 +537,13 @@
      *     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 invode.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+
      */
     public void prepareDownload(String cardId, @Nullable byte[] hashCc, byte[] smdpSigned2,
-            byte[] smdpSignature2, byte[] smdpCertificate, ResultCallback<byte[]> callback) {
+            byte[] smdpSignature2, byte[] smdpCertificate, @CallbackExecutor Executor executor,
+            ResultCallback<byte[]> callback) {
         try {
             getIEuiccCardController().prepareDownload(
                     mContext.getOpPackageName(),
@@ -518,7 +555,7 @@
                     new IPrepareDownloadCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            callback.onComplete(resultCode, response);
+                            executor.execute(() -> callback.onComplete(resultCode, response));
                         }
                     });
         } catch (RemoteException e) {
@@ -532,11 +569,12 @@
      *
      * @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 invode.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+.
      */
     public void loadBoundProfilePackage(String cardId, byte[] boundProfilePackage,
-            ResultCallback<byte[]> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) {
         try {
             getIEuiccCardController().loadBoundProfilePackage(
                     mContext.getOpPackageName(),
@@ -545,7 +583,7 @@
                     new ILoadBoundProfilePackageCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            callback.onComplete(resultCode, response);
+                            executor.execute(() -> callback.onComplete(resultCode, response));
                         }
                     });
         } catch (RemoteException e) {
@@ -560,11 +598,12 @@
      * @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 invode.
      * @param callback the callback to get the result code and an byte[] which represents a
      *     {@code CancelSessionResponse} defined in GSMA RSP v2.0+.
      */
     public void cancelSession(String cardId, byte[] transactionId, @CancelReason int reason,
-            ResultCallback<byte[]> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) {
         try {
             getIEuiccCardController().cancelSession(
                     mContext.getOpPackageName(),
@@ -574,7 +613,7 @@
                     new ICancelSessionCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            callback.onComplete(resultCode, response);
+                            executor.execute(() -> callback.onComplete(resultCode, response));
                         }
                     });
         } catch (RemoteException e) {
@@ -588,16 +627,17 @@
      *
      * @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 invode.
      * @param callback the callback to get the result code and the list of notifications.
      */
     public void listNotifications(String cardId, @EuiccNotification.Event int events,
-            ResultCallback<EuiccNotification[]> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<EuiccNotification[]> callback) {
         try {
             getIEuiccCardController().listNotifications(mContext.getOpPackageName(), cardId, events,
                     new IListNotificationsCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification[] notifications) {
-                            callback.onComplete(resultCode, notifications);
+                            executor.execute(() -> callback.onComplete(resultCode, notifications));
                         }
                     });
         } catch (RemoteException e) {
@@ -611,16 +651,17 @@
      *
      * @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 invode.
      * @param callback the callback to get the result code and the list of notifications.
      */
     public void retrieveNotificationList(String cardId, @EuiccNotification.Event int events,
-            ResultCallback<EuiccNotification[]> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<EuiccNotification[]> callback) {
         try {
             getIEuiccCardController().retrieveNotificationList(mContext.getOpPackageName(), cardId,
                     events, new IRetrieveNotificationListCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification[] notifications) {
-                            callback.onComplete(resultCode, notifications);
+                            executor.execute(() -> callback.onComplete(resultCode, notifications));
                         }
                     });
         } catch (RemoteException e) {
@@ -634,16 +675,17 @@
      *
      * @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 invode.
      * @param callback the callback to get the result code and the notification.
      */
     public void retrieveNotification(String cardId, int seqNumber,
-            ResultCallback<EuiccNotification> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<EuiccNotification> callback) {
         try {
             getIEuiccCardController().retrieveNotification(mContext.getOpPackageName(), cardId,
                     seqNumber, new IRetrieveNotificationCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification notification) {
-                            callback.onComplete(resultCode, notification);
+                            executor.execute(() -> callback.onComplete(resultCode, notification));
                         }
                     });
         } catch (RemoteException e) {
@@ -657,10 +699,11 @@
      *
      * @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 invode.
      * @param callback the callback to get the result code.
      */
     public void removeNotificationFromList(String cardId, int seqNumber,
-            ResultCallback<Void> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
         try {
             getIEuiccCardController().removeNotificationFromList(
                     mContext.getOpPackageName(),
@@ -669,7 +712,7 @@
                     new IRemoveNotificationFromListCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            callback.onComplete(resultCode, null);
+                            executor.execute(() -> callback.onComplete(resultCode, null));
                         }
                     });
         } catch (RemoteException e) {
diff --git a/android/telephony/euicc/EuiccInfo.java b/android/telephony/euicc/EuiccInfo.java
index 5bfff08..a4adf05 100644
--- a/android/telephony/euicc/EuiccInfo.java
+++ b/android/telephony/euicc/EuiccInfo.java
@@ -23,9 +23,6 @@
  * Information about an eUICC chip/device.
  *
  * @see EuiccManager#getEuiccInfo
- * @hide
- *
- * TODO(b/35851809): Make this public.
  */
 // 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
@@ -45,12 +42,17 @@
                 }
             };
 
+    @Nullable
+    private final String osVersion;
+
     /**
-     * Version of the operating system running on the eUICC. This field is hardware-specific and is
-     * not guaranteed to match any particular format.
+     * 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 final String osVersion;
+    public String getOsVersion() {
+        return osVersion;
+    }
 
     public EuiccInfo(@Nullable String osVersion) {
         this.osVersion = osVersion;
diff --git a/android/telephony/euicc/EuiccManager.java b/android/telephony/euicc/EuiccManager.java
index 7f913ce..b732d4d 100644
--- a/android/telephony/euicc/EuiccManager.java
+++ b/android/telephony/euicc/EuiccManager.java
@@ -15,11 +15,12 @@
  */
 package android.telephony.euicc;
 
+import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -42,9 +43,6 @@
  * {@link Context#getSystemService(String)} and {@link Context#EUICC_SERVICE}.
  *
  * <p>See {@link #isEnabled} before attempting to use these APIs.
- *
- * TODO(b/35851809): Make this public.
- * @hide
  */
 public class EuiccManager {
 
@@ -56,6 +54,8 @@
      *
      * <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 =
@@ -69,21 +69,22 @@
      *
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
-     * TODO(b/35851809): Make this a SystemApi.
+     *
+     * @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.
-     *
-     * TODO(b/35851809): Make this a public API.
      */
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_NOTIFY_CARRIER_SETUP =
-            "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP";
+    public static final String ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE =
+            "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE";
 
     /**
      * Intent action to provision an embedded subscription.
@@ -95,8 +96,9 @@
      * <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if
      * {@link #isEnabled} is false.
      *
-     * TODO(b/35851809): Make this a SystemApi.
+     * @hide
      */
+    @SystemApi
     @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION =
             "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
@@ -140,11 +142,8 @@
             "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
 
     /**
-     * Key for an extra set on {@link #getDownloadableSubscriptionMetadata} PendingIntent result
+     * Key for an extra set on {@code #getDownloadableSubscriptionMetadata} PendingIntent result
      * callbacks providing the downloadable subscription metadata.
-     * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
     public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION =
             "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
@@ -153,9 +152,8 @@
      * Key for an extra set on {@link #getDefaultDownloadableSubscriptionList} PendingIntent result
      * callbacks providing the list of available downloadable subscriptions.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
+    @SystemApi
     public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS =
             "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
 
@@ -201,6 +199,7 @@
      * Euicc OTA update status which can be got by {@link #getOtaStatus}
      * @hide
      */
+    @SystemApi
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"EUICC_OTA_"}, value = {
             EUICC_OTA_IN_PROGRESS,
@@ -215,15 +214,37 @@
     /**
      * 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. */
+
+    /**
+     * The OTA update failed.
+     * @hide
+     */
+    @SystemApi
     public static final int EUICC_OTA_FAILED = 2;
-    /** The OTA update finished successfully. */
+
+    /**
+     * 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. */
+
+    /**
+     * 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. */
+
+    /**
+     * The OTA status is unavailable since eUICC service is unavailable.
+     * @hide
+     */
+    @SystemApi
     public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5;
 
     private final Context mContext;
@@ -276,8 +297,11 @@
      *
      * @return the status of eUICC OTA. If {@link #isEnabled()} is false or the eUICC is not ready,
      *     {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned.
-     * TODO(b/35851809): Make this a SystemApi.
+     *
+     * @hide
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public int getOtaStatus() {
         if (!isEnabled()) {
             return EUICC_OTA_STATUS_UNAVAILABLE;
@@ -292,7 +316,7 @@
     /**
      * Attempt to download the given {@link DownloadableSubscription}.
      *
-     * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
+     * <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 downloaded according to the subscription metadata. Without the former,
      * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
@@ -302,6 +326,7 @@
      * @param switchAfterDownload if true, the profile will be activated upon successful download.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void downloadSubscription(DownloadableSubscription subscription,
             boolean switchAfterDownload, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -354,14 +379,17 @@
      *
      * <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.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
         if (!isEnabled()) {
             PendingIntent callbackIntent =
@@ -395,9 +423,9 @@
      * @param subscription the subscription which needs metadata filled in
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDownloadableSubscriptionMetadata(
             DownloadableSubscription subscription, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -426,9 +454,9 @@
      *
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -468,11 +496,12 @@
      *
      * <p>Requires that the calling app has carrier privileges according to the metadata of the
      * profile to be deleted, or the
-     * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      *
      * @param subscriptionId the ID of the subscription to delete.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -489,7 +518,7 @@
     /**
      * Switch to (enable) the given subscription.
      *
-     * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
+     * <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
@@ -500,6 +529,7 @@
      *     current profile without activating another profile to replace it.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -525,6 +555,7 @@
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void updateSubscriptionNickname(
             int subscriptionId, String nickname, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -543,12 +574,13 @@
      * Erase all subscriptions and reset the eUICC.
      *
      * <p>Requires that the calling app has the
-     * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for
-     * internal system use only.
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      *
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void eraseSubscriptions(PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -599,11 +631,7 @@
         }
     }
 
-    /**
-     * @hide
-     */
-    @TestApi
-    protected IEuiccController getIEuiccController() {
+    private static IEuiccController getIEuiccController() {
         return IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller"));
     }
 }
diff --git a/android/telephony/euicc/EuiccNotification.java b/android/telephony/euicc/EuiccNotification.java
index ef3c1ce..43a7707 100644
--- a/android/telephony/euicc/EuiccNotification.java
+++ b/android/telephony/euicc/EuiccNotification.java
@@ -17,6 +17,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -31,10 +32,9 @@
  * disabling, or deleting).
  *
  * @hide
- *
- * TODO(b/35851809): Make this a @SystemApi.
  */
-public class EuiccNotification implements Parcelable {
+@SystemApi
+public final class EuiccNotification implements Parcelable {
     /** Event */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "EVENT_" }, value = {
@@ -43,6 +43,7 @@
             EVENT_DISABLE,
             EVENT_DELETE
     })
+    /** @hide */
     public @interface Event {}
 
     /** A profile is downloaded and installed. */
@@ -57,7 +58,7 @@
     /** A profile is deleted. */
     public static final int EVENT_DELETE = 1 << 3;
 
-    /** Value of the bits of all above events */
+    /** 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;
diff --git a/android/telephony/euicc/EuiccRulesAuthTable.java b/android/telephony/euicc/EuiccRulesAuthTable.java
index 7efe043..67ae983 100644
--- a/android/telephony/euicc/EuiccRulesAuthTable.java
+++ b/android/telephony/euicc/EuiccRulesAuthTable.java
@@ -16,6 +16,7 @@
 package android.telephony.euicc;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.service.carrier.CarrierIdentifier;
@@ -27,20 +28,21 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This represents the RAT (Rules Authorisation Table) stored on eUICC.
- *
  * @hide
- *
- * TODO(b/35851809): Make this a @SystemApi.
  */
+@SystemApi
 public final class EuiccRulesAuthTable implements Parcelable {
     /** Profile policy rule flags */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "POLICY_RULE_FLAG_" }, value = {
             POLICY_RULE_FLAG_CONSENT_REQUIRED
     })
+    /** @hide */
     public @interface PolicyRuleFlag {}
 
     /** User consent is required to install the profile. */
@@ -89,12 +91,14 @@
          * @throws ArrayIndexOutOfBoundsException If the {@code mPosition} is larger than the size
          *     this table.
          */
-        public Builder add(int policyRules, CarrierIdentifier[] carrierId, int policyRuleFlags) {
+        public Builder add(int policyRules, List<CarrierIdentifier> carrierId, int policyRuleFlags) {
             if (mPosition >= mPolicyRules.length) {
                 throw new ArrayIndexOutOfBoundsException(mPosition);
             }
             mPolicyRules[mPosition] = policyRules;
-            mCarrierIds[mPosition] = carrierId;
+            if (carrierId != null && carrierId.size() > 0) {
+                mCarrierIds[mPosition] = carrierId.toArray(new CarrierIdentifier[carrierId.size()]);
+            }
             mPolicyRuleFlags[mPosition] = policyRuleFlags;
             mPosition++;
             return this;
diff --git a/android/telephony/ims/ImsCallForwardInfo.java b/android/telephony/ims/ImsCallForwardInfo.java
new file mode 100644
index 0000000..2831127
--- /dev/null
+++ b/android/telephony/ims/ImsCallForwardInfo.java
@@ -0,0 +1,148 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Provides the call forward information for the supplementary service configuration.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsCallForwardInfo implements Parcelable {
+    // Refer to ImsUtInterface#CDIV_CF_XXX
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
+    public int mCondition;
+    // 0: disabled, 1: enabled
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
+    public int mStatus;
+    // 0x91: International, 0x81: Unknown
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
+    public int mToA;
+    // Service class
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
+    public int mServiceClass;
+    // Number (it will not include the "sip" or "tel" URI scheme)
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
+    public String mNumber;
+    // No reply timer for CF
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
+    public int mTimeSeconds;
+
+    /** @hide */
+    // TODO: Will be removed in the future, use public constructor instead.
+    public ImsCallForwardInfo() {
+    }
+
+    /**
+     * IMS Call Forward Information.
+     */
+    public ImsCallForwardInfo(int condition, int status, int toA, int serviceClass, String number,
+            int replyTimerSec) {
+        mCondition = condition;
+        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);
+    }
+
+    @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 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];
+        }
+    };
+
+    public int getCondition() {
+        return mCondition;
+    }
+
+    public int getStatus() {
+        return mStatus;
+    }
+
+    public int getToA() {
+        return mToA;
+    }
+
+    public int getServiceClass() {
+        return mServiceClass;
+    }
+
+    public String getNumber() {
+        return mNumber;
+    }
+
+    public int getTimeSeconds() {
+        return mTimeSeconds;
+    }
+}
diff --git a/android/telephony/ims/ImsCallProfile.java b/android/telephony/ims/ImsCallProfile.java
new file mode 100644
index 0000000..350dfe3
--- /dev/null
+++ b/android/telephony/ims/ImsCallProfile.java
@@ -0,0 +1,668 @@
+/*
+ * 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.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.telecom.VideoProfile;
+import android.util.Log;
+
+import com.android.internal.telephony.PhoneConstants;
+
+/**
+ * Parcelable object to handle IMS call profile.
+ * It is created from GSMA IR.92/IR.94, 3GPP TS 24.229/TS 26.114/TS26.111.
+ * It provides the service and call type, the additional information related to the call.
+ *
+ * @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;
+
+    /**
+     * Call types
+     */
+    /**
+     * 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.
+     */
+    /**
+     * @hide
+     */
+    public static final String EXTRA_CONFERENCE = "conference";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_E_CALL = "e_call";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_VMS = "vms";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_CALL_MODE_CHANGEABLE = "call_mode_changeable";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_CONFERENCE_AVAIL = "conference_avail";
+
+    // Extra string for internal use only. OEMs should not use
+    // this for packing extras.
+    /**
+     * @hide
+     */
+    public static final String EXTRA_OEM_EXTRAS = "OemCallExtras";
+
+    /**
+     * 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";
+
+    /**
+     * 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;
+
+    //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 causes that restrict call types
+     */
+    // Default cause not restricted at peer and HD is supported
+    public static final int CALL_RESTRICT_CAUSE_NONE = 0;
+    // Service not supported by RAT at peer
+    public static final int CALL_RESTRICT_CAUSE_RAT = 1;
+    // Service Disabled at peer
+    public static final int CALL_RESTRICT_CAUSE_DISABLED = 2;
+    // HD is not supported
+    public static final int CALL_RESTRICT_CAUSE_HD = 3;
+
+    /**
+     * 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";
+
+    /**
+     * 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#
+     *      updateWifiStateFromExtras(Bundle)} to determine whether to set the
+     * {@link android.telecom.Connection#PROPERTY_WIFI} property on a connection.
+     */
+    public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+
+    /**
+     * 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
+     */
+    public static final String EXTRA_CALL_RAT_TYPE_ALT = "callRadioTech";
+
+    /** @hide */
+    public int mServiceType;
+    /** @hide */
+    public int mCallType;
+    /** @hide */
+    public int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
+
+    /**
+     * 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 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 */
+    public Bundle mCallExtras;
+    /** @hide */
+    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);
+    }
+
+    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);
+        }
+    }
+
+    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;
+    }
+
+
+    @Override
+    public String toString() {
+        return "{ serviceType=" + mServiceType +
+                ", callType=" + mCallType +
+                ", restrictCause=" + mRestrictCause +
+                ", mediaProfile=" + mMediaProfile.toString() + " }";
+    }
+
+    @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);
+    }
+
+    private void readFromParcel(Parcel in) {
+        mServiceType = in.readInt();
+        mCallType = in.readInt();
+        mCallExtras = in.readBundle();
+        mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
+    }
+
+    public static final 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;
+    }
+
+    public int getRestrictCause() {
+        return mRestrictCause;
+    }
+
+    public Bundle getCallExtras() {
+        return mCallExtras;
+    }
+
+    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
+     */
+    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;
+            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_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 = extras.filterValues();
+        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;
+    }
+}
diff --git a/android/telephony/ims/ImsCallSession.java b/android/telephony/ims/ImsCallSession.java
new file mode 100644
index 0000000..a20d4f5
--- /dev/null
+++ b/android/telephony/ims/ImsCallSession.java
@@ -0,0 +1,1425 @@
+/*
+ * 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.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+import android.util.Log;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
+
+/**
+ * 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 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>
+     * @hide
+     */
+    public static class Listener {
+        /**
+         * Called when a request is sent out to initiate a new session
+         * and 1xx response is received from the network.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        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 radio technology 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 srcAccessTech original access technology
+         * @param targetAccessTech new access technology
+         */
+        public void callSessionMayHandover(ImsCallSession session, int srcAccessTech,
+                int targetAccessTech) {
+            // no-op
+        }
+
+        /**
+         * Called when session access technology changes
+         *
+         * @param session IMS session object
+         * @param srcAccessTech original access technology
+         * @param targetAccessTech new access technology
+         * @param reasonInfo
+         */
+        public void callSessionHandover(ImsCallSession session,
+                                 int srcAccessTech, int targetAccessTech,
+                                 ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when session access technology change fails
+         *
+         * @param session IMS session object
+         * @param srcAccessTech original access technology
+         * @param targetAccessTech new access technology
+         * @param reasonInfo handover failure reason
+         */
+        public void callSessionHandoverFailed(ImsCallSession session,
+                                       int srcAccessTech, int targetAccessTech,
+                                       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
+        }
+    }
+
+    private final IImsCallSession miSession;
+    private boolean mClosed = false;
+    private Listener mListener;
+
+    /** @hide */
+    public ImsCallSession(IImsCallSession iSession) {
+        miSession = iSession;
+
+        if (iSession != null) {
+            try {
+                iSession.setListener(new IImsCallSessionListenerProxy());
+            } catch (RemoteException e) {
+            }
+        } else {
+            mClosed = true;
+        }
+    }
+
+    /** @hide */
+    public ImsCallSession(IImsCallSession iSession, Listener listener) {
+        this(iSession);
+        setListener(listener);
+    }
+
+    /**
+     * 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
+     */
+    public String getCallId() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getCallId();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * 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.
+     * @hide
+     */
+    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.
+     * @hide
+     */
+    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
+     * @hide
+     */
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * 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 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 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
+     * @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) {
+        }
+    }
+
+    /**
+     * 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 request
+     *
+     * @param to   : expected profile
+     */
+    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) {
+        }
+    }
+
+    /**
+     * 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 callSessionProgressing(ImsStreamMediaProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionProgressing(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionInitiated(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionStarted(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionTerminated(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies the result of the call hold/resume operation.
+         */
+        @Override
+        public void callSessionHeld(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionHeld(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionHoldReceived(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionHoldReceived(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionResumed(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionResumed(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionResumeReceived(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionResumeReceived(ImsCallSession.this, profile);
+            }
+        }
+
+        /**
+         * 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) {
+            if (mListener != null) {
+                if (newSession != null) {
+                    // Check if the active session is the same session that was
+                    // active before the merge request was sent.
+                    ImsCallSession validActiveSession = ImsCallSession.this;
+                    try {
+                        if (!Objects.equals(miSession.getCallId(), newSession.getCallId())) {
+                            // New session created after conference
+                            validActiveSession = new ImsCallSession(newSession);
+                        }
+                    } catch (RemoteException rex) {
+                        Log.e(TAG, "callSessionMergeComplete: exception for getCallId!");
+                    }
+                    mListener.callSessionMergeComplete(validActiveSession);
+               } else {
+                   // Session already exists. Hence no need to pass
+                   mListener.callSessionMergeComplete(null);
+               }
+            }
+        }
+
+        /**
+         * Notifies of a failure to perform a call merge operation.
+         *
+         * @param reasonInfo The merge failure reason.
+         */
+        @Override
+        public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies the result of call upgrade / downgrade or any other call updates.
+         */
+        @Override
+        public void callSessionUpdated(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionUpdated(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionUpdateReceived(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
+            }
+        }
+
+        /**
+         * Notifies the result of conference extension.
+         */
+        @Override
+        public void callSessionConferenceExtended(IImsCallSession newSession,
+                ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionConferenceExtended(ImsCallSession.this,
+                        new ImsCallSession(newSession), profile);
+            }
+        }
+
+        @Override
+        public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionConferenceExtendReceived(IImsCallSession newSession,
+                ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
+                        new ImsCallSession(newSession), profile);
+            }
+        }
+
+        /**
+         * Notifies the result of the participant invitation / removal to/from
+         * the conference session.
+         */
+        @Override
+        public void callSessionInviteParticipantsRequestDelivered() {
+            if (mListener != null) {
+                mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this);
+            }
+        }
+
+        @Override
+        public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
+                        reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestDelivered() {
+            if (mListener != null) {
+                mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
+            }
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
+                        reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies the changes of the conference info. in the conference session.
+         */
+        @Override
+        public void callSessionConferenceStateUpdated(ImsConferenceState state) {
+            if (mListener != null) {
+                mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
+            }
+        }
+
+        /**
+         * Notifies the incoming USSD message.
+         */
+        @Override
+        public void callSessionUssdMessageReceived(int mode, String ussdMessage) {
+            if (mListener != null) {
+                mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage);
+            }
+        }
+
+        /**
+         * Notifies of a case where a {@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}.
+         */
+        @Override
+        public void callSessionMayHandover(int srcAccessTech, int targetAccessTech) {
+            if (mListener != null) {
+                mListener.callSessionMayHandover(ImsCallSession.this, srcAccessTech,
+                        targetAccessTech);
+            }
+        }
+
+        /**
+         * Notifies of handover information for this call
+         */
+        @Override
+        public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+                ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionHandover(ImsCallSession.this, srcAccessTech,
+                        targetAccessTech, reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies of handover failure info for this call
+         */
+        @Override
+        public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+                ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionHandoverFailed(ImsCallSession.this, srcAccessTech,
+                        targetAccessTech, reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies the TTY mode received from remote party.
+         */
+        @Override
+        public void callSessionTtyModeReceived(int mode) {
+            if (mListener != null) {
+                mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
+            }
+        }
+
+        /**
+         * 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) {
+            if (mListener != null) {
+                mListener.callSessionMultipartyStateChanged(ImsCallSession.this, isMultiParty);
+            }
+        }
+
+        @Override
+        public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppServiceInfo ) {
+            if (mListener != null) {
+                mListener.callSessionSuppServiceReceived(ImsCallSession.this, suppServiceInfo);
+            }
+        }
+
+        /**
+         * Received RTT modify request from remote party
+         */
+        @Override
+        public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) {
+            if (mListener != null) {
+                mListener.callSessionRttModifyRequestReceived(ImsCallSession.this, callProfile);
+            }
+        }
+
+        /**
+         * Received response for RTT modify request
+         */
+        @Override
+        public void callSessionRttModifyResponseReceived(int status) {
+            if (mListener != null) {
+                mListener.callSessionRttModifyResponseReceived(status);
+            }
+        }
+
+        /**
+         * RTT Message received
+         */
+        @Override
+        public void callSessionRttMessageReceived(String rttMessage) {
+            if (mListener != null) {
+                mListener.callSessionRttMessageReceived(rttMessage);
+            }
+        }
+    }
+
+    /**
+     * 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(" state:");
+        sb.append(State.toString(getState()));
+        sb.append(" callId:");
+        sb.append(getCallId());
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/android/telephony/ims/ImsCallSessionListener.java b/android/telephony/ims/ImsCallSessionListener.java
new file mode 100644
index 0000000..a7f124a
--- /dev/null
+++ b/android/telephony/ims/ImsCallSessionListener.java
@@ -0,0 +1,603 @@
+/*
+ * 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.os.RemoteException;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+
+import com.android.ims.internal.IImsCallSession;
+
+/**
+ * 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 final IImsCallSessionListener mListener;
+
+    /** @hide */
+    public ImsCallSessionListener(IImsCallSessionListener l) {
+        mListener = l;
+    }
+
+    /**
+     * A request has been sent out to initiate a new IMS call session and a 1xx response has been
+     * received from the network.
+     */
+    public void callSessionProgressing(ImsStreamMediaProfile profile) {
+        try {
+            mListener.callSessionProgressing(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session has been initiated.
+     *
+     * @param profile the associated {@link ImsCallProfile}.
+     */
+    public void callSessionInitiated(ImsCallProfile profile) {
+        try {
+            mListener.callSessionInitiated(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session establishment has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the IMS call session
+     * establishment failure.
+     */
+    public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionInitiatedFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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 {
+            mListener.callSessionMergeStarted(newSession != null ?
+                            newSession.getServiceImpl() : null, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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 {
+            mListener.callSessionMergeComplete(newSession != null ?
+                    newSession.getServiceImpl() : null);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Compatibility method for older implementations of ImsService.
+     *
+     * See {@link #callSessionMergeComplete(ImsCallSessionImplBase)}}.
+     *
+     * @hide
+     */
+    public void callSessionMergeComplete(IImsCallSession newSession) {
+        try {
+            mListener.callSessionMergeComplete(newSession);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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 {
+            mListener.callSessionConferenceExtended(
+                    newSession != null ? newSession.getServiceImpl() : null, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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 {
+            mListener.callSessionConferenceExtendReceived(newSession != null
+                    ? newSession.getServiceImpl() : null, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The request to invite participants to the conference has been delivered to the conference
+     * server.
+     */
+    public void callSessionInviteParticipantsRequestDelivered() {
+        try {
+            mListener.callSessionInviteParticipantsRequestDelivered();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The request to remove participants from the conference has been delivered to the conference
+     * server.
+     */
+    public void callSessionRemoveParticipantsRequestDelivered() {
+        try {
+            mListener.callSessionRemoveParticipantsRequestDelivered();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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}.
+     */
+    public void callSessionMayHandover(int srcAccessTech, int targetAccessTech)
+    {
+        try {
+            mListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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.
+     */
+    public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+            ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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.
+     */
+    public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+            ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 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) {
+            throw new RuntimeException(e);
+        }
+    }
+}
+
diff --git a/android/telephony/ims/ImsConferenceState.java b/android/telephony/ims/ImsConferenceState.java
new file mode 100644
index 0000000..66d2f8d
--- /dev/null
+++ b/android/telephony/ims/ImsConferenceState.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 java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telecom.Call;
+import android.telecom.Connection;
+
+/**
+ * Provides the conference information (defined in RFC 4575) for IMS conference call.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsConferenceState implements Parcelable {
+    /**
+     * 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);
+            mParticipants.put(user, state);
+        }
+    }
+
+    public static final 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;
+    }
+
+    @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(entry.getKey());
+                    sb.append(": ");
+                    Bundle participantData = entry.getValue();
+
+                    for (String key : participantData.keySet()) {
+                        sb.append(key);
+                        sb.append("=");
+                        if (ENDPOINT.equals(key) || USER.equals(key)) {
+                            sb.append(android.telecom.Log.pii(participantData.get(key)));
+                        } else {
+                            sb.append(participantData.get(key));
+                        }
+                        sb.append(", ");
+                    }
+                }
+                sb.append(">");
+            }
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/android/telephony/ims/ImsExternalCallState.java b/android/telephony/ims/ImsExternalCallState.java
new file mode 100644
index 0000000..e82c115
--- /dev/null
+++ b/android/telephony/ims/ImsExternalCallState.java
@@ -0,0 +1,145 @@
+/*
+ * 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.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telecom.Log;
+import android.telephony.Rlog;
+
+/*
+ * This file contains all the api's through which
+ * information received in Dialog Event Package can be
+ * queried
+ */
+
+/**
+ * Parcelable object to handle MultiEndpoint Dialog Information
+ * @hide
+ */
+@SystemApi
+public final class ImsExternalCallState implements Parcelable {
+
+    private static final String TAG = "ImsExternalCallState";
+
+    // Dialog States
+    public static final int CALL_STATE_CONFIRMED = 1;
+    public static final int CALL_STATE_TERMINATED = 2;
+    // Dialog Id
+    private int mCallId;
+    // Number
+    private Uri mAddress;
+    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, 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(Parcel in) {
+        mCallId = in.readInt();
+        ClassLoader classLoader = ImsExternalCallState.class.getClassLoader();
+        mAddress = in.readParcelable(classLoader);
+        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.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 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 Uri getAddress() {
+        return mAddress;
+    }
+
+    public boolean isCallPullable() {
+        return mIsPullable;
+    }
+
+    public int getCallState() {
+        return mCallState;
+    }
+
+    public int getCallType() {
+        return mCallType;
+    }
+
+    public boolean isCallHeld() {
+        return mIsHeld;
+    }
+
+    @Override
+    public String toString() {
+        return "ImsExternalCallState { mCallId = " + mCallId +
+                ", mAddress = " + Log.pii(mAddress) +
+                ", mIsPullable = " + mIsPullable +
+                ", mCallState = " + mCallState +
+                ", mCallType = " + mCallType +
+                ", mIsHeld = " + mIsHeld + "}";
+    }
+}
diff --git a/android/telephony/ims/ImsReasonInfo.java b/android/telephony/ims/ImsReasonInfo.java
new file mode 100644
index 0000000..34d7c65
--- /dev/null
+++ b/android/telephony/ims/ImsReasonInfo.java
@@ -0,0 +1,517 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class enables an application to get details on why a method call failed.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsReasonInfo implements Parcelable {
+
+    /**
+     * Specific code of each types
+     */
+    public static final int CODE_UNSPECIFIED = 0;
+
+    /**
+     * LOCAL
+     */
+    // IMS -> Telephony
+    // The passed argument is an invalid
+    public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101;
+    // The operation is invoked in 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;
+    // IMS service goes down (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; by power off
+    public static final int CODE_LOCAL_POWER_OFF = 111;
+    // Service unavailable; by low battery
+    public static final int CODE_LOCAL_LOW_BATTERY = 112;
+    // Service unavailable; by out of service (data service state)
+    public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121;
+    // Service unavailable; by 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; by located in roaming area
+    public static final int CODE_LOCAL_NETWORK_ROAMING = 123;
+    // Service unavailable; by IP changed
+    public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124;
+    // Service unavailable; other
+    public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131;
+    // Service unavailable; IMS connection is lost (IMS is not registered)
+    public static final int CODE_LOCAL_NOT_REGISTERED = 132;
+
+    // IMS <-> Telephony
+    // Max call exceeded
+    public static final int CODE_LOCAL_CALL_EXCEEDED = 141;
+    // IMS <- Telephony
+    // Call busy
+    public static final int CODE_LOCAL_CALL_BUSY = 142;
+    // Call decline
+    public static final int CODE_LOCAL_CALL_DECLINE = 143;
+    // IMS -> Telephony
+    // SRVCC is in progress
+    public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144;
+    // Resource reservation is failed (QoS precondition)
+    public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145;
+    // Retry CS call; VoLTE service can't be provided by the network or remote end
+    // Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
+    public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146;
+    // Retry VoLTE call; VoLTE service can't be provided by the network temporarily
+    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;
+    // Handover not feasible
+    public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149;
+
+    /**
+     * TIMEOUT (IMS -> Telephony)
+     */
+    // 1xx waiting timer is expired after sending INVITE request (MO only)
+    public static final int CODE_TIMEOUT_1XX_WAITING = 201;
+    // User no 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;
+
+    //Call was blocked by call barring
+    public static final int CODE_CALL_BARRED = 240;
+
+    //Call failures for FDN
+    public static final int CODE_FDN_BLOCKED = 241;
+
+    // Network does not accept 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
+    public static final int CODE_DIAL_MODIFIED_TO_USSD = 244;
+    public static final int CODE_DIAL_MODIFIED_TO_SS = 245;
+    public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246;
+    public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247;
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248;
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249;
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250;
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251;
+
+    /**
+     * STATUSCODE (SIP response code) (IMS -> Telephony)
+     */
+    // 3xx responses
+    // SIP request is redirected
+    public static final int CODE_SIP_REDIRECTED = 321;
+    // 4xx responses
+    // 400 : Bad Request
+    public static final int CODE_SIP_BAD_REQUEST = 331;
+    // 403 : Forbidden
+    public static final int CODE_SIP_FORBIDDEN = 332;
+    // 404 : Not Found
+    public static final int CODE_SIP_NOT_FOUND = 333;
+    // 415 : Unsupported Media Type
+    // 416 : Unsupported URI Scheme
+    // 420 : Bad Extension
+    public static final int CODE_SIP_NOT_SUPPORTED = 334;
+    // 408 : Request Timeout
+    public static final int CODE_SIP_REQUEST_TIMEOUT = 335;
+    // 480 : Temporarily Unavailable
+    public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336;
+    // 484 : Address Incomplete
+    public static final int CODE_SIP_BAD_ADDRESS = 337;
+    // 486 : Busy Here
+    // 600 : Busy Everywhere
+    public static final int CODE_SIP_BUSY = 338;
+    // 487 : Request Terminated
+    public static final int CODE_SIP_REQUEST_CANCELLED = 339;
+    // 406 : Not Acceptable
+    // 488 : Not Acceptable Here
+    // 606 : Not Acceptable
+    public static final int CODE_SIP_NOT_ACCEPTABLE = 340;
+    // 410 : Gone
+    // 604 : Does Not Exist Anywhere
+    public static final int CODE_SIP_NOT_REACHABLE = 341;
+    // Others
+    public static final int CODE_SIP_CLIENT_ERROR = 342;
+    // 5xx responses
+    // 501 : Server Internal Error
+    public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351;
+    // 503 : Service Unavailable
+    public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352;
+    // 504 : Server Time-out
+    public static final int CODE_SIP_SERVER_TIMEOUT = 353;
+    // Others
+    public static final int CODE_SIP_SERVER_ERROR = 354;
+    // 6xx responses
+    // 603 : Decline
+    public static final int CODE_SIP_USER_REJECTED = 361;
+    // Others
+    public static final int CODE_SIP_GLOBAL_ERROR = 362;
+    // Emergency failure
+    public static final int CODE_EMERGENCY_TEMP_FAILURE = 363;
+    public static final int CODE_EMERGENCY_PERM_FAILURE = 364;
+
+    /**
+     * 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;
+    // Unknown media related errors
+    public static final int CODE_MEDIA_UNSPECIFIED = 404;
+
+    /**
+     * USER
+     */
+    // Telephony -> IMS
+    // User triggers the call end
+    public static final int CODE_USER_TERMINATED = 501;
+    // No action while an incoming call is ringing
+    public static final int CODE_USER_NOANSWER = 502;
+    // User ignores an incoming call
+    public static final int CODE_USER_IGNORE = 503;
+    // User declines an incoming call
+    public static final int CODE_USER_DECLINE = 504;
+    // Device declines/ends a call due to low battery
+    public static final int CODE_LOW_BATTERY = 505;
+    // Device declines call due to blacklisted call ID
+    public static final int CODE_BLACKLISTED_CALL_ID = 506;
+    // IMS -> Telephony
+    // The call is terminated by the network or remote user
+    public static final int CODE_USER_TERMINATED_BY_REMOTE = 510;
+
+    /**
+     * Extra codes for the specific code value
+     * This value can be referred when the code is CODE_LOCAL_CALL_CS_RETRY_REQUIRED.
+     */
+    // Try to connect CS call; normal
+    public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1;
+    // Try to connect CS call without the notification to user
+    public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2;
+    // Try to connect CS call by the settings of the menu
+    public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3;
+
+    /**
+     * UT
+     */
+    public static final int CODE_UT_NOT_SUPPORTED = 801;
+    public static final int CODE_UT_SERVICE_UNAVAILABLE = 802;
+    public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803;
+    public static final int CODE_UT_NETWORK_ERROR = 804;
+    public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821;
+    //STK CC errors
+    public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822;
+    public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823;
+    public static final int CODE_UT_SS_MODIFIED_TO_SS = 824;
+    public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825;
+
+    /**
+     * ECBM
+     */
+    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)
+     */
+
+    /**
+     * 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;
+
+    /**
+     * 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;
+
+    /**
+     * Supplementary services (HOLD/RESUME) failure error codes.
+     * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+     */
+    public static final int CODE_SUPP_SVC_FAILED = 1201;
+    public static final int CODE_SUPP_SVC_CANCELLED = 1202;
+    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;
+
+    /* 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;
+
+    /**
+     * Network string error messages.
+     * mExtraMessage may have these values.
+     */
+    public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED
+            = "Forbidden. Not Authorized for Service";
+
+
+    // For main reason code
+    /** @hide */
+    public int mCode;
+    // For the extra code value; it depends on the code value.
+    /** @hide */
+    public int mExtraCode;
+    // For the additional message of the reason info.
+    /** @hide */
+    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 */
+    public ImsReasonInfo(int code, int extraCode) {
+        mCode = code;
+        mExtraCode = extraCode;
+        mExtraMessage = null;
+    }
+
+    public ImsReasonInfo(int code, int extraCode, String extraMessage) {
+        mCode = code;
+        mExtraCode = extraCode;
+        mExtraMessage = extraMessage;
+    }
+
+    /**
+     *
+     */
+    public int getCode() {
+        return mCode;
+    }
+
+    /**
+     *
+     */
+    public int getExtraCode() {
+        return mExtraCode;
+    }
+
+    /**
+     *
+     */
+    public String getExtraMessage() {
+        return mExtraMessage;
+    }
+
+    /**
+     * Returns the string format of {@link ImsReasonInfo}
+     *
+     * @return the string format of {@link ImsReasonInfo}
+     */
+    public String toString() {
+        return "ImsReasonInfo :: {" + mCode + ", " + mExtraCode + ", " + mExtraMessage + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mCode);
+        out.writeInt(mExtraCode);
+        out.writeString(mExtraMessage);
+    }
+
+    public static final 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/telephony/ims/ImsService.java b/android/telephony/ims/ImsService.java
index aaa0f08..c008711 100644
--- a/android/telephony/ims/ImsService.java
+++ b/android/telephony/ims/ImsService.java
@@ -16,25 +16,28 @@
 
 package android.telephony.ims;
 
-import android.annotation.Nullable;
 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.feature.ImsFeature;
-import android.telephony.ims.feature.MMTelFeature;
+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.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.IImsRegistration;
-import com.android.ims.internal.IImsServiceController;
 import com.android.internal.annotations.VisibleForTesting;
 
 import static android.Manifest.permission.MODIFY_PHONE_STATE;
@@ -48,9 +51,7 @@
  * ...
  * <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.ImsService" />
  *     </intent-filter>
@@ -64,13 +65,31 @@
  * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
  *    {@link CarrierConfigManager#KEY_CONFIG_IMS_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 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
+ * - 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 {
@@ -89,20 +108,36 @@
     // call ImsFeature#onFeatureRemoved.
     private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
 
+    private IImsServiceControllerListener mListener;
+
+
+    /**
+     * 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 IImsMMTelFeature createEmergencyMMTelFeature(int slotId,
-                IImsFeatureStatusCallback c) {
-            return createEmergencyMMTelFeatureInternal(slotId, c);
+        public void setListener(IImsServiceControllerListener l) {
+            mListener = l;
         }
 
         @Override
-        public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c) {
-            return createMMTelFeatureInternal(slotId, c);
+        public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
+            return createMmTelFeatureInternal(slotId, c);
         }
 
         @Override
@@ -111,16 +146,41 @@
         }
 
         @Override
-        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
-                throws RemoteException {
+        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) {
             ImsService.this.removeImsFeature(slotId, featureType, c);
         }
 
         @Override
-        public IImsRegistration getRegistration(int slotId) throws RemoteException {
+        public ImsFeatureConfiguration querySupportedImsFeatures() {
+            return ImsService.this.querySupportedImsFeatures();
+        }
+
+        @Override
+        public void notifyImsServiceReadyForFeatureCreation() {
+            ImsService.this.readyForFeatureCreation();
+        }
+
+        @Override
+        public IImsConfig getConfig(int slotId) {
+            ImsConfigImplBase c = ImsService.this.getConfig(slotId);
+            return c != null ? c.getIImsConfig() : null;
+        }
+
+        @Override
+        public IImsRegistration getRegistration(int slotId) {
             ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
             return r != null ? r.getBinder() : null;
         }
+
+        @Override
+        public void enableIms(int slotId) {
+            ImsService.this.enableIms(slotId);
+        }
+
+        @Override
+        public void disableIms(int slotId) {
+            ImsService.this.disableIms(slotId);
+        }
     };
 
     /**
@@ -143,47 +203,35 @@
         return mFeaturesBySlot.get(slotId);
     }
 
-    private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId,
+    private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
             IImsFeatureStatusCallback c) {
-        MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
+        MmTelFeature f = createMmTelFeature(slotId);
         if (f != null) {
-            setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c);
+            setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
             return f.getBinder();
         } else {
-            return null;
-        }
-    }
-
-    private IImsMMTelFeature createMMTelFeatureInternal(int slotId,
-            IImsFeatureStatusCallback c) {
-        MMTelFeature f = onCreateMMTelImsFeature(slotId);
-        if (f != null) {
-            setupFeature(f, slotId, ImsFeature.MMTEL, c);
-            return f.getBinder();
-        } else {
+            Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
             return null;
         }
     }
 
     private IImsRcsFeature createRcsFeatureInternal(int slotId,
             IImsFeatureStatusCallback c) {
-        RcsFeature f = onCreateRcsFeature(slotId);
+        RcsFeature f = createRcsFeature(slotId);
         if (f != null) {
-            setupFeature(f, slotId, ImsFeature.RCS, c);
+            setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
             return f.getBinder();
         } else {
+            Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
             return null;
         }
     }
 
     private void setupFeature(ImsFeature f, int slotId, int featureType,
             IImsFeatureStatusCallback c) {
-        f.setContext(this);
-        f.setSlotId(slotId);
         f.addImsFeatureStatusCallback(c);
+        f.initialize(this, slotId);
         addImsFeature(slotId, featureType, f);
-        // TODO: Remove once new onFeatureReady AIDL is merged in.
-        f.onFeatureReady();
     }
 
     private void addImsFeature(int slotId, int featureType, ImsFeature f) {
@@ -222,37 +270,102 @@
     }
 
     /**
-     * @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
+     * 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 @Nullable MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+    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 {
+        if (mListener == null) {
+            throw new IllegalStateException("Framework is not ready");
+        }
+        mListener.onUpdateSupportedImsFeatures(c);
+    }
+
+    /**
+     * 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 slot specified, the ImsService should register for IMS
+     * and perform all appropriate initialization to bring up all ImsFeatures.
+     */
+    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.
+     */
+    public void disableIms(int slotId) {
+    }
+
+    /**
+     * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
+     * specified slot.
+     *
+     * @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.
+     */
+    public MmTelFeature createMmTelFeature(int slotId) {
         return null;
     }
 
     /**
-     * @return An implementation of MMTelFeature that will be used by the system for MMTel
-     * functionality.
-     * @hide
+     * When called, the framework is requesting that a new {@link RcsFeature} is created for the
+     * specified slot.
+     *
+     * @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.
      */
-    public @Nullable MMTelFeature onCreateMMTelImsFeature(int slotId) {
+    public RcsFeature createRcsFeature(int slotId) {
         return null;
     }
 
     /**
-     * @return An implementation of RcsFeature that will be used by the system for RCS.
-     * @hide
+     * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This
+     * will be used by the platform to get/set specific IMS related configurations.
+     *
+     * @param slotId The slot that the IMS configuration is associated with.
+     * @return ImsConfig implementation that is associated with the specified slot.
      */
-    public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
-        return null;
+    public ImsConfigImplBase getConfig(int slotId) {
+        return new ImsConfigImplBase();
     }
 
     /**
+     * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot.
+     *
      * @param slotId The slot that is associated with the IMS Registration.
      * @return the ImsRegistration implementation associated with the slot.
-     * @hide
      */
     public ImsRegistrationImplBase getRegistration(int slotId) {
         return new ImsRegistrationImplBase();
     }
-}
+}
\ No newline at end of file
diff --git a/android/telephony/ims/ImsSsData.java b/android/telephony/ims/ImsSsData.java
new file mode 100644
index 0000000..49ead77
--- /dev/null
+++ b/android/telephony/ims/ImsSsData.java
@@ -0,0 +1,446 @@
+/*
+ * 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.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides STK Call Control Supplementary Service information.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class ImsSsData implements Parcelable {
+
+    // 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;
+
+    //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;
+
+    // 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;
+
+    // Service Class of Supplementary Service
+    // See 27.007 +CCFC or +CLCK
+    /** @hide */
+    public static final int SERVICE_CLASS_NONE = 0; // no user input
+    /** @hide */
+    public static final int SERVICE_CLASS_VOICE = 1;
+    /** @hide */
+    public static final int SERVICE_CLASS_DATA = (1 << 1);
+    /** @hide */
+    public static final int SERVICE_CLASS_FAX = (1 << 2);
+    /** @hide */
+    public static final int SERVICE_CLASS_SMS = (1 << 3);
+    /** @hide */
+    public static final int SERVICE_CLASS_DATA_SYNC = (1 << 4);
+    /** @hide */
+    public static final int SERVICE_CLASS_DATA_ASYNC = (1 << 5);
+    /** @hide */
+    public static final int SERVICE_CLASS_PACKET = (1 << 6);
+    /** @hide */
+    public static final int SERVICE_CLASS_PAD = (1 << 7);
+
+    /**
+     * Result code used if the operation was successful. See {@link #result}.
+     * @hide
+     */
+    public static final int RESULT_SUCCESS = 0;
+
+    /** @hide */
+    @IntDef(flag = true, 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{}
+
+    /** @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_SYNC,
+            SERVICE_CLASS_DATA_ASYNC,
+            SERVICE_CLASS_PACKET,
+            SERVICE_CLASS_PAD
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ServiceClass{}
+
+    /**
+     * The Service type of this Supplementary service. Valid values include:
+     *     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
+     *
+     * @hide
+     */
+    // TODO: Make final, do not modify this field directly!
+    public int serviceType;
+
+    /**
+     * Supplementary Service request Type. Valid values are:
+     *     SS_ACTIVATION,
+     *     SS_DEACTIVATION,
+     *     SS_INTERROGATION,
+     *     SS_REGISTRATION,
+     *     SS_ERASURE
+     *
+     * @hide
+     */
+    // TODO: Make final, do not modify this field directly!
+    public int requestType;
+
+    /**
+     * Supplementary Service teleservice type:
+     *     SS_TELESERVICE_ALL_TELE_AND_BEARER,
+     *     SS_TELESERVICE_ALL_TELESEVICES,
+     *     SS_TELESERVICE_TELEPHONY,
+     *     SS_TELESERVICE_ALL_DATA,
+     *     SS_TELESERVICE_SMS,
+     *     SS_TELESERVICE_ALL_TELESERVICES_EXCEPT_SMS
+     *
+     * @hide
+     */
+    // TODO: Make this param final! Do not try to modify this param directly.
+    public int teleserviceType;
+
+    /**
+     * Supplementary Service service class. Valid values are:
+     *     SERVICE_CLASS_NONE,
+     *     SERVICE_CLASS_VOICE,
+     *     SERVICE_CLASS_DATA,
+     *     SERVICE_CLASS_FAX,
+     *     SERVICE_CLASS_SMS,
+     *     SERVICE_CLASS_DATA_SYNC,
+     *     SERVICE_CLASS_DATA_ASYNC,
+     *     SERVICE_CLASS_PACKET,
+     *     SERVICE_CLASS_PAD
+     *
+     * @hide
+     */
+    // TODO: Make this param final! Do not try to modify this param directly.
+    public int serviceClass;
+
+    /**
+     * Result of Supplementary Service operation. Valid values are:
+     *     RESULT_SUCCESS if the result is success, or
+     *     ImsReasonInfo code if the result is a failure.
+     *
+     * @hide
+     */
+    // TODO: Make this param final! Do not try to modify this param directly.
+    public final int result;
+
+    private int[] mSsInfo;
+    private ImsCallForwardInfo[] mCfInfo;
+    private ImsSsInfo[] mImsSsInfo;
+
+    /**
+     * Generate IMS Supplementary Service information.
+     * @param serviceType The Supplementary Service type. Valid entries:
+     *     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
+     * @param requestType Supplementary Service request Type. Valid values are:
+     *     SS_ACTIVATION,
+     *     SS_DEACTIVATION,
+     *     SS_INTERROGATION,
+     *     SS_REGISTRATION,
+     *     SS_ERASURE
+     * @param teleserviceType Supplementary Service teleservice type:
+     *     SS_TELESERVICE_ALL_TELE_AND_BEARER,
+     *     SS_TELESERVICE_ALL_TELESEVICES,
+     *     SS_TELESERVICE_TELEPHONY,
+     *     SS_TELESERVICE_ALL_DATA,
+     *     SS_TELESERVICE_SMS,
+     *     SS_TELESERVICE_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,
+            @ServiceClass 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 = (ImsCallForwardInfo[])in.readParcelableArray(this.getClass().getClassLoader());
+    }
+
+    public static final 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(serviceType);
+        out.writeInt(requestType);
+        out.writeInt(teleserviceType);
+        out.writeInt(serviceClass);
+        out.writeInt(result);
+        out.writeIntArray(mSsInfo);
+        out.writeParcelableArray(mCfInfo, 0);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Old method, kept for compatibility. See {@link #isTypeCf()}
+     * @hide
+     */
+    public boolean isTypeCF() {
+        return (serviceType == SS_CFU || serviceType == SS_CF_BUSY ||
+              serviceType == SS_CF_NO_REPLY || serviceType == SS_CF_NOT_REACHABLE ||
+              serviceType == SS_CF_ALL || serviceType == SS_CF_ALL_CONDITIONAL);
+    }
+
+    public boolean isTypeCf() {
+        return isTypeCF();
+    }
+
+    public boolean isTypeUnConditional() {
+        return (serviceType == SS_CFU || serviceType == SS_CF_ALL);
+    }
+
+    /**
+     * Old method, kept for compatibility. See {@link #isTypeCf()}
+     * @hide
+     */
+    public boolean isTypeCW() {
+        return (serviceType == SS_WAIT);
+    }
+
+    public boolean isTypeCw() {
+        return isTypeCW();
+    }
+
+    public boolean isTypeClip() {
+        return (serviceType == SS_CLIP);
+    }
+
+    public boolean isTypeColr() {
+        return (serviceType == SS_COLR);
+    }
+
+    public boolean isTypeColp() {
+        return (serviceType == SS_COLP);
+    }
+
+    public boolean isTypeClir() {
+        return (serviceType == SS_CLIR);
+    }
+
+    public boolean isTypeIcb() {
+        return (serviceType == SS_INCOMING_BARRING_DN ||
+                serviceType == SS_INCOMING_BARRING_ANONYMOUS);
+    }
+
+    public boolean isTypeBarring() {
+        return (serviceType == SS_BAOC || serviceType == SS_BAOIC ||
+              serviceType == SS_BAOIC_EXC_HOME || serviceType == SS_BAIC ||
+              serviceType == SS_BAIC_ROAMING || serviceType == SS_ALL_BARRING ||
+              serviceType == SS_OUTGOING_BARRING || serviceType == SS_INCOMING_BARRING);
+    }
+
+    public boolean isTypeInterrogation() {
+        return (serviceType == SS_INTERROGATION);
+    }
+
+    /** @hide */
+    public void setSuppServiceInfo(int[] ssInfo) {
+        mSsInfo = ssInfo;
+    }
+
+    /** @hide */
+    public void setImsSpecificSuppServiceInfo(ImsSsInfo[] imsSsInfo) {
+        mImsSsInfo = imsSsInfo;
+    }
+
+    /** @hide */
+    public void setCallForwardingInfo(ImsCallForwardInfo[] cfInfo) {
+        mCfInfo = cfInfo;
+    }
+
+    /**
+     * This field will be null for RequestType SS_INTERROGATION
+     * and ServiceType SS_CF_*, SS_INCOMING_BARRING_DN,
+     * SS_INCOMING_BARRING_ANONYMOUS.
+     *
+     * @hide
+     */
+    public int[] getSuppServiceInfo() {
+        return mSsInfo;
+    }
+
+    /**
+     * Valid only for ServiceTypes
+     *  - SS_INCOMING_BARRING_DN and
+     *  - ServiceType SS_INCOMING_BARRING_ANONYMOUS.
+     *  Will be null otherwise.
+     * @hide
+     */
+    public ImsSsInfo[] getImsSpecificSuppServiceInfo() {
+        return mImsSsInfo;
+    }
+
+    /**
+     * Valid only for supplementary services
+     * - ServiceType SS_CF_* and
+     * - RequestType SS_INTERROGATION.
+     * Will be null otherwise.
+     * @hide
+     **/
+    public ImsCallForwardInfo[] getCallForwardInfo() {
+        return mCfInfo;
+    }
+
+    public String toString() {
+        return "[ImsSsData] " + "ServiceType: " + serviceType
+            + " RequestType: " + requestType
+            + " TeleserviceType: " + teleserviceType
+            + " ServiceClass: " + serviceClass
+            + " Result: " + result;
+    }
+}
diff --git a/android/telephony/ims/ImsSsInfo.java b/android/telephony/ims/ImsSsInfo.java
new file mode 100644
index 0000000..c6f8622
--- /dev/null
+++ b/android/telephony/ims/ImsSsInfo.java
@@ -0,0 +1,115 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Provides the result to the update operation for the supplementary service configuration.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsSsInfo implements Parcelable {
+    /**
+     * 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;
+
+    // 0: disabled, 1: enabled
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter!
+    public int mStatus;
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter!
+    public String mIcbNum;
+
+    /**@hide*/
+    // TODO: Remove! Do not use this constructor, instead use public version.
+    public ImsSsInfo() {
+    }
+
+    /**
+     *
+     * @param status The status of the service registration of activation/deactiviation. Valid
+     *    entries include:
+     *    {@link #NOT_REGISTERED},
+     *    {@link #DISABLED},
+     *    {@link #ENABLED}
+     * @param icbNum The Incoming barring number.
+     */
+    public ImsSsInfo(int status, 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);
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + ", Status: " + ((mStatus == 0) ? "disabled" : "enabled");
+    }
+
+    private void readFromParcel(Parcel in) {
+        mStatus = in.readInt();
+        mIcbNum = in.readString();
+    }
+
+    public static final 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. Valid Values are:
+     *     {@link #NOT_REGISTERED},
+     *     {@link #DISABLED},
+     *     {@link #ENABLED}
+     */
+    public int getStatus() {
+        return mStatus;
+    }
+
+    public String getIcbNum() {
+        return mIcbNum;
+    }
+}
diff --git a/android/telephony/ims/ImsStreamMediaProfile.java b/android/telephony/ims/ImsStreamMediaProfile.java
new file mode 100644
index 0000000..137106a
--- /dev/null
+++ b/android/telephony/ims/ImsStreamMediaProfile.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.ims;
+
+import android.annotation.SystemApi;
+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 */
+    public int mAudioQuality;
+    /** @hide */
+    public int mAudioDirection;
+    // Video related information
+    /** @hide */
+    public int mVideoQuality;
+    /** @hide */
+    public int mVideoDirection;
+    // Rtt related information
+    /** @hide */
+    public int mRttMode;
+
+    /** @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 */
+    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;
+        mVideoQuality = profile.mVideoQuality;
+        mVideoDirection = profile.mVideoDirection;
+        mRttMode = profile.mRttMode;
+    }
+
+    @Override
+    public String toString() {
+        return "{ audioQuality=" + mAudioQuality +
+                ", audioDirection=" + mAudioDirection +
+                ", videoQuality=" + mVideoQuality +
+                ", videoDirection=" + mVideoDirection +
+                ", rttMode=" + mRttMode + " }";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mAudioQuality);
+        out.writeInt(mAudioDirection);
+        out.writeInt(mVideoQuality);
+        out.writeInt(mVideoDirection);
+        out.writeInt(mRttMode);
+    }
+
+    private void readFromParcel(Parcel in) {
+        mAudioQuality = in.readInt();
+        mAudioDirection = in.readInt();
+        mVideoQuality = in.readInt();
+        mVideoDirection = in.readInt();
+        mRttMode = in.readInt();
+    }
+
+    public static final 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;
+    }
+
+    public int getAudioQuality() {
+        return mAudioQuality;
+    }
+
+    public int getAudioDirection() {
+        return mAudioDirection;
+    }
+
+    public int getVideoQuality() {
+        return mVideoQuality;
+    }
+
+    public int getVideoDirection() {
+        return mVideoDirection;
+    }
+
+    public int getRttMode() {
+        return mRttMode;
+    }
+}
diff --git a/android/telephony/ims/ImsSuppServiceNotification.java b/android/telephony/ims/ImsSuppServiceNotification.java
new file mode 100644
index 0000000..efaade8
--- /dev/null
+++ b/android/telephony/ims/ImsSuppServiceNotification.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;
+
+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();
+    }
+
+    @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 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/telephony/ims/ImsUtListener.java b/android/telephony/ims/ImsUtListener.java
new file mode 100644
index 0000000..d50a0f7
--- /dev/null
+++ b/android/telephony/ims/ImsUtListener.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;
+
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of the IMS UT listener 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 ImsUtListener {
+    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");
+        }
+    }
+
+    public void onUtConfigurationQueried(int id, Bundle ssInfo) {
+        try {
+            mServiceInterface.utConfigurationQueried(null, id, ssInfo);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationQueried: remote exception");
+        }
+    }
+
+    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;
+    }
+}
diff --git a/android/telephony/ims/ImsVideoCallProvider.java b/android/telephony/ims/ImsVideoCallProvider.java
new file mode 100644
index 0000000..b4f60b9
--- /dev/null
+++ b/android/telephony/ims/ImsVideoCallProvider.java
@@ -0,0 +1,299 @@
+/*
+ * 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.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.Connection;
+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
+     */
+    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/telephony/ims/compat/ImsService.java b/android/telephony/ims/compat/ImsService.java
new file mode 100644
index 0000000..cf1efb3
--- /dev/null
+++ b/android/telephony/ims/compat/ImsService.java
@@ -0,0 +1,237 @@
+/*
+ * 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.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+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
+     */
+    protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
+
+        @Override
+        public IImsMMTelFeature createEmergencyMMTelFeature(int slotId,
+                IImsFeatureStatusCallback c) {
+            return createEmergencyMMTelFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c) {
+            return createMMTelFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
+            return createRcsFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+                throws RemoteException {
+            ImsService.this.removeImsFeature(slotId, featureType, c);
+        }
+    };
+
+    /**
+     * @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,
+            IImsFeatureStatusCallback c) {
+        MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c);
+            return f.getBinder();
+        } else {
+            return null;
+        }
+    }
+
+    private IImsMMTelFeature createMMTelFeatureInternal(int slotId,
+            IImsFeatureStatusCallback c) {
+        MMTelFeature f = onCreateMMTelImsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.MMTEL, c);
+            return f.getBinder();
+        } else {
+            return null;
+        }
+    }
+
+    private IImsRcsFeature createRcsFeatureInternal(int slotId,
+            IImsFeatureStatusCallback c) {
+        RcsFeature f = onCreateRcsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.RCS, c);
+            return f.getBinder();
+        } else {
+            return null;
+        }
+    }
+
+    private void setupFeature(ImsFeature f, int slotId, int featureType,
+            IImsFeatureStatusCallback c) {
+        f.setContext(this);
+        f.setSlotId(slotId);
+        f.addImsFeatureStatusCallback(c);
+        addImsFeature(slotId, featureType, f);
+        // TODO: Remove once new onFeatureReady AIDL is merged in.
+        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 removeImsFeature(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 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.removeImsFeatureStatusCallback(c);
+            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/telephony/ims/compat/feature/ImsFeature.java b/android/telephony/ims/compat/feature/ImsFeature.java
new file mode 100644
index 0000000..0a12cae
--- /dev/null
+++ b/android/telephony/ims/compat/feature/ImsFeature.java
@@ -0,0 +1,202 @@
+/*
+ * 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.content.Context;
+import android.content.Intent;
+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";
+
+    /**
+     * Action to broadcast when ImsService is up.
+     * Internal use only.
+     * Only defined here separately compatibility purposes with the old ImsService.
+     * @hide
+     */
+    public static final String ACTION_IMS_SERVICE_UP =
+            "com.android.ims.IMS_SERVICE_UP";
+
+    /**
+     * Action to broadcast when ImsService is down.
+     * Internal use only.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     * @hide
+     */
+    public static final String ACTION_IMS_SERVICE_DOWN =
+            "com.android.ims.IMS_SERVICE_DOWN";
+
+    /**
+     * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
+     * A long value; the phone ID corresponding to the IMS service coming up or down.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     * @hide
+     */
+    public static final String EXTRA_PHONE_ID = "android:phone_id";
+
+    // 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;
+    }
+
+    public int getFeatureState() {
+        return mState;
+    }
+
+    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());
+                }
+            }
+        }
+        sendImsServiceIntent(state);
+    }
+
+    /**
+     * Provide backwards compatibility using deprecated service UP/DOWN intents.
+     */
+    private void sendImsServiceIntent(@ImsState int state) {
+        if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+            return;
+        }
+        Intent intent;
+        switch (state) {
+            case ImsFeature.STATE_NOT_AVAILABLE:
+            case ImsFeature.STATE_INITIALIZING:
+                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+                break;
+            case ImsFeature.STATE_READY:
+                intent = new Intent(ACTION_IMS_SERVICE_UP);
+                break;
+            default:
+                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+        }
+        intent.putExtra(EXTRA_PHONE_ID, mSlotId);
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * 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/telephony/ims/feature/MMTelFeature.java b/android/telephony/ims/compat/feature/MMTelFeature.java
similarity index 78%
rename from android/telephony/ims/feature/MMTelFeature.java
rename to android/telephony/ims/compat/feature/MMTelFeature.java
index 5197107..d3d17f4 100644
--- a/android/telephony/ims/feature/MMTelFeature.java
+++ b/android/telephony/ims/compat/feature/MMTelFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,14 +14,13 @@
  * limitations under the License
  */
 
-package android.telephony.ims.feature;
+package android.telephony.ims.compat.feature;
 
 import android.app.PendingIntent;
 import android.os.Message;
 import android.os.RemoteException;
-import android.telephony.ims.internal.stub.SmsImplBase;
 
-import com.android.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallProfile;
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsCallSessionListener;
 import com.android.ims.internal.IImsConfig;
@@ -29,9 +28,12 @@
 import com.android.ims.internal.IImsMMTelFeature;
 import com.android.ims.internal.IImsMultiEndpoint;
 import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsSmsListener;
 import com.android.ims.internal.IImsUt;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.compat.stub.ImsCallSessionImplBase;
+import android.telephony.ims.stub.ImsEcbmImplBase;
+import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsUtImplBase;
 
 /**
  * Base implementation for MMTel.
@@ -110,10 +112,10 @@
         }
 
         @Override
-        public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
-                IImsCallSessionListener listener) throws RemoteException {
+        public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
+                throws RemoteException {
             synchronized (mLock) {
-                return MMTelFeature.this.createCallSession(sessionId, profile, listener);
+                return MMTelFeature.this.createCallSession(sessionId, profile, null);
             }
         }
 
@@ -128,7 +130,8 @@
         @Override
         public IImsUt getUtInterface() throws RemoteException {
             synchronized (mLock) {
-                return MMTelFeature.this.getUtInterface();
+                ImsUtImplBase implBase = MMTelFeature.this.getUtInterface();
+                return implBase != null ? implBase.getInterface() : null;
             }
         }
 
@@ -156,7 +159,8 @@
         @Override
         public IImsEcbm getEcbmInterface() throws RemoteException {
             synchronized (mLock) {
-                return MMTelFeature.this.getEcbmInterface();
+                ImsEcbmImplBase implBase = MMTelFeature.this.getEcbmInterface();
+                return implBase != null ? implBase.getImsEcbm() : null;
             }
         }
 
@@ -170,43 +174,8 @@
         @Override
         public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
             synchronized (mLock) {
-                return MMTelFeature.this.getMultiEndpointInterface();
-            }
-        }
-
-        @Override
-        public void setSmsListener(IImsSmsListener l) throws RemoteException {
-            synchronized (mLock) {
-                MMTelFeature.this.setSmsListener(l);
-            }
-        }
-
-        @Override
-        public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
-                byte[] pdu) {
-            synchronized (mLock) {
-                MMTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
-            }
-        }
-
-        @Override
-        public void acknowledgeSms(int token, int messageRef, int result) {
-            synchronized (mLock) {
-                MMTelFeature.this.acknowledgeSms(token, messageRef, result);
-            }
-        }
-
-        @Override
-        public void acknowledgeSmsReport(int token, int messageRef, int result) {
-            synchronized (mLock) {
-                MMTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
-            }
-        }
-
-        @Override
-        public String getSmsFormat() {
-            synchronized (mLock) {
-                return MMTelFeature.this.getSmsFormat();
+                ImsMultiEndpointImplBase implBase = MMTelFeature.this.getMultiEndpointInterface();
+                return implBase != null ? implBase.getIImsMultiEndpoint() : null;
             }
         }
     };
@@ -319,7 +288,6 @@
      *
      * @param sessionId a session id which is obtained from {@link #startSession}
      * @param profile a call profile to make the call
-     * @param listener An implementation of IImsCallSessionListener.
      */
     public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
             IImsCallSessionListener listener) {
@@ -339,7 +307,7 @@
     /**
      * @return The Ut interface for the supplementary service configuration.
      */
-    public IImsUt getUtInterface() {
+    public ImsUtImplBase getUtInterface() {
         return null;
     }
 
@@ -365,7 +333,7 @@
     /**
      * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
      */
-    public IImsEcbm getEcbmInterface() {
+    public ImsEcbmImplBase getEcbmInterface() {
         return null;
     }
 
@@ -380,43 +348,10 @@
     /**
      * @return MultiEndpoint interface for DEP notifications
      */
-    public IImsMultiEndpoint getMultiEndpointInterface() {
+    public ImsMultiEndpointImplBase getMultiEndpointInterface() {
         return null;
     }
 
-    public void setSmsListener(IImsSmsListener listener) {
-        getSmsImplementation().registerSmsListener(listener);
-    }
-
-    public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
-            byte[] pdu) {
-        getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu);
-    }
-
-    public void acknowledgeSms(int token, int messageRef,
-            @SmsImplBase.DeliverStatusResult int result) {
-        getSmsImplementation().acknowledgeSms(token, messageRef, result);
-    }
-
-    public void acknowledgeSmsReport(int token, int messageRef,
-            @SmsImplBase.StatusReportResult int result) {
-        getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
-    }
-
-    /**
-     * 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 SmsImplBase} which should be implemented by the IMS Provider.
-     */
-    protected SmsImplBase getSmsImplementation() {
-        return new SmsImplBase();
-    }
-
-    public String getSmsFormat() {
-        return getSmsImplementation().getSmsFormat();
-    }
-
     @Override
     public void onFeatureReady() {
 
diff --git a/android/telephony/ims/internal/feature/RcsFeature.java b/android/telephony/ims/compat/feature/RcsFeature.java
similarity index 76%
rename from android/telephony/ims/internal/feature/RcsFeature.java
rename to android/telephony/ims/compat/feature/RcsFeature.java
index 8d1bd9d..228b330 100644
--- a/android/telephony/ims/internal/feature/RcsFeature.java
+++ b/android/telephony/ims/compat/feature/RcsFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,9 +14,10 @@
  * limitations under the License
  */
 
-package android.telephony.ims.internal.feature;
+package android.telephony.ims.compat.feature;
 
-import android.telephony.ims.internal.aidl.IImsRcsFeature;
+
+import com.android.ims.internal.IImsRcsFeature;
 
 /**
  * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
@@ -36,9 +37,8 @@
     }
 
     @Override
-    public void changeEnabledCapabilities(CapabilityChangeRequest request,
-            CapabilityCallbackProxy c) {
-        // Do nothing for base implementation.
+    public void onFeatureReady() {
+
     }
 
     @Override
@@ -46,12 +46,6 @@
 
     }
 
-    /**{@inheritDoc}*/
-    @Override
-    public void onFeatureReady() {
-
-    }
-
     @Override
     public final IImsRcsFeature getBinder() {
         return mImsRcsBinder;
diff --git a/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
new file mode 100644
index 0000000..e5ed825
--- /dev/null
+++ b/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -0,0 +1,594 @@
+/*
+ * 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.os.Message;
+import android.os.RemoteException;
+
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsConferenceState;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsSuppServiceNotification;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
+
+import android.telephony.ims.ImsCallSession;
+
+/**
+ * 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 {
+
+    @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) {
+    }
+
+    /**
+     * 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) {
+    }
+
+    /**
+     * 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(srcAccessTech, targetAccessTech, reasonInfo);
+        }
+
+        @Override
+        public void callSessionHandoverFailed(IImsCallSession i, int srcAccessTech,
+                int targetAccessTech, ImsReasonInfo reasonInfo) throws RemoteException {
+            mNewListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+        }
+
+        @Override
+        public void callSessionMayHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech)
+                throws RemoteException {
+            mNewListener.callSessionMayHandover(srcAccessTech, 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);
+        }
+    }
+}
diff --git a/android/telephony/ims/compat/stub/ImsConfigImplBase.java b/android/telephony/ims/compat/stub/ImsConfigImplBase.java
new file mode 100644
index 0000000..2c325ba
--- /dev/null
+++ b/android/telephony/ims/compat/stub/ImsConfigImplBase.java
@@ -0,0 +1,391 @@
+/*
+ * 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.content.Context;
+import android.content.Intent;
+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;
+
+    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 master 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 master 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 master 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 master 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 {
+    }
+
+    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 master 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 master 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/telephony/ims/stub/ImsUtListenerImplBase.java b/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
similarity index 90%
rename from android/telephony/ims/stub/ImsUtListenerImplBase.java
rename to android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
index daa74c8..b2aa080 100644
--- a/android/telephony/ims/stub/ImsUtListenerImplBase.java
+++ b/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,15 +14,15 @@
  * limitations under the License
  */
 
-package android.telephony.ims.stub;
+package android.telephony.ims.compat.stub;
 
 import android.os.Bundle;
 import android.os.RemoteException;
 
-import com.android.ims.ImsCallForwardInfo;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsSsData;
-import com.android.ims.ImsSsInfo;
+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;
 
diff --git a/android/telephony/ims/internal/feature/CapabilityChangeRequest.java b/android/telephony/ims/feature/CapabilityChangeRequest.java
similarity index 79%
rename from android/telephony/ims/internal/feature/CapabilityChangeRequest.java
rename to android/telephony/ims/feature/CapabilityChangeRequest.java
index 5dbf077..7c793a5 100644
--- a/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
+++ b/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -14,8 +14,9 @@
  * limitations under the License
  */
 
-package android.telephony.ims.internal.feature;
+package android.telephony.ims.feature;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -30,17 +31,32 @@
  * the request.
  * {@hide}
  */
-public class CapabilityChangeRequest implements Parcelable {
+@SystemApi
+public final class CapabilityChangeRequest implements Parcelable {
 
+    /**
+     * Contains a feature capability, 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},
+     * along with an associated technology, defined as
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+     */
     public static class CapabilityPair {
         private final int mCapability;
         private final int radioTech;
 
-        public CapabilityPair(int capability, int radioTech) {
+        public CapabilityPair(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+                @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
             this.mCapability = capability;
             this.radioTech = radioTech;
         }
 
+        /**
+         * @hide
+         */
         @Override
         public boolean equals(Object o) {
             if (this == o) return true;
@@ -52,6 +68,9 @@
             return getRadioTech() == that.getRadioTech();
         }
 
+        /**
+         * @hide
+         */
         @Override
         public int hashCode() {
             int result = getCapability();
@@ -59,10 +78,22 @@
             return result;
         }
 
+        /**
+         * @return The stored capability, 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}
+         */
         public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() {
             return mCapability;
         }
 
+        /**
+         * @return the stored radio technology, defined as
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+         */
         public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() {
             return radioTech;
         }
@@ -73,6 +104,7 @@
     // Pair contains <radio tech, mCapability>
     private final Set<CapabilityPair> mCapabilitiesToDisable;
 
+    /** @hide */
     public CapabilityChangeRequest() {
         mCapabilitiesToEnable = new ArraySet<>();
         mCapabilitiesToDisable = new ArraySet<>();
@@ -130,6 +162,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     protected CapabilityChangeRequest(Parcel in) {
         int enableSize = in.readInt();
         mCapabilitiesToEnable = new ArraySet<>(enableSize);
@@ -177,17 +212,24 @@
         }
     }
 
+    /**
+     * @hide
+     */
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (!(o instanceof CapabilityChangeRequest)) return false;
 
-        CapabilityChangeRequest that = (CapabilityChangeRequest) o;
+        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();
diff --git a/android/telephony/ims/feature/ImsFeature.java b/android/telephony/ims/feature/ImsFeature.java
index d47cea3..d537699 100644
--- a/android/telephony/ims/feature/ImsFeature.java
+++ b/android/telephony/ims/feature/ImsFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -17,28 +17,35 @@
 package android.telephony.ims.feature;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
 import android.os.IInterface;
+import android.os.RemoteCallbackList;
 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 java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Set;
 import java.util.WeakHashMap;
 
 /**
- * Base class for all IMS features that are supported by the framework.
+ * 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";
@@ -46,7 +53,8 @@
     /**
      * Action to broadcast when ImsService is up.
      * Internal use only.
-     * Only defined here separately compatibility purposes with the old ImsService.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     *
      * @hide
      */
     public static final String ACTION_IMS_SERVICE_UP =
@@ -56,6 +64,7 @@
      * Action to broadcast when ImsService is down.
      * Internal use only.
      * Only defined here separately for compatibility purposes with the old ImsService.
+     *
      * @hide
      */
     public static final String ACTION_IMS_SERVICE_DOWN =
@@ -65,67 +74,331 @@
      * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
      * A long value; the phone ID corresponding to the IMS service coming up or down.
      * Only defined here separately for compatibility purposes with the old ImsService.
+     *
      * @hide
      */
     public static final String EXTRA_PHONE_ID = "android:phone_id";
 
-    // Invalid feature value
-    public static final int INVALID = -1;
+    /**
+     * 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.
-    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;
+    /**
+     * 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.
+     */
+    public static final int FEATURE_EMERGENCY_MMTEL = 0;
+    /**
+     * This feature supports the MMTEL feature.
+     */
+    public static final int FEATURE_MMTEL = 1;
+    /**
+     * This feature supports the RCS feature.
+     */
+    public static final int FEATURE_RCS = 2;
+    /**
+     * Total number of features defined
+     * @hide
+     */
+    public static final int FEATURE_MAX = 3;
 
-    // Integer values defining the state of the ImsFeature at any time.
+    /**
+     * Integer values defining IMS features that are supported in ImsFeature.
+     * @hide
+     */
     @IntDef(flag = true,
             value = {
-                    STATE_NOT_AVAILABLE,
+                    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 {}
-    public static final int STATE_NOT_AVAILABLE = 0;
+
+    /**
+     * This {@link ImsFeature}'s state is unavailable and should not be communicated with.
+     */
+    public static final int STATE_UNAVAILABLE = 0;
+    /**
+     * This {@link ImsFeature} state is initializing and should not be communicated with.
+     */
     public static final int STATE_INITIALIZING = 1;
+    /**
+     * This {@link ImsFeature} is ready for communication.
+     */
     public static final int STATE_READY = 2;
 
+    /**
+     * 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.
+     */
+    public static final int CAPABILITY_ERROR_GENERIC = -1;
+    /**
+     * The capability was able to be changed.
+     */
+    public static final int CAPABILITY_SUCCESS = 0;
+
+
+    /**
+     * The framework implements this callback in order to register for Feature Capability status
+     * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
+     * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
+     * callbacks when the ImsService can not change the capability as requested, via
+     * {@link #onChangeCapabilityConfigurationError}.
+     *
+     * @hide
+     */
+    public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
+
+        @Override
+        public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
+            onCapabilitiesStatusChanged(new Capabilities(config));
+        }
+
+        /**
+         * Returns the result of a query for the capability configuration of a requested capability.
+         *
+         * @param capability The capability that was requested.
+         * @param radioTech The IMS radio technology associated with the capability.
+         * @param isEnabled true if the capability is enabled, false otherwise.
+         */
+        @Override
+        public void onQueryCapabilityConfiguration(int capability, int radioTech,
+                boolean isEnabled) {
+
+        }
+
+        /**
+         * Called when a change to the capability configuration has returned an error.
+         *
+         * @param capability The capability that was requested to be changed.
+         * @param radioTech The IMS radio technology associated with the capability.
+         * @param reason error associated with the failure to change configuration.
+         */
+        @Override
+        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                @ImsCapabilityError int reason) {
+        }
+
+        /**
+         * 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 config The new availability of the capabilities.
+         */
+        public void onCapabilitiesStatusChanged(Capabilities config) {
+        }
+    }
+
+    /**
+     * 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} or
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
+         * @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 capabilities defined and supported by an ImsFeature in the form of a bit mask.
+     * @hide
+     */
+    public static class Capabilities {
+        protected int mCapabilities = 0;
+
+        public Capabilities() {
+        }
+
+        protected Capabilities(int capabilities) {
+            mCapabilities = capabilities;
+        }
+
+        /**
+         * @param capabilities Capabilities to be added to the configuration in the form of a
+         *     bit mask.
+         */
+        public void addCapabilities(int capabilities) {
+            mCapabilities |= capabilities;
+        }
+
+        /**
+         * @param capabilities Capabilities to be removed to the configuration in the form of a
+         *     bit mask.
+         */
+        public void removeCapabilities(int capabilities) {
+            mCapabilities &= ~capabilities;
+        }
+
+        /**
+         * @return true if all of the capabilities specified are capable.
+         */
+        public boolean isCapable(int capabilities) {
+            return (mCapabilities & capabilities) == capabilities;
+        }
+
+        /**
+         * @return a deep copy of the Capabilites.
+         */
+        public Capabilities copy() {
+            return new Capabilities(mCapabilities);
+        }
+
+        /**
+         * @return a bitmask containing the capability flags directly.
+         */
+        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);
+        }
+    }
+
     private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
             new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
-    private @ImsState int mState = STATE_NOT_AVAILABLE;
+    private @ImsState int mState = STATE_UNAVAILABLE;
     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+    /**
+     * @hide
+     */
     protected Context mContext;
+    private final Object mLock = new Object();
+    private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
+            = new RemoteCallbackList<>();
+    private Capabilities mCapabilityStatus = new Capabilities();
 
-    public void setContext(Context context) {
+    /**
+     * @hide
+     */
+    public final void initialize(Context context, int slotId) {
         mContext = context;
-    }
-
-    public void setSlotId(int slotId) {
         mSlotId = slotId;
     }
 
+    /**
+     * @return The current state of the feature, defined as {@link #STATE_UNAVAILABLE},
+     * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
+     * @hide
+     */
     public int getFeatureState() {
-        return mState;
-    }
-
-    protected final void setFeatureState(@ImsState int state) {
-        if (mState != state) {
-            mState = state;
-            notifyFeatureState(state);
+        synchronized (mLock) {
+            return mState;
         }
     }
 
-    public void addImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
-        if (c == null) {
-            return;
+    /**
+     * 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}.
+     */
+    public final void setFeatureState(@ImsState int state) {
+        synchronized (mLock) {
+            if (mState != state) {
+                mState = state;
+                notifyFeatureState(state);
+            }
         }
+    }
+
+    /**
+     * Not final for testing, but shouldn't be extended!
+     * @hide
+     */
+    @VisibleForTesting
+    public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
         try {
             // If we have just connected, send queued status.
-            c.notifyImsFeatureStatus(mState);
+            c.notifyImsFeatureStatus(getFeatureState());
             // Add the callback if the callback completes successfully without a RemoteException.
-            synchronized (mStatusCallbacks) {
+            synchronized (mLock) {
                 mStatusCallbacks.add(c);
             }
         } catch (RemoteException e) {
@@ -133,23 +406,24 @@
         }
     }
 
-    public void removeImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
-        if (c == null) {
-            return;
-        }
-        synchronized (mStatusCallbacks) {
+    /**
+     * Not final for testing, but shouldn't be extended!
+     * @hide
+     */
+    @VisibleForTesting
+    public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
+        synchronized (mLock) {
             mStatusCallbacks.remove(c);
         }
     }
 
     /**
      * Internal method called by ImsFeature when setFeatureState has changed.
-     * @param state
      */
     private void notifyFeatureState(@ImsState int state) {
-        synchronized (mStatusCallbacks) {
+        synchronized (mLock) {
             for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
-                 iter.hasNext(); ) {
+                    iter.hasNext(); ) {
                 IImsFeatureStatusCallback callback = iter.next();
                 try {
                     Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
@@ -168,12 +442,12 @@
      * Provide backwards compatibility using deprecated service UP/DOWN intents.
      */
     private void sendImsServiceIntent(@ImsState int state) {
-        if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+        if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
             return;
         }
         Intent intent;
         switch (state) {
-            case ImsFeature.STATE_NOT_AVAILABLE:
+            case ImsFeature.STATE_UNAVAILABLE:
             case ImsFeature.STATE_INITIALIZING:
                 intent = new Intent(ACTION_IMS_SERVICE_DOWN);
                 break;
@@ -188,17 +462,104 @@
     }
 
     /**
-     * Called when the feature is ready to use.
+     * @hide
      */
-    public abstract void onFeatureReady();
+    public final void addCapabilityCallback(IImsCapabilityCallback c) {
+        mCapabilityCallbacks.register(c);
+    }
 
     /**
-     * Called when the feature is being removed and must be cleaned up.
+     * @hide
+     */
+    public final void removeCapabilityCallback(IImsCapabilityCallback c) {
+        mCapabilityCallbacks.unregister(c);
+    }
+
+    /**
+     * @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 c A {@link Capabilities} containing the new Capabilities status.
+     *
+     * @hide
+     */
+    protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
+        synchronized (mLock) {
+            mCapabilityStatus = c.copy();
+        }
+        int count = mCapabilityCallbacks.beginBroadcast();
+        try {
+            for (int i = 0; i < count; i++) {
+                try {
+                    mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
+                            c.mCapabilities);
+                } catch (RemoteException e) {
+                    Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
+                            "callback.");
+                }
+            }
+        } finally {
+            mCapabilityCallbacks.finishBroadcast();
+        }
+    }
+
+    /**
+     * 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();
 
     /**
-     * @return Binder instance
+     * Called when the feature has been initialized and communication with the framework is set up.
+     * 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 IInterface getBinder();
+    public abstract void onFeatureReady();
+
+    /**
+     * @return Binder instance that the framework will use to communicate with this feature.
+     * @hide
+     */
+    protected abstract IInterface getBinder();
 }
diff --git a/android/telephony/ims/feature/MmTelFeature.java b/android/telephony/ims/feature/MmTelFeature.java
new file mode 100644
index 0000000..aaf1a1c
--- /dev/null
+++ b/android/telephony/ims/feature/MmTelFeature.java
@@ -0,0 +1,679 @@
+/*
+ * 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.SystemApi;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.TelecomManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
+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.stub.ImsEcbmImplBase;
+import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsUtImplBase;
+import android.util.Log;
+
+import android.telephony.ims.ImsCallProfile;
+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 android.telephony.ims.ImsCallSession;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * 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.
+ * @hide
+ */
+@SystemApi
+public class MmTelFeature extends ImsFeature {
+
+    private static final String LOG_TAG = "MmTelFeature";
+
+    private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
+
+        @Override
+        public void setListener(IImsMmTelListener l) throws RemoteException {
+            synchronized (mLock) {
+                MmTelFeature.this.setListener(l);
+            }
+        }
+
+        @Override
+        public int getFeatureState() throws RemoteException {
+            synchronized (mLock) {
+                try {
+                    return MmTelFeature.this.getFeatureState();
+                } catch (Exception e) {
+                    throw new RemoteException(e.getMessage());
+                }
+            }
+        }
+
+
+        @Override
+        public ImsCallProfile createCallProfile(int callSessionType, int callType)
+                throws RemoteException {
+            synchronized (mLock) {
+                try {
+                    return MmTelFeature.this.createCallProfile(callSessionType, callType);
+                } catch (Exception e) {
+                    throw new RemoteException(e.getMessage());
+                }
+            }
+        }
+
+        @Override
+        public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
+            synchronized (mLock) {
+                return createCallSessionInterface(profile);
+            }
+        }
+
+        @Override
+        public int shouldProcessCall(String[] numbers) {
+            synchronized (mLock) {
+                return MmTelFeature.this.shouldProcessCall(numbers);
+            }
+        }
+
+        @Override
+        public IImsUt getUtInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.getUtInterface();
+            }
+        }
+
+        @Override
+        public IImsEcbm getEcbmInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.getEcbmInterface();
+            }
+        }
+
+        @Override
+        public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
+            synchronized (mLock) {
+                try {
+                    MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
+                } catch (Exception e) {
+                    throw new RemoteException(e.getMessage());
+                }
+            }
+        }
+
+        @Override
+        public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.getMultiEndpointInterface();
+            }
+        }
+
+        @Override
+        public int queryCapabilityStatus() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
+            }
+        }
+
+        @Override
+        public void addCapabilityCallback(IImsCapabilityCallback c) {
+            // no need to lock, structure already handles multithreading.
+            MmTelFeature.this.addCapabilityCallback(c);
+        }
+
+        @Override
+        public void removeCapabilityCallback(IImsCapabilityCallback c) {
+            // no need to lock, structure already handles multithreading.
+            MmTelFeature.this.removeCapabilityCallback(c);
+        }
+
+        @Override
+        public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
+                IImsCapabilityCallback c) throws RemoteException {
+            synchronized (mLock) {
+                MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
+            }
+        }
+
+        @Override
+        public void queryCapabilityConfiguration(int capability, int radioTech,
+                IImsCapabilityCallback c) {
+            synchronized (mLock) {
+                queryCapabilityConfigurationInternal(capability, radioTech, c);
+            }
+        }
+
+        @Override
+        public void setSmsListener(IImsSmsListener l) throws RemoteException {
+            synchronized (mLock) {
+                MmTelFeature.this.setSmsListener(l);
+            }
+        }
+
+        @Override
+        public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
+                byte[] pdu) {
+            synchronized (mLock) {
+                MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
+            }
+        }
+
+        @Override
+        public void acknowledgeSms(int token, int messageRef, int result) {
+            synchronized (mLock) {
+                MmTelFeature.this.acknowledgeSms(token, messageRef, result);
+            }
+        }
+
+        @Override
+        public void acknowledgeSmsReport(int token, int messageRef, int result) {
+            synchronized (mLock) {
+                MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
+            }
+        }
+
+        @Override
+        public String getSmsFormat() {
+            synchronized (mLock) {
+                return MmTelFeature.this.getSmsFormat();
+            }
+        }
+
+        @Override
+        public void onSmsReady() {
+            synchronized (mLock) {
+                MmTelFeature.this.onSmsReady();
+            }
+        }
+    };
+
+    /**
+     * 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}, and
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}.
+     *
+     * The capabilities of this MmTelFeature will be set by the framework and can be queried with
+     * {@link #queryCapabilityStatus()}.
+     *
+     * This MmTelFeature can then return the status of each of these capabilities (enabled or not)
+     * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
+     * status can also be queried using {@link #queryCapabilityStatus()}.
+     */
+    public static class MmTelCapabilities extends Capabilities {
+
+        /**
+         * @hide
+         */
+        @VisibleForTesting
+        public MmTelCapabilities() {
+            super();
+        }
+
+        public MmTelCapabilities(Capabilities c) {
+            mCapabilities = c.mCapabilities;
+        }
+
+        public MmTelCapabilities(int capabilities) {
+            mCapabilities = capabilities;
+        }
+
+        @IntDef(flag = true,
+                value = {
+                        CAPABILITY_TYPE_VOICE,
+                        CAPABILITY_TYPE_VIDEO,
+                        CAPABILITY_TYPE_UT,
+                        CAPABILITY_TYPE_SMS
+                })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface MmTelCapability {}
+
+        /**
+         * 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;
+
+        @Override
+        public final void addCapabilities(@MmTelCapability int capabilities) {
+            super.addCapabilities(capabilities);
+        }
+
+        @Override
+        public final void removeCapabilities(@MmTelCapability int capability) {
+            super.removeCapabilities(capability);
+        }
+
+        @Override
+        public final boolean isCapable(@MmTelCapability int capabilities) {
+            return super.isCapable(capabilities);
+        }
+
+        @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("]");
+            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.
+         */
+        @Override
+        public void onIncomingCall(IImsCallSession c, Bundle extras) {
+
+        }
+
+        /**
+         * Updates the Listener when the voice message count for IMS has changed.
+         * @param count an integer representing the new message count.
+         */
+        @Override
+        public void onVoiceMessageCountUpdate(int count) {
+
+        }
+    }
+
+    /**
+     * To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the
+     * outgoing call as IMS.
+     */
+    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.
+     */
+    public static final int PROCESS_CALL_CSFB = 1;
+
+    @IntDef(flag = true,
+            value = {
+                    PROCESS_CALL_IMS,
+                    PROCESS_CALL_CSFB
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProcessCallResult {}
+
+
+    // Lock for feature synchronization
+    private final Object mLock = new Object();
+    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();
+        }
+    }
+
+    private 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!");
+        }
+    }
+
+    /**
+     * 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.
+     */
+    @Override
+    public 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.
+     */
+    public final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
+        super.notifyCapabilitiesStatusChanged(c);
+    }
+
+    /**
+     * Notify the framework of an incoming call.
+     * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
+     */
+    public final void notifyIncomingCall(ImsCallSessionImplBase c, Bundle extras) {
+        synchronized (mLock) {
+            if (mListener == null) {
+                throw new IllegalStateException("Session is not available.");
+            }
+            try {
+                mListener.onIncomingCall(c.getServiceImpl(), extras);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     *
+     * @hide
+     */
+    public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
+        synchronized (mLock) {
+            if (mListener == null) {
+                throw new IllegalStateException("Session is not available.");
+            }
+            try {
+                mListener.onIncomingCall(c, 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.
+     */
+    public final void notifyVoiceMessageCountUpdate(int count) {
+        synchronized (mLock) {
+            if (mListener == null) {
+                throw new IllegalStateException("Session is not available.");
+            }
+            try {
+                mListener.onVoiceMessageCountUpdate(count);
+            } 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.
+     */
+    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.
+     */
+    @Override
+    public void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c) {
+        // Base implementation, no-op
+    }
+
+    /**
+     * 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
+     */
+    public ImsCallProfile createCallProfile(int callSessionType, int callType) {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
+            throws RemoteException {
+        ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
+        return s != null ? s.getServiceImpl() : 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
+     */
+    public ImsCallSessionImplBase createCallSession(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.
+     */
+    public @ProcessCallResult int shouldProcessCall(String[] numbers) {
+        return PROCESS_CALL_IMS;
+    }
+
+    /**
+     *
+     * @hide
+     */
+    protected IImsUt getUtInterface() throws RemoteException {
+        ImsUtImplBase utImpl = getUt();
+        return utImpl != null ? utImpl.getInterface() : null;
+    }
+
+    /**
+     * @hide
+     */
+    protected IImsEcbm getEcbmInterface() throws RemoteException {
+        ImsEcbmImplBase ecbmImpl = getEcbm();
+        return ecbmImpl != null ? ecbmImpl.getImsEcbm() : null;
+    }
+
+    /**
+     * @hide
+     */
+    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+        ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
+        return multiendpointImpl != null ? multiendpointImpl.getIImsMultiEndpoint() : null;
+    }
+
+    /**
+     * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
+     * configuration.
+     */
+    public 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.
+     */
+    public ImsEcbmImplBase getEcbm() {
+        // Base Implementation - Should be overridden
+        return new ImsEcbmImplBase();
+    }
+
+    /**
+     * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
+     * package processing for multi-endpoint.
+     */
+    public 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 A {@link Message} to be used when the mode has been set.
+     */
+    public void setUiTtyMode(int mode, Message onCompleteMessage) {
+        // Base Implementation - Should be overridden
+    }
+
+    private void setSmsListener(IImsSmsListener listener) {
+        getSmsImplementation().registerSmsListener(listener);
+    }
+
+    private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
+            byte[] pdu) {
+        getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu);
+    }
+
+    private void acknowledgeSms(int token, int messageRef,
+            @ImsSmsImplBase.DeliverStatusResult int result) {
+        getSmsImplementation().acknowledgeSms(token, messageRef, result);
+    }
+
+    private void acknowledgeSmsReport(int token, int messageRef,
+            @ImsSmsImplBase.StatusReportResult int result) {
+        getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
+    }
+
+    private void onSmsReady() {
+        getSmsImplementation().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.
+     */
+    public ImsSmsImplBase getSmsImplementation() {
+        return new ImsSmsImplBase();
+    }
+
+    private String getSmsFormat() {
+        return getSmsImplementation().getSmsFormat();
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureRemoved() {
+        // Base Implementation - Should be overridden
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureReady() {
+        // Base Implementation - Should be overridden
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IImsMmTelFeature getBinder() {
+        return mImsMMTelBinder;
+    }
+}
diff --git a/android/telephony/ims/feature/RcsFeature.java b/android/telephony/ims/feature/RcsFeature.java
index 40c5181..a637e16 100644
--- a/android/telephony/ims/feature/RcsFeature.java
+++ b/android/telephony/ims/feature/RcsFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,16 +16,18 @@
 
 package android.telephony.ims.feature;
 
-import com.android.ims.internal.IImsRcsFeature;
+import android.annotation.SystemApi;
+import android.telephony.ims.aidl.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
  */
-
+@SystemApi
 public class RcsFeature extends ImsFeature {
 
+    /**{@inheritDoc}*/
     private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
         // Empty Default Implementation.
     };
@@ -35,16 +37,30 @@
         super();
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    public void onFeatureReady() {
-
+    public void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c) {
+        // Do nothing for base implementation.
     }
 
+    /**{@inheritDoc}*/
     @Override
     public void onFeatureRemoved() {
 
     }
 
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureReady() {
+
+    }
+
+    /**
+     * @hide
+     */
     @Override
     public final IImsRcsFeature getBinder() {
         return mImsRcsBinder;
diff --git a/android/telephony/ims/internal/ImsCallSessionListener.java b/android/telephony/ims/internal/ImsCallSessionListener.java
deleted file mode 100644
index 5d16dd5..0000000
--- a/android/telephony/ims/internal/ImsCallSessionListener.java
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * 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.internal;
-
-import android.os.RemoteException;
-import android.telephony.ims.internal.aidl.IImsCallSessionListener;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsConferenceState;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsSuppServiceNotification;
-import com.android.ims.internal.ImsCallSession;
-
-/**
- * Proxy class for interfacing with the framework's Call session for an ongoing IMS call.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
- *
- * @hide
- */
-public class ImsCallSessionListener {
-
-    private final IImsCallSessionListener mListener;
-
-    public ImsCallSessionListener(IImsCallSessionListener l) {
-        mListener = l;
-    }
-
-    /**
-     * Called when a request is sent out to initiate a new session
-     * and 1xx response is received from the network.
-     */
-    public void callSessionProgressing(ImsStreamMediaProfile profile)
-            throws RemoteException {
-        mListener.callSessionProgressing(profile);
-    }
-
-    /**
-     * Called when the session is initiated.
-     *
-     * @param profile the associated {@link ImsCallSession}.
-     */
-    public void callSessionInitiated(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionInitiated(profile);
-    }
-
-    /**
-     * Called when the session establishment has failed.
-     *
-     * @param reasonInfo detailed reason of the session establishment failure
-     */
-    public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionInitiatedFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session is terminated.
-     *
-     * @param reasonInfo detailed reason of the session termination
-     */
-    public void callSessionTerminated(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionTerminated(reasonInfo);
-    }
-
-    /**
-     * Called when the session is on hold.
-     */
-    public void callSessionHeld(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionHeld(profile);
-    }
-
-    /**
-     * Called when the session hold has failed.
-     *
-     * @param reasonInfo detailed reason of the session hold failure
-     */
-    public void callSessionHoldFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionHoldFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session hold is received from the remote user.
-     */
-    public void callSessionHoldReceived(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionHoldReceived(profile);
-    }
-
-    /**
-     * Called when the session resume is done.
-     */
-    public void callSessionResumed(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionResumed(profile);
-    }
-
-    /**
-     * Called when the session resume has failed.
-     *
-     * @param reasonInfo detailed reason of the session resume failure
-     */
-    public void callSessionResumeFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionResumeFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session resume is received from the remote user.
-     */
-    public void callSessionResumeReceived(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionResumeReceived(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 newSession the session object that is merged with an active & hold session
-     */
-    public void callSessionMergeStarted(ImsCallSession newSession, ImsCallProfile profile)
-            throws RemoteException {
-        mListener.callSessionMergeStarted(newSession != null ? newSession.getSession() : null,
-                profile);
-    }
-
-    /**
-     * Called when the session merge is successful and the merged session is active.
-     *
-     * @param newSession the new session object that is used for the conference
-     */
-    public void callSessionMergeComplete(ImsCallSession newSession) throws RemoteException {
-        mListener.callSessionMergeComplete(newSession != null ? newSession.getSession() : null);
-    }
-
-    /**
-     * Called when the session merge has failed.
-     *
-     * @param reasonInfo detailed reason of the call merge failure
-     */
-    public void callSessionMergeFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionMergeFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session is updated (except for hold/unhold).
-     */
-    public void callSessionUpdated(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionUpdated(profile);
-    }
-
-    /**
-     * Called when the session update has failed.
-     *
-     * @param reasonInfo detailed reason of the session update failure
-     */
-    public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionUpdateFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session update is received from the remote user.
-     */
-    public void callSessionUpdateReceived(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionUpdateReceived(profile);
-    }
-
-    /**
-     * Called when the session has been extended to a conference session.
-     *
-     * @param newSession the session object that is extended to the conference
-     *      from the active session
-     */
-    public void callSessionConferenceExtended(ImsCallSession newSession, ImsCallProfile profile)
-            throws RemoteException {
-        mListener.callSessionConferenceExtended(newSession != null ? newSession.getSession() : null,
-                profile);
-    }
-
-    /**
-     * Called when the conference extension has failed.
-     *
-     * @param reasonInfo detailed reason of the conference extension failure
-     */
-    public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionConferenceExtendFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the conference extension is received from the remote user.
-     */
-    public void callSessionConferenceExtendReceived(ImsCallSession newSession,
-            ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionConferenceExtendReceived(newSession != null
-                ? newSession.getSession() : null, profile);
-    }
-
-    /**
-     * Called when the invitation request of the participants is delivered to the conference
-     * server.
-     */
-    public void callSessionInviteParticipantsRequestDelivered() throws RemoteException {
-        mListener.callSessionInviteParticipantsRequestDelivered();
-    }
-
-    /**
-     * Called when the invitation request of the participants has failed.
-     *
-     * @param reasonInfo detailed reason of the conference invitation failure
-     */
-    public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo)
-            throws RemoteException {
-        mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the removal request of the participants is delivered to the conference
-     * server.
-     */
-    public void callSessionRemoveParticipantsRequestDelivered() throws RemoteException {
-        mListener.callSessionRemoveParticipantsRequestDelivered();
-    }
-
-    /**
-     * Called when the removal request of the participants has failed.
-     *
-     * @param reasonInfo detailed reason of the conference removal failure
-     */
-    public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo)
-            throws RemoteException {
-        mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
-    }
-
-    /**
-     * Notifies the framework of the updated Call session conference state.
-     *
-     * @param state the new {@link ImsConferenceState} associated with the conference.
-     */
-    public void callSessionConferenceStateUpdated(ImsConferenceState state) throws RemoteException {
-        mListener.callSessionConferenceStateUpdated(state);
-    }
-
-    /**
-     * Notifies the incoming USSD message.
-     */
-    public void callSessionUssdMessageReceived(int mode, String ussdMessage)
-            throws RemoteException {
-        mListener.callSessionUssdMessageReceived(mode, ussdMessage);
-    }
-
-    /**
-     * Notifies of a case where a {@link com.android.ims.internal.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}.
-     */
-    public void callSessionMayHandover(int srcAccessTech, int targetAccessTech)
-            throws RemoteException {
-        mListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
-    }
-
-    /**
-     * Called when session access technology changes.
-     *
-     * @param srcAccessTech original access technology
-     * @param targetAccessTech new access technology
-     * @param reasonInfo
-     */
-    public void callSessionHandover(int srcAccessTech, int targetAccessTech,
-            ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
-    }
-
-    /**
-     * Called when session access technology change fails.
-     *
-     * @param srcAccessTech original access technology
-     * @param targetAccessTech new access technology
-     * @param reasonInfo handover failure reason
-     */
-    public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
-            ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
-    }
-
-    /**
-     * Called when the TTY mode is 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) throws RemoteException {
-        mListener.callSessionTtyModeReceived(mode);
-    }
-
-    /**
-     * Called when the multiparty state is changed for this {@code ImsCallSession}.
-     *
-     * @param isMultiParty {@code true} if the session became multiparty,
-     *                     {@code false} otherwise.
-     */
-
-    public void callSessionMultipartyStateChanged(boolean isMultiParty) throws RemoteException {
-        mListener.callSessionMultipartyStateChanged(isMultiParty);
-    }
-
-    /**
-     * Called when the supplementary service information is received for the current session.
-     */
-    public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification)
-            throws RemoteException {
-        mListener.callSessionSuppServiceReceived(suppSrvNotification);
-    }
-
-    /**
-     * Received RTT modify request from the remote party.
-     *
-     * @param callProfile ImsCallProfile with updated attributes
-     */
-    public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile)
-            throws RemoteException {
-        mListener.callSessionRttModifyRequestReceived(callProfile);
-    }
-
-    /**
-     * @param status the received response for RTT modify request.
-     */
-    public void callSessionRttModifyResponseReceived(int status) throws RemoteException {
-        mListener.callSessionRttModifyResponseReceived(status);
-    }
-
-    /**
-     * Device received RTT message from Remote UE.
-     *
-     * @param rttMessage RTT message received
-     */
-    public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
-        mListener.callSessionRttMessageReceived(rttMessage);
-    }
-}
-
diff --git a/android/telephony/ims/internal/ImsService.java b/android/telephony/ims/internal/ImsService.java
deleted file mode 100644
index afaf332..0000000
--- a/android/telephony/ims/internal/ImsService.java
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * 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.internal;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.telephony.CarrierConfigManager;
-import android.telephony.ims.internal.aidl.IImsConfig;
-import android.telephony.ims.internal.aidl.IImsMmTelFeature;
-import android.telephony.ims.internal.aidl.IImsRcsFeature;
-import android.telephony.ims.internal.aidl.IImsServiceController;
-import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
-import android.telephony.ims.internal.feature.ImsFeature;
-import android.telephony.ims.internal.feature.MmTelFeature;
-import android.telephony.ims.internal.feature.RcsFeature;
-import android.telephony.ims.internal.stub.ImsConfigImplBase;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsRegistration;
-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.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.
- *   @hide
- */
-public class ImsService extends Service {
-
-    private static final String LOG_TAG = "ImsService";
-
-    /**
-     * 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<>();
-
-    private IImsServiceControllerListener mListener;
-
-
-    /**
-     * Listener that notifies the framework of ImsService changes.
-     */
-    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) {
-            mListener = l;
-        }
-
-        @Override
-        public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
-            return createMmTelFeatureInternal(slotId, c);
-        }
-
-        @Override
-        public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
-            return createRcsFeatureInternal(slotId, c);
-        }
-
-        @Override
-        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
-                throws RemoteException {
-            ImsService.this.removeImsFeature(slotId, featureType, c);
-        }
-
-        @Override
-        public ImsFeatureConfiguration querySupportedImsFeatures() {
-            return ImsService.this.querySupportedImsFeatures();
-        }
-
-        @Override
-        public void notifyImsServiceReadyForFeatureCreation() {
-            ImsService.this.readyForFeatureCreation();
-        }
-
-        @Override
-        public void notifyImsFeatureReady(int slotId, int featureType)
-                throws RemoteException {
-            ImsService.this.notifyImsFeatureReady(slotId, featureType);
-        }
-
-        @Override
-        public IImsConfig getConfig(int slotId) throws RemoteException {
-            ImsConfigImplBase c = ImsService.this.getConfig(slotId);
-            return c != null ? c.getBinder() : null;
-        }
-
-        @Override
-        public IImsRegistration getRegistration(int slotId) throws RemoteException {
-            ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
-            return r != null ? r.getBinder() : null;
-        }
-    };
-
-    /**
-     * @hide
-     */
-    @Override
-    public IBinder onBind(Intent intent) {
-        if(SERVICE_INTERFACE.equals(intent.getAction())) {
-            Log.i(LOG_TAG, "ImsService Bound.");
-            return mImsServiceController;
-        }
-        return null;
-    }
-
-    /**
-     * @hide
-     */
-    @VisibleForTesting
-    public SparseArray<ImsFeature> getFeatures(int slotId) {
-        return mFeaturesBySlot.get(slotId);
-    }
-
-    private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
-            IImsFeatureStatusCallback c) {
-        MmTelFeature f = createMmTelFeature(slotId);
-        if (f != null) {
-            setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
-            return f.getBinder();
-        } else {
-            Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
-            return null;
-        }
-    }
-
-    private IImsRcsFeature createRcsFeatureInternal(int slotId,
-            IImsFeatureStatusCallback c) {
-        RcsFeature f = createRcsFeature(slotId);
-        if (f != null) {
-            setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
-            return f.getBinder();
-        } else {
-            Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
-            return null;
-        }
-    }
-
-    private void setupFeature(ImsFeature f, int slotId, int featureType,
-            IImsFeatureStatusCallback c) {
-        f.addImsFeatureStatusCallback(c);
-        f.initialize(this, slotId);
-        addImsFeature(slotId, featureType, f);
-    }
-
-    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,
-            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 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.removeImsFeatureStatusCallback(c);
-            f.onFeatureRemoved();
-            features.remove(featureType);
-        }
-    }
-
-    private void notifyImsFeatureReady(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 notify ImsFeature ready. No ImsFeatures exist on " +
-                        "slot " + slotId);
-                return;
-            }
-            ImsFeature f = features.get(featureType);
-            if (f == null) {
-                Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type "
-                        + featureType + " exists on slot " + slotId);
-                return;
-            }
-            f.onFeatureReady();
-        }
-    }
-
-    /**
-     * When called, provide the {@link ImsFeatureConfiguration} that this ImsService currently
-     * supports. This will trigger the framework to set up the {@link ImsFeature}s that correspond
-     * to the {@link ImsFeature.FeatureType}s configured here.
-     * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports,
-     * defined in {@link ImsFeature.FeatureType}.
-     */
-    public ImsFeatureConfiguration querySupportedImsFeatures() {
-        // Return empty for base implementation
-        return new ImsFeatureConfiguration();
-    }
-
-    /**
-     * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
-     * features, defined in {@link ImsFeature.FeatureType} that this ImsService supports. This may
-     * trigger the framework to add/remove new ImsFeatures, depending on the configuration.
-     */
-    public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
-            throws RemoteException {
-        if (mListener == null) {
-            throw new IllegalStateException("Framework is not ready");
-        }
-        mListener.onUpdateSupportedImsFeatures(c);
-    }
-
-    /**
-     * 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
-     * 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() {
-    }
-
-    /**
-     * When called, the framework is requesting that a new MmTelFeature is created for the specified
-     * slot.
-     *
-     * @param slotId The slot ID that the MMTel Feature is being created for.
-     * @return The newly created MmTelFeature associated with the slot or null if the feature is not
-     * supported.
-     */
-    public MmTelFeature createMmTelFeature(int slotId) {
-        return null;
-    }
-
-    /**
-     * When called, the framework is requesting that a new RcsFeature is created for the specified
-     * slot
-     *
-     * @param slotId The slot ID that the RCS Feature is being created for.
-     * @return The newly created RcsFeature associated with the slot or null if the feature is not
-     * supported.
-     */
-    public RcsFeature createRcsFeature(int slotId) {
-        return null;
-    }
-
-    /**
-     * @param slotId The slot that the IMS configuration is associated with.
-     * @return ImsConfig implementation that is associated with the specified slot.
-     */
-    public ImsConfigImplBase getConfig(int slotId) {
-        return new ImsConfigImplBase();
-    }
-
-    /**
-     * @param slotId The slot that is associated with the IMS Registration.
-     * @return the ImsRegistration implementation associated with the slot.
-     */
-    public ImsRegistrationImplBase getRegistration(int slotId) {
-        return new ImsRegistrationImplBase();
-    }
-}
diff --git a/android/telephony/ims/internal/feature/ImsFeature.java b/android/telephony/ims/internal/feature/ImsFeature.java
deleted file mode 100644
index 9f82ad2..0000000
--- a/android/telephony/ims/internal/feature/ImsFeature.java
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * 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.internal.feature;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IInterface;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.telephony.SubscriptionManager;
-import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
-import android.util.Log;
-
-import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.internal.annotations.VisibleForTesting;
-
-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";
-
-    /**
-     * Action to broadcast when ImsService is up.
-     * Internal use only.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String ACTION_IMS_SERVICE_UP =
-            "com.android.ims.IMS_SERVICE_UP";
-
-    /**
-     * Action to broadcast when ImsService is down.
-     * Internal use only.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String ACTION_IMS_SERVICE_DOWN =
-            "com.android.ims.IMS_SERVICE_DOWN";
-
-    /**
-     * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
-     * A long value; the phone ID corresponding to the IMS service coming up or down.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String EXTRA_PHONE_ID = "android:phone_id";
-
-    // Invalid feature value
-    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.
-    public static final int FEATURE_EMERGENCY_MMTEL = 0;
-    public static final int FEATURE_MMTEL = 1;
-    public static final int FEATURE_RCS = 2;
-    // Total number of features defined
-    public static final int FEATURE_MAX = 3;
-
-    // Integer values defining IMS features that are supported in ImsFeature.
-    @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.
-    @IntDef(flag = true,
-            value = {
-                    STATE_UNAVAILABLE,
-                    STATE_INITIALIZING,
-                    STATE_READY,
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ImsState {}
-
-    public static final int STATE_UNAVAILABLE = 0;
-    public static final int STATE_INITIALIZING = 1;
-    public static final int STATE_READY = 2;
-
-    // Integer values defining the result codes that should be returned from
-    // {@link changeEnabledCapabilities} when the framework tries to set a feature's capability.
-    @IntDef(flag = true,
-            value = {
-                    CAPABILITY_ERROR_GENERIC,
-                    CAPABILITY_SUCCESS
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ImsCapabilityError {}
-
-    public static final int CAPABILITY_ERROR_GENERIC = -1;
-    public static final int CAPABILITY_SUCCESS = 0;
-
-
-    /**
-     * The framework implements this callback in order to register for Feature Capability status
-     * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
-     * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
-     * callbacks when the ImsService can not change the capability as requested, via
-     * {@link #onChangeCapabilityConfigurationError}.
-     */
-    public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
-
-        @Override
-        public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
-            onCapabilitiesStatusChanged(new Capabilities(config));
-        }
-
-        /**
-         * Returns the result of a query for the capability configuration of a requested capability.
-         *
-         * @param capability The capability that was requested.
-         * @param radioTech The IMS radio technology associated with the capability.
-         * @param isEnabled true if the capability is enabled, false otherwise.
-         */
-        @Override
-        public void onQueryCapabilityConfiguration(int capability, int radioTech,
-                boolean isEnabled) {
-
-        }
-
-        /**
-         * Called when a change to the capability configuration has returned an error.
-         *
-         * @param capability The capability that was requested to be changed.
-         * @param radioTech The IMS radio technology associated with the capability.
-         * @param reason error associated with the failure to change configuration.
-         */
-        @Override
-        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
-                int reason) {
-        }
-
-        /**
-         * 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 config The new availability of the capabilities.
-         */
-        public void onCapabilitiesStatusChanged(Capabilities config) {
-        }
-    }
-
-    /**
-     * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
-     * provided.
-     */
-    protected static class CapabilityCallbackProxy {
-        private final IImsCapabilityCallback mCallback;
-
-        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.
-         * @param radioTech The radio tech that this capability failed for.
-         * @param reason The reason this capability was unable to be changed.
-         */
-        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
-                @ImsCapabilityError int reason) {
-            try {
-                mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder.");
-            }
-        }
-
-        public void onQueryCapabilityConfiguration(int capability, int radioTech,
-                boolean isEnabled) {
-            try {
-                mCallback.onQueryCapabilityConfiguration(capability, radioTech, isEnabled);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "onQueryCapabilityConfiguration called on dead binder.");
-            }
-        }
-    }
-
-    /**
-     * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask.
-     */
-    public static class Capabilities {
-        protected int mCapabilities = 0;
-
-        public Capabilities() {
-        }
-
-        protected Capabilities(int capabilities) {
-            mCapabilities = capabilities;
-        }
-
-        /**
-         * @param capabilities Capabilities to be added to the configuration in the form of a
-         *     bit mask.
-         */
-        public void addCapabilities(int capabilities) {
-            mCapabilities |= capabilities;
-        }
-
-        /**
-         * @param capabilities Capabilities to be removed to the configuration in the form of a
-         *     bit mask.
-         */
-        public void removeCapabilities(int capabilities) {
-            mCapabilities &= ~capabilities;
-        }
-
-        /**
-         * @return true if all of the capabilities specified are capable.
-         */
-        public boolean isCapable(int capabilities) {
-            return (mCapabilities & capabilities) == capabilities;
-        }
-
-        public Capabilities copy() {
-            return new Capabilities(mCapabilities);
-        }
-
-        /**
-         * @return a bitmask containing the capability flags directly.
-         */
-        public int getMask() {
-            return mCapabilities;
-        }
-
-        @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;
-        }
-
-        @Override
-        public int hashCode() {
-            return mCapabilities;
-        }
-    }
-
-    private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
-            new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
-    private @ImsState int mState = STATE_UNAVAILABLE;
-    private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
-    private Context mContext;
-    private final Object mLock = new Object();
-    private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
-            = new RemoteCallbackList<>();
-    private Capabilities mCapabilityStatus = new Capabilities();
-
-    public final void initialize(Context context, int slotId) {
-        mContext = context;
-        mSlotId = slotId;
-    }
-
-    public final int getFeatureState() {
-        synchronized (mLock) {
-            return mState;
-        }
-    }
-
-    protected final void setFeatureState(@ImsState int state) {
-        synchronized (mLock) {
-            if (mState != state) {
-                mState = state;
-                notifyFeatureState(state);
-            }
-        }
-    }
-
-    // Not final for testing, but shouldn't be extended!
-    @VisibleForTesting
-    public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
-        try {
-            // If we have just connected, send queued status.
-            c.notifyImsFeatureStatus(getFeatureState());
-            // Add the callback if the callback completes successfully without a RemoteException.
-            synchronized (mLock) {
-                mStatusCallbacks.add(c);
-            }
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
-        }
-    }
-
-    @VisibleForTesting
-    // Not final for testing, but should not be extended!
-    public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
-        synchronized (mLock) {
-            mStatusCallbacks.remove(c);
-        }
-    }
-
-    /**
-     * Internal method called by ImsFeature when setFeatureState has changed.
-     */
-    private void notifyFeatureState(@ImsState int state) {
-        synchronized (mLock) {
-            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());
-                }
-            }
-        }
-        sendImsServiceIntent(state);
-    }
-
-    /**
-     * Provide backwards compatibility using deprecated service UP/DOWN intents.
-     */
-    private void sendImsServiceIntent(@ImsState int state) {
-        if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
-            return;
-        }
-        Intent intent;
-        switch (state) {
-            case ImsFeature.STATE_UNAVAILABLE:
-            case ImsFeature.STATE_INITIALIZING:
-                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
-                break;
-            case ImsFeature.STATE_READY:
-                intent = new Intent(ACTION_IMS_SERVICE_UP);
-                break;
-            default:
-                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
-        }
-        intent.putExtra(EXTRA_PHONE_ID, mSlotId);
-        mContext.sendBroadcast(intent);
-    }
-
-    public final void addCapabilityCallback(IImsCapabilityCallback c) {
-        mCapabilityCallbacks.register(c);
-    }
-
-    public final void removeCapabilityCallback(IImsCapabilityCallback c) {
-        mCapabilityCallbacks.unregister(c);
-    }
-
-    /**
-     * @return the cached capabilities status for this feature.
-     */
-    @VisibleForTesting
-    public Capabilities queryCapabilityStatus() {
-        synchronized (mLock) {
-            return mCapabilityStatus.copy();
-        }
-    }
-
-    // Called internally to request the change of enabled capabilities.
-    @VisibleForTesting
-    public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request,
-            IImsCapabilityCallback c) throws RemoteException {
-        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 c A {@link Capabilities} containing the new Capabilities status.
-     */
-    protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
-        synchronized (mLock) {
-            mCapabilityStatus = c.copy();
-        }
-        int count = mCapabilityCallbacks.beginBroadcast();
-        try {
-            for (int i = 0; i < count; i++) {
-                try {
-                    mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
-                            c.mCapabilities);
-                } catch (RemoteException e) {
-                    Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
-                            "callback.");
-                }
-            }
-        } finally {
-            mCapabilityCallbacks.finishBroadcast();
-        }
-    }
-
-    /**
-     * 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 when the feature has been initialized and communication with the framework is set up.
-     * 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.
-     */
-    protected abstract IInterface getBinder();
-}
diff --git a/android/telephony/ims/internal/feature/MmTelFeature.java b/android/telephony/ims/internal/feature/MmTelFeature.java
deleted file mode 100644
index 9b576c7..0000000
--- a/android/telephony/ims/internal/feature/MmTelFeature.java
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * 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.internal.feature;
-
-import android.annotation.IntDef;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telecom.TelecomManager;
-import android.telephony.ims.internal.ImsCallSessionListener;
-import android.telephony.ims.internal.aidl.IImsCallSessionListener;
-import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
-import android.telephony.ims.internal.aidl.IImsMmTelFeature;
-import android.telephony.ims.internal.aidl.IImsMmTelListener;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.telephony.ims.stub.ImsEcbmImplBase;
-import android.telephony.ims.stub.ImsMultiEndpointImplBase;
-import android.telephony.ims.stub.ImsUtImplBase;
-import android.util.Log;
-
-import com.android.ims.ImsCallProfile;
-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.ims.internal.ImsCallSession;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * 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.
- * @hide
- */
-
-public class MmTelFeature extends ImsFeature {
-
-    private static final String LOG_TAG = "MmTelFeature";
-
-    private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
-
-        @Override
-        public void setListener(IImsMmTelListener l) throws RemoteException {
-            synchronized (mLock) {
-                MmTelFeature.this.setListener(l);
-            }
-        }
-
-        @Override
-        public int getFeatureState() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.getFeatureState();
-            }
-        }
-
-
-        @Override
-        public ImsCallProfile createCallProfile(int callSessionType, int callType)
-                throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.createCallProfile(callSessionType,  callType);
-            }
-        }
-
-        @Override
-        public IImsCallSession createCallSession(ImsCallProfile profile,
-                IImsCallSessionListener listener) throws RemoteException {
-            synchronized (mLock) {
-                ImsCallSession s = MmTelFeature.this.createCallSession(profile,
-                        new ImsCallSessionListener(listener));
-                return s != null ? s.getSession() : null;
-            }
-        }
-
-        @Override
-        public IImsUt getUtInterface() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.getUt();
-            }
-        }
-
-        @Override
-        public IImsEcbm getEcbmInterface() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.getEcbm();
-            }
-        }
-
-        @Override
-        public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
-            synchronized (mLock) {
-                MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
-            }
-        }
-
-        @Override
-        public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.getMultiEndpoint();
-            }
-        }
-
-        @Override
-        public int queryCapabilityStatus() throws RemoteException {
-            return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
-        }
-
-        @Override
-        public void addCapabilityCallback(IImsCapabilityCallback c) {
-            MmTelFeature.this.addCapabilityCallback(c);
-        }
-
-        @Override
-        public void removeCapabilityCallback(IImsCapabilityCallback c) {
-            MmTelFeature.this.removeCapabilityCallback(c);
-        }
-
-        @Override
-        public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
-                IImsCapabilityCallback c) throws RemoteException {
-            MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
-        }
-
-        @Override
-        public void queryCapabilityConfiguration(int capability, int radioTech,
-                IImsCapabilityCallback c) {
-            queryCapabilityConfigurationInternal(capability, radioTech, c);
-        }
-    };
-
-    /**
-     * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
-     * The capabilities that are used in MmTelFeature are defined by {@link MmTelCapability}.
-     *
-     * The capabilities of this MmTelFeature will be set by the framework and can be queried with
-     * {@link #queryCapabilityStatus()}.
-     *
-     * This MmTelFeature can then return the status of each of these capabilities (enabled or not)
-     * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
-     * status can also be queried using {@link #queryCapabilityStatus()}.
-     */
-    public static class MmTelCapabilities extends Capabilities {
-
-        @VisibleForTesting
-        public MmTelCapabilities() {
-            super();
-        }
-
-        public MmTelCapabilities(Capabilities c) {
-            mCapabilities = c.mCapabilities;
-        }
-
-        @IntDef(flag = true,
-                value = {
-                        CAPABILITY_TYPE_VOICE,
-                        CAPABILITY_TYPE_VIDEO,
-                        CAPABILITY_TYPE_UT,
-                        CAPABILITY_TYPE_SMS
-                })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface MmTelCapability {}
-
-        /**
-         * 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;
-
-        @Override
-        public final void addCapabilities(@MmTelCapability int capabilities) {
-            super.addCapabilities(capabilities);
-        }
-
-        @Override
-        public final void removeCapabilities(@MmTelCapability int capability) {
-            super.removeCapabilities(capability);
-        }
-
-        @Override
-        public final boolean isCapable(@MmTelCapability int capabilities) {
-            return super.isCapable(capabilities);
-        }
-    }
-
-    /**
-     * Listener that the framework implements for communication from the MmTelFeature.
-     */
-    public static class Listener extends IImsMmTelListener.Stub {
-
-        @Override
-        public final void onIncomingCall(IImsCallSession c) {
-            onIncomingCall(new ImsCallSession(c));
-        }
-
-        /**
-         * Updates the Listener when the voice message count for IMS has changed.
-         * @param count an integer representing the new message count.
-         */
-        @Override
-        public void onVoiceMessageCountUpdate(int count) {
-
-        }
-
-        /**
-         * Called when the IMS provider receives an incoming call.
-         * @param c The {@link ImsCallSession} associated with the new call.
-         */
-        public void onIncomingCall(ImsCallSession c) {
-        }
-    }
-
-    // Lock for feature synchronization
-    private final Object mLock = new Object();
-    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;
-        }
-    }
-
-    private 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!");
-        }
-    }
-
-    /**
-     * 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.
-     */
-    @Override
-    public 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.
-     */
-    protected final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
-        super.notifyCapabilitiesStatusChanged(c);
-    }
-
-    /**
-     * Notify the framework of an incoming call.
-     * @param c The {@link ImsCallSession} of the new incoming call.
-     *
-     * @throws RemoteException if the connection to the framework is not available. If this happens,
-     *     the call should be no longer considered active and should be cleaned up.
-     * */
-    protected final void notifyIncomingCall(ImsCallSession c) throws RemoteException {
-        synchronized (mLock) {
-            if (mListener == null) {
-                throw new IllegalStateException("Session is not available.");
-            }
-            mListener.onIncomingCall(c.getSession());
-        }
-    }
-
-    /**
-     * 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.
-     */
-    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.
-     */
-    @Override
-    public void changeEnabledCapabilities(CapabilityChangeRequest request,
-            CapabilityCallbackProxy c) {
-        // Base implementation, no-op
-    }
-
-    /**
-     * 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
-     */
-    public ImsCallProfile createCallProfile(int callSessionType, int callType) {
-        // Base Implementation - Should be overridden
-        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
-     * @param listener An implementation of IImsCallSessionListener.
-     */
-    public ImsCallSession createCallSession(ImsCallProfile profile,
-            ImsCallSessionListener listener) {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * @return The Ut interface for the supplementary service configuration.
-     */
-    public ImsUtImplBase getUt() {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
-     */
-    public ImsEcbmImplBase getEcbm() {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
-     */
-    public ImsMultiEndpointImplBase getMultiEndpoint() {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * 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 A {@link Message} to be used when the mode has been set.
-     */
-    void setUiTtyMode(int mode, Message onCompleteMessage) {
-        // Base Implementation - Should be overridden
-    }
-
-    /**{@inheritDoc}*/
-    @Override
-    public void onFeatureRemoved() {
-        // Base Implementation - Should be overridden
-    }
-
-    /**{@inheritDoc}*/
-    @Override
-    public void onFeatureReady() {
-        // Base Implementation - Should be overridden
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public final IImsMmTelFeature getBinder() {
-        return mImsMMTelBinder;
-    }
-}
diff --git a/android/telephony/ims/internal/stub/ImsConfigImplBase.java b/android/telephony/ims/internal/stub/ImsConfigImplBase.java
deleted file mode 100644
index 33aec5d..0000000
--- a/android/telephony/ims/internal/stub/ImsConfigImplBase.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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.internal.stub;
-
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.telephony.ims.internal.aidl.IImsConfig;
-import android.telephony.ims.internal.aidl.IImsConfigCallback;
-
-import com.android.ims.ImsConfig;
-
-/**
- * Controls the modification of IMS specific configurations. For more information on the supported
- * IMS configuration constants, see {@link ImsConfig}.
- *
- * @hide
- */
-
-public class ImsConfigImplBase {
-
-    //TODO: Implement the Binder logic to call base APIs. Need to finish other ImsService Config
-    // work first.
-    private final IImsConfig mBinder = new IImsConfig.Stub() {
-
-        @Override
-        public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
-            ImsConfigImplBase.this.addImsConfigCallback(c);
-        }
-
-        @Override
-        public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
-            ImsConfigImplBase.this.removeImsConfigCallback(c);
-        }
-
-        @Override
-        public int getConfigInt(int item) throws RemoteException {
-            return Integer.MIN_VALUE;
-        }
-
-        @Override
-        public String getConfigString(int item) throws RemoteException {
-            return null;
-        }
-
-        @Override
-        public int setConfigInt(int item, int value) throws RemoteException {
-            return Integer.MIN_VALUE;
-        }
-
-        @Override
-        public int setConfigString(int item, String value) throws RemoteException {
-            return Integer.MIN_VALUE;
-        }
-    };
-
-    public class Callback extends IImsConfigCallback.Stub {
-
-        @Override
-        public final void onIntConfigChanged(int item, int value) throws RemoteException {
-            onConfigChanged(item, value);
-        }
-
-        @Override
-        public final void onStringConfigChanged(int item, String value) throws RemoteException {
-            onConfigChanged(item, value);
-        }
-
-        /**
-         * Called when the IMS configuration has changed.
-         * @param item the IMS configuration key constant, as defined in ImsConfig.
-         * @param value the new integer value of the IMS configuration constant.
-         */
-        public void onConfigChanged(int item, int value) {
-            // Base Implementation
-        }
-
-        /**
-         * Called when the IMS configuration has changed.
-         * @param item the IMS configuration key constant, as defined in ImsConfig.
-         * @param value the new String value of the IMS configuration constant.
-         */
-        public void onConfigChanged(int item, String value) {
-            // Base Implementation
-        }
-    }
-
-    private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
-
-    /**
-     * Adds a {@link 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 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);
-    }
-
-    public final IImsConfig getBinder() {
-        return mBinder;
-    }
-
-    /**
-     * 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 master value is derived.
-     *
-     * @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 setConfig(int item, int value) {
-        // Base Implementation - To be overridden.
-        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 master value is derived.
-     *
-     * @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 setConfig(int item, String value) {
-        return ImsConfig.OperationStatusConstants.FAILED;
-    }
-
-    /**
-     * Gets the value for ims service/capabilities parameters from the provisioned
-     * value storage.
-     *
-     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @return value in Integer format.
-     */
-    public int getConfigInt(int item) {
-        return ImsConfig.OperationStatusConstants.FAILED;
-    }
-
-    /**
-     * Gets the value for ims service/capabilities parameters from the provisioned
-     * value storage.
-     *
-     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @return value in String format.
-     */
-    public String getConfigString(int item) {
-        return null;
-    }
-}
diff --git a/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java b/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java
deleted file mode 100644
index 244c957..0000000
--- a/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * 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.internal.stub;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.ims.internal.feature.ImsFeature;
-import android.util.ArraySet;
-
-import java.util.Arrays;
-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.FeatureType}.
- * @hide
- */
-public class ImsFeatureConfiguration implements Parcelable {
-    /**
-     * Features that this ImsService supports.
-     */
-    private final Set<Integer> mFeatures;
-
-    /**
-     * Creates an ImsFeatureConfiguration with the features
-     */
-    public static class Builder {
-            ImsFeatureConfiguration mConfig;
-        public Builder() {
-            mConfig = new ImsFeatureConfiguration();
-        }
-
-        /**
-         * @param feature A feature defined in {@link ImsFeature.FeatureType} that this service
-         *         supports.
-         * @return a {@link Builder} to continue constructing the ImsFeatureConfiguration.
-         */
-        public Builder addFeature(@ImsFeature.FeatureType int feature) {
-            mConfig.addFeature(feature);
-            return this;
-        }
-
-        public ImsFeatureConfiguration build() {
-            return mConfig;
-        }
-    }
-
-    /**
-     * Creates with all registration features empty.
-     *
-     * Consider using the provided {@link Builder} to create this configuration instead.
-     */
-    public ImsFeatureConfiguration() {
-        mFeatures = new ArraySet<>();
-    }
-
-    /**
-     * Configuration of the ImsService, which describes which features the ImsService supports
-     * (for registration).
-     * @param features an array of feature integers defined in {@link ImsFeature} that describe
-     * which features this ImsService supports.
-     */
-    public ImsFeatureConfiguration(int[] features) {
-        mFeatures = new ArraySet<>();
-
-        if (features != null) {
-            for (int i : features) {
-                mFeatures.add(i);
-            }
-        }
-    }
-
-    /**
-     * @return an int[] containing the features that this ImsService supports.
-     */
-    public int[] getServiceFeatures() {
-        return mFeatures.stream().mapToInt(i->i).toArray();
-    }
-
-    void addFeature(int feature) {
-        mFeatures.add(feature);
-    }
-
-    protected ImsFeatureConfiguration(Parcel in) {
-        int[] features = in.createIntArray();
-        if (features != null) {
-            mFeatures = new ArraySet<>(features.length);
-            for(Integer i : features) {
-                mFeatures.add(i);
-            }
-        } else {
-            mFeatures = new ArraySet<>();
-        }
-    }
-
-    public static final 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) {
-        dest.writeIntArray(mFeatures.stream().mapToInt(i->i).toArray());
-    }
-
-    @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);
-    }
-
-    @Override
-    public int hashCode() {
-        return mFeatures.hashCode();
-    }
-}
diff --git a/android/telephony/ims/stub/ImsCallSessionImplBase.java b/android/telephony/ims/stub/ImsCallSessionImplBase.java
index 80b2f78..7b9fe2b 100644
--- a/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -16,105 +16,264 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.os.Message;
 import android.os.RemoteException;
+import android.telephony.ims.ImsCallSessionListener;
+import android.telephony.ims.aidl.IImsCallSessionListener;
 
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsCallSession;
 import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
 import com.android.ims.internal.IImsVideoCallProvider;
+import android.telephony.ims.ImsVideoCallProvider;
+
+import dalvik.system.CloseGuard;
 
 /**
- * Base implementation of IImsCallSession, which implements stub versions of the methods in the
- * IImsCallSession AIDL. Override the methods that your implementation of ImsCallSession supports.
+ * Base implementation of IImsCallSession, which implements stub versions of the methods available.
  *
- * 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.
+ * Override the methods that your implementation of ImsCallSession supports.
  *
  * @hide
  */
-
-public class ImsCallSessionImplBase extends IImsCallSession.Stub {
+@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 {
+    /**
+     * Notify USSD Mode.
+     */
+    public static final int USSD_MODE_NOTIFY = 0;
+    /**
+     * Request USSD Mode
+     */
+    public static final int USSD_MODE_REQUEST = 1;
 
     /**
-     * Closes the object. This object is not usable after being closed.
+     * Defines IMS call session state.
      */
-    @Override
-    public void close() throws RemoteException {
+    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() {
+        }
     }
 
-    /**
-     * Gets the call ID of the session.
-     *
-     * @return the call ID
-     */
-    @Override
-    public String getCallId() throws RemoteException {
-        return null;
-    }
+    // Non-final for injection by tests
+    private IImsCallSession mServiceImpl = new IImsCallSession.Stub() {
+        @Override
+        public void close() {
+            ImsCallSessionImplBase.this.close();
+        }
+
+        @Override
+        public String getCallId() {
+            return ImsCallSessionImplBase.this.getCallId();
+        }
+
+        @Override
+        public ImsCallProfile getCallProfile() {
+            return ImsCallSessionImplBase.this.getCallProfile();
+        }
+
+        @Override
+        public ImsCallProfile getLocalCallProfile() {
+            return ImsCallSessionImplBase.this.getLocalCallProfile();
+        }
+
+        @Override
+        public ImsCallProfile getRemoteCallProfile() {
+            return ImsCallSessionImplBase.this.getRemoteCallProfile();
+        }
+
+        @Override
+        public String getProperty(String name) {
+            return ImsCallSessionImplBase.this.getProperty(name);
+        }
+
+        @Override
+        public int getState() {
+            return ImsCallSessionImplBase.this.getState();
+        }
+
+        @Override
+        public boolean isInCall() {
+            return ImsCallSessionImplBase.this.isInCall();
+        }
+
+        @Override
+        public void setListener(IImsCallSessionListener listener) {
+            ImsCallSessionImplBase.this.setListener(new ImsCallSessionListener(listener));
+        }
+
+        @Override
+        public void setMute(boolean muted) {
+            ImsCallSessionImplBase.this.setMute(muted);
+        }
+
+        @Override
+        public void start(String callee, ImsCallProfile profile) {
+            ImsCallSessionImplBase.this.start(callee, profile);
+        }
+
+        @Override
+        public void startConference(String[] participants, ImsCallProfile profile) throws
+                RemoteException {
+            ImsCallSessionImplBase.this.startConference(participants, profile);
+        }
+
+        @Override
+        public void accept(int callType, ImsStreamMediaProfile profile) {
+            ImsCallSessionImplBase.this.accept(callType, profile);
+        }
+
+        @Override
+        public void deflect(String deflectNumber) {
+            ImsCallSessionImplBase.this.deflect(deflectNumber);
+        }
+
+        @Override
+        public void reject(int reason) {
+            ImsCallSessionImplBase.this.reject(reason);
+        }
+
+        @Override
+        public void terminate(int reason) {
+            ImsCallSessionImplBase.this.terminate(reason);
+        }
+
+        @Override
+        public void hold(ImsStreamMediaProfile profile) {
+            ImsCallSessionImplBase.this.hold(profile);
+        }
+
+        @Override
+        public void resume(ImsStreamMediaProfile profile) {
+            ImsCallSessionImplBase.this.resume(profile);
+        }
+
+        @Override
+        public void merge() {
+            ImsCallSessionImplBase.this.merge();
+        }
+
+        @Override
+        public void update(int callType, ImsStreamMediaProfile profile) {
+            ImsCallSessionImplBase.this.update(callType, profile);
+        }
+
+        @Override
+        public void extendToConference(String[] participants) {
+            ImsCallSessionImplBase.this.extendToConference(participants);
+        }
+
+        @Override
+        public void inviteParticipants(String[] participants) {
+            ImsCallSessionImplBase.this.inviteParticipants(participants);
+        }
+
+        @Override
+        public void removeParticipants(String[] participants) {
+            ImsCallSessionImplBase.this.removeParticipants(participants);
+        }
+
+        @Override
+        public void sendDtmf(char c, Message result) {
+            ImsCallSessionImplBase.this.sendDtmf(c, result);
+        }
+
+        @Override
+        public void startDtmf(char c) {
+            ImsCallSessionImplBase.this.startDtmf(c);
+        }
+
+        @Override
+        public void stopDtmf() {
+            ImsCallSessionImplBase.this.stopDtmf();
+        }
+
+        @Override
+        public void sendUssd(String ussdMessage) {
+            ImsCallSessionImplBase.this.sendUssd(ussdMessage);
+        }
+
+        @Override
+        public IImsVideoCallProvider getVideoCallProvider() {
+            return ImsCallSessionImplBase.this.getVideoCallProvider();
+        }
+
+        @Override
+        public boolean isMultiparty() {
+            return ImsCallSessionImplBase.this.isMultiparty();
+        }
+
+        @Override
+        public void sendRttModifyRequest(ImsCallProfile toProfile) {
+            ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile);
+        }
+
+        @Override
+        public void sendRttModifyResponse(boolean status) {
+            ImsCallSessionImplBase.this.sendRttModifyResponse(status);
+        }
+
+        @Override
+        public void sendRttMessage(String rttMessage) {
+            ImsCallSessionImplBase.this.sendRttMessage(rttMessage);
+        }
+    };
 
     /**
-     * Gets the call profile that this session is associated with
-     *
-     * @return the {@link ImsCallProfile} that this session is associated with
+     * @hide
      */
-    @Override
-    public ImsCallProfile getCallProfile() throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Gets the local call profile that this session is associated with
-     *
-     * @return the local {@link ImsCallProfile} that this session is associated with
-     */
-    @Override
-    public ImsCallProfile getLocalCallProfile() throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Gets the remote call profile that this session is associated with
-     *
-     * @return the remote {@link ImsCallProfile} that this session is associated with
-     */
-    @Override
-    public ImsCallProfile getRemoteCallProfile() throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Gets the value associated with the specified property of this session.
-     *
-     * @return the string value associated with the specified property
-     */
-    @Override
-    public String getProperty(String name) throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Gets the session state.
-     * The value returned must be one of the states in {@link ImsCallSession.State}.
-     *
-     * @return the session state
-     */
-    @Override
-    public int getState() throws RemoteException {
-        return ImsCallSession.State.INVALID;
-    }
-
-    /**
-     * Checks if the session is in call.
-     *
-     * @return true if the session is in call, false otherwise
-     */
-    @Override
-    public boolean isInCall() throws RemoteException {
-        return false;
+    public final void setListener(IImsCallSessionListener listener) throws RemoteException {
+        setListener(new ImsCallSessionListener(listener));
     }
 
     /**
@@ -122,25 +281,87 @@
      * 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 listener {@link ImsCallSessionListener} used to notify the framework of updates
+     * to the ImsCallSession
+     */
+    public void setListener(ImsCallSessionListener listener) {
+    }
+
+    /**
+     * Closes the object. This {@link ImsCallSessionImplBase} is not usable after being closed.
      */
     @Override
-    public void setListener(IImsCallSessionListener listener) throws RemoteException {
+    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 is muted, false otherwise
+     * @param muted true if the call should be muted, false otherwise.
      */
-    @Override
-    public void setMute(boolean muted) throws RemoteException {
+    public void setMute(boolean muted) {
     }
 
     /**
-     * Initiates an IMS call with the specified target and call profile.
-     * The session listener set in {@link #setListener} is called back upon defined session events.
-     * The method is only valid to call when the session state is in
+     * 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
@@ -149,13 +370,13 @@
      * @see {@link ImsCallSession.Listener#callSessionStarted},
      * {@link ImsCallSession.Listener#callSessionStartFailed}
      */
-    @Override
-    public void start(String callee, ImsCallProfile profile) throws RemoteException {
+    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} is called back upon defined session events.
+     * 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}.
      *
@@ -165,9 +386,7 @@
      * @see {@link ImsCallSession.Listener#callSessionStarted},
      * {@link ImsCallSession.Listener#callSessionStartFailed}
      */
-    @Override
-    public void startConference(String[] participants, ImsCallProfile profile)
-            throws RemoteException {
+    public void startConference(String[] participants, ImsCallProfile profile) {
     }
 
     /**
@@ -177,31 +396,34 @@
      * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
      * @see {@link ImsCallSession.Listener#callSessionStarted}
      */
-    @Override
-    public void accept(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+    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
-     *         com.android.ims.ImsReasonInfo
+     * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
      * {@link ImsCallSession.Listener#callSessionStartFailed}
      */
-    @Override
-    public void reject(int reason) throws RemoteException {
+    public void reject(int reason) {
     }
 
     /**
      * Terminates a call.
      *
-     * @param reason reason code to terminate a call, defined in
-     *         com.android.ims.ImsReasonInfo
+     * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
      *
      * @see {@link ImsCallSession.Listener#callSessionTerminated}
      */
-    @Override
-    public void terminate(int reason) throws RemoteException {
+    public void terminate(int reason) {
     }
 
     /**
@@ -212,8 +434,7 @@
      * @see {@link ImsCallSession.Listener#callSessionHeld},
      * {@link ImsCallSession.Listener#callSessionHoldFailed}
      */
-    @Override
-    public void hold(ImsStreamMediaProfile profile) throws RemoteException {
+    public void hold(ImsStreamMediaProfile profile) {
     }
 
     /**
@@ -224,12 +445,11 @@
      * @see {@link ImsCallSession.Listener#callSessionResumed},
      * {@link ImsCallSession.Listener#callSessionResumeFailed}
      */
-    @Override
-    public void resume(ImsStreamMediaProfile profile) throws RemoteException {
+    public void resume(ImsStreamMediaProfile profile) {
     }
 
     /**
-     * Merges the active & hold call. When the merge starts,
+     * 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
@@ -239,8 +459,7 @@
      * {@link ImsCallSession.Listener#callSessionMergeComplete},
      *      {@link ImsCallSession.Listener#callSessionMergeFailed}
      */
-    @Override
-    public void merge() throws RemoteException {
+    public void merge() {
     }
 
     /**
@@ -251,8 +470,7 @@
      * @see {@link ImsCallSession.Listener#callSessionUpdated},
      * {@link ImsCallSession.Listener#callSessionUpdateFailed}
      */
-    @Override
-    public void update(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+    public void update(int callType, ImsStreamMediaProfile profile) {
     }
 
     /**
@@ -263,8 +481,7 @@
      * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
      * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
      */
-    @Override
-    public void extendToConference(String[] participants) throws RemoteException {
+    public void extendToConference(String[] participants) {
     }
 
     /**
@@ -274,8 +491,7 @@
      * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
      *      {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
      */
-    @Override
-    public void inviteParticipants(String[] participants) throws RemoteException {
+    public void inviteParticipants(String[] participants) {
     }
 
     /**
@@ -285,8 +501,7 @@
      * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
      *      {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
      */
-    @Override
-    public void removeParticipants(String[] participants) throws RemoteException {
+    public void removeParticipants(String[] participants) {
     }
 
     /**
@@ -296,8 +511,7 @@
      *
      * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
      */
-    @Override
-    public void sendDtmf(char c, Message result) throws RemoteException {
+    public void sendDtmf(char c, Message result) {
     }
 
     /**
@@ -307,15 +521,13 @@
      *
      * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
      */
-    @Override
-    public void startDtmf(char c) throws RemoteException {
+    public void startDtmf(char c) {
     }
 
     /**
      * Stop a DTMF code.
      */
-    @Override
-    public void stopDtmf() throws RemoteException {
+    public void stopDtmf() {
     }
 
     /**
@@ -323,17 +535,23 @@
      *
      * @param ussdMessage USSD message to send
      */
-    @Override
-    public void sendUssd(String ussdMessage) throws RemoteException {
+    public void sendUssd(String ussdMessage) {
     }
 
     /**
-     * Returns a binder for the video call provider implementation contained within the IMS service
-     * process. This binder is used by the VideoCallProvider subclass in Telephony which
-     * intermediates between the propriety implementation and Telecomm/InCall.
+     * See {@link #getImsVideoCallProvider()}, used directly in older ImsService implementations.
+     * @hide
      */
-    @Override
-    public IImsVideoCallProvider getVideoCallProvider() throws RemoteException {
+    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;
     }
 
@@ -341,8 +559,7 @@
      * Determines if the current session is multiparty.
      * @return {@code True} if the session is multiparty.
      */
-    @Override
-    public boolean isMultiparty() throws RemoteException {
+    public boolean isMultiparty() {
         return false;
     }
 
@@ -350,16 +567,13 @@
      * 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  Accepted the request
-     *                false  Declined the request
+     * @param status true if the the request was accepted or false of the request is defined.
      */
-    @Override
     public void sendRttModifyResponse(boolean status) {
     }
 
@@ -367,7 +581,16 @@
      * Device sends RTT message
      * @param rttMessage RTT message to be sent
      */
-    @Override
     public void sendRttMessage(String rttMessage) {
     }
+
+    /** @hide */
+    public IImsCallSession getServiceImpl() {
+        return mServiceImpl;
+    }
+
+    /** @hide */
+    public void setServiceImpl(IImsCallSession serviceImpl) {
+        mServiceImpl = serviceImpl;
+    }
 }
diff --git a/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java b/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
deleted file mode 100644
index 6c18935..0000000
--- a/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * 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 com.android.ims.ImsCallProfile;
-import com.android.ims.ImsConferenceState;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsSuppServiceNotification;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-
-/**
- * Base implementation of ImsCallSessionListenerBase, which implements stub versions of the methods
- * in the IImsCallSessionListener AIDL. Override the methods that your implementation of
- * ImsCallSessionListener supports.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
- *
- * @hide
- */
-public class ImsCallSessionListenerImplBase extends IImsCallSessionListener.Stub {
-    /**
-     * Notifies the result of the basic session operation (setup / terminate).
-     */
-    @Override
-    public void callSessionProgressing(IImsCallSession session, ImsStreamMediaProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionStarted(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionStartFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionTerminated(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of the call hold/resume operation.
-     */
-    @Override
-    public void callSessionHeld(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionHoldFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionHoldReceived(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionResumed(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionResumeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionResumeReceived(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of call merge operation.
-     */
-    @Override
-    public void callSessionMergeStarted(IImsCallSession session, IImsCallSession newSession,
-            ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionMergeComplete(IImsCallSession session) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionMergeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of call upgrade / downgrade or any other call
-     * updates.
-     */
-    @Override
-    public void callSessionUpdated(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionUpdateFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionUpdateReceived(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of conference extension.
-     */
-    @Override
-    public void callSessionConferenceExtended(IImsCallSession session, IImsCallSession newSession,
-            ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionConferenceExtendFailed(IImsCallSession session,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionConferenceExtendReceived(IImsCallSession session,
-            IImsCallSession newSession,
-            ImsCallProfile profile) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of the participant invitation / removal to/from the
-     * conference session.
-     */
-    @Override
-    public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    /**
-     * Notifies the changes of the conference info. the conference session.
-     */
-    @Override
-    public void callSessionConferenceStateUpdated(IImsCallSession session,
-            ImsConferenceState state) {
-        // no-op
-    }
-
-    /**
-     * Notifies the incoming USSD message.
-     */
-    @Override
-    public void callSessionUssdMessageReceived(IImsCallSession session, int mode,
-            String ussdMessage) {
-        // no-op
-    }
-
-    /**
-     * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may potentially
-     * handover from one radio technology to another.
-     * @param session
-     * @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}.
-     */
-    @Override
-    public void callSessionMayHandover(IImsCallSession session, int srcAccessTech,
-            int targetAccessTech) {
-        // no-op
-    }
-
-    /**
-     * Notifies of handover information for this call
-     */
-    @Override
-    public void callSessionHandover(IImsCallSession session, int srcAccessTech,
-            int targetAccessTech,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionHandoverFailed(IImsCallSession session, int srcAccessTech,
-            int targetAccessTech,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    /**
-     * Notifies the TTY mode change by 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}
-     */
-    @Override
-    public void callSessionTtyModeReceived(IImsCallSession 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.
-     */
-    @Override
-    public void callSessionMultipartyStateChanged(IImsCallSession session, boolean isMultiParty) {
-        // no-op
-    }
-
-    /**
-     * Notifies the supplementary service information for the current session.
-     */
-    @Override
-    public void callSessionSuppServiceReceived(IImsCallSession session,
-            ImsSuppServiceNotification suppSrvNotification) {
-        // no-op
-    }
-
-    /**
-     * Received RTT modify request from Remote Party
-     * @param session The call session.
-     * @param callProfile ImsCallProfile with updated attribute
-     */
-    @Override
-    public void callSessionRttModifyRequestReceived(IImsCallSession session,
-            ImsCallProfile callProfile) {
-        // no-op
-    }
-
-    /**
-     * Received response for RTT modify request
-     * @param status true : Accepted the request
-     *               false : Declined the request
-     */
-    @Override
-    public void callSessionRttModifyResponseReceived(int status) {
-        // no -op
-    }
-
-    /**
-     * Device received RTT message from Remote UE
-     * @param rttMessage RTT message received
-     */
-    @Override
-    public void callSessionRttMessageReceived(String rttMessage) {
-        // no-op
-    }
-}
-
diff --git a/android/telephony/ims/stub/ImsConfigImplBase.java b/android/telephony/ims/stub/ImsConfigImplBase.java
index 1670e6b..dcd7ea7 100644
--- a/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,31 +16,24 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
 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.
+ * 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.
@@ -51,142 +44,10 @@
  * performed every time.
  * @hide
  */
-
+@SystemApi
 public class ImsConfigImplBase {
 
-    static final private String TAG = "ImsConfigImplBase";
-
-    ImsConfigStub mImsConfigStub;
-
-    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 master 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 master 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 master 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 master 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 {
-    }
-
-    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);
-    }
+    private static final String TAG = "ImsConfigImplBase";
 
     /**
      * Implements the IImsConfig AIDL interface, which is called by potentially many processes
@@ -208,32 +69,41 @@
      */
     @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;
+        public ImsConfigStub(ImsConfigImplBase imsConfigImplBase) {
             mImsConfigImplBaseWeakReference =
                     new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
         }
 
+        @Override
+        public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+            getImsConfigImpl().addImsConfigCallback(c);
+        }
+
+        @Override
+        public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+            getImsConfigImpl().removeImsConfigCallback(c);
+        }
+
         /**
          * Gets the value for ims service/capabilities parameters. It first checks its local cache,
-         * if missed, it will call ImsConfigImplBase.getProvisionedValue.
+         * if missed, it will call ImsConfigImplBase.getConfigInt.
          * Synchronous blocking call.
          *
-         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-         * @return value in Integer format.
+         * @param item integer key
+         * @return value in Integer format or {@link #CONFIG_RESULT_UNKNOWN} if
+         * unavailable.
          */
         @Override
-        public synchronized int getProvisionedValue(int item) throws RemoteException {
+        public synchronized int getConfigInt(int item) throws RemoteException {
             if (mProvisionedIntValue.containsKey(item)) {
                 return mProvisionedIntValue.get(item);
             } else {
-                int retVal = getImsConfigImpl().getProvisionedValue(item);
+                int retVal = getImsConfigImpl().getConfigInt(item);
                 if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
                     updateCachedValue(item, retVal, false);
                 }
@@ -243,18 +113,18 @@
 
         /**
          * Gets the value for ims service/capabilities parameters. It first checks its local cache,
-         * if missed, it will call #ImsConfigImplBase.getProvisionedValue.
+         * if missed, it will call #ImsConfigImplBase.getConfigString.
          * Synchronous blocking call.
          *
-         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @param item integer key
          * @return value in String format.
          */
         @Override
-        public synchronized String getProvisionedStringValue(int item) throws RemoteException {
+        public synchronized String getConfigString(int item) throws RemoteException {
             if (mProvisionedIntValue.containsKey(item)) {
                 return mProvisionedStringValue.get(item);
             } else {
-                String retVal = getImsConfigImpl().getProvisionedStringValue(item);
+                String retVal = getImsConfigImpl().getConfigString(item);
                 if (retVal != null) {
                     updateCachedValue(item, retVal, false);
                 }
@@ -268,16 +138,17 @@
          * from which the master value is derived, and write it into local cache.
          * Synchronous blocking call.
          *
-         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @param item integer key
          * @param value in Integer format.
-         * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+         * @return the result of setting the configuration value, defined as either
+         * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
          */
         @Override
-        public synchronized int setProvisionedValue(int item, int value) throws RemoteException {
+        public synchronized int setConfigInt(int item, int value) throws RemoteException {
             mProvisionedIntValue.remove(item);
-            int retVal = getImsConfigImpl().setProvisionedValue(item, value);
+            int retVal = getImsConfigImpl().setConfig(item, value);
             if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
-                updateCachedValue(item, retVal, true);
+                updateCachedValue(item, value, true);
             } else {
                 Log.d(TAG, "Set provision value of " + item +
                         " to " + value + " failed with error code " + retVal);
@@ -294,63 +165,21 @@
          *
          * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
          * @param value in String format.
-         * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+         * @return the result of setting the configuration value, defined as either
+         * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
          */
         @Override
-        public synchronized int setProvisionedStringValue(int item, String value)
+        public synchronized int setConfigString(int item, String value)
                 throws RemoteException {
             mProvisionedStringValue.remove(item);
-            int retVal = getImsConfigImpl().setProvisionedStringValue(item, value);
+            int retVal = getImsConfigImpl().setConfig(item, value);
             if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
-                updateCachedValue(item, retVal, true);
+                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) {
@@ -360,32 +189,228 @@
             }
         }
 
-        private void sendImsConfigChangedIntent(int item, int value) {
-            sendImsConfigChangedIntent(item, Integer.toString(value));
+        private void notifyImsConfigChanged(int item, int value) throws RemoteException {
+            getImsConfigImpl().notifyConfigChanged(item, 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);
-            }
+        private void notifyImsConfigChanged(int item, String value) throws RemoteException {
+            getImsConfigImpl().notifyConfigChanged(item, value);
         }
 
-        protected synchronized void updateCachedValue(int item, int value, boolean notifyChange) {
+        protected synchronized void updateCachedValue(int item, int value, boolean notifyChange)
+        throws RemoteException {
             mProvisionedIntValue.put(item, value);
             if (notifyChange) {
-                sendImsConfigChangedIntent(item, value);
+                notifyImsConfigChanged(item, value);
             }
         }
 
-        protected synchronized void updateCachedValue(
-                int item, String value, boolean notifyChange) {
+        protected synchronized void updateCachedValue(int item, String value,
+                boolean notifyChange) throws RemoteException {
             mProvisionedStringValue.put(item, value);
             if (notifyChange) {
-                sendImsConfigChangedIntent(item, value);
+                notifyImsConfigChanged(item, value);
             }
         }
     }
+
+    /**
+     * Callback that the framework uses for receiving Configuration change updates.
+     * {@hide}
+     */
+    public static class Callback extends IImsConfigCallback.Stub {
+
+        @Override
+        public final void onIntConfigChanged(int item, int value) throws RemoteException {
+            onConfigChanged(item, value);
+        }
+
+        @Override
+        public final void onStringConfigChanged(int item, String value) throws RemoteException {
+            onConfigChanged(item, value);
+        }
+
+        /**
+         * Called when the IMS configuration has changed.
+         * @param item the IMS configuration key constant, as defined in ImsConfig.
+         * @param value the new integer value of the IMS configuration constant.
+         */
+        public void onConfigChanged(int item, int value) {
+            // Base Implementation
+        }
+
+        /**
+         * Called when the IMS configuration has changed.
+         * @param item the IMS configuration key constant, as defined in ImsConfig.
+         * @param value the new String value of the IMS configuration constant.
+         */
+        public void onConfigChanged(int item, String value) {
+            // Base Implementation
+        }
+    }
+
+    /**
+     * The configuration requested resulted in an unknown result. This may happen if the
+     * IMS configurations are unavailable.
+     */
+    public static final int CONFIG_RESULT_UNKNOWN = -1;
+    /**
+     * 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;
+
+    private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
+    ImsConfigStub mImsConfigStub;
+
+    /**
+     * Used for compatibility between older versions of the ImsService.
+     * @hide
+     */
+    public ImsConfigImplBase(Context context) {
+        mImsConfigStub = new ImsConfigStub(this);
+    }
+
+    public ImsConfigImplBase() {
+        mImsConfigStub = new ImsConfigStub(this);
+    }
+
+    /**
+     * Adds a {@link 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 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;
+        }
+        mCallbacks.broadcast(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;
+        }
+        mCallbacks.broadcast(c -> {
+            try {
+                c.onStringConfigChanged(item, value);
+            } catch (RemoteException e) {
+                Log.w(TAG, "notifyConfigChanged(string): dead binder in notify, skipping.");
+            }
+        });
+    }
+
+    /**
+     * @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) {
+        try {
+            mImsConfigStub.updateCachedValue(item, value, true);
+        } 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) {
+        try {
+        mImsConfigStub.updateCachedValue(item, value, true);
+        } catch (RemoteException e) {
+            Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead.");
+        }
+    }
+
+    /**
+     * 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, defined as either
+     * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+     */
+    public 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, defined as either
+     * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+     */
+    public 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;
+    }
 }
diff --git a/android/telephony/ims/stub/ImsEcbmImplBase.java b/android/telephony/ims/stub/ImsEcbmImplBase.java
index 89f95ff..06c35ea 100644
--- a/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -16,7 +16,9 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.os.RemoteException;
+import android.util.Log;
 
 import com.android.ims.internal.IImsEcbm;
 import com.android.ims.internal.IImsEcbmListener;
@@ -30,22 +32,65 @@
  *
  * @hide
  */
+@SystemApi
+public class ImsEcbmImplBase {
+    private static final String TAG = "ImsEcbmImplBase";
 
-public class ImsEcbmImplBase extends IImsEcbm.Stub {
+    private IImsEcbmListener mListener;
+    private IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
+        @Override
+        public void setListener(IImsEcbmListener listener) {
+            mListener = listener;
+        }
 
-    /**
-     * Sets the listener.
-     */
-    @Override
-    public void setListener(IImsEcbmListener listener) throws RemoteException {
+        @Override
+        public void exitEmergencyCallbackMode() {
+            ImsEcbmImplBase.this.exitEmergencyCallbackMode();
+        }
+    };
 
+    /** @hide */
+    public IImsEcbm getImsEcbm() {
+        return mImsEcbm;
     }
 
     /**
-     * Requests Modem to come out of ECBM mode
+     * This method should be implemented by the IMS provider. Framework will trigger this method to
+     * request to come out of ECBM mode
      */
-    @Override
-    public void exitEmergencyCallbackMode() throws RemoteException {
+    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.");
+        if (mListener != null) {
+            try {
+                mListener.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.");
+        if (mListener != null) {
+            try {
+                mListener.exitedECBM();
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
     }
 }
diff --git a/android/telephony/ims/stub/ImsFeatureConfiguration.java b/android/telephony/ims/stub/ImsFeatureConfiguration.java
new file mode 100644
index 0000000..2f52c0a
--- /dev/null
+++ b/android/telephony/ims/stub/ImsFeatureConfiguration.java
@@ -0,0 +1,215 @@
+/*
+ * 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.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.feature.ImsFeature;
+import android.util.ArraySet;
+import android.util.Pair;
+
+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(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;
+        }
+    }
+
+    /**
+     * 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 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/telephony/ims/stub/ImsMultiEndpointImplBase.java b/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index 05da9da..ce2d89a 100644
--- a/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -16,11 +16,16 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.os.RemoteException;
+import android.util.Log;
 
+import android.telephony.ims.ImsExternalCallState;
 import com.android.ims.internal.IImsExternalCallStateListener;
 import com.android.ims.internal.IImsMultiEndpoint;
 
+import java.util.List;
+
 /**
  * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
  * in the IImsMultiEndpoint AIDL. Override the methods that your implementation of
@@ -31,23 +36,49 @@
  *
  * @hide
  */
+@SystemApi
+public class ImsMultiEndpointImplBase {
+    private static final String TAG = "MultiEndpointImplBase";
 
-public class ImsMultiEndpointImplBase extends IImsMultiEndpoint.Stub {
+    private IImsExternalCallStateListener mListener;
+    private IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
+        @Override
+        public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+            mListener = listener;
+        }
 
-    /**
-     * Sets the listener.
-     */
-    @Override
-    public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+        @Override
+        public void requestImsExternalCallStateInfo() throws RemoteException {
+            ImsMultiEndpointImplBase.this.requestImsExternalCallStateInfo();
+        }
+    };
 
+    /** @hide */
+    public IImsMultiEndpoint getIImsMultiEndpoint() {
+        return mImsMultiEndpoint;
     }
 
     /**
-     * Query API to get the latest Dialog Event Package information
-     * Should be invoked only after setListener is done
+     * Notifies framework when Dialog Event Package update is received
+     *
+     * @throws RuntimeException if the connection to the framework is not available.
      */
-    @Override
-    public void requestImsExternalCallStateInfo() throws RemoteException {
+    public final void onImsExternalCallStateUpdate(List<ImsExternalCallState> externalCallDialogs) {
+        Log.d(TAG, "ims external call state update triggered.");
+        if (mListener != null) {
+            try {
+                mListener.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");
     }
 }
diff --git a/android/telephony/ims/stub/ImsRegistrationImplBase.java b/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 42af083..4334d3a 100644
--- a/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -17,15 +17,16 @@
 package android.telephony.ims.stub;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.net.Uri;
-import android.os.IBinder;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.util.Log;
 
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.internal.IImsRegistration;
-import com.android.ims.internal.IImsRegistrationCallback;
+import android.telephony.ims.ImsReasonInfo;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
@@ -36,11 +37,14 @@
  * registration for this ImsService has changed status.
  * @hide
  */
-
+@SystemApi
 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(flag = true,
             value = {
@@ -155,6 +159,9 @@
     // Locked on mLock, create unspecified disconnect cause.
     private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo();
 
+    /**
+     * @hide
+     */
     public final IImsRegistration getBinder() {
         return mBinder;
     }
@@ -171,8 +178,8 @@
     /**
      * Notify the framework that the device is connected to the IMS network.
      *
-     * @param imsRadioTech the radio access technology. Valid values are defined in
-     * {@link ImsRegistrationTech}.
+     * @param imsRadioTech the radio access technology. Valid values are defined as
+     * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
      */
     public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
         updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERED);
@@ -189,8 +196,8 @@
     /**
      * Notify the framework that the device is trying to connect the IMS network.
      *
-     * @param imsRadioTech the radio access technology. Valid values are defined in
-     * {@link ImsRegistrationTech}.
+     * @param imsRadioTech the radio access technology. Valid values are defined as
+     * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
      */
     public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
         updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERING);
@@ -221,6 +228,13 @@
         });
     }
 
+    /**
+     * 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} and {@link #REGISTRATION_TECH_IWLAN}.
+     * @param info The {@link ImsReasonInfo} for the failure to change technology.
+     */
     public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
             ImsReasonInfo info) {
         mCallbacks.broadcast((c) -> {
@@ -233,6 +247,11 @@
         });
     }
 
+    /**
+     * The this device's subscriber associated {@link Uri}s have changed, which are used to filter
+     * out this device's {@link Uri}s during conference calling.
+     * @param uris
+     */
     public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
         mCallbacks.broadcast((c) -> {
             try {
@@ -264,6 +283,11 @@
         }
     }
 
+    /**
+     * @return the current registration connection type. Valid values are
+     * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}
+     * @hide
+     */
     @VisibleForTesting
     public final @ImsRegistrationTech int getConnectionType() {
         synchronized (mLock) {
diff --git a/android/telephony/ims/internal/stub/SmsImplBase.java b/android/telephony/ims/stub/ImsSmsImplBase.java
similarity index 66%
rename from android/telephony/ims/internal/stub/SmsImplBase.java
rename to android/telephony/ims/stub/ImsSmsImplBase.java
index 113dad4..852c8e0 100644
--- a/android/telephony/ims/internal/stub/SmsImplBase.java
+++ b/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -14,17 +14,16 @@
  * limitations under the License
  */
 
-package android.telephony.ims.internal.stub;
+package android.telephony.ims.stub;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.os.RemoteException;
 import android.telephony.SmsManager;
 import android.telephony.SmsMessage;
-import android.telephony.ims.internal.feature.MmTelFeature;
+import android.telephony.ims.aidl.IImsSmsListener;
 import android.util.Log;
 
-import com.android.ims.internal.IImsSmsListener;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -33,11 +32,14 @@
  *
  * Any service wishing to provide SMS over IMS should extend this class and implement all methods
  * that the service supports.
+ *
  * @hide
  */
-public class SmsImplBase {
+@SystemApi
+public class ImsSmsImplBase {
     private static final String LOG_TAG = "SmsImplBase";
 
+    /** @hide */
     @IntDef({
             SEND_STATUS_OK,
             SEND_STATUS_ERROR,
@@ -58,8 +60,8 @@
     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.
+     * 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;
 
@@ -69,9 +71,12 @@
      */
     public static final int SEND_STATUS_ERROR_FALLBACK = 4;
 
+    /** @hide */
     @IntDef({
             DELIVER_STATUS_OK,
-            DELIVER_STATUS_ERROR
+            DELIVER_STATUS_ERROR_GENERIC,
+            DELIVER_STATUS_ERROR_NO_MEMORY,
+            DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED
         })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DeliverStatusResult {}
@@ -83,8 +88,19 @@
     /**
      * Message was not delivered.
      */
-    public static final int DELIVER_STATUS_ERROR = 2;
+    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
@@ -102,7 +118,6 @@
      */
     public static final int STATUS_REPORT_STATUS_ERROR = 2;
 
-
     // Lock for feature synchronization
     private final Object mLock = new Object();
     private IImsSmsListener mListener;
@@ -140,21 +155,25 @@
         try {
             onSendSmsResult(token, messageRef, SEND_STATUS_ERROR,
                     SmsManager.RESULT_ERROR_GENERIC_FAILURE);
-        } catch (RemoteException e) {
+        } catch (RuntimeException e) {
             Log.e(LOG_TAG, "Can not send sms: " + e.getMessage());
         }
     }
 
     /**
-     * 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.
+     * 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.
      *
      * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
-     * @param result result of delivering the message. Valid values are defined in
-     * {@link DeliverStatusResult}
+     * @param result result of delivering the message. Valid values are:
+     *  {@link #DELIVER_STATUS_OK},
+     *  {@link #DELIVER_STATUS_ERROR_GENERIC},
+     *  {@link #DELIVER_STATUS_ERROR_NO_MEMORY},
+     *  {@link #DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED}
      * @param messageRef the message reference
      */
-    public void acknowledgeSms(int token, int messageRef, @DeliverStatusResult int result) {
+    public void acknowledgeSms(int token, @DeliverStatusResult int messageRef, int result) {
         Log.e(LOG_TAG, "acknowledgeSms() not implemented.");
     }
 
@@ -164,8 +183,9 @@
      * result to the IMS provider.
      *
      * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
-     * @param result result of delivering the message. Valid values are defined in
-     * {@link StatusReportResult}
+     * @param result result of delivering the message. Valid values are:
+     *  {@link #STATUS_REPORT_STATUS_OK},
+     *  {@link #STATUS_REPORT_STATUS_ERROR}
      * @param messageRef the message reference
      */
     public void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) {
@@ -177,26 +197,33 @@
      * 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 MmTelFeature#onFeatureReady()} is called.
-     *
+     * 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. Valid values are {@link SmsMessage#FORMAT_3GPP} and
      * {@link SmsMessage#FORMAT_3GPP2}.
      * @param pdu PDUs representing the contents of the message.
-     * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
+     * @throws RuntimeException if called before {@link #onReady()} is triggered.
      */
-    public final void onSmsReceived(int token, String format, byte[] pdu)
-            throws IllegalStateException {
+    public final void onSmsReceived(int token, String format, byte[] pdu) throws RuntimeException {
         synchronized (mLock) {
             if (mListener == null) {
-                throw new IllegalStateException("Feature not ready.");
+                throw new RuntimeException("Feature not ready.");
             }
             try {
                 mListener.onSmsReceived(token, format, pdu);
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
-                acknowledgeSms(token, 0, DELIVER_STATUS_ERROR);
+                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);
+                }
             }
         }
     }
@@ -205,11 +232,13 @@
      * This method should be triggered by the IMS providers to pass the result of the sent message
      * to the platform.
      *
-     * This method must not be called before {@link MmTelFeature#onFeatureReady()} is called.
-     *
      * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
      * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
-     * @param status result of sending the SMS. Valid values are defined in {@link SendStatusResult}
+     * @param status result of sending the SMS. Valid values are:
+     *  {@link #SEND_STATUS_OK},
+     *  {@link #SEND_STATUS_ERROR},
+     *  {@link #SEND_STATUS_ERROR_RETRY},
+     *  {@link #SEND_STATUS_ERROR_FALLBACK},
      * @param reason reason in case status is failure. Valid values are:
      *  {@link SmsManager#RESULT_ERROR_NONE},
      *  {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
@@ -217,19 +246,41 @@
      *  {@link SmsManager#RESULT_ERROR_NULL_PDU},
      *  {@link SmsManager#RESULT_ERROR_NO_SERVICE},
      *  {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
+     *  {@link SmsManager#RESULT_ERROR_FDN_CHECK_FAILURE},
      *  {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
-     *  {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED}
-     * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
-     * @throws RemoteException if the connection to the framework is not available. If this happens
-     *  attempting to send the SMS should be aborted.
+     *  {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED},
+     *  {@link SmsManager#RESULT_RADIO_NOT_AVAILABLE},
+     *  {@link SmsManager#RESULT_NETWORK_REJECT},
+     *  {@link SmsManager#RESULT_INVALID_ARGUMENTS},
+     *  {@link SmsManager#RESULT_INVALID_STATE},
+     *  {@link SmsManager#RESULT_NO_MEMORY},
+     *  {@link SmsManager#RESULT_INVALID_SMS_FORMAT},
+     *  {@link SmsManager#RESULT_SYSTEM_ERROR},
+     *  {@link SmsManager#RESULT_MODEM_ERROR},
+     *  {@link SmsManager#RESULT_NETWORK_ERROR},
+     *  {@link SmsManager#RESULT_ENCODING_ERROR},
+     *  {@link SmsManager#RESULT_INVALID_SMSC_ADDRESS},
+     *  {@link SmsManager#RESULT_OPERATION_NOT_ALLOWED},
+     *  {@link SmsManager#RESULT_INTERNAL_ERROR},
+     *  {@link SmsManager#RESULT_NO_RESOURCES},
+     *  {@link SmsManager#RESULT_CANCELLED},
+     *  {@link SmsManager#RESULT_REQUEST_NOT_SUPPORTED}
+     *
+     * @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 onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
-            int reason) throws IllegalStateException, RemoteException {
+    public final void onSendSmsResult(int token, int messageRef,  @SendStatusResult int status,
+            int reason) throws RuntimeException {
         synchronized (mLock) {
             if (mListener == null) {
-                throw new IllegalStateException("Feature not ready.");
+                throw new RuntimeException("Feature not ready.");
             }
-            mListener.onSendSmsResult(token, messageRef, status, reason);
+            try {
+                mListener.onSendSmsResult(token, messageRef, status, reason);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
         }
     }
 
@@ -241,13 +292,13 @@
      * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
      * {@link SmsMessage#FORMAT_3GPP2}.
      * @param pdu PDUs representing the content of the status report.
-     * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
+     * @throws RuntimeException if called before {@link #onReady()} is triggered
      */
     public final void onSmsStatusReportReceived(int token, int messageRef, String format,
-            byte[] pdu) {
+            byte[] pdu) throws RuntimeException{
         synchronized (mLock) {
             if (mListener == null) {
-                throw new IllegalStateException("Feature not ready.");
+                throw new RuntimeException("Feature not ready.");
             }
             try {
                 mListener.onSmsStatusReportReceived(token, messageRef, format, pdu);
@@ -268,4 +319,13 @@
     public 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
+    }
 }
diff --git a/android/telephony/ims/stub/ImsUtImplBase.java b/android/telephony/ims/stub/ImsUtImplBase.java
index 054a8b2..fcd7faf 100644
--- a/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/android/telephony/ims/stub/ImsUtImplBase.java
@@ -16,177 +16,332 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.telephony.ims.ImsUtListener;
 
 import com.android.ims.internal.IImsUt;
 import com.android.ims.internal.IImsUtListener;
 
 /**
- * Base implementation of ImsUt, which implements stub versions of the methods
- * in the IImsUt AIDL. Override the methods that your implementation of ImsUt supports.
- *
- * 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.
- *
- * Provides the Ut interface interworking to get/set the supplementary service configuration.
+ * 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 {
 
-public class ImsUtImplBase extends IImsUt.Stub {
+    private IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
+        @Override
+        public void close() throws RemoteException {
+            ImsUtImplBase.this.close();
+        }
+
+        @Override
+        public int queryCallBarring(int cbType) throws RemoteException {
+            return ImsUtImplBase.this.queryCallBarring(cbType);
+        }
+
+        @Override
+        public int queryCallForward(int condition, String number) throws RemoteException {
+            return ImsUtImplBase.this.queryCallForward(condition, number);
+        }
+
+        @Override
+        public int queryCallWaiting() throws RemoteException {
+            return ImsUtImplBase.this.queryCallWaiting();
+        }
+
+        @Override
+        public int queryCLIR() throws RemoteException {
+            return ImsUtImplBase.this.queryCLIR();
+        }
+
+        @Override
+        public int queryCLIP() throws RemoteException {
+            return ImsUtImplBase.this.queryCLIP();
+        }
+
+        @Override
+        public int queryCOLR() throws RemoteException {
+            return ImsUtImplBase.this.queryCOLR();
+        }
+
+        @Override
+        public int queryCOLP() throws RemoteException {
+            return ImsUtImplBase.this.queryCOLP();
+        }
+
+        @Override
+        public int transact(Bundle ssInfo) throws RemoteException {
+            return ImsUtImplBase.this.transact(ssInfo);
+        }
+
+        @Override
+        public int updateCallBarring(int cbType, int action, String[] barrList) throws
+                RemoteException {
+            return ImsUtImplBase.this.updateCallBarring(cbType, action, barrList);
+        }
+
+        @Override
+        public int updateCallForward(int action, int condition, String number, int serviceClass,
+                int timeSeconds) throws RemoteException {
+            return ImsUtImplBase.this.updateCallForward(action, condition, number, serviceClass,
+                    timeSeconds);
+        }
+
+        @Override
+        public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+            return ImsUtImplBase.this.updateCallWaiting(enable, serviceClass);
+        }
+
+        @Override
+        public int updateCLIR(int clirMode) throws RemoteException {
+            return ImsUtImplBase.this.updateCLIR(clirMode);
+        }
+
+        @Override
+        public int updateCLIP(boolean enable) throws RemoteException {
+            return ImsUtImplBase.this.updateCLIP(enable);
+        }
+
+        @Override
+        public int updateCOLR(int presentation) throws RemoteException {
+            return ImsUtImplBase.this.updateCOLR(presentation);
+        }
+
+        @Override
+        public int updateCOLP(boolean enable) throws RemoteException {
+            return ImsUtImplBase.this.updateCOLP(enable);
+        }
+
+        @Override
+        public void setListener(IImsUtListener listener) throws RemoteException {
+            ImsUtImplBase.this.setListener(new ImsUtListener(listener));
+        }
+
+        @Override
+        public int queryCallBarringForServiceClass(int cbType, int serviceClass)
+                throws RemoteException {
+            return ImsUtImplBase.this.queryCallBarringForServiceClass(cbType, serviceClass);
+        }
+
+        @Override
+        public int updateCallBarringForServiceClass(int cbType, int action,
+                String[] barrList, int serviceClass) throws RemoteException {
+            return ImsUtImplBase.this.updateCallBarringForServiceClass(
+                    cbType, action, barrList, serviceClass);
+        }
+    };
 
     /**
-     * Closes the object. This object is not usable after being closed.
+     * Called when the framework no longer needs to interact with the IMS UT implementation any
+     * longer.
      */
-    @Override
-    public void close() throws RemoteException {
+    public void close() {
 
     }
 
     /**
-     * Retrieves the configuration of the call barring.
+     * Retrieves the call barring configuration.
+     * @param cbType
      */
-    @Override
-    public int queryCallBarring(int cbType) throws RemoteException {
+    public int queryCallBarring(int cbType) {
         return -1;
     }
 
     /**
      * Retrieves the configuration of the call barring for specified service class.
      */
-    @Override
-    public int queryCallBarringForServiceClass(int cbType, int serviceClass)
-            throws RemoteException {
+    public int queryCallBarringForServiceClass(int cbType, int serviceClass) {
         return -1;
     }
 
     /**
      * Retrieves the configuration of the call forward.
      */
-    @Override
-    public int queryCallForward(int condition, String number) throws RemoteException {
+    public int queryCallForward(int condition, String number) {
         return -1;
     }
 
     /**
      * Retrieves the configuration of the call waiting.
      */
-    @Override
-    public int queryCallWaiting() throws RemoteException {
+    public int queryCallWaiting() {
         return -1;
     }
 
     /**
      * Retrieves the default CLIR setting.
+     * @hide
      */
-    @Override
-    public int queryCLIR() throws RemoteException {
+    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.
      */
-    @Override
-    public int queryCLIP() throws RemoteException {
+    public int queryClip() {
         return -1;
     }
 
     /**
      * Retrieves the COLR call setting.
      */
-    @Override
-    public int queryCOLR() throws RemoteException {
+    public int queryColr() {
         return -1;
     }
 
     /**
      * Retrieves the COLP call setting.
      */
-    @Override
-    public int queryCOLP() throws RemoteException {
+    public int queryColp() {
         return -1;
     }
 
     /**
      * Updates or retrieves the supplementary service configuration.
      */
-    @Override
-    public int transact(Bundle ssInfo) throws RemoteException {
+    public int transact(Bundle ssInfo) {
         return -1;
     }
 
     /**
      * Updates the configuration of the call barring.
      */
-    @Override
-    public int updateCallBarring(int cbType, int action, String[] barrList) throws RemoteException {
+    public int updateCallBarring(int cbType, int action, String[] barrList) {
         return -1;
     }
 
     /**
      * Updates the configuration of the call barring for specified service class.
      */
-    @Override
     public int updateCallBarringForServiceClass(int cbType, int action, String[] barrList,
-            int serviceClass) throws RemoteException {
+            int serviceClass) {
         return -1;
     }
 
     /**
      * Updates the configuration of the call forward.
      */
-    @Override
     public int updateCallForward(int action, int condition, String number, int serviceClass,
-            int timeSeconds) throws RemoteException {
+            int timeSeconds) {
         return 0;
     }
 
     /**
      * Updates the configuration of the call waiting.
      */
-    @Override
-    public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+    public int updateCallWaiting(boolean enable, int serviceClass) {
         return -1;
     }
 
     /**
      * Updates the configuration of the CLIR supplementary service.
+     * @hide
      */
-    @Override
-    public int updateCLIR(int clirMode) throws RemoteException {
+    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.
      */
-    @Override
-    public int updateCLIP(boolean enable) throws RemoteException {
+    public int updateClip(boolean enable) {
         return -1;
     }
 
     /**
      * Updates the configuration of the COLR supplementary service.
      */
-    @Override
-    public int updateCOLR(int presentation) throws RemoteException {
+    public int updateColr(int presentation) {
         return -1;
     }
 
     /**
      * Updates the configuration of the COLP supplementary service.
      */
-    @Override
-    public int updateCOLP(boolean enable) throws RemoteException {
+    public int updateColp(boolean enable) {
         return -1;
     }
 
     /**
      * Sets the listener.
      */
-    @Override
-    public void setListener(IImsUtListener listener) throws RemoteException {
+    public void setListener(ImsUtListener listener) {
+    }
+
+    /**
+     * @hide
+     */
+    public IImsUt getInterface() {
+        return mServiceImpl;
     }
 }
diff --git a/android/telephony/mbms/DownloadProgressListener.java b/android/telephony/mbms/DownloadProgressListener.java
new file mode 100644
index 0000000..4301cb1
--- /dev/null
+++ b/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/telephony/mbms/DownloadRequest.java b/android/telephony/mbms/DownloadRequest.java
index f0d60b6..602c796 100644
--- a/android/telephony/mbms/DownloadRequest.java
+++ b/android/telephony/mbms/DownloadRequest.java
@@ -27,10 +27,13 @@
 
 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.io.Serializable;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
@@ -54,34 +57,116 @@
     public static final int MAX_DESTINATION_URI_SIZE = 50000;
 
     /** @hide */
-    private static class OpaqueDataContainer implements Serializable {
-        private final String appIntent;
-        private final int version;
+    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 OpaqueDataContainer(String appIntent, int version) {
-            this.appIntent = appIntent;
-            this.version = 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) {
-            if (sourceUri == null) {
-                throw new IllegalArgumentException("Source URI must be non-null.");
+        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;
         }
 
         /**
@@ -130,68 +215,34 @@
             return this;
         }
 
-        /**
-         * For use by the middleware to set the byte array of opaque data. The opaque data
-         * includes information about the download request that is used by the client app and the
-         * manager code, but is irrelevant to the middleware.
-         * @param data A byte array, the contents of which should have been originally obtained
-         *             from {@link DownloadRequest#getOpaqueData()}.
-         * @hide
-         */
-        @SystemApi
-        public Builder setOpaqueData(byte[] data) {
-            try {
-                ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
-                OpaqueDataContainer dataContainer = (OpaqueDataContainer) stream.readObject();
-                version = dataContainer.version;
-                appIntent = dataContainer.appIntent;
-            } 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 this;
-        }
-
         public DownloadRequest build() {
-            return new DownloadRequest(fileServiceId, source, subscriptionId, appIntent, version);
+            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, int sub,
+            Uri source, Uri destination, int sub,
             String appIntent, int version) {
         this.fileServiceId = fileServiceId;
         sourceUri = source;
         subscriptionId = sub;
+        destinationUri = destination;
         serializedResultIntentForApp = appIntent;
         this.version = version;
     }
 
-    public static DownloadRequest copy(DownloadRequest other) {
-        return new DownloadRequest(other);
-    }
-
-    private DownloadRequest(DownloadRequest dr) {
-        fileServiceId = dr.fileServiceId;
-        sourceUri = dr.sourceUri;
-        subscriptionId = dr.subscriptionId;
-        serializedResultIntentForApp = dr.serializedResultIntentForApp;
-        version = dr.version;
-    }
-
     private DownloadRequest(Parcel in) {
         fileServiceId = in.readString();
         sourceUri = in.readParcelable(getClass().getClassLoader());
+        destinationUri = in.readParcelable(getClass().getClassLoader());
         subscriptionId = in.readInt();
         serializedResultIntentForApp = in.readString();
         version = in.readInt();
@@ -204,6 +255,7 @@
     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);
@@ -224,6 +276,13 @@
     }
 
     /**
+     * @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() {
@@ -244,19 +303,16 @@
     }
 
     /**
-     * For use by the middleware only. The byte array returned from this method should be
-     * persisted and sent back to the app upon download completion or failure by passing it into
-     * {@link Builder#setOpaqueData(byte[])}.
-     * @return A byte array of opaque data to persist.
-     * @hide
+     * 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.
      */
-    @SystemApi
-    public byte[] getOpaqueData() {
+    public byte[] toByteArray() {
         try {
             ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
-            OpaqueDataContainer container = new OpaqueDataContainer(
-                    serializedResultIntentForApp, version);
+            SerializationDataContainer container = new SerializationDataContainer(this);
             stream.writeObject(container);
             stream.flush();
             return byteArrayOutputStream.toByteArray();
@@ -299,15 +355,6 @@
     }
 
     /**
-     * @hide
-     */
-    public boolean isMultipartDownload() {
-        // TODO: figure out what qualifies a request as a multipart download request.
-        return getSourceUri().getLastPathSegment() != null &&
-                getSourceUri().getLastPathSegment().contains("*");
-    }
-
-    /**
      * Retrieves the hash string that should be used as the filename when storing a token for
      * this DownloadRequest.
      * @hide
@@ -320,8 +367,9 @@
             throw new RuntimeException("Could not get sha256 hash object");
         }
         if (version >= 1) {
-            // Hash the source URI and the app intent
+            // 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));
             }
@@ -344,12 +392,13 @@
                 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,
+        return Objects.hash(fileServiceId, sourceUri, destinationUri,
                 subscriptionId, serializedResultIntentForApp, version);
     }
 }
diff --git a/android/telephony/mbms/DownloadStateCallback.java b/android/telephony/mbms/DownloadStateCallback.java
deleted file mode 100644
index 9f60cc3..0000000
--- a/android/telephony/mbms/DownloadStateCallback.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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 DownloadStateCallback {
-
-    /**
-     * Bitmask flags used for filtering out callback methods. Used when constructing the
-     * DownloadStateCallback as an optional parameter.
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, value = {ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES})
-    public @interface FilterFlag {}
-
-    /**
-     * Receive all callbacks.
-     * Default value.
-     */
-    public static final int ALL_UPDATES = 0x00;
-    /**
-     * Receive callbacks for {@link #onProgressUpdated}.
-     */
-    public static final int PROGRESS_UPDATES = 0x01;
-    /**
-     * Receive callbacks for {@link #onStateUpdated}.
-     */
-    public static final int STATE_UPDATES = 0x02;
-
-    private final int mCallbackFilterFlags;
-
-    /**
-     * Creates a DownloadStateCallback that will receive all callbacks.
-     */
-    public DownloadStateCallback() {
-        mCallbackFilterFlags = ALL_UPDATES;
-    }
-
-    /**
-     * Creates a DownloadStateCallback that will only receive callbacks for the methods specified
-     * via the filterFlags parameter.
-     * @param filterFlags A bitmask of filter flags that will specify which callback this instance
-     *     is interested in.
-     */
-    public DownloadStateCallback(int filterFlags) {
-        mCallbackFilterFlags = filterFlags;
-    }
-
-    /**
-     * Return the currently set filter flags.
-     * @return An integer containing the bitmask of flags that this instance is interested in.
-     * @hide
-     */
-    public int getCallbackFilterFlags() {
-        return mCallbackFilterFlags;
-    }
-
-    /**
-     * Returns true if a filter flag is set for a particular callback method. If the flag is set,
-     * the callback will be delivered to the listening process.
-     * @param flag A filter flag specifying whether or not a callback method is registered to
-     *     receive callbacks.
-     * @return true if registered to receive callbacks in the listening process, false if not.
-     */
-    public final boolean isFilterFlagSet(@FilterFlag int flag) {
-        if (mCallbackFilterFlags == ALL_UPDATES) {
-            return true;
-        }
-        return (mCallbackFilterFlags & flag) > 0;
-    }
-
-    /**
-     * 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) {
-    }
-
-    /**
-     * Gives download state 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 state The current state of the download.
-     */
-    public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
-            @MbmsDownloadSession.DownloadStatus int state) {
-    }
-}
diff --git a/android/telephony/mbms/DownloadStatusListener.java b/android/telephony/mbms/DownloadStatusListener.java
new file mode 100644
index 0000000..ca77932
--- /dev/null
+++ b/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/telephony/mbms/InternalDownloadProgressListener.java b/android/telephony/mbms/InternalDownloadProgressListener.java
new file mode 100644
index 0000000..403f758
--- /dev/null
+++ b/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;
+        }
+
+        mExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppListener.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                            fullDownloadSize, currentDecodedSize, fullDecodedSize);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        });
+    }
+
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/android/telephony/mbms/InternalDownloadSessionCallback.java b/android/telephony/mbms/InternalDownloadSessionCallback.java
index a7a5958..2916f81 100644
--- a/android/telephony/mbms/InternalDownloadSessionCallback.java
+++ b/android/telephony/mbms/InternalDownloadSessionCallback.java
@@ -16,70 +16,81 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
-import android.os.RemoteException;
+import android.os.Binder;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /** @hide */
 public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallback.Stub {
 
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private final MbmsDownloadSessionCallback mAppCallback;
     private volatile boolean mIsStopped = false;
 
     public InternalDownloadSessionCallback(MbmsDownloadSessionCallback appCallback,
-            Handler handler) {
+            Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
-    public void onError(final int errorCode, final String message) throws RemoteException {
+    public void onError(final int errorCode, final String message) {
         if (mIsStopped) {
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onError(errorCode, message);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
 
     @Override
-    public void onFileServicesUpdated(final List<FileServiceInfo> services) throws RemoteException {
+    public void onFileServicesUpdated(final List<FileServiceInfo> services) {
         if (mIsStopped) {
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onFileServicesUpdated(services);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onFileServicesUpdated(services);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
 
     @Override
-    public void onMiddlewareReady() throws RemoteException {
+    public void onMiddlewareReady() {
         if (mIsStopped) {
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onMiddlewareReady();
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onMiddlewareReady();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
 
-    public Handler getHandler() {
-        return mHandler;
-    }
-
     public void stop() {
         mIsStopped = true;
     }
diff --git a/android/telephony/mbms/InternalDownloadStateCallback.java b/android/telephony/mbms/InternalDownloadStateCallback.java
deleted file mode 100644
index 8702952..0000000
--- a/android/telephony/mbms/InternalDownloadStateCallback.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.Handler;
-import android.os.RemoteException;
-
-/**
- * @hide
- */
-public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
-    private final Handler mHandler;
-    private final DownloadStateCallback mAppCallback;
-    private volatile boolean mIsStopped = false;
-
-    public InternalDownloadStateCallback(DownloadStateCallback appCallback, Handler handler) {
-        mAppCallback = appCallback;
-        mHandler = handler;
-    }
-
-    @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;
-        }
-
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mAppCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
-                        fullDownloadSize, currentDecodedSize, fullDecodedSize);
-            }
-        });
-    }
-
-    @Override
-    public void onStateUpdated(final DownloadRequest request, final FileInfo fileInfo,
-            final int state) throws RemoteException {
-        if (mIsStopped) {
-            return;
-        }
-
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mAppCallback.onStateUpdated(request, fileInfo, state);
-            }
-        });
-    }
-
-    public void stop() {
-        mIsStopped = true;
-    }
-}
diff --git a/android/telephony/mbms/InternalDownloadStatusListener.java b/android/telephony/mbms/InternalDownloadStatusListener.java
new file mode 100644
index 0000000..ad6bb54
--- /dev/null
+++ b/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;
+        }
+
+        mExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppListener.onStatusUpdated(request, fileInfo, status);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        });
+    }
+
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/android/telephony/mbms/InternalStreamingServiceCallback.java b/android/telephony/mbms/InternalStreamingServiceCallback.java
index eb6579c..e9f39ff 100644
--- a/android/telephony/mbms/InternalStreamingServiceCallback.java
+++ b/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -16,18 +16,21 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
+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 Handler mHandler;
+    private final Executor mExecutor;
     private volatile boolean mIsStopped = false;
 
-    public InternalStreamingServiceCallback(StreamingServiceCallback appCallback, Handler handler) {
+    public InternalStreamingServiceCallback(StreamingServiceCallback appCallback,
+            Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
@@ -36,10 +39,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onError(errorCode, message);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -50,10 +58,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStreamStateUpdated(state, reason);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStreamStateUpdated(state, reason);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -64,10 +77,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onMediaDescriptionUpdated();
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onMediaDescriptionUpdated();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -78,10 +96,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -92,10 +115,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStreamMethodUpdated(methodType);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStreamMethodUpdated(methodType);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
diff --git a/android/telephony/mbms/InternalStreamingSessionCallback.java b/android/telephony/mbms/InternalStreamingSessionCallback.java
index d782d12..d47f5ad 100644
--- a/android/telephony/mbms/InternalStreamingSessionCallback.java
+++ b/android/telephony/mbms/InternalStreamingSessionCallback.java
@@ -16,21 +16,22 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
+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 Handler mHandler;
+    private final Executor mExecutor;
     private final MbmsStreamingSessionCallback mAppCallback;
     private volatile boolean mIsStopped = false;
 
     public InternalStreamingSessionCallback(MbmsStreamingSessionCallback appCallback,
-            Handler handler) {
+            Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
@@ -39,10 +40,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onError(errorCode, message);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -54,10 +60,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStreamingServicesUpdated(services);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStreamingServicesUpdated(services);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -68,18 +79,19 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onMiddlewareReady();
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onMiddlewareReady();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
 
-    public Handler getHandler() {
-        return mHandler;
-    }
-
     public void stop() {
         mIsStopped = true;
     }
diff --git a/android/telephony/mbms/MbmsDownloadReceiver.java b/android/telephony/mbms/MbmsDownloadReceiver.java
index 9ef188c..dd1061f 100644
--- a/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -21,24 +21,25 @@
 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.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 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;
@@ -62,6 +63,8 @@
     /** @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
@@ -137,6 +140,8 @@
     /** @hide */
     @Override
     public void onReceive(Context context, Intent intent) {
+        verifyPermissionIntegrity(context);
+
         if (!verifyIntentContents(context, intent)) {
             setResultCode(RESULT_MALFORMED_INTENT);
             return;
@@ -260,20 +265,21 @@
 
         FileInfo completedFileInfo =
                 (FileInfo) intent.getParcelableExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO);
-        Path stagingDirectory = FileSystems.getDefault().getPath(
-                MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath(),
-                TEMP_FILE_STAGING_LOCATION);
+        Path appSpecifiedDestination = FileSystems.getDefault().getPath(
+                request.getDestinationUri().getPath());
 
-        Uri stagedFileLocation;
+        Uri finalLocation;
         try {
-            stagedFileLocation = stageTempFile(finalTempFile, stagingDirectory);
+            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,
-                stagedFileLocation);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI, finalLocation);
         intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, completedFileInfo);
 
         context.sendBroadcast(intentForApp);
@@ -296,7 +302,9 @@
         for (Uri tempFileUri : tempFiles) {
             if (verifyTempFilePath(context, request.getFileServiceId(), tempFileUri)) {
                 File tempFile = new File(tempFileUri.getSchemeSpecificPart());
-                tempFile.delete();
+                if (!tempFile.delete()) {
+                    Log.w(LOG_TAG, "Failed to delete temp file at " + tempFile.getPath());
+                }
             }
         }
     }
@@ -437,23 +445,57 @@
     }
 
     /*
-     * Moves a tempfile located at fromPath to a new location in the staging directory.
+     * Moves a tempfile located at fromPath to its final home where the app wants it
      */
-    private static Uri stageTempFile(Uri fromPath, Path stagingDirectory) throws IOException {
+    private static Uri moveToFinalLocation(Uri fromPath, Path appSpecifiedPath,
+            String relativeLocation) throws IOException {
         if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
-            Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme");
+            Log.w(LOG_TAG, "Downloaded file location uri " + fromPath +
+                    " does not have a file scheme");
             return null;
         }
 
         Path fromFile = FileSystems.getDefault().getPath(fromPath.getPath());
-        if (!Files.isDirectory(stagingDirectory)) {
-            Files.createDirectory(stagingDirectory);
+        Path toFile = appSpecifiedPath.resolve(relativeLocation);
+
+        if (!Files.isDirectory(toFile.getParent())) {
+            Files.createDirectories(toFile.getParent());
         }
-        Path result = Files.move(fromFile, stagingDirectory.resolve(fromFile.getFileName()));
+        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())) {
@@ -470,6 +512,8 @@
 
         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;
         }
 
@@ -513,39 +557,29 @@
         return mMiddlewarePackageNameCache;
     }
 
-    private static boolean manualMove(File src, File dst) {
-        InputStream in = null;
-        OutputStream out = null;
-        try {
-            if (!dst.exists()) {
-                dst.createNewFile();
-            }
-            in = new FileInputStream(src);
-            out = new FileOutputStream(dst);
-            byte[] buffer = new byte[2048];
-            int len;
-            do {
-                len = in.read(buffer);
-                out.write(buffer, 0, len);
-            } while (len > 0);
-        } catch (IOException e) {
-            Log.w(LOG_TAG, "Manual file move failed due to exception "  + e);
-            if (dst.exists()) {
-                dst.delete();
-            }
-            return false;
-        } finally {
-            try {
-                if (in != null) {
-                    in.close();
-                }
-                if (out != null) {
-                    out.close();
-                }
-            } catch (IOException e) {
-                Log.w(LOG_TAG, "Error closing streams: " + e);
-            }
+    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");
         }
-        return true;
+        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/telephony/mbms/MbmsDownloadSessionCallback.java b/android/telephony/mbms/MbmsDownloadSessionCallback.java
index 77dea6f..5003b57 100644
--- a/android/telephony/mbms/MbmsDownloadSessionCallback.java
+++ b/android/telephony/mbms/MbmsDownloadSessionCallback.java
@@ -16,8 +16,11 @@
 
 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;
 
 /**
@@ -25,6 +28,26 @@
  * 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.
@@ -32,7 +55,7 @@
      * @param message A message, intended for debugging purposes, describing the error in further
      *                detail.
      */
-    public void onError(int errorCode, String message) {
+    public void onError(@DownloadError int errorCode, String message) {
         // default implementation empty
     }
 
diff --git a/android/telephony/mbms/MbmsErrors.java b/android/telephony/mbms/MbmsErrors.java
index af0af24..7c4321b 100644
--- a/android/telephony/mbms/MbmsErrors.java
+++ b/android/telephony/mbms/MbmsErrors.java
@@ -19,6 +19,13 @@
 import android.telephony.MbmsStreamingSession;
 
 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;
 
@@ -108,8 +115,8 @@
 
         /**
          * Indicates that the app called
-         * {@link MbmsStreamingSession#startStreaming(
-         * StreamingServiceInfo, StreamingServiceCallback, android.os.Handler)}
+         * {@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;
@@ -128,6 +135,9 @@
 
         /** 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;
     }
 
     private MbmsErrors() {}
diff --git a/android/telephony/mbms/MbmsStreamingSessionCallback.java b/android/telephony/mbms/MbmsStreamingSessionCallback.java
index 5c130a0..1bdb20b 100644
--- a/android/telephony/mbms/MbmsStreamingSessionCallback.java
+++ b/android/telephony/mbms/MbmsStreamingSessionCallback.java
@@ -16,26 +16,50 @@
 
 package android.telephony.mbms;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.os.Handler;
 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, MbmsStreamingSessionCallback, int, Handler)}.
+ * {@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(int errorCode, @Nullable String message) {
+    public void onError(@StreamingError int errorCode, @Nullable String message) {
         // default implementation empty
     }
 
diff --git a/android/telephony/mbms/MbmsUtils.java b/android/telephony/mbms/MbmsUtils.java
index b4ad1d7..06b2120 100644
--- a/android/telephony/mbms/MbmsUtils.java
+++ b/android/telephony/mbms/MbmsUtils.java
@@ -50,7 +50,7 @@
         return new ComponentName(ci.packageName, ci.name);
     }
 
-    private static ComponentName getOverrideServiceName(Context context, String serviceAction) {
+    public static ComponentName getOverrideServiceName(Context context, String serviceAction) {
         String metaDataKey = null;
         switch (serviceAction) {
             case MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION:
@@ -130,8 +130,12 @@
      * 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, serviceId);
+        return new File(embmsTempFileDir, sanitizedServiceId);
     }
 }
diff --git a/android/telephony/mbms/StreamingService.java b/android/telephony/mbms/StreamingService.java
index ec9134a..b6239fe 100644
--- a/android/telephony/mbms/StreamingService.java
+++ b/android/telephony/mbms/StreamingService.java
@@ -29,11 +29,11 @@
 
 /**
  * Class used to represent a single MBMS stream. After a stream has been started with
- * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
- * StreamingServiceCallback, android.os.Handler)},
+ * {@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 {
+public class StreamingService implements AutoCloseable {
     private static final String LOG_TAG = "MbmsStreamingService";
 
     /**
@@ -41,7 +41,7 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({STATE_STOPPED, STATE_STARTED, STATE_STALLED})
+    @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;
@@ -53,7 +53,8 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT,
+    @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 {}
@@ -64,9 +65,9 @@
     public static final int REASON_NONE = 0;
 
     /**
-     * State changed due to a call to {@link #stopStreaming()} or
+     * State changed due to a call to {@link #close()} or
      * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
-     * StreamingServiceCallback, android.os.Handler)}
+     * java.util.concurrent.Executor, StreamingServiceCallback)}
      */
     public static final int REASON_BY_USER_REQUEST = 1;
 
@@ -161,7 +162,8 @@
      *
      * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
      */
-    public void stopStreaming() {
+    @Override
+    public void close() {
         if (mService == null) {
             throw new IllegalStateException("No streaming service attached");
         }
diff --git a/android/telephony/mbms/StreamingServiceCallback.java b/android/telephony/mbms/StreamingServiceCallback.java
index 0903824..c265db6 100644
--- a/android/telephony/mbms/StreamingServiceCallback.java
+++ b/android/telephony/mbms/StreamingServiceCallback.java
@@ -16,13 +16,34 @@
 
 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.
@@ -39,7 +60,7 @@
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    public void onError(int errorCode, @Nullable String message) {
+    public void onError(@StreamingServiceError int errorCode, @Nullable String message) {
         // default implementation empty
     }
 
diff --git a/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 4fee3df..a9f10b1 100644
--- a/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -24,11 +24,13 @@
 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.DownloadStateCallback;
+import android.telephony.mbms.DownloadStatusListener;
 import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.FileServiceInfo;
-import android.telephony.mbms.IDownloadStateCallback;
+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;
@@ -45,47 +47,50 @@
 @SystemApi
 @TestApi
 public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
-    private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>();
+    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;
+        }
 
-    // Filters the DownloadStateCallbacks by its configuration from the app.
-    private abstract static class FilteredDownloadStateCallback extends DownloadStateCallback {
+        @Override
+        public void onStatusUpdated(DownloadRequest request, FileInfo fileInfo,
+                @MbmsDownloadSession.DownloadStatus int state) {
+            try {
+                mListener.onStatusUpdated(request, fileInfo, state);
+            } catch (RemoteException e) {
+                onRemoteException(e);
+            }
+        }
 
-        private final IDownloadStateCallback mCallback;
-        public FilteredDownloadStateCallback(IDownloadStateCallback callback, int callbackFlags) {
-            super(callbackFlags);
-            mCallback = callback;
+        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) {
-            if (!isFilterFlagSet(PROGRESS_UPDATES)) {
-                return;
-            }
             try {
-                mCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                mListener.onProgressUpdated(request, fileInfo, currentDownloadSize,
                         fullDownloadSize, currentDecodedSize, fullDecodedSize);
             } catch (RemoteException e) {
                 onRemoteException(e);
             }
         }
 
-        @Override
-        public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
-                @MbmsDownloadSession.DownloadStatus int state) {
-            if (!isFilterFlagSet(STATE_UPDATES)) {
-                return;
-            }
-            try {
-                mCallback.onStateUpdated(request, fileInfo, state);
-            } catch (RemoteException e) {
-                onRemoteException(e);
-            }
-        }
-
         protected abstract void onRemoteException(RemoteException e);
     }
 
@@ -125,6 +130,10 @@
             @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);
@@ -223,71 +232,70 @@
     }
 
     /**
-     * Registers a download state callbacks for the provided {@link DownloadRequest}.
+     * 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 progress or
-     * status of the download.
+     * 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 callback The callback object to use.
+     * @param listener The listener object to use.
      */
-    public int registerStateCallback(DownloadRequest downloadRequest,
-            DownloadStateCallback callback) throws RemoteException {
+    public int addStatusListener(DownloadRequest downloadRequest,
+            DownloadStatusListener listener) throws RemoteException {
         return 0;
     }
 
     /**
-     * Actual AIDL implementation -- hides the callback AIDL from the API.
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
      * @hide
      */
     @Override
-    public final int registerStateCallback(final DownloadRequest downloadRequest,
-            final IDownloadStateCallback callback, int flags) throws RemoteException {
+    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 (callback == null) {
+        if (listener == null) {
             throw new NullPointerException("Callback must not be null");
         }
 
-        DownloadStateCallback exposedCallback = new FilteredDownloadStateCallback(callback, flags) {
+        DownloadStatusListener exposedCallback = new VendorDownloadStatusListener(listener) {
             @Override
             protected void onRemoteException(RemoteException e) {
                 onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
             }
         };
 
-        int result = registerStateCallback(downloadRequest, exposedCallback);
+        int result = addStatusListener(downloadRequest, exposedCallback);
 
         if (result == MbmsErrors.SUCCESS) {
             DeathRecipient deathRecipient = new DeathRecipient() {
                 @Override
                 public void binderDied() {
                     onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
-                    mDownloadCallbackBinderMap.remove(callback.asBinder());
-                    mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+                    mDownloadStatusListenerBinderMap.remove(listener.asBinder());
+                    mDownloadCallbackDeathRecipients.remove(listener.asBinder());
                 }
             };
-            mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
-            callback.asBinder().linkToDeath(deathRecipient, 0);
-            mDownloadCallbackBinderMap.put(callback.asBinder(), exposedCallback);
+            mDownloadCallbackDeathRecipients.put(listener.asBinder(), deathRecipient);
+            listener.asBinder().linkToDeath(deathRecipient, 0);
+            mDownloadStatusListenerBinderMap.put(listener.asBinder(), exposedCallback);
         }
 
         return result;
     }
 
     /**
-     * Un-registers a download state callbacks for the provided {@link DownloadRequest}.
+     * 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 updates on the
+     * 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
@@ -296,45 +304,157 @@
      * must be returned.
      *
      * @param downloadRequest The {@link DownloadRequest} that was used to register the callback
-     * @param callback The callback object that
-     *                 {@link #registerStateCallback(DownloadRequest, DownloadStateCallback)}
+     * @param listener The callback object that
+     *                 {@link #addStatusListener(DownloadRequest, DownloadStatusListener)}
      *                 was called with.
      */
-    public int unregisterStateCallback(DownloadRequest downloadRequest,
-            DownloadStateCallback callback) throws RemoteException {
+    public int removeStatusListener(DownloadRequest downloadRequest,
+            DownloadStatusListener listener) throws RemoteException {
         return 0;
     }
 
     /**
-     * Actual AIDL implementation -- hides the callback AIDL from the API.
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
      * @hide
      */
-    @Override
-    public final int unregisterStateCallback(
-            final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
+    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 (callback == null) {
+        if (listener == null) {
             throw new NullPointerException("Callback must not be null");
         }
 
         DeathRecipient deathRecipient =
-                mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+                mDownloadCallbackDeathRecipients.remove(listener.asBinder());
         if (deathRecipient == null) {
-            throw new IllegalArgumentException("Unknown callback");
+            throw new IllegalArgumentException("Unknown listener");
         }
 
-        callback.asBinder().unlinkToDeath(deathRecipient, 0);
+        listener.asBinder().unlinkToDeath(deathRecipient, 0);
 
-        DownloadStateCallback exposedCallback =
-                mDownloadCallbackBinderMap.remove(callback.asBinder());
+        DownloadStatusListener exposedCallback =
+                mDownloadStatusListenerBinderMap.remove(listener.asBinder());
         if (exposedCallback == null) {
-            throw new IllegalArgumentException("Unknown callback");
+            throw new IllegalArgumentException("Unknown listener");
         }
 
-        return unregisterStateCallback(downloadRequest, exposedCallback);
+        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);
     }
 
     /**
@@ -370,18 +490,18 @@
     }
 
     /**
-     * Gets information about the status of a file pending download.
+     * Requests information about the state of a file pending download.
      *
-     * If the middleware has not yet been properly initialized or if it has no records of the
+     * If the middleware has no records of the
      * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
-     * {@link MbmsDownloadSession#STATUS_UNKNOWN} must be returned.
+     * {@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 The status of the download.
+     * @return {@link MbmsErrors#SUCCESS} if the request was successful, an error code otherwise.
      */
     @Override
-    public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo)
+    public int requestDownloadState(DownloadRequest downloadRequest, FileInfo fileInfo)
             throws RemoteException {
         return 0;
     }
diff --git a/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index db177c0..5ce612d 100644
--- a/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -77,6 +77,10 @@
             @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);
@@ -173,6 +177,10 @@
             @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);