Snap for 10428683 from 94ec5a14cea628292aa957a95d5fa325e015072f to mainline-adbd-release

Change-Id: I5c3e057d50413c56a9c0e036edbde0d564aee05d
diff --git a/Android.bp b/Android.bp
index fab53d1..4097571 100644
--- a/Android.bp
+++ b/Android.bp
@@ -83,13 +83,14 @@
         "android.hardware.radio-V1.4-java",
         "android.hardware.radio-V1.5-java",
         "android.hardware.radio-V1.6-java",
-        "android.hardware.radio.config-V1-java",
-        "android.hardware.radio.data-V1-java",
-        "android.hardware.radio.messaging-V1-java",
-        "android.hardware.radio.modem-V1-java",
-        "android.hardware.radio.network-V1-java",
-        "android.hardware.radio.sim-V1-java",
-        "android.hardware.radio.voice-V1-java",
+        "android.hardware.radio.config-V2-java",
+        "android.hardware.radio.data-V2-java",
+        "android.hardware.radio.ims-V1-java",
+        "android.hardware.radio.messaging-V2-java",
+        "android.hardware.radio.modem-V2-java",
+        "android.hardware.radio.network-V2-java",
+        "android.hardware.radio.sim-V2-java",
+        "android.hardware.radio.voice-V2-java",
         "voip-common",
         "ims-common",
         "unsupportedappusage",
diff --git a/OWNERS b/OWNERS
index 94509fe..a061cf0 100644
--- a/OWNERS
+++ b/OWNERS
@@ -8,15 +8,15 @@
 jackyu@google.com
 jayachandranc@google.com
 linggm@google.com
-mtelang@google.com
+pmadapurmath@google.com
 rgreenwalt@google.com
 sarahchin@google.com
 sasindran@google.com
 tgunn@google.com
 tjstuart@google.com
+tnd@google.com
 xiaotonj@google.com
 
 
 
 
-
diff --git a/README.txt b/README.txt
index 9e40b77..1a44beb 100644
--- a/README.txt
+++ b/README.txt
@@ -15,7 +15,7 @@
 implement in this directory and packages/services/Telephony. This IPC scheme
 allows us to run public API code in the calling process, while the
 telephony-related code runs in the privileged com.android.phone process. Such
-implementations include PhoneInterfaceManager, SubscriptionController and
+implementations include PhoneInterfaceManager, SubscriptionManagerService and
 others.
 
 The declaration of the com.android.phone process is in
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index c10eeb5..61e44a3 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -23,7 +23,7 @@
 
 // Holds atoms to store on persist storage in case of power cycle or process crash.
 // NOTE: using int64 rather than google.protobuf.Timestamp for timestamps simplifies implementation.
-// Next id: 50
+// Next id: 70
 message PersistAtoms {
     /* Aggregated RAT usage during the call. */
     repeated VoiceCallRatUsage voice_call_rat_usage = 1;
@@ -177,6 +177,60 @@
 
     /* Timestamp of last network_requests_v2 pull. */
     optional int64 network_requests_v2_pull_timestamp_millis = 51;
+
+    /* Unmetered networks information. */
+    repeated UnmeteredNetworks unmetered_networks = 52;
+
+    /* Outgoing Short Code SMS statistics and information. */
+    repeated OutgoingShortCodeSms outgoing_short_code_sms = 53;
+
+    /* Timestamp of last outgoing_short_code_sms pull. */
+    optional int64 outgoing_short_code_sms_pull_timestamp_millis = 54;
+
+    /* Number of time the user toggled the data switch feature since the last collection. */
+    optional int32 auto_data_switch_toggle_count = 55;
+
+    /** Snapshot of satellite controller. */
+    repeated SatelliteController satellite_controller = 58;
+
+    /* Timestamp of last satellite_controller pull. */
+    optional int64 satellite_controller_pull_timestamp_millis = 59;
+
+    /** Snapshot of satellite controller. */
+    repeated SatelliteSession satellite_session = 60;
+
+    /* Timestamp of last satellite_controller pull. */
+    optional int64 satellite_session_pull_timestamp_millis = 61;
+
+    /** Snapshot of satellite incoming datagram. */
+    repeated SatelliteIncomingDatagram satellite_incoming_datagram = 62;
+
+    /* Timestamp of last satellite_incoming_datagram pull. */
+    optional int64 satellite_incoming_datagram_pull_timestamp_millis = 63;
+
+    /** Snapshot of satellite outgoing datagram. */
+    repeated SatelliteOutgoingDatagram satellite_outgoing_datagram = 64;
+
+    /* Timestamp of last satellite_outgoing_datagram pull. */
+    optional int64 satellite_outgoing_datagram_pull_timestamp_millis = 65;
+
+    /** Snapshot of satellite provision datagram. */
+    repeated SatelliteProvision satellite_provision = 66;
+
+    /* Timestamp of last satellite_provision pull. */
+    optional int64 satellite_provision_pull_timestamp_millis = 67;
+
+    /** Snapshot of satellite SOS message recommender. */
+    repeated SatelliteSosMessageRecommender satellite_sos_message_recommender = 68;
+
+    /* Timestamp of last satellite_sos_message_recommender pull. */
+    optional int64 satellite_sos_message_recommender_pull_timestamp_millis = 69;
+
+    /* Consolidated emergency numbers list information. */
+    repeated EmergencyNumbersInfo emergency_numbers_info = 56;
+
+    /* Timestamp of last emergency number pull. */
+    optional int64 emergency_number_pull_timestamp_millis = 57;
 }
 
 // The canonical versions of the following enums live in:
@@ -187,10 +241,10 @@
 // NOTE: StatsLog functions use int in place of enum
 
 message VoiceCallSession {
+    reserved 4;
     optional int32 bearer_at_start = 1;
     optional int32 bearer_at_end = 2;
     optional int32 direction = 3;
-    optional int32 setup_duration = 4;
     optional bool setup_failed = 5;
     optional int32 disconnect_reason_code = 6;
     optional int32 disconnect_extra_code = 7;
@@ -219,6 +273,9 @@
     optional int32 rat_at_connected = 30;
     optional bool is_multiparty = 31;
     optional int32 call_duration = 32;
+    optional int32 last_known_rat = 33;
+    optional int32 fold_state = 34;
+
     // Internal use only
     optional int64 setup_begin_millis = 10001;
 }
@@ -245,6 +302,11 @@
     optional bool is_esim = 12;
     optional int32 carrier_id = 13;
     optional int64 message_id = 14;
+    optional int32 count = 15;
+    optional bool is_managed_profile = 16;
+
+    // Internal use only
+    optional int32 hashCode = 10001;
 }
 
 message OutgoingSms {
@@ -262,6 +324,13 @@
     optional int64 message_id = 12;
     optional int32 retry_id = 13;
     optional int64 interval_millis = 14;
+    optional int32 count = 15;
+    optional int32 send_error_code = 16;
+    optional int32 network_error_code = 17;
+    optional bool is_managed_profile = 18;
+
+    // Internal use only
+    optional int32 hashCode = 10001;
 }
 
 message CarrierIdMismatch {
@@ -292,6 +361,8 @@
     optional bool ongoing = 18;
     optional int32 band_at_end = 19;
     repeated int32 handover_failure_causes = 20;
+    repeated int32 handover_failure_rat = 21;
+    optional bool is_non_dds = 22;
 }
 
 message CellularServiceState {
@@ -305,6 +376,8 @@
     optional int32 carrier_id = 8;
     optional int64 total_time_millis = 9; // Duration needs to be rounded when pulled
     optional bool is_emergency_only = 10;
+    optional bool is_internet_pdn_up = 11;
+    optional int32 fold_state = 12;
 
     // Internal use only
     optional int64 last_used_millis = 10001;
@@ -498,3 +571,104 @@
     optional int32 failed_reason = 4;
     optional int32 count = 5;
 }
+
+message UnmeteredNetworks {
+    optional int32 phone_id = 1;
+    optional int32 carrier_id = 2;
+    optional int64 unmetered_networks_bitmask = 3;
+}
+
+message OutgoingShortCodeSms {
+    optional int32 category = 1;
+    optional int32 xml_version = 2;
+    optional int32 short_code_sms_count = 3;
+}
+
+message SatelliteController {
+    optional int32 count_of_satellite_service_enablements_success = 1;
+    optional int32 count_of_satellite_service_enablements_fail = 2;
+    optional int32 count_of_outgoing_datagram_success = 3;
+    optional int32 count_of_outgoing_datagram_fail = 4;
+    optional int32 count_of_incoming_datagram_success = 5;
+    optional int32 count_of_incoming_datagram_fail = 6;
+    optional int32 count_of_datagram_type_sos_sms_success = 7;
+    optional int32 count_of_datagram_type_sos_sms_fail = 8;
+    optional int32 count_of_datagram_type_location_sharing_success = 9;
+    optional int32 count_of_datagram_type_location_sharing_fail = 10;
+    optional int32 count_of_provision_success = 11;
+    optional int32 count_of_provision_fail = 12;
+    optional int32 count_of_deprovision_success = 13;
+    optional int32 count_of_deprovision_fail = 14;
+    optional int32 total_service_uptime_sec = 15;
+    optional int32 total_battery_consumption_percent = 16;
+    optional int32 total_battery_charged_time_sec = 17;
+}
+
+message SatelliteSession {
+    optional int32 satellite_service_initialization_result = 1;
+    optional int32 satellite_technology = 2;
+    optional int32 count = 3;
+}
+
+message SatelliteIncomingDatagram {
+    optional int32 result_code = 1;
+    optional int32 datagram_size_bytes = 2;
+    optional int64 datagram_transfer_time_millis = 3;
+}
+
+message SatelliteOutgoingDatagram {
+    optional int32 datagram_type = 1;
+    optional int32 result_code = 2;
+    optional int32 datagram_size_bytes = 3;
+    optional int64 datagram_transfer_time_millis = 4;
+}
+
+message SatelliteProvision {
+    optional int32 result_code = 1;
+    optional int32 provisioning_time_sec = 2;
+    optional bool is_provision_request = 3;
+    optional bool is_canceled = 4;
+}
+
+message SatelliteSosMessageRecommender {
+    optional bool is_display_sos_message_sent = 1;
+    optional int32 count_of_timer_started = 2;
+    optional bool is_ims_registered = 3;
+    optional int32 cellular_service_state = 4;
+    optional int32 count = 5;
+}
+
+message EmergencyNumbersInfo {
+    enum ServiceCategory {
+        EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED = 0;
+        EMERGENCY_SERVICE_CATEGORY_POLICE = 1;
+        EMERGENCY_SERVICE_CATEGORY_AMBULANCE = 2;
+        EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE = 3;
+        EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD = 4;
+        EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE = 5;
+        EMERGENCY_SERVICE_CATEGORY_MIEC = 6;
+        EMERGENCY_SERVICE_CATEGORY_AIEC = 7;
+    }
+    enum Source {
+        EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 0;
+        EMERGENCY_NUMBER_SOURCE_SIM = 1;
+        EMERGENCY_NUMBER_SOURCE_DATABASE = 2;
+        EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 3;
+        EMERGENCY_NUMBER_SOURCE_DEFAULT = 4;
+    }
+    enum CallRoute {
+        EMERGENCY_CALL_ROUTE_UNKNOWN = 0;
+        EMERGENCY_CALL_ROUTE_EMERGENCY = 1;
+        EMERGENCY_CALL_ROUTE_NORMAL = 2;
+    }
+    optional bool is_db_version_ignored = 1;
+    optional int32 asset_version = 2;
+    optional int32 ota_version = 3;
+    optional string number = 4;
+    optional string country_iso = 5;
+    optional string mnc = 6;
+    optional CallRoute route = 7;
+    repeated string urns = 8;
+    repeated ServiceCategory service_categories = 9;
+    repeated Source sources = 10;
+}
diff --git a/proto/src/telephony.proto b/proto/src/telephony.proto
index 6d3d711..b87728b 100644
--- a/proto/src/telephony.proto
+++ b/proto/src/telephony.proto
@@ -931,6 +931,9 @@
 
       /** Data switch caused by CBRS switch. */
       DATA_SWITCH_REASON_CBRS = 3;
+
+      /** Data switch caused by non-default SIM having better availability(e.g.signal). */
+      DATA_SWITCH_REASON_AUTO = 4;
     }
 
     /** The reason for data switch. */
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 972884a..b8de975 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -17,6 +17,8 @@
 
 package com.android.internal.telephony;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.AsyncResult;
@@ -26,6 +28,7 @@
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.telephony.Annotation.RadioPowerState;
+import android.telephony.BarringInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.emergency.EmergencyNumber;
 
@@ -114,6 +117,17 @@
     protected RegistrantList mBarringInfoChangedRegistrants = new RegistrantList();
     protected RegistrantList mSimPhonebookChangedRegistrants = new RegistrantList();
     protected RegistrantList mSimPhonebookRecordsReceivedRegistrants = new RegistrantList();
+    protected RegistrantList mEmergencyNetworkScanRegistrants = new RegistrantList();
+    protected RegistrantList mConnectionSetupFailureRegistrants = new RegistrantList();
+    protected RegistrantList mNotifyAnbrRegistrants = new RegistrantList();
+    protected RegistrantList mTriggerImsDeregistrationRegistrants = new RegistrantList();
+    protected RegistrantList mPendingSatelliteMessageCountRegistrants = new RegistrantList();
+    protected RegistrantList mNewSatelliteMessagesRegistrants = new RegistrantList();
+    protected RegistrantList mSatelliteMessagesTransferCompleteRegistrants = new RegistrantList();
+    protected RegistrantList mSatellitePointingInfoChangedRegistrants = new RegistrantList();
+    protected RegistrantList mSatelliteModeChangedRegistrants = new RegistrantList();
+    protected RegistrantList mSatelliteRadioTechnologyChangedRegistrants = new RegistrantList();
+    protected RegistrantList mSatelliteProvisionStateChangedRegistrants = new RegistrantList();
 
     @UnsupportedAppUsage
     protected Registrant mGsmSmsRegistrant;
@@ -160,6 +174,8 @@
     // Cache last emergency number list indication from radio
     private final List<EmergencyNumber> mLastEmergencyNumberListIndication = new ArrayList<>();
 
+    // The last barring information received
+    protected BarringInfo mLastBarringInfo = new BarringInfo();
     // Preferred network type received from PhoneFactory.
     // This is used when establishing a connection to the
     // vendor ril so it starts up in the correct mode.
@@ -900,6 +916,7 @@
                     || mState == TelephonyManager.RADIO_POWER_UNAVAILABLE)
                     && (oldState == TelephonyManager.RADIO_POWER_ON)) {
                 mOffOrNotAvailRegistrants.notifyRegistrants();
+                mLastBarringInfo = new BarringInfo();
             }
         }
     }
@@ -918,6 +935,12 @@
         }
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public @NonNull BarringInfo getLastBarringInfo() {
+        return mLastBarringInfo;
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -1132,4 +1155,133 @@
     @Override
     public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) {
     }
+
+    /**
+     * Register for Emergency network scan result.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    @Override
+    public void registerForEmergencyNetworkScan(Handler h, int what, Object obj) {
+        mEmergencyNetworkScanRegistrants.add(h, what, obj);
+    }
+
+    /**
+     * Unregister for Emergency network scan result.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    @Override
+    public void unregisterForEmergencyNetworkScan(Handler h) {
+        mEmergencyNetworkScanRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForConnectionSetupFailure(Handler h, int what, Object obj) {
+        mConnectionSetupFailureRegistrants.addUnique(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForConnectionSetupFailure(Handler h) {
+        mConnectionSetupFailureRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForNotifyAnbr(Handler h, int what, Object obj) {
+        mNotifyAnbrRegistrants.addUnique(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForNotifyAnbr(Handler h) {
+        mNotifyAnbrRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForTriggerImsDeregistration(Handler h, int what, Object obj) {
+        mTriggerImsDeregistrationRegistrants.add(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForTriggerImsDeregistration(Handler h) {
+        mTriggerImsDeregistrationRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForPendingSatelliteMessageCount(
+            @NonNull Handler h, int what, @Nullable Object obj) {
+        mPendingSatelliteMessageCountRegistrants.add(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForPendingSatelliteMessageCount(@NonNull Handler h) {
+        mPendingSatelliteMessageCountRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForNewSatelliteMessages(
+            @NonNull Handler h, int what, @Nullable Object obj) {
+        mNewSatelliteMessagesRegistrants.add(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForNewSatelliteMessages(@NonNull Handler h) {
+        mNewSatelliteMessagesRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForSatelliteMessagesTransferComplete(@NonNull Handler h,
+            int what, @Nullable Object obj) {
+        mSatelliteMessagesTransferCompleteRegistrants.add(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForSatelliteMessagesTransferComplete(@NonNull Handler h) {
+        mSatelliteMessagesTransferCompleteRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForSatellitePointingInfoChanged(@NonNull Handler h,
+            int what, @Nullable Object obj) {
+        mSatellitePointingInfoChangedRegistrants.add(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForSatellitePointingInfoChanged(@NonNull Handler h) {
+        mSatellitePointingInfoChangedRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForSatelliteModeChanged(@NonNull Handler h,
+            int what, @Nullable Object obj) {
+        mSatelliteModeChangedRegistrants.add(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForSatelliteModeChanged(@NonNull Handler h) {
+        mSatelliteModeChangedRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForSatelliteRadioTechnologyChanged(@NonNull Handler h,
+            int what, @Nullable Object obj) {
+        mSatelliteRadioTechnologyChangedRegistrants.add(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForSatelliteRadioTechnologyChanged(@NonNull Handler h) {
+        mSatelliteRadioTechnologyChangedRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForSatelliteProvisionStateChanged(@NonNull Handler h,
+            int what, @Nullable Object obj) {
+        mSatelliteProvisionStateChangedRegistrants.add(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForSatelliteProvisionStateChanged(@NonNull Handler h) {
+        mSatelliteProvisionStateChangedRegistrants.remove(h);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/CallStateException.java b/src/java/com/android/internal/telephony/CallStateException.java
index 3fdc444..4de16cb 100644
--- a/src/java/com/android/internal/telephony/CallStateException.java
+++ b/src/java/com/android/internal/telephony/CallStateException.java
@@ -35,6 +35,7 @@
     public static final int ERROR_CALLING_DISABLED = 5;
     public static final int ERROR_TOO_MANY_CALLS = 6;
     public static final int ERROR_OTASP_PROVISIONING_IN_PROCESS = 7;
+    public static final int ERROR_FDN_BLOCKED = 8;
 
     public
     CallStateException()
diff --git a/src/java/com/android/internal/telephony/CallWaitingController.java b/src/java/com/android/internal/telephony/CallWaitingController.java
new file mode 100644
index 0000000..49940fc
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CallWaitingController.java
@@ -0,0 +1,684 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_CHANGE;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_POWER_UP;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_IMS_ONLY;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_NONE;
+import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_USER_CHANGE;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT;
+import static android.telephony.CarrierConfigManager.ImsSs.KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CW;
+
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.telephony.Rlog;
+
+import java.io.PrintWriter;
+
+/**
+ * Controls the change of the user setting of the call waiting service
+ */
+public class CallWaitingController extends Handler {
+
+    public static final String LOG_TAG = "CallWaitingCtrl";
+    private static final boolean DBG = false; /* STOPSHIP if true */
+
+    // Terminal-based call waiting is not supported. */
+    public static final int TERMINAL_BASED_NOT_SUPPORTED = -1;
+    // Terminal-based call waiting is supported but not activated. */
+    public static final int TERMINAL_BASED_NOT_ACTIVATED = 0;
+    // Terminal-based call waiting is supported and activated. */
+    public static final int TERMINAL_BASED_ACTIVATED = 1;
+
+    private static final int EVENT_SET_CALL_WAITING_DONE = 1;
+    private static final int EVENT_GET_CALL_WAITING_DONE = 2;
+    private static final int EVENT_REGISTERED_TO_NETWORK = 3;
+
+    // Class to pack mOnComplete object passed by the caller
+    private static class Cw {
+        final boolean mEnable;
+        final Message mOnComplete;
+        final boolean mImsRegistered;
+
+        Cw(boolean enable, boolean imsRegistered, Message onComplete) {
+            mEnable = enable;
+            mOnComplete = onComplete;
+            mImsRegistered = imsRegistered;
+        }
+    }
+
+    @VisibleForTesting
+    public static final String PREFERENCE_TBCW = "terminal_based_call_waiting";
+    @VisibleForTesting
+    public static final String KEY_SUB_ID = "subId";
+    @VisibleForTesting
+    public static final String KEY_STATE = "state";
+    @VisibleForTesting
+    public static final String KEY_CS_SYNC = "cs_sync";
+
+    private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+            (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigurationChanged(
+                    slotIndex);
+
+    private boolean mSupportedByImsService = false;
+    private boolean mValidSubscription = false;
+
+    // The user's last setting of terminal-based call waiting
+    private int mCallWaitingState = TERMINAL_BASED_NOT_SUPPORTED;
+
+    private int mSyncPreference = CALL_WAITING_SYNC_NONE;
+    private int mLastSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+    private boolean mCsEnabled = false;
+    private boolean mRegisteredForNetworkAttach = false;
+    private boolean mImsRegistered = false;
+
+    private final GsmCdmaPhone mPhone;
+    private final ServiceStateTracker mSST;
+    private final Context mContext;
+
+    // Constructors
+    public CallWaitingController(GsmCdmaPhone phone) {
+        mPhone = phone;
+        mSST = phone.getServiceStateTracker();
+        mContext = phone.getContext();
+    }
+
+    private void initialize() {
+        CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class);
+        if (ccm != null) {
+            // Callback directly handle carrier config change should be executed in handler thread
+            ccm.registerCarrierConfigChangeListener(this::post, mCarrierConfigChangeListener);
+        } else {
+            loge("CarrierConfigLoader is not available.");
+        }
+
+        int phoneId = mPhone.getPhoneId();
+        int subId = mPhone.getSubId();
+        SharedPreferences sp =
+                mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
+        mLastSubId = sp.getInt(KEY_SUB_ID + phoneId, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        mCallWaitingState = sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED);
+        mSyncPreference = sp.getInt(KEY_CS_SYNC + phoneId, CALL_WAITING_SYNC_NONE);
+
+        logi("initialize phoneId=" + phoneId
+                + ", lastSubId=" + mLastSubId + ", subId=" + subId
+                + ", state=" + mCallWaitingState + ", sync=" + mSyncPreference
+                + ", csEnabled=" + mCsEnabled);
+    }
+
+    /**
+     * Returns the cached user setting.
+     *
+     * Possible values are
+     * {@link #TERMINAL_BASED_NOT_SUPPORTED},
+     * {@link #TERMINAL_BASED_NOT_ACTIVATED}, and
+     * {@link #TERMINAL_BASED_ACTIVATED}.
+     *
+     * @param forCsOnly indicates the caller expects the result for CS calls only
+     */
+    @VisibleForTesting
+    public synchronized int getTerminalBasedCallWaitingState(boolean forCsOnly) {
+        if (forCsOnly && (!mImsRegistered) && mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+            return TERMINAL_BASED_NOT_SUPPORTED;
+        }
+        if (!mValidSubscription) return TERMINAL_BASED_NOT_SUPPORTED;
+        return mCallWaitingState;
+    }
+
+    /**
+     * Serves the user's requests to interrogate the call waiting service
+     *
+     * @return true when terminal-based call waiting is supported, otherwise false
+     */
+    @VisibleForTesting
+    public synchronized boolean getCallWaiting(@Nullable Message onComplete) {
+        if (mCallWaitingState == TERMINAL_BASED_NOT_SUPPORTED) return false;
+
+        logi("getCallWaiting " + mCallWaitingState);
+
+        if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+            // Interrogate CW in CS network
+            if (!mCsEnabled) {
+                // skip interrogation if CS is not available and IMS is registered
+                if (isCircuitSwitchedNetworkAvailable() || !isImsRegistered()) {
+                    Cw cw = new Cw(false, isImsRegistered(), onComplete);
+                    Message resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, 0, 0, cw);
+                    mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, resp);
+                    return true;
+                }
+            }
+        }
+
+        if (mSyncPreference == CALL_WAITING_SYNC_NONE
+                || mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE
+                || mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP
+                || isSyncImsOnly()) {
+            sendGetCallWaitingResponse(onComplete);
+            return true;
+        } else if (mSyncPreference == CALL_WAITING_SYNC_USER_CHANGE
+                || mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+            Cw cw = new Cw(false, isImsRegistered(), onComplete);
+            Message resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, 0, 0, cw);
+            mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, resp);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Serves the user's requests to set the call waiting service
+     *
+     * @param serviceClass the target service class. Values are CommandsInterface.SERVICE_CLASS_*.
+     * @return true when terminal-based call waiting is supported, otherwise false
+     */
+    @VisibleForTesting
+    public synchronized boolean setCallWaiting(boolean enable,
+            int serviceClass, @Nullable Message onComplete) {
+        if (mCallWaitingState == TERMINAL_BASED_NOT_SUPPORTED) return false;
+
+        if ((serviceClass & SERVICE_CLASS_VOICE) != SERVICE_CLASS_VOICE) return false;
+
+        logi("setCallWaiting enable=" + enable + ", service=" + serviceClass);
+
+        if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+            // Enable CW in the CS network
+            if (!mCsEnabled && enable) {
+                if (isCircuitSwitchedNetworkAvailable() || !isImsRegistered()) {
+                    Cw cw = new Cw(true, isImsRegistered(), onComplete);
+                    Message resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, 0, 0, cw);
+                    mPhone.mCi.setCallWaiting(true, serviceClass, resp);
+                    return true;
+                } else {
+                    // CS network is not available, however, IMS is registered.
+                    // Enabling the service in the CS network will be delayed.
+                    registerForNetworkAttached();
+                }
+            }
+        }
+
+        if (mSyncPreference == CALL_WAITING_SYNC_NONE
+                || mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE
+                || mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP
+                || isSyncImsOnly()) {
+            updateState(
+                    enable ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED);
+
+            sendToTarget(onComplete, null, null);
+            return true;
+        } else if (mSyncPreference == CALL_WAITING_SYNC_USER_CHANGE
+                || mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+            Cw cw = new Cw(enable, isImsRegistered(), onComplete);
+            Message resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, 0, 0, cw);
+            mPhone.mCi.setCallWaiting(enable, serviceClass, resp);
+            return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case EVENT_SET_CALL_WAITING_DONE:
+                onSetCallWaitingDone((AsyncResult) msg.obj);
+                break;
+            case EVENT_GET_CALL_WAITING_DONE:
+                onGetCallWaitingDone((AsyncResult) msg.obj);
+                break;
+            case EVENT_REGISTERED_TO_NETWORK:
+                onRegisteredToNetwork();
+                break;
+            default:
+                break;
+        }
+    }
+
+    private synchronized void onSetCallWaitingDone(AsyncResult ar) {
+        if (ar.userObj == null) {
+            // For the case, CALL_WAITING_SYNC_FIRST_POWER_UP
+            if (DBG) logd("onSetCallWaitingDone to sync on network attached");
+            if (ar.exception == null) {
+                updateSyncState(true);
+            } else {
+                loge("onSetCallWaitingDone e=" + ar.exception);
+            }
+            return;
+        }
+
+        if (!(ar.userObj instanceof Cw)) {
+            // Unexpected state
+            if (DBG) logd("onSetCallWaitingDone unexpected result");
+            return;
+        }
+
+        if (DBG) logd("onSetCallWaitingDone");
+        Cw cw = (Cw) ar.userObj;
+
+        if (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+            // do not synchronize service state between CS and IMS
+            sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+            return;
+        }
+
+        if (ar.exception == null) {
+            if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+                // SYNC_FIRST_CHANGE implies cw.mEnable is true.
+                updateSyncState(true);
+            }
+            updateState(
+                    cw.mEnable ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED);
+        } else if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+            if (cw.mImsRegistered) {
+                // IMS is registered. Do not notify error.
+                // SYNC_FIRST_CHANGE implies cw.mEnable is true.
+                updateState(TERMINAL_BASED_ACTIVATED);
+                sendToTarget(cw.mOnComplete, null, null);
+                return;
+            }
+        }
+        sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+    }
+
+    private synchronized void onGetCallWaitingDone(AsyncResult ar) {
+        if (ar.userObj == null) {
+            // For the case, CALL_WAITING_SYNC_FIRST_POWER_UP
+            if (DBG) logd("onGetCallWaitingDone to sync on network attached");
+            boolean enabled = false;
+            if (ar.exception == null) {
+                //resp[0]: 1 if enabled, 0 otherwise
+                //resp[1]: bitwise ORs of SERVICE_CLASS_* constants
+                int[] resp = (int[]) ar.result;
+                if (resp != null && resp.length > 1) {
+                    enabled = (resp[0] == 1)
+                            && (resp[1] & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE;
+                } else {
+                    loge("onGetCallWaitingDone unexpected response");
+                }
+            } else {
+                loge("onGetCallWaitingDone e=" + ar.exception);
+            }
+            if (enabled) {
+                updateSyncState(true);
+            } else {
+                logi("onGetCallWaitingDone enabling CW service in CS network");
+                mPhone.mCi.setCallWaiting(true, SERVICE_CLASS_VOICE,
+                        obtainMessage(EVENT_SET_CALL_WAITING_DONE));
+            }
+            unregisterForNetworkAttached();
+            return;
+        }
+
+        if (!(ar.userObj instanceof Cw)) {
+            // Unexpected state
+            if (DBG) logd("onGetCallWaitingDone unexpected result");
+            return;
+        }
+
+        if (DBG) logd("onGetCallWaitingDone");
+        Cw cw = (Cw) ar.userObj;
+
+        if (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) {
+            // do not synchronize service state between CS and IMS
+            sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+            return;
+        }
+
+        if (ar.exception == null) {
+            int[] resp = (int[]) ar.result;
+            //resp[0]: 1 if enabled, 0 otherwise
+            //resp[1]: bitwise ORs of SERVICE_CLASS_
+            if (resp == null || resp.length < 2) {
+                logi("onGetCallWaitingDone unexpected response");
+                if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+                    // no exception but unexpected response, local setting is preferred.
+                    sendGetCallWaitingResponse(cw.mOnComplete);
+                } else {
+                    sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+                }
+                return;
+            }
+
+            boolean enabled = resp[0] == 1
+                    && (resp[1] & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE;
+
+            if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+                updateSyncState(enabled);
+
+                if (!enabled && !cw.mImsRegistered) {
+                    // IMS is not registered, change the local setting
+                    logi("onGetCallWaitingDone CW in CS network is disabled.");
+                    updateState(TERMINAL_BASED_NOT_ACTIVATED);
+                }
+
+                // return the user setting saved
+                sendGetCallWaitingResponse(cw.mOnComplete);
+                return;
+            }
+            updateState(enabled ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED);
+        } else if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) {
+            // Got an exception
+            if (cw.mImsRegistered) {
+                // queryCallWaiting failed. However, IMS is registered. Do not notify error.
+                // return the user setting saved
+                logi("onGetCallWaitingDone get an exception, but IMS is registered");
+                sendGetCallWaitingResponse(cw.mOnComplete);
+                return;
+            }
+        }
+        sendToTarget(cw.mOnComplete, ar.result, ar.exception);
+    }
+
+    private void sendToTarget(Message onComplete, Object result, Throwable exception) {
+        if (onComplete != null) {
+            AsyncResult.forMessage(onComplete, result, exception);
+            onComplete.sendToTarget();
+        }
+    }
+
+    private void sendGetCallWaitingResponse(Message onComplete) {
+        if (onComplete != null) {
+            int serviceClass = SERVICE_CLASS_NONE;
+            if (mCallWaitingState == TERMINAL_BASED_ACTIVATED) {
+                serviceClass = SERVICE_CLASS_VOICE;
+            }
+            sendToTarget(onComplete, new int[] { mCallWaitingState, serviceClass }, null);
+        }
+    }
+
+    private synchronized void onRegisteredToNetwork() {
+        if (mCsEnabled) return;
+
+        if (DBG) logd("onRegisteredToNetwork");
+
+        mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE,
+                obtainMessage(EVENT_GET_CALL_WAITING_DONE));
+    }
+
+    private synchronized void onCarrierConfigurationChanged(int slotIndex) {
+        if (slotIndex != mPhone.getPhoneId()) return;
+
+        int subId = mPhone.getSubId();
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            logi("onCarrierConfigChanged invalid subId=" + subId);
+
+            mValidSubscription = false;
+            unregisterForNetworkAttached();
+            return;
+        }
+
+        if (!updateCarrierConfig(subId, false /* ignoreSavedState */)) {
+            return;
+        }
+
+        logi("onCarrierConfigChanged cs_enabled=" + mCsEnabled);
+
+        if (mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP) {
+            if (!mCsEnabled) {
+                registerForNetworkAttached();
+            }
+        }
+    }
+
+    /**
+     * @param ignoreSavedState only used for test
+     * @return true when succeeded.
+     */
+    @VisibleForTesting
+    public boolean updateCarrierConfig(int subId, boolean ignoreSavedState) {
+        mValidSubscription = true;
+
+        PersistableBundle b =
+                CarrierConfigManager.getCarrierConfigSubset(
+                        mContext,
+                        subId,
+                        KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY,
+                        KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT,
+                        KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL);
+        if (b.isEmpty()) return false;
+
+        boolean supportsTerminalBased = false;
+        int[] services = b.getIntArray(KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY);
+        if (services != null) {
+            for (int service : services) {
+                if (service == SUPPLEMENTARY_SERVICE_CW) {
+                    supportsTerminalBased = true;
+                }
+            }
+        }
+        int syncPreference = b.getInt(KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT,
+                CALL_WAITING_SYNC_FIRST_CHANGE);
+        boolean activated = b.getBoolean(KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL);
+        int defaultState = supportsTerminalBased
+                ? (activated ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED)
+                : TERMINAL_BASED_NOT_SUPPORTED;
+        int savedState = getSavedState(subId);
+
+        if (DBG) {
+            logd("updateCarrierConfig phoneId=" + mPhone.getPhoneId()
+                    + ", subId=" + subId + ", support=" + supportsTerminalBased
+                    + ", sync=" + syncPreference + ", default=" + defaultState
+                    + ", savedState=" + savedState);
+        }
+
+        int desiredState = savedState;
+
+        if (ignoreSavedState) {
+            desiredState = defaultState;
+        } else if ((mLastSubId != subId)
+                && (syncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP
+                        || syncPreference == CALL_WAITING_SYNC_FIRST_CHANGE)) {
+            desiredState = defaultState;
+        } else {
+            if (defaultState == TERMINAL_BASED_NOT_SUPPORTED) {
+                desiredState = TERMINAL_BASED_NOT_SUPPORTED;
+            } else if (savedState == TERMINAL_BASED_NOT_SUPPORTED) {
+                desiredState = defaultState;
+            }
+        }
+
+        updateState(desiredState, syncPreference, ignoreSavedState);
+        return true;
+    }
+
+    private void updateState(int state) {
+        updateState(state, mSyncPreference, false);
+    }
+
+    private void updateState(int state, int syncPreference, boolean ignoreSavedState) {
+        int subId = mPhone.getSubId();
+
+        if (mLastSubId == subId
+                && mCallWaitingState == state
+                && mSyncPreference == syncPreference
+                && (!ignoreSavedState)) {
+            return;
+        }
+
+        int phoneId = mPhone.getPhoneId();
+
+        logi("updateState phoneId=" + phoneId
+                + ", subId=" + subId + ", state=" + state
+                + ", sync=" + syncPreference + ", ignoreSavedState=" + ignoreSavedState);
+
+        SharedPreferences sp =
+                mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
+
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putInt(KEY_SUB_ID + phoneId, subId);
+        editor.putInt(KEY_STATE + subId, state);
+        editor.putInt(KEY_CS_SYNC + phoneId, syncPreference);
+        editor.apply();
+
+        mCallWaitingState = state;
+        mLastSubId = subId;
+        mSyncPreference = syncPreference;
+        if (mLastSubId != subId) {
+            mCsEnabled = false;
+        }
+
+        mPhone.setTerminalBasedCallWaitingStatus(mCallWaitingState);
+    }
+
+    private int getSavedState(int subId) {
+        SharedPreferences sp =
+                mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE);
+        int state = sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED);
+
+        logi("getSavedState subId=" + subId + ", state=" + state);
+
+        return state;
+    }
+
+    private void updateSyncState(boolean enabled) {
+        int phoneId = mPhone.getPhoneId();
+
+        logi("updateSyncState phoneId=" + phoneId + ", enabled=" + enabled);
+
+        mCsEnabled = enabled;
+    }
+
+    /**
+     * @return whether the service is enabled in the CS network
+     */
+    @VisibleForTesting
+    public boolean getSyncState() {
+        return mCsEnabled;
+    }
+
+    private boolean isCircuitSwitchedNetworkAvailable() {
+        logi("isCircuitSwitchedNetworkAvailable="
+                + (mSST.getServiceState().getState() == ServiceState.STATE_IN_SERVICE));
+        return mSST.getServiceState().getState() == ServiceState.STATE_IN_SERVICE;
+    }
+
+    private boolean isImsRegistered() {
+        logi("isImsRegistered " + mImsRegistered);
+        return mImsRegistered;
+    }
+
+    /**
+     * Sets the registration state of IMS service.
+     */
+    public synchronized void setImsRegistrationState(boolean registered) {
+        logi("setImsRegistrationState prev=" + mImsRegistered
+                + ", new=" + registered);
+        mImsRegistered = registered;
+    }
+
+    private void registerForNetworkAttached() {
+        logi("registerForNetworkAttached");
+        if (mRegisteredForNetworkAttach) return;
+
+        mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
+        mRegisteredForNetworkAttach = true;
+    }
+
+    private void unregisterForNetworkAttached() {
+        logi("unregisterForNetworkAttached");
+        if (!mRegisteredForNetworkAttach) return;
+
+        mSST.unregisterForNetworkAttached(this);
+        removeMessages(EVENT_REGISTERED_TO_NETWORK);
+        mRegisteredForNetworkAttach = false;
+    }
+
+    /**
+     * Sets whether the device supports the terminal-based call waiting.
+     * Only for test
+     */
+    @VisibleForTesting
+    public synchronized void setTerminalBasedCallWaitingSupported(boolean supported) {
+        if (mSupportedByImsService == supported) return;
+
+        logi("setTerminalBasedCallWaitingSupported " + supported);
+
+        mSupportedByImsService = supported;
+
+        if (supported) {
+            initialize();
+            onCarrierConfigurationChanged(mPhone.getPhoneId());
+        } else {
+            CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class);
+            if (ccm != null && mCarrierConfigChangeListener != null) {
+                ccm.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
+            }
+            updateState(TERMINAL_BASED_NOT_SUPPORTED);
+        }
+    }
+
+    /**
+     * Notifies that the UE has attached to the network
+     * Only for test
+     */
+    @VisibleForTesting
+    public void notifyRegisteredToNetwork() {
+        sendEmptyMessage(EVENT_REGISTERED_TO_NETWORK);
+    }
+
+    private boolean isSyncImsOnly() {
+        return (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY && mImsRegistered);
+    }
+
+    /**
+     * Dump this instance into a readable format for dumpsys usage.
+     */
+    public void dump(PrintWriter printWriter) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        pw.increaseIndent();
+        pw.println("CallWaitingController:");
+        pw.println(" mSupportedByImsService=" + mSupportedByImsService);
+        pw.println(" mValidSubscription=" + mValidSubscription);
+        pw.println(" mCallWaitingState=" + mCallWaitingState);
+        pw.println(" mSyncPreference=" + mSyncPreference);
+        pw.println(" mLastSubId=" + mLastSubId);
+        pw.println(" mCsEnabled=" + mCsEnabled);
+        pw.println(" mRegisteredForNetworkAttach=" + mRegisteredForNetworkAttach);
+        pw.println(" mImsRegistered=" + mImsRegistered);
+        pw.decreaseIndent();
+    }
+
+    private void loge(String msg) {
+        Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
+    }
+
+    private void logi(String msg) {
+        Rlog.i(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
+    }
+
+    private void logd(String msg) {
+        Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/CarrierActionAgent.java b/src/java/com/android/internal/telephony/CarrierActionAgent.java
index 42a4f23..6d74c18 100644
--- a/src/java/com/android/internal/telephony/CarrierActionAgent.java
+++ b/src/java/com/android/internal/telephony/CarrierActionAgent.java
@@ -27,6 +27,7 @@
 import android.os.RegistrantList;
 import android.provider.Settings;
 import android.provider.Telephony;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.LocalLog;
 import android.util.Log;
@@ -93,7 +94,11 @@
                     // ignore rebroadcast since carrier apps are direct boot aware.
                     return;
                 }
-                sendMessage(obtainMessage(EVENT_SIM_STATE_CHANGED, iccState));
+                final int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY,
+                        SubscriptionManager.INVALID_PHONE_INDEX);
+                if (mPhone.getPhoneId() == phoneId) {
+                    sendMessage(obtainMessage(EVENT_SIM_STATE_CHANGED, iccState));
+                }
             }
         }
     };
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java
index 3e2baa5..863db93 100644
--- a/src/java/com/android/internal/telephony/CarrierInfoManager.java
+++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -287,16 +287,17 @@
             return;
         }
         mLastAccessResetCarrierKey = now;
-        int[] subIds = context.getSystemService(SubscriptionManager.class)
-                .getSubscriptionIds(mPhoneId);
-        if (subIds == null || subIds.length < 1) {
+
+        int subId = SubscriptionManager.getSubscriptionId(mPhoneId);
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             Log.e(LOG_TAG, "Could not reset carrier keys, subscription for mPhoneId=" + mPhoneId);
             return;
         }
+
         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
-                .createForSubscriptionId(subIds[0]);
+                .createForSubscriptionId(subId);
         int carrierId = telephonyManager.getSimCarrierId();
-        deleteCarrierInfoForImsiEncryption(context, subIds[0], carrierId);
+        deleteCarrierInfoForImsiEncryption(context, subId, carrierId);
         Intent resetIntent = new Intent(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
         SubscriptionManager.putPhoneIdAndSubIdExtra(resetIntent, mPhoneId);
         context.sendBroadcastAsUser(resetIntent, UserHandle.ALL);
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
index edf3d5f..beb6b26 100644
--- a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.telephony;
 
-import static android.telephony.CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL;
-
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import android.app.AlarmManager;
@@ -32,7 +30,6 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.PersistableBundle;
-import android.provider.Telephony;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.SubscriptionManager;
@@ -122,13 +119,22 @@
         mPhone = phone;
         mContext = phone.getContext();
         IntentFilter filter = new IntentFilter();
-        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX);
         filter.addAction(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
         mContext.registerReceiver(mBroadcastReceiver, filter, null, phone);
         mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
         mTelephonyManager = mContext.getSystemService(TelephonyManager.class)
                 .createForSubscriptionId(mPhone.getSubId());
+        CarrierConfigManager carrierConfigManager = mContext.getSystemService(
+                CarrierConfigManager.class);
+        // Callback which directly handle config change should be executed on handler thread
+        carrierConfigManager.registerCarrierConfigChangeListener(this::post,
+                (slotIndex, subId, carrierId, specificCarrierId) -> {
+                    if (slotIndex == mPhone.getPhoneId()) {
+                        Log.d(LOG_TAG, "Carrier Config changed: slotIndex=" + slotIndex);
+                        handleAlarmOrConfigChange();
+                    }
+                });
     }
 
     private final BroadcastReceiver mDownloadReceiver = new BroadcastReceiver() {
@@ -161,12 +167,6 @@
                     Log.d(LOG_TAG, "Handling reset intent: " + action);
                     sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
                 }
-            } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
-                if (phoneId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
-                        SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
-                    Log.d(LOG_TAG, "Carrier Config changed: " + action);
-                    sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
-                }
             }
         }
     };
@@ -389,14 +389,23 @@
             return false;
         }
         int subId = mPhone.getSubId();
-        PersistableBundle b = carrierConfigManager.getConfigForSubId(subId);
-        if (b == null) {
+        PersistableBundle b = null;
+        try {
+            b = carrierConfigManager.getConfigForSubId(subId,
+                    CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT,
+                    CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING,
+                    CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL);
+        } catch (RuntimeException e) {
+            Log.e(LOG_TAG, "CarrierConfigLoader is not available.");
+        }
+        if (b == null || b.isEmpty()) {
             return false;
         }
+
         mKeyAvailability = b.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT);
         mURL = b.getString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING);
         mAllowedOverMeteredNetwork = b.getBoolean(
-                KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL);
+                CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL);
         if (mKeyAvailability == 0 || TextUtils.isEmpty(mURL)) {
             Log.d(LOG_TAG,
                     "Carrier not enabled or invalid values. mKeyAvailability=" + mKeyAvailability
diff --git a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
index 1e184bb..ab7ebc4 100644
--- a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
@@ -16,11 +16,8 @@
 
 package com.android.internal.telephony;
 
-import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
-import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY;
 import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
 import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
 import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
@@ -31,8 +28,6 @@
 import static android.telephony.TelephonyManager.SIM_STATE_READY;
 import static android.telephony.TelephonyManager.SIM_STATE_UNKNOWN;
 
-import static com.android.internal.telephony.SubscriptionInfoUpdater.simStateString;
-
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -54,8 +49,6 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.Process;
-import android.os.Registrant;
-import android.os.RegistrantList;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -69,7 +62,6 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.IntArray;
 import android.util.LocalLog;
 import android.util.Pair;
 
@@ -86,6 +78,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -111,6 +104,7 @@
     private static final int PACKAGE_NOT_PRIVILEGED = 0;
     private static final int PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG = 1;
     private static final int PACKAGE_PRIVILEGED_FROM_SIM = 2;
+    private static final int PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE = 3;
 
     // TODO(b/232273884): Turn feature on when find solution to handle the inter-carriers switching
     /**
@@ -133,25 +127,6 @@
                     | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS;
 
     /**
-     * Action to register a Registrant with this Tracker.
-     * obj: Registrant that will be notified of Carrier Privileged UID changes.
-     */
-    private static final int ACTION_REGISTER_LISTENER = 1;
-
-    /**
-     * Action to unregister a Registrant with this Tracker.
-     * obj: Handler used by the Registrant that will be removed.
-     */
-    private static final int ACTION_UNREGISTER_LISTENER = 2;
-
-    /**
-     * Action for tracking when Carrier Configs are updated.
-     * arg1: Subscription Id for the Carrier Configs update being broadcast
-     * arg2: Slot Index for the Carrier Configs update being broadcast
-     */
-    private static final int ACTION_CARRIER_CONFIG_CERTS_UPDATED = 3;
-
-    /**
      * Action for tracking when the Phone's SIM state changes.
      * arg1: slotId that this Action applies to
      * arg2: simState reported by this Broadcast
@@ -192,6 +167,14 @@
      */
     private static final int ACTION_UICC_ACCESS_RULES_LOADED = 10;
 
+    /**
+     * Action to set the test override rule through {@link
+     * TelephonyManager#setCarrierServicePackageOverride}.
+     *
+     * <p>obj: String of the carrierServicePackage from method setCarrierServicePackageOverride.
+     */
+    private static final int ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE = 11;
+
     private final Context mContext;
     private final Phone mPhone;
     private final PackageManager mPackageManager;
@@ -201,7 +184,6 @@
     private final TelephonyRegistryManager mTelephonyRegistryManager;
 
     @NonNull private final LocalLog mLocalLog = new LocalLog(64);
-    @NonNull private final RegistrantList mRegistrantList = new RegistrantList();
     // Stores rules for Carrier Config-loaded rules
     @NonNull private final List<UiccAccessRule> mCarrierConfigRules = new ArrayList<>();
     // Stores rules for SIM-loaded rules.
@@ -211,6 +193,7 @@
     // - Empty list indicates test override to simulate no rules (CC and UICC rules are ignored)
     // - Non-empty list indicates test override with specific rules (CC and UICC rules are ignored)
     @Nullable private List<UiccAccessRule> mTestOverrideRules = null;
+    @Nullable private String mTestOverrideCarrierServicePackage = null;
     // Map of PackageName -> Certificate hashes for that Package
     @NonNull private final Map<String, Set<String>> mInstalledPackageCerts = new ArrayMap<>();
     // Map of PackageName -> UIDs for that Package
@@ -300,19 +283,6 @@
                     if (action == null) return;
 
                     switch (action) {
-                        case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED: {
-                            Bundle extras = intent.getExtras();
-                            int slotIndex = extras.getInt(EXTRA_SLOT_INDEX);
-                            int subId =
-                                    extras.getInt(
-                                            EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID);
-                            sendMessage(
-                                    obtainMessage(
-                                            ACTION_CARRIER_CONFIG_CERTS_UPDATED,
-                                            subId,
-                                            slotIndex));
-                            break;
-                        }
                         case TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED: // fall through
                         case TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED: {
                             Bundle extras = intent.getExtras();
@@ -375,13 +345,16 @@
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         mCarrierConfigManager =
                 (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        // Callback is executed in handler thread and directly handles carrier config update
+        mCarrierConfigManager.registerCarrierConfigChangeListener(this::post,
+                (slotIndex, subId, carrierId, specificCarrierId) -> handleCarrierConfigUpdated(
+                        subId, slotIndex));
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
         mTelephonyRegistryManager =
                 (TelephonyRegistryManager)
                         mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
 
         IntentFilter certFilter = new IntentFilter();
-        certFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         certFilter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
         certFilter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
         mContext.registerReceiver(mIntentReceiver, certFilter);
@@ -403,20 +376,6 @@
     @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
-            case ACTION_REGISTER_LISTENER: {
-                handleRegisterListener((Registrant) msg.obj);
-                break;
-            }
-            case ACTION_UNREGISTER_LISTENER: {
-                handleUnregisterListener((Handler) msg.obj);
-                break;
-            }
-            case ACTION_CARRIER_CONFIG_CERTS_UPDATED: {
-                int subId = msg.arg1;
-                int slotIndex = msg.arg2;
-                handleCarrierConfigUpdated(subId, slotIndex);
-                break;
-            }
             case ACTION_SIM_STATE_UPDATED: {
                 handleSimStateChanged(msg.arg1, msg.arg2);
                 break;
@@ -448,6 +407,11 @@
                 handleUiccAccessRulesLoaded();
                 break;
             }
+            case ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE: {
+                String carrierServicePackage = (String) msg.obj;
+                handleSetTestOverrideCarrierServicePackage(carrierServicePackage);
+                break;
+            }
             default: {
                 Rlog.e(TAG, "Received unknown msg type: " + msg.what);
                 break;
@@ -455,23 +419,6 @@
         }
     }
 
-    private void handleRegisterListener(@NonNull Registrant registrant) {
-        mRegistrantList.add(registrant);
-        mPrivilegedPackageInfoLock.readLock().lock();
-        try {
-            // Old registrant callback still takes int[] as parameter, need conversion here
-            int[] uids = intSetToArray(mPrivilegedPackageInfo.mUids);
-            registrant.notifyResult(
-                    Arrays.copyOf(uids, uids.length));
-        } finally {
-            mPrivilegedPackageInfoLock.readLock().unlock();
-        }
-    }
-
-    private void handleUnregisterListener(@NonNull Handler handler) {
-        mRegistrantList.remove(handler);
-    }
-
     private void handleCarrierConfigUpdated(int subId, int slotIndex) {
         if (slotIndex != mPhone.getPhoneId()) return;
 
@@ -493,7 +440,10 @@
 
     @NonNull
     private List<UiccAccessRule> getCarrierConfigRules(int subId) {
-        PersistableBundle carrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
+        PersistableBundle carrierConfigs =
+                CarrierConfigManager.getCarrierConfigSubset(
+                        mContext, subId, KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
+        // CarrierConfigManager#isConfigForIdentifiedCarrier can handle null or empty bundle
         if (!mCarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfigs)) {
             return Collections.EMPTY_LIST;
         }
@@ -529,7 +479,7 @@
                         SystemClock.uptimeMillis() + CLEAR_UICC_RULES_DELAY_MILLIS;
                 sendMessageAtTime(obtainMessage(ACTION_CLEAR_UICC_RULES),
                         mClearUiccRulesUptimeMillis);
-                mLocalLog.log("SIM is gone, simState=" + simStateString(simState)
+                mLocalLog.log("SIM is gone, simState=" + TelephonyManager.simStateToString(simState)
                         + ". Delay " + TimeUnit.MILLISECONDS.toSeconds(
                         CLEAR_UICC_RULES_DELAY_MILLIS) + " seconds to clear UICC rules.");
             } else {
@@ -614,10 +564,10 @@
         List<Signature> signatures = UiccAccessRule.getSignatures(pkg);
         for (Signature signature : signatures) {
             byte[] sha1 = UiccAccessRule.getCertHash(signature, SHA_1);
-            certs.add(IccUtils.bytesToHexString(sha1).toUpperCase());
+            certs.add(IccUtils.bytesToHexString(sha1).toUpperCase(Locale.ROOT));
 
             byte[] sha256 = UiccAccessRule.getCertHash(signature, SHA_256);
-            certs.add(IccUtils.bytesToHexString(sha256).toUpperCase());
+            certs.add(IccUtils.bytesToHexString(sha256).toUpperCase(Locale.ROOT));
         }
 
         mInstalledPackageCerts.put(pkg.packageName, certs);
@@ -731,12 +681,6 @@
 
         mPrivilegedPackageInfoLock.readLock().lock();
         try {
-            // The obsoleted callback only care about UIDs
-            if (carrierPrivilegesUidsChanged) {
-                int[] uids = intSetToArray(mPrivilegedPackageInfo.mUids);
-                mRegistrantList.notifyResult(Arrays.copyOf(uids, uids.length));
-            }
-
             if (carrierPrivilegesPackageNamesChanged || carrierPrivilegesUidsChanged) {
                 mTelephonyRegistryManager.notifyCarrierPrivilegesChanged(
                         mPhone.getPhoneId(),
@@ -768,6 +712,7 @@
             final int priv = getPackagePrivilegedStatus(e.getKey(), e.getValue());
             switch (priv) {
                 case PACKAGE_PRIVILEGED_FROM_SIM:
+                case PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE: // fallthrough
                     carrierServiceEligiblePackages.add(e.getKey());
                     // fallthrough
                 case PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG:
@@ -809,7 +754,9 @@
                 }
                 for (UiccAccessRule rule : mCarrierConfigRules) {
                     if (rule.matches(cert, pkgName)) {
-                        return PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG;
+                        return pkgName.equals(mTestOverrideCarrierServicePackage)
+                                ? PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE
+                                : PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG;
                     }
                 }
             }
@@ -882,33 +829,6 @@
     }
 
     /**
-     * Registers the given Registrant with this tracker.
-     *
-     * <p>After being registered, the Registrant will be notified with the current Carrier
-     * Privileged UIDs for this Phone.
-     *
-     * @deprecated Use {@link TelephonyManager#addCarrierPrivilegesListener} instead, which also
-     *     provides package names
-     *     <p>TODO(b/211658797) migrate callers, then delete all Registrant logic from CPT
-     */
-    @Deprecated
-    public void registerCarrierPrivilegesListener(@NonNull Handler h, int what,
-            @Nullable Object obj) {
-        sendMessage(obtainMessage(ACTION_REGISTER_LISTENER, new Registrant(h, what, obj)));
-    }
-
-    /**
-     * Unregisters the given listener with this tracker.
-     *
-     * @deprecated Use {@link TelephonyManager#removeCarrierPrivilegesListener} instead
-     *     <p>TODO(b/211658797) migrate callers, then delete all Registrant logic from CPT
-     */
-    @Deprecated
-    public void unregisterCarrierPrivilegesListener(@NonNull Handler handler) {
-        sendMessage(obtainMessage(ACTION_UNREGISTER_LISTENER, handler));
-    }
-
-    /**
      * Set test carrier privilege rules which will override the actual rules on both Carrier Config
      * and SIM.
      *
@@ -921,6 +841,30 @@
         sendMessage(obtainMessage(ACTION_SET_TEST_OVERRIDE_RULE, carrierPrivilegeRules));
     }
 
+    /**
+     * Override the carrier provisioning package, if it exists.
+     *
+     * <p>This API is to be used ONLY for testing, and requires the provided package to be carrier
+     * privileged. While this override is set, ONLY the specified package will be considered
+     * eligible to be bound as the carrier provisioning package, and any existing bindings will be
+     * terminated.
+     *
+     * @param carrierServicePackage the package to be used as the overridden carrier service
+     *     package, or {@code null} to reset override
+     * @see TelephonyManager#setCarrierServicePackageOverride
+     */
+    public void setTestOverrideCarrierServicePackage(@Nullable String carrierServicePackage) {
+        sendMessage(obtainMessage(
+                ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE, carrierServicePackage));
+    }
+
+    private void handleSetTestOverrideCarrierServicePackage(
+            @Nullable String carrierServicePackage) {
+        mTestOverrideCarrierServicePackage = carrierServicePackage;
+        refreshInstalledPackageCache();
+        maybeUpdatePrivilegedPackagesAndNotifyRegistrants();
+    }
+
     private void handleSetTestOverrideRules(@Nullable String carrierPrivilegeRules) {
         if (carrierPrivilegeRules == null) {
             mTestOverrideRules = null;
@@ -1089,6 +1033,11 @@
         String carrierServicePackageName = null;
         for (ResolveInfo resolveInfo : carrierServiceResolveInfos) {
             String packageName = getPackageName(resolveInfo);
+            if (mTestOverrideCarrierServicePackage != null
+                    && !mTestOverrideCarrierServicePackage.equals(packageName)) {
+                continue;
+            }
+
             if (simPrivilegedPackages.contains(packageName)) {
                 carrierServicePackageName = packageName;
                 break;
@@ -1098,11 +1047,4 @@
                 ? new Pair<>(null, Process.INVALID_UID)
                 : new Pair<>(carrierServicePackageName, getPackageUid(carrierServicePackageName));
     }
-
-    @NonNull
-    private static int[] intSetToArray(@NonNull Set<Integer> intSet) {
-        IntArray converter = new IntArray(intSet.size());
-        intSet.forEach(converter::add);
-        return converter.toArray();
-    }
 }
diff --git a/src/java/com/android/internal/telephony/CarrierResolver.java b/src/java/com/android/internal/telephony/CarrierResolver.java
index ec6a5a0..8a9b3e3 100644
--- a/src/java/com/android/internal/telephony/CarrierResolver.java
+++ b/src/java/com/android/internal/telephony/CarrierResolver.java
@@ -43,6 +43,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.metrics.CarrierIdMatchStats;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.uicc.IccRecords;
 import com.android.internal.telephony.uicc.UiccController;
 import com.android.internal.telephony.util.TelephonyUtils;
@@ -54,6 +55,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * CarrierResolver identifies the subscription carrier and returns a canonical carrier Id
@@ -191,14 +193,14 @@
     }
 
     /**
-     * This is triggered from SubscriptionInfoUpdater after sim state change.
+     * This is triggered from UiccController after sim state change.
      * The sequence of sim loading would be
-     *  1. ACTION_SUBINFO_CONTENT_CHANGE
+     *  1. OnSubscriptionsChangedListener
      *  2. ACTION_SIM_STATE_CHANGED/ACTION_SIM_CARD_STATE_CHANGED
      *  /ACTION_SIM_APPLICATION_STATE_CHANGED
      *  3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED
      *
-     *  For SIM refresh either reset or init refresh type, SubscriptionInfoUpdater will re-trigger
+     *  For SIM refresh either reset or init refresh type, UiccController will re-trigger
      *  carrier identification with sim loaded state. Framework today silently handle single file
      *  refresh type.
      *  TODO: check fileId from single file refresh, if the refresh file is IMSI, gid1 or other
@@ -547,7 +549,7 @@
         // subscriptioninfo db to make sure we have correct carrier id set.
         if (SubscriptionManager.isValidSubscriptionId(mPhone.getSubId()) && !isSimOverride) {
             // only persist carrier id to simInfo db when subId is valid.
-            SubscriptionController.getInstance().setCarrierId(mCarrierId, mPhone.getSubId());
+            SubscriptionManagerService.getInstance().setCarrierId(mPhone.getSubId(), mCarrierId);
         }
     }
 
@@ -751,7 +753,8 @@
         // Ideally we should do full string match. However due to SIM manufacture issues
         // gid from some SIM might has garbage tail.
         private boolean gidMatch(String gidFromSim, String gid) {
-            return (gidFromSim != null) && gidFromSim.toLowerCase().startsWith(gid.toLowerCase());
+            return (gidFromSim != null) && gidFromSim.toLowerCase(Locale.ROOT)
+                    .startsWith(gid.toLowerCase(Locale.ROOT));
         }
 
         private boolean carrierPrivilegeRulesMatch(List<String> certsFromSubscription,
diff --git a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
index dfa53b3..960d794 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -200,7 +201,13 @@
         }
     }
 
-    void updateForPhoneId(int phoneId, String simState) {
+    /**
+     * Update SIM state.
+     *
+     * @param phoneId The phone id.
+     * @param simState The legacy SIM state.
+     */
+    public void updateForPhoneId(int phoneId, @NonNull String simState) {
         logdWithLocalLog("update binding for phoneId: " + phoneId + " simState: " + simState);
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
             return;
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index 64dc7ec..6b44998 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -19,10 +19,8 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.HandlerExecutor;
@@ -102,8 +100,34 @@
         this.mSST = sst;
         mTelephonyManager = mPhone.getContext().getSystemService(
                 TelephonyManager.class).createForSubscriptionId(mPhone.getSubId());
-        phone.getContext().registerReceiver(mBroadcastReceiver, new IntentFilter(
-                CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
+        ccm.registerCarrierConfigChangeListener(
+                mPhone.getContext().getMainExecutor(),
+                (slotIndex, subId, carrierId, specificCarrierId) -> {
+                    if (slotIndex != mPhone.getPhoneId()) return;
+
+                    Rlog.d(LOG_TAG, "onCarrierConfigChanged: slotIndex=" + slotIndex
+                            + ", subId=" + subId + ", carrierId=" + carrierId);
+
+                    // Only get carrier configs used for EmergencyNetworkNotification
+                    // and PrefNetworkNotification
+                    PersistableBundle b =
+                            CarrierConfigManager.getCarrierConfigSubset(
+                                    mPhone.getContext(),
+                                    mPhone.getSubId(),
+                                    CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT,
+                                    CarrierConfigManager
+                                            .KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT);
+                    if (b.isEmpty()) return;
+
+                    for (Map.Entry<Integer, NotificationType> entry :
+                            mNotificationTypeMap.entrySet()) {
+                        NotificationType notificationType = entry.getValue();
+                        notificationType.setDelay(b);
+                    }
+                    handleConfigChanges();
+                });
+
         // Listen for subscriber changes
         SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener(
                 new OnSubscriptionsChangedListener(this.getLooper()) {
@@ -246,7 +270,7 @@
         TelephonyManager tm = ((TelephonyManager) context.getSystemService(
                 Context.TELEPHONY_SERVICE)).createForSubscriptionId(mPhone.getSubId());
 
-        boolean isCarrierConfigEnabled = isCarrierConfigEnableNr(context);
+        boolean isCarrierConfigEnabled = isCarrierConfigEnableNr();
         boolean isRadioAccessFamilySupported = checkSupportedBitmask(
                 tm.getSupportedRadioAccessFamily(), TelephonyManager.NETWORK_TYPE_BITMASK_NR);
         boolean isNrNetworkTypeAllowed = checkSupportedBitmask(
@@ -261,15 +285,13 @@
         return (isCarrierConfigEnabled && isRadioAccessFamilySupported && isNrNetworkTypeAllowed);
     }
 
-    private boolean isCarrierConfigEnableNr(Context context) {
-        CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
-                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (carrierConfigManager == null) {
-            Rlog.e(LOG_TAG, "isCarrierConfigEnableNr: CarrierConfigManager is null");
-            return false;
-        }
-        PersistableBundle config = carrierConfigManager.getConfigForSubId(mPhone.getSubId());
-        if (config == null) {
+    private boolean isCarrierConfigEnableNr() {
+        PersistableBundle config =
+                CarrierConfigManager.getCarrierConfigSubset(
+                        mPhone.getContext(),
+                        mPhone.getSubId(),
+                        CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
+        if (config.isEmpty()) {
             Rlog.e(LOG_TAG, "isCarrierConfigEnableNr: Cannot get config " + mPhone.getSubId());
             return false;
         }
@@ -348,21 +370,6 @@
         return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
     }
 
-    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
-                    context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-            PersistableBundle b = carrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
-            for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) {
-                NotificationType notificationType = entry.getValue();
-                notificationType.setDelay(b);
-            }
-            handleConfigChanges();
-        }
-    };
-
     /**
      * Post a notification to the NotificationManager for changing network type.
      */
@@ -376,6 +383,7 @@
         Notification.Builder builder = getNotificationBuilder(notificationType);
         // set some common attributes
         builder.setWhen(System.currentTimeMillis())
+                .setShowWhen(true)
                 .setAutoCancel(true)
                 .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
                 .setColor(context.getResources().getColor(
@@ -590,7 +598,7 @@
                     .setContentTitle(title)
                     .setStyle(new Notification.BigTextStyle().bigText(details))
                     .setContentText(details)
-                    .setFlag(Notification.FLAG_NO_CLEAR, true)
+                    .setOngoing(true)
                     .setChannelId(NotificationChannelController.CHANNEL_ID_WFC);
         }
     }
diff --git a/src/java/com/android/internal/telephony/CarrierSignalAgent.java b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
index 2914ff9..3250cef 100644
--- a/src/java/com/android/internal/telephony/CarrierSignalAgent.java
+++ b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
@@ -20,11 +20,8 @@
 
 import android.annotation.Nullable;
 import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.Network;
@@ -48,7 +45,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -102,51 +98,45 @@
     /**
      * This is a list of supported signals from CarrierSignalAgent
      */
-    private static final Set<String> VALID_CARRIER_SIGNAL_ACTIONS = new HashSet<>(Arrays.asList(
+    private static final Set<String> VALID_CARRIER_SIGNAL_ACTIONS = Set.of(
             TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE,
             TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED,
             TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
             TelephonyManager.ACTION_CARRIER_SIGNAL_RESET,
-            TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE));
+            TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE);
 
-    private static final Map<String, String> NEW_ACTION_TO_COMPAT_MAP =
-            new HashMap<String, String>() {{
-                put(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE,
-                        TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE);
-                put(TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED,
-                        TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED);
-                put(TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
-                        TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED);
-                put(TelephonyManager.ACTION_CARRIER_SIGNAL_RESET,
-                        TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET);
-                put(TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE,
-                        TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE);
-            }};
+    private static final Map<String, String> NEW_ACTION_TO_COMPAT_MAP = Map.of(
+            TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE,
+                    TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE,
+            TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED,
+                    TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED,
+            TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+                    TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+            TelephonyManager.ACTION_CARRIER_SIGNAL_RESET,
+                    TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET,
+            TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE,
+                    TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE);
 
     private static final Map<String, String> COMPAT_ACTION_TO_NEW_MAP = NEW_ACTION_TO_COMPAT_MAP
             .entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
 
     private final LocalLog mErrorLocalLog = new LocalLog(16);
 
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (DBG) log("CarrierSignalAgent receiver action: " + action);
-            if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
-                loadCarrierConfig();
-            }
-        }
-    };
-
     private ConnectivityManager.NetworkCallback mNetworkCallback;
 
     /** Constructor */
     public CarrierSignalAgent(Phone phone) {
         mPhone = phone;
+        CarrierConfigManager carrierConfigManager = mPhone.getContext().getSystemService(
+                CarrierConfigManager.class);
         loadCarrierConfig();
-        // reload configurations on CARRIER_CONFIG_CHANGED
-        mPhone.getContext().registerReceiver(mReceiver,
-                new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        carrierConfigManager.registerCarrierConfigChangeListener(
+                mPhone.getContext().getMainExecutor(),
+                (slotIndex, subId, carrierId, specificCarrierId) -> {
+                    if (slotIndex == mPhone.getPhoneId()) {
+                        loadCarrierConfig();
+                    }
+                });
         mPhone.getCarrierActionAgent().registerForCarrierAction(
                 CarrierActionAgent.CARRIER_ACTION_REPORT_DEFAULT_NETWORK_STATUS, this,
                 EVENT_REGISTER_DEFAULT_NETWORK_AVAIL, null, false);
@@ -208,45 +198,47 @@
      * load carrier config and cached the results into a hashMap action -> array list of components.
      */
     private void loadCarrierConfig() {
-        CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
-                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        PersistableBundle b = null;
-        if (configManager != null) {
-            b = configManager.getConfigForSubId(mPhone.getSubId());
+        PersistableBundle b =
+                CarrierConfigManager.getCarrierConfigSubset(
+                        mPhone.getContext(),
+                        mPhone.getSubId(),
+                        KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                        KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
+        if (b.isEmpty()) {
+            return;
         }
-        if (b != null) {
-            synchronized (mCachedWakeSignalConfigs) {
-                log("Loading carrier config: " + KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
-                Map<String, Set<ComponentName>> config = parseAndCache(
-                        b.getStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
-                // In some rare cases, up-to-date config could be fetched with delay and all signals
-                // have already been delivered the receivers from the default carrier config.
-                // To handle this raciness, we should notify those receivers (from old configs)
-                // and reset carrier actions. This should be done before cached Config got purged
-                // and written with the up-to-date value, Otherwise those receivers from the
-                // old config might lingers without properly clean-up.
-                if (!mCachedWakeSignalConfigs.isEmpty()
-                        && !config.equals(mCachedWakeSignalConfigs)) {
-                    if (VDBG) log("carrier config changed, reset receivers from old config");
-                    mPhone.getCarrierActionAgent().sendEmptyMessage(
-                            CarrierActionAgent.CARRIER_ACTION_RESET);
-                }
-                mCachedWakeSignalConfigs = config;
-            }
 
-            synchronized (mCachedNoWakeSignalConfigs) {
-                log("Loading carrier config: "
-                        + KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
-                Map<String, Set<ComponentName>> config = parseAndCache(
-                        b.getStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
-                if (!mCachedNoWakeSignalConfigs.isEmpty()
-                        && !config.equals(mCachedNoWakeSignalConfigs)) {
-                    if (VDBG) log("carrier config changed, reset receivers from old config");
-                    mPhone.getCarrierActionAgent().sendEmptyMessage(
-                            CarrierActionAgent.CARRIER_ACTION_RESET);
-                }
-                mCachedNoWakeSignalConfigs = config;
+        synchronized (mCachedWakeSignalConfigs) {
+            log("Loading carrier config: " + KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
+            Map<String, Set<ComponentName>> config = parseAndCache(
+                    b.getStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
+            // In some rare cases, up-to-date config could be fetched with delay and all signals
+            // have already been delivered the receivers from the default carrier config.
+            // To handle this raciness, we should notify those receivers (from old configs)
+            // and reset carrier actions. This should be done before cached Config got purged
+            // and written with the up-to-date value, Otherwise those receivers from the
+            // old config might lingers without properly clean-up.
+            if (!mCachedWakeSignalConfigs.isEmpty()
+                    && !config.equals(mCachedWakeSignalConfigs)) {
+                if (VDBG) log("carrier config changed, reset receivers from old config");
+                mPhone.getCarrierActionAgent().sendEmptyMessage(
+                        CarrierActionAgent.CARRIER_ACTION_RESET);
             }
+            mCachedWakeSignalConfigs = config;
+        }
+
+        synchronized (mCachedNoWakeSignalConfigs) {
+            log("Loading carrier config: "
+                    + KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
+            Map<String, Set<ComponentName>> config = parseAndCache(
+                    b.getStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
+            if (!mCachedNoWakeSignalConfigs.isEmpty()
+                    && !config.equals(mCachedNoWakeSignalConfigs)) {
+                if (VDBG) log("carrier config changed, reset receivers from old config");
+                mPhone.getCarrierActionAgent().sendEmptyMessage(
+                        CarrierActionAgent.CARRIER_ACTION_RESET);
+            }
+            mCachedNoWakeSignalConfigs = config;
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java b/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java
new file mode 100644
index 0000000..82d4409
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java
@@ -0,0 +1,534 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.annotation.NonNull;
+import android.os.AsyncResult;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Message;
+import android.telephony.CellBroadcastIdRange;
+import android.telephony.SmsCbMessage;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Consumer;
+
+/**
+ * This class is to track the state to set cell broadcast config
+ */
+
+public final class CellBroadcastConfigTracker extends StateMachine {
+    private static final boolean DBG = Build.IS_DEBUGGABLE;
+
+    private static final int EVENT_REQUEST = 1;
+    private static final int EVENT_CONFIGURATION_DONE = 2;
+    private static final int EVENT_ACTIVATION_DONE = 3;
+    private static final int EVENT_RADIO_OFF = 4;
+    private static final int EVENT_SUBSCRIPTION_CHANGED = 5;
+
+    private static final int SMS_CB_CODE_SCHEME_MIN = 0;
+    private static final int SMS_CB_CODE_SCHEME_MAX = 255;
+
+    // Cache of current cell broadcast id ranges of 3gpp
+    private List<CellBroadcastIdRange> mCbRanges3gpp = new CopyOnWriteArrayList<>();
+    // Cache of current cell broadcast id ranges of 3gpp2
+    private List<CellBroadcastIdRange> mCbRanges3gpp2 = new CopyOnWriteArrayList<>();
+    private Phone mPhone;
+    @VisibleForTesting
+    public int mSubId;
+    @VisibleForTesting
+    public final SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener =
+            new SubscriptionManager.OnSubscriptionsChangedListener() {
+                @Override
+                public void onSubscriptionsChanged() {
+                    sendMessage(EVENT_SUBSCRIPTION_CHANGED);
+                }
+            };
+
+    /**
+     * The class is to present the request to set cell broadcast id ranges
+     */
+    private static class Request {
+        private final List<CellBroadcastIdRange> mCbRangesRequest3gpp =
+                new CopyOnWriteArrayList<>();
+        private final List<CellBroadcastIdRange> mCbRangesRequest3gpp2 =
+                new CopyOnWriteArrayList<>();
+        Consumer<Integer> mCallback;
+
+        Request(@NonNull List<CellBroadcastIdRange> ranges, @NonNull Consumer<Integer> callback) {
+            ranges.forEach(r -> {
+                if (r.getType() == SmsCbMessage.MESSAGE_FORMAT_3GPP) {
+                    mCbRangesRequest3gpp.add(r);
+                } else {
+                    mCbRangesRequest3gpp2.add(r);
+                }
+            });
+            mCallback = callback;
+        }
+
+        List<CellBroadcastIdRange> get3gppRanges() {
+            return mCbRangesRequest3gpp;
+        }
+
+        List<CellBroadcastIdRange> get3gpp2Ranges() {
+            return mCbRangesRequest3gpp2;
+        }
+
+        Consumer<Integer> getCallback() {
+            return mCallback;
+        }
+
+        @Override
+        public String toString() {
+            return "Request[mCbRangesRequest3gpp = " + mCbRangesRequest3gpp + ", "
+                    + "mCbRangesRequest3gpp2 = " + mCbRangesRequest3gpp2 + ", "
+                    + "mCallback = " + mCallback + "]";
+        }
+    }
+
+    /**
+     * The default state.
+     */
+    private class DefaultState extends State {
+        @Override
+        public void enter() {
+            mPhone.registerForRadioOffOrNotAvailable(getHandler(), EVENT_RADIO_OFF, null);
+            mPhone.getContext().getSystemService(SubscriptionManager.class)
+                    .addOnSubscriptionsChangedListener(new HandlerExecutor(getHandler()),
+                            mSubChangedListener);
+        }
+
+        @Override
+        public void exit() {
+            mPhone.unregisterForRadioOffOrNotAvailable(getHandler());
+            mPhone.getContext().getSystemService(SubscriptionManager.class)
+                    .removeOnSubscriptionsChangedListener(mSubChangedListener);
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            boolean retVal = HANDLED;
+            if (DBG) {
+                logd("DefaultState message:" + msg.what);
+            }
+            switch (msg.what) {
+                case EVENT_RADIO_OFF:
+                    resetConfig();
+                    break;
+                case EVENT_SUBSCRIPTION_CHANGED:
+                    int subId = mPhone.getSubId();
+                    if (mSubId != subId) {
+                        log("SubId changed from " + mSubId + " to " + subId);
+                        mSubId = subId;
+                        resetConfig();
+                    }
+                    break;
+                default:
+                    log("unexpected message!");
+                    break;
+
+            }
+
+            return retVal;
+        }
+    }
+
+    private DefaultState mDefaultState = new DefaultState();
+
+    /*
+     * The idle state which does not have ongoing radio request.
+     */
+    private class IdleState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
+            boolean retVal = NOT_HANDLED;
+            if (DBG) {
+                logd("IdleState message:" + msg.what);
+            }
+            switch (msg.what) {
+                case EVENT_REQUEST:
+                    Request request = (Request) msg.obj;
+                    if (DBG) {
+                        logd("IdleState handle EVENT_REQUEST with request:" + request);
+                    }
+                    if (!mCbRanges3gpp.equals(request.get3gppRanges())) {
+                        // set gsm config if the config is changed
+                        setGsmConfig(request.get3gppRanges(), request);
+                        transitionTo(mGsmConfiguringState);
+                    } else if (!mCbRanges3gpp2.equals(request.get3gpp2Ranges())) {
+                        // set cdma config directly if no gsm config change but cdma config is
+                        // changed
+                        setCdmaConfig(request.get3gpp2Ranges(), request);
+                        transitionTo(mCdmaConfiguringState);
+                    } else {
+                        logd("Do nothing as the requested ranges are same as now");
+                        request.getCallback().accept(
+                                TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS);
+                    }
+                    retVal = HANDLED;
+                    break;
+                default:
+                    break;
+            }
+            return retVal;
+        }
+    }
+    private IdleState mIdleState = new IdleState();
+
+    /*
+     * The state waiting for the result to set gsm config.
+     */
+    private class GsmConfiguringState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
+            boolean retVal = NOT_HANDLED;
+            if (DBG) {
+                logd("GsmConfiguringState message:" + msg.what);
+            }
+            switch (msg.what) {
+                case EVENT_REQUEST:
+                    deferMessage(msg);
+                    retVal = HANDLED;
+                    break;
+                case EVENT_CONFIGURATION_DONE:
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    Request request = (Request) ar.userObj;
+                    if (DBG) {
+                        logd("GsmConfiguringState handle EVENT_CONFIGURATION_DONE with request:"
+                                + request);
+                    }
+                    if (ar.exception == null) {
+                        // set gsm activation and transit to gsm activating state
+                        setActivation(SmsCbMessage.MESSAGE_FORMAT_3GPP,
+                                !request.get3gppRanges().isEmpty(), request);
+                        transitionTo(mGsmActivatingState);
+                    } else {
+                        logd("Failed to set gsm config");
+                        request.getCallback().accept(
+                                TelephonyManager.CELL_BROADCAST_RESULT_FAIL_CONFIG);
+                        // transit to idle state on the failure case
+                        transitionTo(mIdleState);
+                    }
+                    retVal = HANDLED;
+                    break;
+                default:
+                    break;
+            }
+            return retVal;
+        }
+    }
+    private GsmConfiguringState mGsmConfiguringState = new GsmConfiguringState();
+
+    /*
+     * The state waiting for the result to set gsm activation.
+     */
+    private class GsmActivatingState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
+            boolean retVal = NOT_HANDLED;
+            if (DBG) {
+                logd("GsmActivatingState message:" + msg.what);
+            }
+            switch (msg.what) {
+                case EVENT_REQUEST:
+                    deferMessage(msg);
+                    retVal = HANDLED;
+                    break;
+                case EVENT_ACTIVATION_DONE:
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    Request request = (Request) ar.userObj;
+                    if (DBG) {
+                        logd("GsmActivatingState handle EVENT_ACTIVATION_DONE with request:"
+                                + request);
+                    }
+                    if (ar.exception == null) {
+                        mCbRanges3gpp = request.get3gppRanges();
+                        if (!mCbRanges3gpp2.equals(request.get3gpp2Ranges())) {
+                            // set cdma config and transit to cdma configuring state if the config
+                            // is changed.
+                            setCdmaConfig(request.get3gpp2Ranges(), request);
+                            transitionTo(mCdmaConfiguringState);
+                        } else {
+                            logd("Done as no need to update ranges for 3gpp2");
+                            request.getCallback().accept(
+                                    TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS);
+                            // transit to idle state if there is no cdma config change
+                            transitionTo(mIdleState);
+                        }
+                    } else {
+                        logd("Failed to set gsm activation");
+                        request.getCallback().accept(
+                                TelephonyManager.CELL_BROADCAST_RESULT_FAIL_ACTIVATION);
+                        // transit to idle state on the failure case
+                        transitionTo(mIdleState);
+                    }
+                    retVal = HANDLED;
+                    break;
+                default:
+                    break;
+            }
+            return retVal;
+        }
+    }
+    private GsmActivatingState mGsmActivatingState = new GsmActivatingState();
+
+    /*
+     * The state waiting for the result to set cdma config.
+     */
+    private class CdmaConfiguringState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
+            boolean retVal = NOT_HANDLED;
+            if (DBG) {
+                logd("CdmaConfiguringState message:" + msg.what);
+            }
+            switch (msg.what) {
+                case EVENT_REQUEST:
+                    deferMessage(msg);
+                    retVal = HANDLED;
+                    break;
+                case EVENT_CONFIGURATION_DONE:
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    Request request = (Request) ar.userObj;
+                    if (DBG) {
+                        logd("CdmaConfiguringState handle EVENT_ACTIVATION_DONE with request:"
+                                + request);
+                    }
+                    if (ar.exception == null) {
+                        // set cdma activation and transit to cdma activating state
+                        setActivation(SmsCbMessage.MESSAGE_FORMAT_3GPP2,
+                                !request.get3gpp2Ranges().isEmpty(), request);
+                        transitionTo(mCdmaActivatingState);
+                    } else {
+                        logd("Failed to set cdma config");
+                        request.getCallback().accept(
+                                TelephonyManager.CELL_BROADCAST_RESULT_FAIL_CONFIG);
+                        // transit to idle state on the failure case
+                        transitionTo(mIdleState);
+                    }
+                    retVal = HANDLED;
+                    break;
+                default:
+                    break;
+            }
+            return retVal;
+        }
+    }
+    private CdmaConfiguringState mCdmaConfiguringState = new CdmaConfiguringState();
+
+    /*
+     * The state waiting for the result to set cdma activation.
+     */
+    private class CdmaActivatingState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
+            boolean retVal = NOT_HANDLED;
+            if (DBG) {
+                logd("CdmaActivatingState message:" + msg.what);
+            }
+            switch (msg.what) {
+                case EVENT_REQUEST:
+                    deferMessage(msg);
+                    retVal = HANDLED;
+                    break;
+                case EVENT_ACTIVATION_DONE:
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    Request request = (Request) ar.userObj;
+                    if (DBG) {
+                        logd("CdmaActivatingState handle EVENT_ACTIVATION_DONE with request:"
+                                + request);
+                    }
+                    if (ar.exception == null) {
+                        mCbRanges3gpp2 = request.get3gpp2Ranges();
+                        request.getCallback().accept(
+                                    TelephonyManager.CELL_BROADCAST_RESULT_SUCCESS);
+                    } else {
+                        logd("Failed to set cdma activation");
+                        request.getCallback().accept(
+                                TelephonyManager.CELL_BROADCAST_RESULT_FAIL_ACTIVATION);
+                    }
+                    // transit to idle state anyway
+                    transitionTo(mIdleState);
+                    retVal = HANDLED;
+                    break;
+                default:
+                    break;
+            }
+            return retVal;
+        }
+    }
+    private CdmaActivatingState mCdmaActivatingState = new CdmaActivatingState();
+
+    private CellBroadcastConfigTracker(Phone phone) {
+        super("CellBroadcastConfigTracker-" + phone.getPhoneId());
+        init(phone);
+    }
+
+    private CellBroadcastConfigTracker(Phone phone, Handler handler) {
+        super("CellBroadcastConfigTracker-" + phone.getPhoneId(), handler);
+        init(phone);
+    }
+
+    private void init(Phone phone) {
+        logd("init");
+        mPhone = phone;
+        mSubId = mPhone.getSubId();
+
+        addState(mDefaultState);
+        addState(mIdleState, mDefaultState);
+        addState(mGsmConfiguringState, mDefaultState);
+        addState(mGsmActivatingState, mDefaultState);
+        addState(mCdmaConfiguringState, mDefaultState);
+        addState(mCdmaActivatingState, mDefaultState);
+        setInitialState(mIdleState);
+    }
+
+    /**
+     * create a CellBroadcastConfigTracker instance for the phone
+     */
+    public static CellBroadcastConfigTracker make(Phone phone, Handler handler,
+            boolean shouldStart) {
+        CellBroadcastConfigTracker tracker = handler == null
+                ? new CellBroadcastConfigTracker(phone)
+                : new CellBroadcastConfigTracker(phone, handler);
+        if (shouldStart) {
+            tracker.start();
+        }
+        return tracker;
+    }
+
+    /**
+     * Return current cell broadcast ranges.
+     */
+    @NonNull public List<CellBroadcastIdRange> getCellBroadcastIdRanges() {
+        List<CellBroadcastIdRange> ranges = new ArrayList<>();
+        ranges.addAll(mCbRanges3gpp);
+        ranges.addAll(mCbRanges3gpp2);
+        return ranges;
+    }
+
+    /**
+     * Set reception of cell broadcast messages with the list of the given ranges.
+     */
+    public void setCellBroadcastIdRanges(
+            @NonNull List<CellBroadcastIdRange> ranges, @NonNull Consumer<Integer> callback) {
+        if (DBG) {
+            logd("setCellBroadcastIdRanges with ranges:" + ranges);
+        }
+        ranges = mergeRangesAsNeeded(ranges);
+        sendMessage(EVENT_REQUEST, new Request(ranges, callback));
+    }
+
+    /**
+     * Merge the overlapped CellBroadcastIdRanges in the list as needed
+     * @param ranges the list of CellBroadcastIdRanges
+     * @return the list of CellBroadcastIdRanges without overlapping
+     *
+     * @throws IllegalArgumentException if there is conflict of the ranges. For instance,
+     * the channel is enabled in some range, but disable in others.
+     */
+    @VisibleForTesting
+    public static @NonNull List<CellBroadcastIdRange> mergeRangesAsNeeded(
+            @NonNull List<CellBroadcastIdRange> ranges) throws IllegalArgumentException {
+        ranges.sort((r1, r2) -> r1.getType() != r2.getType() ? r1.getType() - r2.getType()
+                : (r1.getStartId() != r2.getStartId() ? r1.getStartId() - r2.getStartId()
+                : r2.getEndId() - r1.getEndId()));
+        final List<CellBroadcastIdRange> newRanges = new ArrayList<>();
+        ranges.forEach(r -> {
+            if (newRanges.isEmpty() || newRanges.get(newRanges.size() - 1).getType() != r.getType()
+                    || newRanges.get(newRanges.size() - 1).getEndId() + 1 < r.getStartId()
+                    || (newRanges.get(newRanges.size() - 1).getEndId() + 1 == r.getStartId()
+                    && newRanges.get(newRanges.size() - 1).isEnabled() != r.isEnabled())) {
+                newRanges.add(new CellBroadcastIdRange(r.getStartId(), r.getEndId(),
+                        r.getType(), r.isEnabled()));
+            } else {
+                if (newRanges.get(newRanges.size() - 1).isEnabled() != r.isEnabled()) {
+                    throw new IllegalArgumentException("range conflict " + r);
+                }
+                if (r.getEndId() > newRanges.get(newRanges.size() - 1).getEndId()) {
+                    CellBroadcastIdRange range = newRanges.get(newRanges.size() - 1);
+                    newRanges.set(newRanges.size() - 1, new CellBroadcastIdRange(
+                            range.getStartId(), r.getEndId(), range.getType(), range.isEnabled()));
+                }
+            }
+        });
+        return newRanges;
+    }
+
+    private void resetConfig() {
+        mCbRanges3gpp.clear();
+        mCbRanges3gpp2.clear();
+    }
+
+    private void setGsmConfig(List<CellBroadcastIdRange> ranges, Request request) {
+        if (DBG) {
+            logd("setGsmConfig with " + ranges);
+        }
+
+        SmsBroadcastConfigInfo[] configs = new SmsBroadcastConfigInfo[ranges.size()];
+        for (int i = 0; i < configs.length; i++) {
+            CellBroadcastIdRange r = ranges.get(i);
+            configs[i] = new SmsBroadcastConfigInfo(r.getStartId(), r.getEndId(),
+                    SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, r.isEnabled());
+        }
+
+        Message response = obtainMessage(EVENT_CONFIGURATION_DONE, request);
+        mPhone.mCi.setGsmBroadcastConfig(configs, response);
+    }
+
+    private void setCdmaConfig(List<CellBroadcastIdRange> ranges, Request request) {
+        if (DBG) {
+            logd("setCdmaConfig with " + ranges);
+        }
+
+        CdmaSmsBroadcastConfigInfo[] configs =
+                new CdmaSmsBroadcastConfigInfo[ranges.size()];
+        for (int i = 0; i < configs.length; i++) {
+            CellBroadcastIdRange r = ranges.get(i);
+            configs[i] = new CdmaSmsBroadcastConfigInfo(
+                    r.getStartId(), r.getEndId(), 1, r.isEnabled());
+        }
+
+        Message response = obtainMessage(EVENT_CONFIGURATION_DONE, request);
+        mPhone.mCi.setCdmaBroadcastConfig(configs, response);
+    }
+
+    private void setActivation(int type, boolean activate, Request request) {
+        if (DBG) {
+            logd("setActivation(" + type + "." + activate + ')');
+        }
+
+        Message response = obtainMessage(EVENT_ACTIVATION_DONE, request);
+
+        if (type == SmsCbMessage.MESSAGE_FORMAT_3GPP) {
+            mPhone.mCi.setGsmBroadcastActivation(activate, response);
+        } else if (type == SmsCbMessage.MESSAGE_FORMAT_3GPP2) {
+            mPhone.mCi.setCdmaBroadcastActivation(activate, response);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/CellularNetworkService.java b/src/java/com/android/internal/telephony/CellularNetworkService.java
index 19a585d..9cbd7a6 100644
--- a/src/java/com/android/internal/telephony/CellularNetworkService.java
+++ b/src/java/com/android/internal/telephony/CellularNetworkService.java
@@ -18,9 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.hardware.radio.V1_0.RegState;
 import android.hardware.radio.V1_4.DataRegStateResult.VopsInfo.hidl_discriminator;
 import android.hardware.radio.V1_6.RegStateResult.AccessTechnologySpecificInfo;
+import android.hardware.radio.network.RegState;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
@@ -35,6 +35,7 @@
 import android.telephony.CellIdentityNr;
 import android.telephony.CellIdentityTdscdma;
 import android.telephony.CellIdentityWcdma;
+import android.telephony.DataSpecificRegistrationInfo;
 import android.telephony.LteVopsSupportInfo;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.NetworkService;
@@ -190,6 +191,8 @@
                     return NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
                 case RegState.REG_ROAMING:
                     return NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
+                case RegState.REG_EM:
+                    return NetworkRegistrationInfo.REGISTRATION_STATE_EMERGENCY;
                 default:
                     return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
             }
@@ -201,6 +204,7 @@
                 case RegState.NOT_REG_MT_SEARCHING_OP_EM:
                 case RegState.REG_DENIED_EM:
                 case RegState.UNKNOWN_EM:
+                case RegState.REG_EM:
                     return true;
                 case RegState.NOT_REG_MT_NOT_SEARCHING_OP:
                 case RegState.REG_HOME:
@@ -500,6 +504,14 @@
             final String rplmn = regResult.registeredPlmn;
             final int reasonForDenial = regResult.reasonForDenial;
 
+            if (regState == NetworkRegistrationInfo.REGISTRATION_STATE_DENIED
+                    && reasonForDenial
+                    == android.hardware.radio.network.RegistrationFailCause.NONE) {
+                AnomalyReporter.reportAnomaly(
+                        UUID.fromString("62ed270f-e139-418a-a427-8bcc1bca8f21"),
+                            "RIL Missing Reg Fail Reason", mPhone.getCarrierId());
+            }
+
             int networkType = ServiceState.rilRadioTechnologyToNetworkType(regResult.rat);
             if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
                 networkType = TelephonyManager.NETWORK_TYPE_LTE;
@@ -514,6 +526,8 @@
             boolean isNrAvailable = false;
             boolean isDcNrRestricted = false;
             VopsSupportInfo vopsInfo = null;
+            int lteAttachResultType = 0;
+            int lteAttachExtraInfo = 0;
 
             android.hardware.radio.network.AccessTechnologySpecificInfo info =
                     regResult.accessTechnologySpecificInfo;
@@ -532,6 +546,8 @@
                     vopsInfo = convertHalLteVopsSupportInfo(
                             info.getEutranInfo().lteVopsInfo.isVopsSupported,
                             info.getEutranInfo().lteVopsInfo.isEmcBearerSupported);
+                    lteAttachResultType = info.getEutranInfo().lteAttachResultType;
+                    lteAttachExtraInfo = info.getEutranInfo().extraInfo;
                     break;
                 case android.hardware.radio.network.AccessTechnologySpecificInfo.ngranNrVopsInfo:
                     vopsInfo = new NrVopsSupportInfo(info.getNgranNrVopsInfo().vopsSupported,
@@ -557,10 +573,26 @@
                     loge("Unknown domain passed to CellularNetworkService= " + domain);
                     // fall through
                 case NetworkRegistrationInfo.DOMAIN_PS:
-                    return new NetworkRegistrationInfo(domain, transportType, regState, networkType,
-                            reasonForDenial, isEmergencyOnly, availableServices, cellIdentity,
-                            rplmn, MAX_DATA_CALLS, isDcNrRestricted, isNrAvailable, isEndcAvailable,
-                            vopsInfo);
+                    return new NetworkRegistrationInfo.Builder()
+                        .setDomain(domain)
+                        .setTransportType(transportType)
+                        .setRegistrationState(regState)
+                        .setAccessNetworkTechnology(networkType)
+                        .setRejectCause(reasonForDenial)
+                        .setEmergencyOnly(isEmergencyOnly)
+                        .setAvailableServices(availableServices)
+                        .setCellIdentity(cellIdentity)
+                        .setRegisteredPlmn(rplmn)
+                        .setDataSpecificInfo(
+                                new DataSpecificRegistrationInfo.Builder(MAX_DATA_CALLS)
+                                     .setDcNrRestricted(isDcNrRestricted)
+                                     .setNrAvailable(isNrAvailable)
+                                     .setEnDcAvailable(isEndcAvailable)
+                                     .setVopsSupportInfo(vopsInfo)
+                                     .setLteAttachResultType(lteAttachResultType)
+                                     .setLteAttachExtraInfo(lteAttachExtraInfo)
+                                     .build())
+                        .build();
             }
         }
 
@@ -582,6 +614,14 @@
             networkType =
                     getNetworkTypeForCellIdentity(networkType, cellIdentity, mPhone.getCarrierId());
 
+            if (regState == NetworkRegistrationInfo.REGISTRATION_STATE_DENIED
+                    && reasonForDenial
+                    == android.hardware.radio.network.RegistrationFailCause.NONE) {
+                AnomalyReporter.reportAnomaly(
+                        UUID.fromString("62ed270f-e139-418a-a427-8bcc1bca8f21"),
+                            "RIL Missing Reg Fail Reason", mPhone.getCarrierId());
+            }
+
             // Conditional parameters for specific RANs
             boolean cssSupported = false;
             int roamingIndicator = 0;
diff --git a/src/java/com/android/internal/telephony/CommandException.java b/src/java/com/android/internal/telephony/CommandException.java
index 72bb6a3..e068c1c 100644
--- a/src/java/com/android/internal/telephony/CommandException.java
+++ b/src/java/com/android/internal/telephony/CommandException.java
@@ -341,7 +341,6 @@
                 return new CommandException(Error.RF_HARDWARE_ISSUE);
             case RILConstants.NO_RF_CALIBRATION_INFO:
                 return new CommandException(Error.NO_RF_CALIBRATION_INFO);
-
             default:
                 Rlog.e("GSM", "Unrecognized RIL errno " + ril_errno);
                 return new CommandException(Error.INVALID_RESPONSE);
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 27cedfe..971e051 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -25,22 +25,31 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.WorkSource;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.BarringInfo;
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.ClientRequestStats;
+import android.telephony.DomainSelectionService;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.NetworkScanRequest;
 import android.telephony.RadioAccessSpecifier;
 import android.telephony.SignalThresholdInfo;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.HalService;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
 import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.TrafficDescriptor;
 import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.emergency.EmergencyConstants;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.telephony.imsphone.ImsCallInfo;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
 import com.android.internal.telephony.uicc.IccCardStatus;
 import com.android.internal.telephony.uicc.SimPhonebookRecord;
@@ -124,6 +133,15 @@
     static final int CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM     = 39;
     static final int CDMA_SMS_FAIL_CAUSE_ENCODING_PROBLEM           = 96;
 
+    /** IMS voice capability */
+    int IMS_MMTEL_CAPABILITY_VOICE = 1 << 0;
+    /** IMS video capability */
+    int IMS_MMTEL_CAPABILITY_VIDEO = 1 << 1;
+    /** IMS SMS capability */
+    int IMS_MMTEL_CAPABILITY_SMS = 1 << 2;
+    /** IMS RCS capabilities */
+    int IMS_RCS_CAPABILITIES = 1 << 3;
+
     //***** Methods
 
     /**
@@ -1773,6 +1791,17 @@
     public void getDeviceIdentity(Message response);
 
     /**
+     * Request the device IMEI / IMEI type / IMEISV
+     * "response" is ImeiInfo object that contains
+     *  [0] ImeiType Indicates whether IMEI is of primary or secondary type
+     *  [1] IMEI if GSM subscription is available
+     *  [2] IMEISV if GSM subscription is available
+     *
+     * @param response Message
+     */
+    public void getImei(Message response);
+
+    /**
      * Request the device MDN / H_SID / H_NID / MIN.
      * "response" is const char **
      *   [0] is MDN if CDMA subscription is available
@@ -2086,10 +2115,15 @@
      *
      * Input parameters equivalent to TS 27.007 AT+CCHC command.
      *
+     * Per spec SGP.22 V3.0, ES10 commands needs to be sent over command port of MEP-A. In order
+     * to close proper logical channel, should pass information about whether the logical channel
+     * was opened for sending ES10 commands or not.
+     *
      * @param channel Channel id. Id of the channel to be closed.
+     * @param isEs10  Whether the logical channel is opened to perform ES10 operations.
      * @param response Callback message.
      */
-    public void iccCloseLogicalChannel(int channel, Message response);
+    public void iccCloseLogicalChannel(int channel, boolean isEs10, Message response);
 
     /**
      * Exchange APDUs with the SIM on a logical channel.
@@ -2105,11 +2139,12 @@
      * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
      *            is sent to the SIM.
      * @param data Data to be sent with the APDU.
+     * @param isEs10Command whether APDU command is an ES10 command or a regular APDU
      * @param response Callback message. response.obj.userObj will be
      *            an IccIoResult on success.
      */
-    public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction,
-            int p1, int p2, int p3, String data, Message response);
+    void iccTransmitApduLogicalChannel(int channel, int cla, int instruction,
+            int p1, int p2, int p3, String data, boolean isEs10Command, Message response);
 
     /**
      * Exchange APDUs with the SIM on a basic channel.
@@ -2186,11 +2221,21 @@
 
     /**
      * @return the radio hal version
+     * @deprecated use {@link #getHalVersion(int)}
      */
+    @Deprecated
     default HalVersion getHalVersion() {
         return HalVersion.UNKNOWN;
     }
 
+    /**
+     * @param service indicate the service id to query.
+     * @return the hal version of a specific service
+     */
+    default HalVersion getHalVersion(@HalService int service) {
+        return HalVersion.UNKNOWN;
+    }
+
    /**
      * Sets user selected subscription at Modem.
      *
@@ -2619,6 +2664,15 @@
     default void getBarringInfo(Message result) {};
 
     /**
+     * Returns the last barring information received.
+     *
+     * @return the last barring information.
+     */
+    default @Nullable BarringInfo getLastBarringInfo() {
+        return null;
+    };
+
+    /**
      * Allocates a pdu session id
      *
      * AsyncResult.result is the allocated pdu session id
@@ -2747,6 +2801,54 @@
      public void unregisterForSimPhonebookRecordsReceived(Handler h);
 
     /**
+     * Registers for notifications of connection setup failure.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForConnectionSetupFailure(Handler h, int what, Object obj) {}
+
+    /**
+     * Unregisters for notifications of connection setup failure.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForConnectionSetupFailure(Handler h) {}
+
+    /**
+     * Registers for notifications when ANBR is received form the network.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForNotifyAnbr(Handler h, int what, Object obj) {}
+
+    /**
+     * Unregisters for notifications when ANBR is received form the network.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForNotifyAnbr(Handler h) {}
+
+    /**
+     * Registers for IMS deregistration trigger from modem.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForTriggerImsDeregistration(Handler h, int what, Object obj) {}
+
+    /**
+     * Unregisters for IMS deregistration trigger from modem.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForTriggerImsDeregistration(Handler h) {}
+
+    /**
      * Set the UE's usage setting.
      *
      * @param result Callback message containing the success or failure status.
@@ -2761,4 +2863,417 @@
      * @param result Callback message containing the usage setting (or a failure status).
      */
     default void getUsageSetting(Message result) {}
+
+    /**
+     * Sets the emergency mode.
+     *
+     * @param emcMode Defines the radio emergency mode type.
+     * @param result Callback message containing the success or failure status.
+     */
+    default void setEmergencyMode(@EmergencyConstants.EmergencyMode int emcMode,
+            @Nullable Message result) {}
+
+    /**
+     * Triggers an emergency network scan.
+     *
+     * @param accessNetwork Contains the list of access network types to be prioritized
+     *        during emergency scan. The 1st entry has the highest priority.
+     * @param scanType Indicates the type of scans to be performed i.e. limited scan,
+     *        full service scan or both.
+     * @param result Callback message containing the success or failure status.
+     */
+    default void triggerEmergencyNetworkScan(
+            @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetwork,
+            @DomainSelectionService.EmergencyScanType int scanType, @Nullable Message result) {}
+
+    /**
+     * Cancels ongoing emergency network scan.
+     *
+     * @param resetScan Indicates how the next {@link #triggerEmergencyNetworkScan} should work.
+     *        If {@code true}, then the modem shall start the new scan from the beginning,
+     *        otherwise the modem shall resume from the last search.
+     * @param result Callback message containing the success or failure status.
+     */
+    default void cancelEmergencyNetworkScan(boolean resetScan, @Nullable Message result) {}
+
+    /**
+     * Exits ongoing emergency mode.
+     *
+     * @param result Callback message containing the success or failure status.
+     */
+    default void exitEmergencyMode(@Nullable Message result) {}
+
+    /**
+     * Registers for emergency network scan result.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForEmergencyNetworkScan(@NonNull Handler h,
+            int what, @Nullable Object obj) {}
+
+    /**
+     * Unregisters for emergency network scan result.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForEmergencyNetworkScan(@NonNull Handler h) {}
+
+    /**
+     * Provides a list of SRVCC call information to radio
+     *
+     * @param srvccConnections the list of connections.
+     */
+    default void setSrvccCallInfo(SrvccConnection[] srvccConnections, Message result) {}
+
+    /**
+     * Updates the IMS registration information to the radio.
+     *
+     * @param state The current IMS registration state.
+     * @param imsRadioTech The type of underlying radio access network used.
+     * @param suggestedAction The suggested action for the radio to perform.
+     * @param capabilities IMS capabilities such as VOICE, VIDEO and SMS.
+     */
+    default void updateImsRegistrationInfo(int state,
+            @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech,
+            @RegistrationManager.SuggestedAction int suggestedAction,
+            int capabilities, Message result) {}
+
+    /**
+     * Notifies the NAS and RRC layers of the radio the type of upcoming IMS traffic.
+     *
+     * @param token A nonce to identify the request.
+     * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc.
+     * @param accessNetworkType The type of underlying radio access network used.
+     * @param trafficDirection Indicates whether traffic is originated by mobile originated or
+     *        mobile terminated use case eg. MO/MT call/SMS etc.
+     */
+    default void startImsTraffic(int token,
+            @MmTelFeature.ImsTrafficType int trafficType,
+            @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+            @MmTelFeature.ImsTrafficDirection int trafficDirection,
+            Message result) {}
+
+    /**
+     * Notifies IMS traffic has been stopped.
+     *
+     * @param token The token assigned by startImsTraffic.
+     */
+    default void stopImsTraffic(int token, Message result) {}
+
+    /**
+     * Triggers the UE initiated EPS fallback procedure.
+     *
+     * @param reason Specifies the reason for EPS fallback.
+     */
+    default void triggerEpsFallback(int reason, Message result) {}
+
+    /**
+     * Triggers radio to send ANBRQ message to the network.
+     *
+     * @param mediaType Media type is used to identify media stream such as audio or video.
+     * @param direction Direction of this packet stream (e.g. uplink or downlink).
+     * @param bitsPerSecond The bit rate requested by the opponent UE.
+     * @param result Callback message to receive the result.
+     */
+    default void sendAnbrQuery(int mediaType, int direction, int bitsPerSecond, Message result) {}
+
+    /**
+     * Set the UE's ability to accept/reject null ciphered and/or null integrity-protected
+     * connections.
+     *
+     * @param enabled true to allow null ciphered and/or null integrity-protected connections,
+     * false to disallow.
+     * @param result Callback message containing the success or failure status.
+     */
+    default void setNullCipherAndIntegrityEnabled(boolean enabled, Message result) {}
+
+    /**
+     * Check whether null ciphering and/or null integrity-protected connections are allowed.
+     *
+     * @param result Callback message containing the success or failure status.
+     */
+    default void isNullCipherAndIntegrityEnabled(Message result) {}
+
+    /**
+     * Notifies the IMS call status to the modem.
+     *
+     * @param imsCallInfo The list of {@link ImsCallInfo}.
+     * @param result A callback to receive the response.
+     */
+    default void updateImsCallStatus(@NonNull List<ImsCallInfo> imsCallInfo, Message result) {}
+
+    /**
+     * Enables or disables N1 mode (access to 5G core network) in accordance with
+     * 3GPP TS 24.501 4.9.
+     * @param enable {@code true} to enable N1 mode, {@code false} to disable N1 mode.
+     * @param result Callback message to receive the result.
+     */
+    default void setN1ModeEnabled(boolean enable, Message result) {}
+
+    /**
+     * Check whether N1 mode (access to 5G core network) is enabled or not.
+     * @param result Callback message to receive the result.
+     */
+    default void isN1ModeEnabled(Message result) {}
+
+    /**
+     * Get feature capabilities supported by satellite.
+     *
+     * @param result Message that will be sent back to the requester
+     */
+    default void getSatelliteCapabilities(Message result) {}
+
+    /**
+     * Turn satellite modem on/off.
+     *
+     * @param result Message that will be sent back to the requester
+     * @param on {@code true} for turning on.
+     *           {@code false} for turning off.
+     */
+    default void setSatellitePower(Message result, boolean on) {}
+
+    /**
+     * Get satellite modem state.
+     *
+     * @param result Message that will be sent back to the requester
+     */
+    default void getSatellitePowerState(Message result) {}
+
+    /**
+     * Get satellite provision state.
+     *
+     * @param result Message that will be sent back to the requester
+     */
+    default void getSatelliteProvisionState(Message result) {}
+
+    /**
+     * Check whether satellite modem is supported by the device.
+     *
+     * @param result Message that will be sent back to the requester
+     */
+    default void isSatelliteSupported(Message result) {}
+
+    /**
+     * Provision the subscription with a satellite provider. This is needed to register the
+     * subscription if the provider allows dynamic registration.
+     *
+     * @param result Message that will be sent back to the requester.
+     * @param imei IMEI of the SIM associated with the satellite modem.
+     * @param msisdn MSISDN of the SIM associated with the satellite modem.
+     * @param imsi IMSI of the SIM associated with the satellite modem.
+     * @param features List of features to be provisioned.
+     */
+    default void provisionSatelliteService(
+            Message result, String imei, String msisdn, String imsi, int[] features) {}
+
+    /**
+     * Add contacts that are allowed to be used for satellite communication. This is applicable for
+     * incoming messages as well.
+     *
+     * @param result Message that will be sent back to the requester.
+     * @param contacts List of allowed contacts to be added.
+     */
+    default void addAllowedSatelliteContacts(Message result, String[] contacts) {}
+
+    /**
+     * Remove contacts that are allowed to be used for satellite communication. This is applicable
+     * for incoming messages as well.
+     *
+     * @param result Message that will be sent back to the requester.
+     * @param contacts List of allowed contacts to be removed.
+     */
+    default void removeAllowedSatelliteContacts(Message result, String[] contacts) {}
+
+    /**
+     * Send text messages.
+     *
+     * @param result Message that will be sent back to the requester.
+     * @param messages List of messages in text format to be sent.
+     * @param destination The recipient of the message.
+     * @param latitude The current latitude of the device.
+     * @param longitude The current longitude of the device. The location (i.e., latitude and
+     *        longitude) of the device will be filled for emergency messages.
+     */
+    default void sendSatelliteMessages(Message result, String[] messages, String destination,
+            double latitude, double longitude) {}
+
+    /**
+     * Get pending messages.
+     *
+     * @param result Message that will be sent back to the requester.
+     */
+    default void getPendingSatelliteMessages(Message result) {}
+
+    /**
+     * Get current satellite registration mode.
+     *
+     * @param result Message that will be sent back to the requester.
+     */
+    default void getSatelliteMode(Message result) {}
+
+    /**
+     * Set the filter for what type of indication framework want to receive from modem.
+     *
+     * @param result Message that will be sent back to the requester.
+     * @param filterBitmask The filter bitmask identifying what type of indication Telephony
+     *                      framework wants to receive from modem.
+     */
+    default void setSatelliteIndicationFilter(Message result, int filterBitmask) {}
+
+    /**
+     * User started pointing to the satellite. Modem should continue to update the ponting input
+     * as user moves device.
+     *
+     * @param result Message that will be sent back to the requester.
+     */
+    default void startSendingSatellitePointingInfo(Message result) {}
+
+    /**
+     * Stop sending satellite pointing info to the framework.
+     *
+     * @param result Message that will be sent back to the requester.
+     */
+    default void stopSendingSatellitePointingInfo(Message result) {}
+
+    /**
+     * Get max number of characters per text message.
+     *
+     * @param result Message that will be sent back to the requester.
+     */
+    default void getMaxCharactersPerSatelliteTextMessage(Message result) {}
+
+    /**
+     * Get whether satellite communication is allowed for the current location.
+     *
+     * @param result Message that will be sent back to the requester.
+     */
+    default void isSatelliteCommunicationAllowedForCurrentLocation(Message result) {}
+
+    /**
+     * Get the time after which the satellite will be visible.
+     *
+     * @param result Message that will be sent back to the requester.
+     */
+    default void getTimeForNextSatelliteVisibility(Message result) {}
+
+    /**
+     * Registers for pending message count from satellite modem.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForPendingSatelliteMessageCount(@NonNull Handler h,
+            int what, @Nullable Object obj) {}
+
+    /**
+     * Unregisters for pending message count from satellite modem.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForPendingSatelliteMessageCount(@NonNull Handler h) {}
+
+    /**
+     * Registers for new messages from satellite modem.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForNewSatelliteMessages(@NonNull Handler h,
+            int what, @Nullable Object obj) {}
+
+    /**
+     * Unregisters for new messages from satellite modem.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForNewSatelliteMessages(@NonNull Handler h) {}
+
+    /**
+     * Registers for messages transfer complete from satellite modem.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForSatelliteMessagesTransferComplete(@NonNull Handler h,
+            int what, @Nullable Object obj) {}
+
+    /**
+     * Unregisters for messages transfer complete from satellite modem.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForSatelliteMessagesTransferComplete(@NonNull Handler h) {}
+
+    /**
+     * Registers for pointing info changed from satellite modem.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForSatellitePointingInfoChanged(@NonNull Handler h,
+            int what, @Nullable Object obj) {}
+
+    /**
+     * Unregisters for pointing info changed from satellite modem.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForSatellitePointingInfoChanged(@NonNull Handler h) {}
+
+    /**
+     * Registers for mode changed from satellite modem.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForSatelliteModeChanged(@NonNull Handler h,
+            int what, @Nullable Object obj) {}
+
+    /**
+     * Unregisters for mode changed from satellite modem.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForSatelliteModeChanged(@NonNull Handler h) {}
+
+    /**
+     * Registers for radio technology changed from satellite modem.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForSatelliteRadioTechnologyChanged(@NonNull Handler h,
+            int what, @Nullable Object obj) {}
+
+    /**
+     * Unregisters for radio technology changed from satellite modem.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForSatelliteRadioTechnologyChanged(@NonNull Handler h) {}
+
+    /**
+     * Registers for provision state changed from satellite modem.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerForSatelliteProvisionStateChanged(@NonNull Handler h,
+            int what, @Nullable Object obj) {}
+
+    /**
+     * Unregisters for provision state changed from satellite modem.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForSatelliteProvisionStateChanged(@NonNull Handler h) {}
 }
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index c60e5df..68fd6ab 100644
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -27,6 +27,8 @@
 import android.telephony.ServiceState.RilRadioTechnology;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.RtpHeaderExtension;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.MmTelFeature.ImsAudioHandler;
 import android.util.Log;
 
 import com.android.ims.internal.ConferenceParticipant;
@@ -139,6 +141,13 @@
          * @param extensionData The extension data.
          */
         public void onReceivedRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> extensionData);
+
+        /**
+         * Indicates that the audio handler for this connection is changed.
+         *
+         * @param imsAudioHandler {@link MmTelFeature#ImsAudioHandler}.
+         */
+        void onAudioModeIsVoipChanged(@ImsAudioHandler int imsAudioHandler);
     }
 
     /**
@@ -194,6 +203,8 @@
         public void onReceivedDtmfDigit(char digit) {}
         @Override
         public void onReceivedRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> extensionData) {}
+        @Override
+        public void onAudioModeIsVoipChanged(@ImsAudioHandler int imsAudioHandler) {}
     }
 
     public static final int AUDIO_QUALITY_STANDARD = 1;
@@ -328,6 +339,41 @@
     /* Instance Methods */
 
     /**
+     * PhoneFactory Dependencies for testing.
+     */
+    @VisibleForTesting
+    public interface PhoneFactoryProxy {
+        Phone getPhone(int index);
+        Phone getDefaultPhone();
+        Phone[] getPhones();
+    }
+
+    private PhoneFactoryProxy mPhoneFactoryProxy = new PhoneFactoryProxy() {
+        @Override
+        public Phone getPhone(int index) {
+            return PhoneFactory.getPhone(index);
+        }
+
+        @Override
+        public Phone getDefaultPhone() {
+            return PhoneFactory.getDefaultPhone();
+        }
+
+        @Override
+        public Phone[] getPhones() {
+            return PhoneFactory.getPhones();
+        }
+    };
+
+    /**
+     * Overrides PhoneFactory dependencies for testing.
+     */
+    @VisibleForTesting
+    public void setPhoneFactoryProxy(PhoneFactoryProxy proxy) {
+        mPhoneFactoryProxy = proxy;
+    }
+
+    /**
      * @return The telecom internal call ID associated with this connection.  Only to be used for
      * debugging purposes.
      */
@@ -590,14 +636,35 @@
      */
     public void setEmergencyCallInfo(CallTracker ct) {
         if (ct != null) {
-            Phone phone = ct.getPhone();
-            if (phone != null) {
-                EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
+            Phone currentPhone = ct.getPhone();
+            if (currentPhone != null) {
+                EmergencyNumberTracker tracker = currentPhone.getEmergencyNumberTracker();
                 if (tracker != null) {
                     EmergencyNumber num = tracker.getEmergencyNumber(mAddress);
+                    Phone[] allPhones = mPhoneFactoryProxy.getPhones();
                     if (num != null) {
                         mIsEmergencyCall = true;
                         mEmergencyNumberInfo = num;
+                    } else if (allPhones.length > 1) {
+                        // If there are multiple active SIMs, check all instances:
+                        boolean found = false;
+                        for (Phone phone : allPhones) {
+                            // If the current iteration was already checked, skip:
+                            if (phone.getPhoneId() == currentPhone.getPhoneId()){
+                                continue;
+                            }
+                            num = phone.getEmergencyNumberTracker()
+                                    .getEmergencyNumber(mAddress);
+                            if (num != null){
+                                found = true;
+                                mIsEmergencyCall = true;
+                                mEmergencyNumberInfo = num;
+                                break;
+                            }
+                        }
+                        if (!found){
+                            Rlog.e(TAG, "setEmergencyCallInfo: emergency number is null");
+                        }
                     } else {
                         Rlog.e(TAG, "setEmergencyCallInfo: emergency number is null");
                     }
@@ -1519,6 +1586,25 @@
     }
 
     /**
+     * Called to report audio mode changed for Voip.
+     * @param imsAudioHandler the received value to handle the audio for this IMS call.
+     */
+    public void onAudioModeIsVoipChanged(@ImsAudioHandler int imsAudioHandler) {
+        Rlog.i(TAG, "onAudioModeIsVoipChanged: conn imsAudioHandler " + imsAudioHandler);
+
+        boolean isVoip = imsAudioHandler == MmTelFeature.AUDIO_HANDLER_ANDROID;
+        if (isVoip == mAudioModeIsVoip) return;
+        mAudioModeIsVoip = isVoip;
+
+        Rlog.i(TAG, "onAudioModeIsVoipChanged: isVoip: " + isVoip
+                + "mAudioModeIsVoip:" + mAudioModeIsVoip);
+
+        for (Listener l : mListeners) {
+            l.onAudioModeIsVoipChanged(imsAudioHandler);
+        }
+    }
+
+    /**
      * Called to report RTP header extensions received from the network.
      * @param extensionData the received extension data.
      */
diff --git a/src/java/com/android/internal/telephony/DataIndication.java b/src/java/com/android/internal/telephony/DataIndication.java
index c16cbaa..205c4d8 100644
--- a/src/java/com/android/internal/telephony/DataIndication.java
+++ b/src/java/com/android/internal/telephony/DataIndication.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DATA_CALL_LIST_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_KEEPALIVE_STATUS;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PCO_DATA;
@@ -53,9 +55,9 @@
      */
     public void dataCallListChanged(int indicationType,
             android.hardware.radio.data.SetupDataCallResult[] dcList) {
-        mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_DATA, indicationType);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList);
+        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, dcList);
         ArrayList<DataCallResponse> response = RILUtils.convertHalDataCallResultList(dcList);
         mRil.mDataCallListChangedRegistrants.notifyRegistrants(
                 new AsyncResult(null, response, null));
@@ -68,11 +70,12 @@
      */
     public void keepaliveStatus(int indicationType,
             android.hardware.radio.data.KeepaliveStatus halStatus) {
-        mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_DATA, indicationType);
 
-        if (RIL.RILJ_LOGD) {
-            mRil.unsljLogRet(RIL_UNSOL_KEEPALIVE_STATUS, "handle=" + halStatus.sessionHandle
-                    + " code=" +  halStatus.code);
+        if (mRil.isLogOrTrace()) {
+            mRil.unsljLogRet(
+                    RIL_UNSOL_KEEPALIVE_STATUS,
+                    "handle=" + halStatus.sessionHandle + " code=" + halStatus.code);
         }
 
         KeepaliveStatus ks = new KeepaliveStatus(
@@ -86,11 +89,11 @@
      * @param pco New PcoData
      */
     public void pcoData(int indicationType, android.hardware.radio.data.PcoDataInfo pco) {
-        mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_DATA, indicationType);
 
         PcoData response = new PcoData(pco.cid, pco.bearerProto, pco.pcoId, pco.contents);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PCO_DATA, response);
+        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_PCO_DATA, response);
 
         mRil.mPcoDataRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
     }
@@ -103,10 +106,10 @@
      */
     public void unthrottleApn(int indicationType, android.hardware.radio.data.DataProfileInfo dpi)
             throws RemoteException {
-        mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_DATA, indicationType);
         DataProfile response = RILUtils.convertToDataProfile(dpi);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_UNTHROTTLE_APN, response);
+        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_UNTHROTTLE_APN, response);
 
         mRil.mApnUnthrottledRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
     }
@@ -119,8 +122,10 @@
      */
     public void slicingConfigChanged(int indicationType,
             android.hardware.radio.data.SlicingConfig slicingConfig) throws RemoteException {
-        mRil.processIndication(RIL.DATA_SERVICE, indicationType);
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SLICING_CONFIG_CHANGED, slicingConfig);
+        mRil.processIndication(HAL_SERVICE_DATA, indicationType);
+        if (mRil.isLogOrTrace()) {
+            mRil.unsljLogRet(RIL_UNSOL_SLICING_CONFIG_CHANGED, slicingConfig);
+        }
         NetworkSlicingConfig ret = RILUtils.convertHalSlicingConfig(slicingConfig);
         mRil.mSlicingConfigChangedRegistrants.notifyRegistrants(
                 new AsyncResult(null, ret, null));
diff --git a/src/java/com/android/internal/telephony/DataResponse.java b/src/java/com/android/internal/telephony/DataResponse.java
index 7cfe13b..bef1da7 100644
--- a/src/java/com/android/internal/telephony/DataResponse.java
+++ b/src/java/com/android/internal/telephony/DataResponse.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+
 import android.hardware.radio.RadioError;
 import android.hardware.radio.RadioResponseInfo;
 import android.hardware.radio.data.IRadioDataResponse;
@@ -51,7 +53,7 @@
      * @param id The pdu session id allocated
      */
     public void allocatePduSessionIdResponse(RadioResponseInfo responseInfo, int id) {
-        RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
         if (rr != null) {
             if (responseInfo.error == RadioError.NONE) {
                 RadioResponse.sendMessageResponse(rr.mResult, id);
@@ -64,14 +66,14 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void cancelHandoverResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void deactivateDataCallResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
@@ -80,7 +82,7 @@
      */
     public void getDataCallListResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.data.SetupDataCallResult[] dataCallResultList) {
-        RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
 
         if (rr != null) {
             ArrayList<DataCallResponse> response =
@@ -98,7 +100,7 @@
      */
     public void getSlicingConfigResponse(RadioResponseInfo responseInfo,
                 android.hardware.radio.data.SlicingConfig slicingConfig) {
-        RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
 
         if (rr != null) {
             NetworkSlicingConfig ret = RILUtils.convertHalSlicingConfig(slicingConfig);
@@ -113,35 +115,35 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void releasePduSessionIdResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setDataAllowedResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setDataProfileResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setDataThrottlingResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setInitialAttachApnResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
@@ -150,7 +152,7 @@
      */
     public void setupDataCallResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.data.SetupDataCallResult setupDataCallResult) {
-        RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
 
         if (rr != null) {
             DataCallResponse response = RILUtils.convertHalDataCallResult(setupDataCallResult);
@@ -165,7 +167,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void startHandoverResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_DATA, mRil, responseInfo);
     }
 
     /**
@@ -175,7 +177,7 @@
     public void startKeepaliveResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.data.KeepaliveStatus keepaliveStatus) {
 
-        RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
         if (rr == null) return;
 
         KeepaliveStatus ret = null;
@@ -214,7 +216,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void stopKeepaliveResponse(RadioResponseInfo responseInfo) {
-        RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_DATA, responseInfo);
         if (rr == null) return;
 
         try {
diff --git a/src/java/com/android/internal/telephony/DebugService.java b/src/java/com/android/internal/telephony/DebugService.java
index 0b759b5..3341577 100644
--- a/src/java/com/android/internal/telephony/DebugService.java
+++ b/src/java/com/android/internal/telephony/DebugService.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import android.os.Build;
+
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.telephony.Rlog;
 
@@ -50,8 +52,16 @@
                     TelephonyMetrics.getInstance().dump(fd, pw, args);
                     return;
                 case "--saveatoms":
-                    log("Saving atoms..");
-                    PhoneFactory.getMetricsCollector().getAtomsStorage().flushAtoms();
+                    if (Build.IS_DEBUGGABLE) {
+                        log("Saving atoms..");
+                        PhoneFactory.getMetricsCollector().flushAtomsStorage();
+                    }
+                    return;
+                case "--clearatoms":
+                    if (Build.IS_DEBUGGABLE) {
+                        log("Clearing atoms..");
+                        PhoneFactory.getMetricsCollector().clearAtomsStorage();
+                    }
                     return;
             }
         }
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index e4aff4c..e5a5c8f 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.telephony.Annotation;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SrvccState;
 import android.telephony.BarringInfo;
@@ -32,9 +33,13 @@
 import android.telephony.ServiceState;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager.DataEnabledReason;
+import android.telephony.TelephonyManager.EmergencyCallbackModeStopReason;
+import android.telephony.TelephonyManager.EmergencyCallbackModeType;
 import android.telephony.TelephonyRegistryManager;
 import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsCallSession;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.MediaQualityStatus;
 
 import com.android.telephony.Rlog;
 
@@ -142,15 +147,28 @@
         mTelephonyRegistryMgr.notifyCellInfoChanged(subId, cellInfo);
     }
 
-    public void notifyPreciseCallState(Phone sender) {
+    /**
+     * Notify precise call state of foreground, background and ringing call states.
+     *
+     * @param imsCallIds Array of IMS call session ID{@link ImsCallSession#getCallId} for
+     *                   ringing, foreground & background calls.
+     * @param imsCallServiceTypes Array of IMS call service type for ringing, foreground &
+     *                        background calls.
+     * @param imsCallTypes Array of IMS call type for ringing, foreground & background calls.
+     */
+    public void notifyPreciseCallState(Phone sender, String[] imsCallIds,
+            @Annotation.ImsCallServiceType int[] imsCallServiceTypes,
+            @Annotation.ImsCallType int[] imsCallTypes) {
         Call ringingCall = sender.getRingingCall();
         Call foregroundCall = sender.getForegroundCall();
         Call backgroundCall = sender.getBackgroundCall();
+
         if (ringingCall != null && foregroundCall != null && backgroundCall != null) {
-            mTelephonyRegistryMgr.notifyPreciseCallState(sender.getPhoneId(), sender.getSubId(),
-                    convertPreciseCallState(ringingCall.getState()),
+            int[] callStates = {convertPreciseCallState(ringingCall.getState()),
                     convertPreciseCallState(foregroundCall.getState()),
-                    convertPreciseCallState(backgroundCall.getState()));
+                    convertPreciseCallState(backgroundCall.getState())};
+            mTelephonyRegistryMgr.notifyPreciseCallState(sender.getPhoneId(), sender.getSubId(),
+                    callStates, imsCallIds, imsCallServiceTypes, imsCallTypes);
         }
     }
 
@@ -223,6 +241,12 @@
     }
 
     @Override
+    public void notifyMediaQualityStatusChanged(Phone sender, MediaQualityStatus status) {
+        mTelephonyRegistryMgr.notifyMediaQualityStatusChanged(
+                sender.getPhoneId(), sender.getSubId(), status);
+    }
+
+    @Override
     public void notifyRegistrationFailed(Phone sender, @NonNull CellIdentity cellIdentity,
             @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) {
         mTelephonyRegistryMgr.notifyRegistrationFailed(sender.getPhoneId(), sender.getSubId(),
@@ -262,6 +286,18 @@
                 sender.getSubId(), linkCapacityEstimateList);
     }
 
+    @Override
+    public void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type) {
+        mTelephonyRegistryMgr.notifyCallBackModeStarted(sender.getPhoneId(),
+                sender.getSubId(), type);
+    }
+
+    @Override
+    public void notifyCallbackModeStopped(Phone sender, @EmergencyCallbackModeType int type,
+            @EmergencyCallbackModeStopReason int reason) {
+        mTelephonyRegistryMgr.notifyCallbackModeStopped(sender.getPhoneId(),
+                sender.getSubId(), type, reason);
+    }
     /**
      * Convert the {@link Call.State} enum into the PreciseCallState.PRECISE_CALL_STATE_* constants
      * for the public API.
diff --git a/src/java/com/android/internal/telephony/DeviceStateMonitor.java b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
index 3d63a29..ecc6208 100644
--- a/src/java/com/android/internal/telephony/DeviceStateMonitor.java
+++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -20,6 +20,7 @@
 import static android.hardware.radio.V1_0.DeviceStateType.CHARGING_STATE;
 import static android.hardware.radio.V1_0.DeviceStateType.LOW_DATA_EXPECTED;
 import static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
 
 import android.app.UiModeManager;
 import android.content.BroadcastReceiver;
@@ -669,7 +670,7 @@
                 LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.EUTRAN);
         mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
                 LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.CDMA2000);
-        if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
+        if (mPhone.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
             mPhone.setLinkCapacityReportingCriteria(LINK_CAPACITY_DOWNLINK_THRESHOLDS,
                     LINK_CAPACITY_UPLINK_THRESHOLDS, AccessNetworkType.NGRAN);
         }
diff --git a/src/java/com/android/internal/telephony/DisplayInfoController.java b/src/java/com/android/internal/telephony/DisplayInfoController.java
index 886a899..567331d 100644
--- a/src/java/com/android/internal/telephony/DisplayInfoController.java
+++ b/src/java/com/android/internal/telephony/DisplayInfoController.java
@@ -18,11 +18,11 @@
 
 import android.annotation.NonNull;
 import android.os.Handler;
+import android.os.Message;
 import android.os.Registrant;
 import android.os.RegistrantList;
-import android.telephony.AccessNetworkConstants;
 import android.telephony.AnomalyReporter;
-import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
 import android.util.IndentingPrintWriter;
@@ -45,8 +45,6 @@
  * TelephonyDisplayInfo via {@link #getTelephonyDisplayInfo}.
  */
 public class DisplayInfoController extends Handler {
-    private static final String TAG = "DisplayInfoController";
-
     private final String mLogTag;
     private final LocalLog mLocalLog = new LocalLog(128);
 
@@ -66,22 +64,36 @@
                     TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED)
             );
 
+    /** Event for service state changed (roaming). */
+    private static final int EVENT_SERVICE_STATE_CHANGED = 1;
+
     private final Phone mPhone;
     private final NetworkTypeController mNetworkTypeController;
     private final RegistrantList mTelephonyDisplayInfoChangedRegistrants = new RegistrantList();
-    private TelephonyDisplayInfo mTelephonyDisplayInfo;
+    private @NonNull TelephonyDisplayInfo mTelephonyDisplayInfo;
+    private @NonNull ServiceState mServiceState;
 
     public DisplayInfoController(Phone phone) {
         mPhone = phone;
         mLogTag = "DIC-" + mPhone.getPhoneId();
+        mTelephonyDisplayInfo = new TelephonyDisplayInfo(
+                TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
         mNetworkTypeController = new NetworkTypeController(phone, this);
         mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+
+        mServiceState = mPhone.getServiceStateTracker().getServiceState();
+        post(() -> {
+            mPhone.getServiceStateTracker()
+                    .registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
+            updateTelephonyDisplayInfo();
+        });
     }
 
     /**
      * @return the current TelephonyDisplayInfo
      */
-    public TelephonyDisplayInfo getTelephonyDisplayInfo() {
+    public @NonNull TelephonyDisplayInfo getTelephonyDisplayInfo() {
         return mTelephonyDisplayInfo;
     }
 
@@ -90,12 +102,10 @@
      * NetworkTypeController.
      */
     public void updateTelephonyDisplayInfo() {
-        NetworkRegistrationInfo nri =  mPhone.getServiceState().getNetworkRegistrationInfo(
-                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-        int dataNetworkType = nri == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN
-                : nri.getAccessNetworkTechnology();
-        TelephonyDisplayInfo newDisplayInfo = new TelephonyDisplayInfo(dataNetworkType,
-                mNetworkTypeController.getOverrideNetworkType());
+        TelephonyDisplayInfo newDisplayInfo = new TelephonyDisplayInfo(
+                mNetworkTypeController.getDataNetworkType(),
+                mNetworkTypeController.getOverrideNetworkType(),
+                mServiceState.getRoaming());
         if (!newDisplayInfo.equals(mTelephonyDisplayInfo)) {
             logl("TelephonyDisplayInfo changed from " + mTelephonyDisplayInfo + " to "
                     + newDisplayInfo);
@@ -133,7 +143,7 @@
             }
         } catch (InvalidArgumentException e) {
             logel(e.getMessage());
-            AnomalyReporter.reportAnomaly(UUID.fromString("3aa92a2c-94ed-46a0-a744-d6b1dfec2a55"),
+            AnomalyReporter.reportAnomaly(UUID.fromString("3aa92a2c-94ed-46a0-a744-d6b1dfec2a56"),
                     e.getMessage(), mPhone.getCarrierId());
         }
     }
@@ -157,6 +167,17 @@
         mTelephonyDisplayInfoChangedRegistrants.remove(h);
     }
 
+    @Override
+    public void handleMessage(@NonNull Message msg) {
+        switch (msg.what) {
+            case EVENT_SERVICE_STATE_CHANGED:
+                mServiceState = mPhone.getServiceStateTracker().getServiceState();
+                log("ServiceState updated, isRoaming=" + mServiceState.getRoaming());
+                updateTelephonyDisplayInfo();
+                break;
+        }
+    }
+
     /**
      * Log debug messages.
      * @param s debug messages
diff --git a/src/java/com/android/internal/telephony/FdnUtils.java b/src/java/com/android/internal/telephony/FdnUtils.java
new file mode 100644
index 0000000..aa2bcfd
--- /dev/null
+++ b/src/java/com/android/internal/telephony/FdnUtils.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.text.TextUtils;
+
+import com.android.i18n.phonenumbers.NumberParseException;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
+import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.uicc.AdnRecord;
+import com.android.internal.telephony.uicc.AdnRecordCache;
+import com.android.internal.telephony.uicc.IccConstants;
+import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.UiccCardApplication;
+import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccProfile;
+import com.android.telephony.Rlog;
+
+import java.util.ArrayList;
+
+/**
+ * This is a basic utility class for common functions related to Fixed Dialing Numbers
+ * designed as per 3GPP 22.101.
+ */
+public class FdnUtils {
+    private static final boolean VDBG = false;
+    private static final String LOG_TAG = FdnUtils.class.getSimpleName();
+
+    /**
+     * The following function checks if dialed number is blocked due to FDN.
+     *
+     * @param phoneId The phone object id for which the FDN check is performed
+     * @param dialStr dialed phone number
+     * @param defaultCountryIso country ISO for the subscription associated with this phone
+     * @return {@code true} if dialStr is blocked due to FDN check.
+     */
+    public static boolean isNumberBlockedByFDN(int phoneId, String dialStr,
+            String defaultCountryIso) {
+        if (!isFdnEnabled(phoneId)) {
+            return false;
+        }
+
+        ArrayList<AdnRecord> fdnList = getFdnList(phoneId);
+        return !isFDN(dialStr, defaultCountryIso, fdnList);
+    }
+
+    /**
+     * Checks if FDN is enabled
+     * @param phoneId The phone object id for which the FDN check is performed
+     * @return {@code true} if FDN is enabled
+     */
+    public static boolean isFdnEnabled(int phoneId) {
+        UiccCardApplication app = getUiccCardApplication(phoneId);
+        if (app == null || (!app.getIccFdnAvailable())) {
+            return false;
+        }
+
+        return app.getIccFdnEnabled();
+    }
+
+    /**
+     * If FDN is enabled, check to see if the given supplementary service control strings are
+     * blocked due to FDN.
+     * @param phoneId The phone object id for which the FDN check is performed
+     * @param controlStrings control strings associated with the supplementary service request
+     * @param defaultCountryIso country ISO for the subscription associated with this phone
+     * @return {@code true} if the FDN list does not contain any of the control strings.
+     */
+    public static boolean isSuppServiceRequestBlockedByFdn(int phoneId,
+            ArrayList<String> controlStrings, String defaultCountryIso) {
+        if (!isFdnEnabled(phoneId)) {
+            return false;
+        }
+
+        ArrayList<AdnRecord> fdnList = getFdnList(phoneId);
+        for(String controlString : controlStrings) {
+            if(isFDN(controlString, defaultCountryIso, fdnList)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Checks if dialStr is part of FDN list.
+     *
+     * @param fdnList List of all FDN records associated with a sim card
+     * @param dialStr dialed phone number
+     * @param defaultCountryIso country ISO for the subscription associated with this phone
+     * @return {@code true} if dialStr is present in the fdnList.
+     */
+    @VisibleForTesting
+    public static boolean isFDN(String dialStr, String defaultCountryIso,
+            ArrayList<AdnRecord> fdnList) {
+        if (fdnList == null || fdnList.isEmpty() || TextUtils.isEmpty(dialStr)) {
+            Rlog.w(LOG_TAG, "isFDN: unexpected null value");
+            return false;
+        }
+
+        // Parse the dialStr and convert it to E164 format
+        String dialStrE164 = null;
+        String dialStrNational = null;
+        final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
+        try {
+            PhoneNumber phoneNumber = phoneNumberUtil.parse(dialStr, defaultCountryIso);
+            dialStrE164 = phoneNumberUtil.format(phoneNumber, PhoneNumberFormat.E164);
+            dialStrNational = String.valueOf(phoneNumber.getNationalNumber());
+        } catch (NumberParseException ignored) {
+            Rlog.w(LOG_TAG, "isFDN: could not parse dialStr");
+        }
+
+        /**
+         * Returns true if dialStrE164 or dialStrNational or dialStr starts with fdnNumber
+         * E.g.1: returns true if fdnNumber="123" and dialStr="12345"
+         * E.g.2: does not return true if fdnNumber="1123" and dialStr="12345"
+         */
+        for (AdnRecord fdn: fdnList) {
+            String fdnNumber = fdn.getNumber();
+            if (TextUtils.isEmpty(fdnNumber)) {
+                continue;
+            }
+
+            if(!TextUtils.isEmpty(dialStrE164)) {
+                if(dialStrE164.startsWith(fdnNumber)) {
+                    return true;
+                }
+            }
+
+            if(!TextUtils.isEmpty(dialStrNational)) {
+                if (dialStrNational.startsWith(fdnNumber)) {
+                    return true;
+                }
+            }
+
+            if (dialStr.startsWith(fdnNumber)) {
+                return true;
+            }
+        }
+
+        if (VDBG) {
+            Rlog.i(LOG_TAG, "isFDN: dialed number not present in FDN list");
+        }
+        return false;
+    }
+
+    private static ArrayList<AdnRecord> getFdnList(int phoneId) {
+        UiccCardApplication app = getUiccCardApplication(phoneId);
+        if (app == null) {
+            return null;
+        }
+
+        IccRecords iccRecords = app.getIccRecords();
+        if (iccRecords == null) {
+            return null;
+        }
+
+        AdnRecordCache adnRecordCache = iccRecords.getAdnCache();
+        if(adnRecordCache == null) {
+            return null;
+        }
+
+        return adnRecordCache.getRecordsIfLoaded(IccConstants.EF_FDN);
+    }
+
+    private static UiccCardApplication getUiccCardApplication(int phoneId) {
+        UiccProfile uiccProfile = UiccController.getInstance()
+                .getUiccProfileForPhone(phoneId);
+        if (uiccProfile == null) {
+            return null;
+        }
+
+        return uiccProfile.getApplication(UiccController.APP_FAM_3GPP);
+    }
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
old mode 100755
new mode 100644
index 3293558..d76ee19
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -45,6 +45,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.telephony.Rlog;
 
@@ -182,11 +184,6 @@
             mCi.unregisterForCallWaitingInfo(this);
             // Prior to phone switch to GSM, if CDMA has any emergency call
             // data will be in disabled state, after switching to GSM enable data.
-            if (mIsInEmergencyCall) {
-                if (!mPhone.isUsingNewDataStack()) {
-                    mPhone.getDataEnabledSettings().setInternalDataEnabled(true);
-                }
-            }
         } else {
             mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_CDMA];
             mPendingCallInEcm = false;
@@ -400,9 +397,6 @@
     //CDMA
     public void setIsInEmergencyCall() {
         mIsInEmergencyCall = true;
-        if (!mPhone.isUsingNewDataStack()) {
-            mPhone.getDataEnabledSettings().setInternalDataEnabled(false);
-        }
         mPhone.notifyEmergencyCallRegistrants(true);
         mPhone.sendEmergencyCallStateChange(true);
     }
@@ -488,16 +482,29 @@
             disableDataCallInEmergencyCall(dialString);
 
             // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
-            if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
+            if (!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
                 mCi.dial(mPendingMO.getAddress(), mPendingMO.isEmergencyCall(),
                         mPendingMO.getEmergencyNumberInfo(),
-                        mPendingMO.hasKnownUserIntentEmergency(),
-                        clirMode, obtainCompleteMessage());
+                        mPendingMO.hasKnownUserIntentEmergency(), clirMode,
+                        obtainCompleteMessage());
+            } else if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+                mPendingCallInEcm = true;
+                final int finalClirMode = clirMode;
+                Runnable onComplete = new Runnable() {
+                    @Override
+                    public void run() {
+                        mCi.dial(mPendingMO.getAddress(), mPendingMO.isEmergencyCall(),
+                        mPendingMO.getEmergencyNumberInfo(),
+                        mPendingMO.hasKnownUserIntentEmergency(), finalClirMode,
+                        obtainCompleteMessage());
+                    }
+                };
+                EmergencyStateTracker.getInstance().exitEmergencyCallbackMode(onComplete);
             } else {
                 mPhone.exitEmergencyCallbackMode();
-                mPhone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null);
-                mPendingCallClirMode=clirMode;
-                mPendingCallInEcm=true;
+                mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null);
+                mPendingCallClirMode = clirMode;
+                mPendingCallInEcm = true;
             }
         }
 
@@ -967,6 +974,8 @@
                             } else {
                                 newUnknownConnectionCdma = mConnections[i];
                             }
+                        } else if (hangupWaitingCallSilently(i)) {
+                            return;
                         }
                     }
                 }
@@ -1018,6 +1027,9 @@
 
                 if (mConnections[i].getCall() == mRingingCall) {
                     newRinging = mConnections[i];
+                    if (hangupWaitingCallSilently(i)) {
+                        return;
+                    }
                 } // else something strange happened
                 hasNonHangupStateChanged = true;
             } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
@@ -1754,9 +1766,6 @@
             }
             if (!inEcm) {
                 // Re-initiate data connection
-                if (!mPhone.isUsingNewDataStack()) {
-                    mPhone.getDataEnabledSettings().setInternalDataEnabled(true);
-                }
                 mPhone.notifyEmergencyCallRegistrants(false);
             }
             mPhone.sendEmergencyCallStateChange(false);
@@ -1830,6 +1839,10 @@
     }
 
     private boolean isEmcRetryCause(int causeCode) {
+        if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+            log("isEmcRetryCause AP based domain selection ignores the cause");
+            return false;
+        }
         if (causeCode == CallFailCause.EMC_REDIAL_ON_IMS ||
             causeCode == CallFailCause.EMC_REDIAL_ON_VOWIFI) {
             return true;
@@ -1909,4 +1922,22 @@
     public void cleanupCalls() {
         pollCallsWhenSafe();
     }
+
+    private boolean hangupWaitingCallSilently(int index) {
+        if (index < 0 || index >= mConnections.length) return false;
+
+        GsmCdmaConnection newRinging = mConnections[index];
+        if (newRinging == null) return false;
+
+        if ((mPhone.getTerminalBasedCallWaitingState(true)
+                        == CallWaitingController.TERMINAL_BASED_NOT_ACTIVATED)
+                && (newRinging.getState() == Call.State.WAITING)) {
+            Rlog.d(LOG_TAG, "hangupWaitingCallSilently");
+            newRinging.dispose();
+            mConnections[index] = null;
+            mCi.hangupWaitingOrBackground(obtainCompleteMessage());
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index b79bdef..e06520a 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -478,7 +478,7 @@
      * @return the corresponding value from {@link DisconnectCause}
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    int disconnectCauseFromCode(int causeCode) {
+    public int disconnectCauseFromCode(int causeCode) {
         /**
          * See 22.001 Annex F.4 for mapping of cause codes
          * to local tones
@@ -565,6 +565,14 @@
             case CallFailCause.RADIO_OFF:
                 return DisconnectCause.POWER_OFF;
 
+            case CallFailCause.NO_VALID_SIM:
+                return DisconnectCause.ICC_ERROR;
+
+            case CallFailCause.LOCAL_NETWORK_NO_SERVICE:
+                // fallthrough
+            case CallFailCause.LOCAL_SERVICE_UNAVAILABLE:
+                return DisconnectCause.OUT_OF_SERVICE;
+
             case CallFailCause.ACCESS_CLASS_BLOCKED:
             case CallFailCause.ERROR_UNSPECIFIED:
             case CallFailCause.NORMAL_CLEARING:
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index ff8412f..5eae061 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -16,6 +16,11 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS_PS;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_UNKNOWN;
+
 import static com.android.internal.telephony.CommandException.Error.GENERIC_FAILURE;
 import static com.android.internal.telephony.CommandException.Error.SIM_BUSY;
 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
@@ -32,6 +37,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.ContentValues;
@@ -40,6 +46,7 @@
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.database.SQLException;
+import android.hardware.radio.modem.ImeiInfo;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Build;
@@ -57,6 +64,7 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.preference.PreferenceManager;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.provider.Telephony;
 import android.sysprop.TelephonyProperties;
@@ -64,11 +72,13 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
-import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.Annotation.DataActivityType;
 import android.telephony.Annotation.RadioPowerState;
+import android.telephony.AnomalyReporter;
 import android.telephony.BarringInfo;
 import android.telephony.CarrierConfigManager;
+import android.telephony.CellBroadcastIdRange;
 import android.telephony.CellIdentity;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.LinkCapacityEstimate;
@@ -82,7 +92,7 @@
 import android.telephony.TelephonyManager;
 import android.telephony.UiccAccessRule;
 import android.telephony.UssdResponse;
-import android.telephony.data.ApnSetting;
+import android.telephony.ims.ImsCallProfile;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -94,17 +104,19 @@
 import com.android.internal.telephony.data.AccessNetworksManager;
 import com.android.internal.telephony.data.DataNetworkController;
 import com.android.internal.telephony.data.LinkBandwidthEstimator;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
-import com.android.internal.telephony.dataconnection.DcTracker;
-import com.android.internal.telephony.dataconnection.TransportManager;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
 import com.android.internal.telephony.gsm.GsmMmiCode;
+import com.android.internal.telephony.gsm.SsData;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhoneMmiCode;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.metrics.VoiceCallSessionStats;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
 import com.android.internal.telephony.uicc.IccCardStatus;
@@ -131,6 +143,9 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.UUID;
 import java.util.function.Consumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -157,6 +172,8 @@
     // Key used to read/write the SIM IMSI used for storing the voice mail
     private static final String VM_SIM_IMSI = "vm_sim_imsi_key";
     /** List of Registrants to receive Supplementary Service Notifications. */
+    // Key used to read/write the current sub Id. Updated on SIM loaded.
+    public static final String CURR_SUBID = "curr_subid";
     private RegistrantList mSsnRegistrants = new RegistrantList();
 
     //CDMA
@@ -227,10 +244,17 @@
 
     private final RegistrantList mVolteSilentRedialRegistrants = new RegistrantList();
     private DialArgs mDialArgs = null;
-
+    private final RegistrantList mEmergencyDomainSelectedRegistrants = new RegistrantList();
     private String mImei;
     private String mImeiSv;
     private String mVmNumber;
+    private int mImeiType = IMEI_TYPE_UNKNOWN;
+
+    @VisibleForTesting
+    public CellBroadcastConfigTracker mCellBroadcastConfigTracker =
+            CellBroadcastConfigTracker.make(this, null, true);
+
+    private boolean mIsNullCipherAndIntegritySupported = false;
 
     // Create Cfu (Call forward unconditional) so that dialing number &
     // mOnComplete (Message object passed by client) can be packed &
@@ -271,12 +295,11 @@
     private CarrierKeyDownloadManager mCDM;
     private CarrierInfoManager mCIM;
 
-    private final SettingsObserver mSettingsObserver;
-
     private final ImsManagerFactory mImsManagerFactory;
     private final CarrierPrivilegesTracker mCarrierPrivilegesTracker;
 
     private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener;
+    private final CallWaitingController mCallWaitingController;
 
     // Constructors
 
@@ -316,10 +339,6 @@
         mAccessNetworksManager = mTelephonyComponentFactory
                 .inject(AccessNetworksManager.class.getName())
                 .makeAccessNetworksManager(this, getLooper());
-        if (!isUsingNewDataStack()) {
-            mTransportManager = mTelephonyComponentFactory.inject(TransportManager.class.getName())
-                    .makeTransportManager(this);
-        }
         // SST/DSM depends on SSC, so SSC is instanced before SST/DSM
         mSignalStrengthController = mTelephonyComponentFactory.inject(
                 SignalStrengthController.class.getName()).makeSignalStrengthController(this);
@@ -328,10 +347,6 @@
         mEmergencyNumberTracker = mTelephonyComponentFactory
                 .inject(EmergencyNumberTracker.class.getName()).makeEmergencyNumberTracker(
                         this, this.mCi);
-        if (!isUsingNewDataStack()) {
-            mDataEnabledSettings = mTelephonyComponentFactory
-                    .inject(DataEnabledSettings.class.getName()).makeDataEnabledSettings(this);
-        }
         mDeviceStateMonitor = mTelephonyComponentFactory.inject(DeviceStateMonitor.class.getName())
                 .makeDeviceStateMonitor(this);
 
@@ -340,20 +355,9 @@
         mDisplayInfoController = mTelephonyComponentFactory.inject(
                 DisplayInfoController.class.getName()).makeDisplayInfoController(this);
 
-        if (isUsingNewDataStack()) {
-            mDataNetworkController = mTelephonyComponentFactory.inject(
-                    DataNetworkController.class.getName())
-                    .makeDataNetworkController(this, getLooper());
-        } else {
-            // DcTracker uses ServiceStateTracker and DisplayInfoController so needs to be created
-            // after they are instantiated
-            for (int transport : mAccessNetworksManager.getAvailableTransports()) {
-                DcTracker dcTracker = mTelephonyComponentFactory.inject(DcTracker.class.getName())
-                        .makeDcTracker(this, transport);
-                mDcTrackers.put(transport, dcTracker);
-                mAccessNetworksManager.registerDataThrottler(dcTracker.getDataThrottler());
-            }
-        }
+        mDataNetworkController = mTelephonyComponentFactory.inject(
+                DataNetworkController.class.getName())
+                .makeDataNetworkController(this, getLooper());
 
         mCarrierResolver = mTelephonyComponentFactory.inject(CarrierResolver.class.getName())
                 .makeCarrierResolver(this);
@@ -365,23 +369,22 @@
 
         mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
         mSST.registerForVoiceRegStateOrRatChanged(this, EVENT_VRS_OR_RAT_CHANGED, null);
+        mSST.getServiceStateStats().registerDataNetworkControllerCallback();
 
-        // TODO: Remove SettingsObserver and provisioning events when DataEnabledSettings is removed
-        mSettingsObserver = new SettingsObserver(context, this);
-        mSettingsObserver.observe(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
-                EVENT_DEVICE_PROVISIONED_CHANGE);
-        mSettingsObserver.observe(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED),
-                EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE);
-
-        SubscriptionController.getInstance().registerForUiccAppsEnabled(this,
-                EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED, null, false);
+        mSubscriptionManagerService.registerCallback(new SubscriptionManagerServiceCallback(
+                this::post) {
+            @Override
+            public void onUiccApplicationsEnabledChanged(int subId) {
+                reapplyUiccAppsEnablementIfNeeded(ENABLE_UICC_APPS_MAX_RETRIES);
+            }
+        });
 
         mLinkBandwidthEstimator = mTelephonyComponentFactory
                 .inject(LinkBandwidthEstimator.class.getName())
                 .makeLinkBandwidthEstimator(this);
 
+        mCallWaitingController = new CallWaitingController(this);
+
         loadTtyMode();
 
         CallManager.getInstance().registerPhone(this);
@@ -419,6 +422,17 @@
                 int newPreferredTtyMode = intent.getIntExtra(
                         TelecomManager.EXTRA_TTY_PREFERRED_MODE, TelecomManager.TTY_MODE_OFF);
                 updateUiTtyMode(newPreferredTtyMode);
+            } else if (TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED.equals(action)) {
+                if (mPhoneId == intent.getIntExtra(
+                        SubscriptionManager.EXTRA_SLOT_INDEX,
+                        SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
+                    int simState = intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE,
+                            TelephonyManager.SIM_STATE_UNKNOWN);
+                    if (simState == TelephonyManager.SIM_STATE_LOADED
+                            && currentSlotSubIdChanged()) {
+                        setNetworkSelectionModeAutomatic(null);
+                    }
+                }
             }
         }
     };
@@ -475,15 +489,20 @@
         mCi.registerForLceInfo(this, EVENT_LINK_CAPACITY_CHANGED, null);
         mCi.registerForCarrierInfoForImsiEncryption(this,
                 EVENT_RESET_CARRIER_KEY_IMSI_ENCRYPTION, null);
+        mCi.registerForTriggerImsDeregistration(this, EVENT_IMS_DEREGISTRATION_TRIGGERED, null);
+        mCi.registerForNotifyAnbr(this, EVENT_TRIGGER_NOTIFY_ANBR, null);
         IntentFilter filter = new IntentFilter(
                 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
         filter.addAction(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
+        filter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
         mContext.registerReceiver(mBroadcastReceiver, filter,
                 android.Manifest.permission.MODIFY_PHONE_STATE, null, Context.RECEIVER_EXPORTED);
 
         mCDM = new CarrierKeyDownloadManager(this);
         mCIM = new CarrierInfoManager();
+
+        initializeCarrierApps();
     }
 
     private void initRatSpecific(int precisePhoneType) {
@@ -506,8 +525,12 @@
             // This is needed to handle phone process crashes
             mIsPhoneInEcmState = getInEcmMode();
             if (mIsPhoneInEcmState) {
-                // Send a message which will invoke handleExitEmergencyCallbackMode
-                mCi.exitEmergencyCallbackMode(null);
+                if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+                    EmergencyStateTracker.getInstance().exitEmergencyCallbackMode();
+                } else {
+                    // Send a message which will invoke handleExitEmergencyCallbackMode
+                    mCi.exitEmergencyCallbackMode(null);
+                }
             }
 
             mCi.setPhoneType(PhoneConstants.PHONE_TYPE_CDMA);
@@ -530,7 +553,7 @@
                 logd("update icc_operator_numeric=" + operatorNumeric);
                 tm.setSimOperatorNumericForPhone(mPhoneId, operatorNumeric);
 
-                SubscriptionController.getInstance().setMccMnc(operatorNumeric, getSubId());
+                mSubscriptionManagerService.setMccMnc(getSubId(), operatorNumeric);
 
                 // Sets iso country property by retrieving from build-time system property
                 String iso = "";
@@ -542,7 +565,7 @@
 
                 logd("init: set 'gsm.sim.operator.iso-country' to iso=" + iso);
                 tm.setSimCountryIsoForPhone(mPhoneId, iso);
-                SubscriptionController.getInstance().setCountryIso(iso, getSubId());
+                mSubscriptionManagerService.setCountryIso(getSubId(), iso);
 
                 // Updates MCC MNC device configuration information
                 logd("update mccmnc=" + operatorNumeric);
@@ -554,6 +577,31 @@
         }
     }
 
+    /**
+     * Initialize the carrier apps.
+     */
+    private void initializeCarrierApps() {
+        // Only perform on the default phone. There is no need to do it twice on the DSDS device.
+        if (mPhoneId != 0) return;
+
+        logd("initializeCarrierApps");
+        mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                // Remove this line after testing
+                if (Intent.ACTION_USER_FOREGROUND.equals(intent.getAction())) {
+                    UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
+                    // If couldn't get current user ID, guess it's 0.
+                    CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+                            TelephonyManager.getDefault(),
+                            userHandle != null ? userHandle.getIdentifier() : 0, mContext);
+                }
+            }
+        }, new IntentFilter(Intent.ACTION_USER_FOREGROUND), null, null);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+                TelephonyManager.getDefault(), ActivityManager.getCurrentUser(), mContext);
+    }
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isPhoneTypeGsm() {
         return mPrecisePhoneType == PhoneConstants.PHONE_TYPE_GSM;
@@ -680,11 +728,6 @@
     }
 
     @Override
-    public TransportManager getTransportManager() {
-        return mTransportManager;
-    }
-
-    @Override
     public AccessNetworksManager getAccessNetworksManager() {
         return mAccessNetworksManager;
     }
@@ -736,83 +779,8 @@
     }
 
     @Override
-    public PhoneConstants.DataState getDataConnectionState(String apnType) {
-        PhoneConstants.DataState ret = PhoneConstants.DataState.DISCONNECTED;
-
-        if (mSST == null) {
-            // Radio Technology Change is ongoing, dispose() and removeReferences() have
-            // already been called
-
-            ret = PhoneConstants.DataState.DISCONNECTED;
-        } else if (mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE
-                && (isPhoneTypeCdma() || isPhoneTypeCdmaLte() ||
-                (isPhoneTypeGsm() && !apnType.equals(ApnSetting.TYPE_EMERGENCY_STRING)))) {
-            // If we're out of service, open TCP sockets may still work
-            // but no data will flow
-
-            // Emergency APN is available even in Out Of Service
-            // Pass the actual State of EPDN
-
-            ret = PhoneConstants.DataState.DISCONNECTED;
-        } else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */
-            int currentTransport = mAccessNetworksManager.getCurrentTransport(
-                    ApnSetting.getApnTypesBitmaskFromString(apnType));
-            if (getDcTracker(currentTransport) != null) {
-                switch (getDcTracker(currentTransport).getState(apnType)) {
-                    case CONNECTED:
-                    case DISCONNECTING:
-                        if (isDataSuspended()) {
-                            ret = PhoneConstants.DataState.SUSPENDED;
-                        } else {
-                            ret = PhoneConstants.DataState.CONNECTED;
-                        }
-                        break;
-                    case CONNECTING:
-                        ret = PhoneConstants.DataState.CONNECTING;
-                        break;
-                    default:
-                        ret = PhoneConstants.DataState.DISCONNECTED;
-                }
-            }
-        }
-
-        logd("getDataConnectionState apnType=" + apnType + " ret=" + ret);
-        return ret;
-    }
-
-    @Override
     public @DataActivityType int getDataActivityState() {
-        if (isUsingNewDataStack()) {
-            return getDataNetworkController().getDataActivity();
-        }
-        int ret = TelephonyManager.DATA_ACTIVITY_NONE;
-
-        if (mSST.getCurrentDataConnectionState() == ServiceState.STATE_IN_SERVICE
-                && getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) {
-            switch (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).getActivity()) {
-                case DATAIN:
-                    ret = TelephonyManager.DATA_ACTIVITY_IN;
-                break;
-
-                case DATAOUT:
-                    ret = TelephonyManager.DATA_ACTIVITY_OUT;
-                break;
-
-                case DATAINANDOUT:
-                    ret = TelephonyManager.DATA_ACTIVITY_INOUT;
-                break;
-
-                case DORMANT:
-                    ret = TelephonyManager.DATA_ACTIVITY_DORMANT;
-                break;
-
-                default:
-                    ret = TelephonyManager.DATA_ACTIVITY_NONE;
-                break;
-            }
-        }
-
-        return ret;
+        return getDataNetworkController().getDataActivity();
     }
 
     /**
@@ -831,7 +799,10 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void notifyPreciseCallStateChanged() {
         /* we'd love it if this was package-scoped*/
-        super.notifyPreciseCallStateChangedP();
+        AsyncResult ar = new AsyncResult(null, this, null);
+        mPreciseCallStateRegistrants.notifyRegistrants(ar);
+
+        mNotifier.notifyPreciseCallState(this, null, null, null);
     }
 
     public void notifyNewRingingConnection(Connection c) {
@@ -1023,14 +994,6 @@
     }
 
     @Override
-    public void dispose() {
-        // Note: this API is currently never called. We are defining actions here in case
-        // we need to dispose GsmCdmaPhone/Phone object.
-        super.dispose();
-        SubscriptionController.getInstance().unregisterForUiccAppsEnabled(this);
-    }
-
-    @Override
     public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) {
         if (isPhoneTypeGsm()) {
             loge("enableEnhancedVoicePrivacy: not expected on GSM");
@@ -1434,8 +1397,7 @@
         // emergency number list on another SIM, but is not on theirs.  In this case we will use the
         // emergency number list for this carrier's SIM only.
         if (useOnlyDialedSimEccList) {
-            isEmergency = getEmergencyNumberTracker().isEmergencyNumber(dialString,
-                    true /* exactMatch */);
+            isEmergency = getEmergencyNumberTracker().isEmergencyNumber(dialString);
             logi("dial; isEmergency=" + isEmergency
                     + " (based on this phone only); globalIsEmergency="
                     + tm.isEmergencyNumber(dialString));
@@ -1444,6 +1406,12 @@
             logi("dial; isEmergency=" + isEmergency + " (based on all phones)");
         }
 
+        // Undetectable emergeny number indicated by new domain selection service
+        if (dialArgs.isEmergency) {
+            logi("dial; isEmergency=" + isEmergency + " (domain selection module)");
+            isEmergency = true;
+        }
+
         /** Check if the call is Wireless Priority Service call */
         boolean isWpsCall = dialString != null ? (dialString.startsWith(PREFIX_WPS)
                 || dialString.startsWith(PREFIX_WPS_CLIR_ACTIVATE)
@@ -1469,6 +1437,55 @@
         boolean useImsForCall = useImsForCall(dialArgs)
                 && (isWpsCall ? allowWpsOverIms : true);
 
+        Bundle extras = dialArgs.intentExtras;
+        if (extras != null && extras.containsKey(PhoneConstants.EXTRA_COMPARE_DOMAIN)) {
+            int domain = extras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN);
+            if (!isEmergency && (!isMmiCode || isPotentialUssdCode)) {
+                if ((domain == DOMAIN_PS && !useImsForCall)
+                        || (domain == DOMAIN_CS && useImsForCall)
+                        || domain == DOMAIN_UNKNOWN || domain == DOMAIN_CS_PS) {
+                    loge("[Anomaly] legacy-useImsForCall:" + useImsForCall
+                            + ", NCDS-domain:" + domain);
+
+                    AnomalyReporter.reportAnomaly(
+                            UUID.fromString("bfae6c2e-ca2f-4121-b167-9cad26a3b353"),
+                            "Domain selection results don't match. useImsForCall:"
+                                    + useImsForCall + ", NCDS-domain:" + domain);
+                }
+            }
+            extras.remove(PhoneConstants.EXTRA_COMPARE_DOMAIN);
+        }
+
+        // Only when the domain selection service is supported, EXTRA_DIAL_DOMAIN extra shall exist.
+        if (extras != null && extras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN)) {
+            int domain = extras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN);
+            logi("dial domain=" + domain);
+            useImsForCall = false;
+            useImsForUt = false;
+            useImsForEmergency = false;
+            if (domain == DOMAIN_PS) {
+                if (isEmergency) {
+                    useImsForEmergency = true;
+                } else if (!isMmiCode || isPotentialUssdCode) {
+                    useImsForCall = true;
+                } else {
+                    // should not reach here
+                    loge("dial unexpected Ut domain selection, ignored");
+                }
+            } else if (domain == PhoneConstants.DOMAIN_NON_3GPP_PS) {
+                if (isEmergency) {
+                    useImsForEmergency = true;
+                    extras.putString(ImsCallProfile.EXTRA_CALL_RAT_TYPE,
+                            String.valueOf(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN));
+                } else {
+                    // should not reach here
+                    loge("dial DOMAIN_NON_3GPP_PS should be used only for emergency calls");
+                }
+            }
+
+            extras.remove(PhoneConstants.EXTRA_DIAL_DOMAIN);
+        }
+
         if (DBG) {
             logi("useImsForCall=" + useImsForCall
                     + ", useOnlyDialedSimEccList=" + useOnlyDialedSimEccList
@@ -1491,6 +1508,12 @@
                     + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
         }
 
+        // Perform FDN check for non-emergency calls - shouldn't dial if number is blocked by FDN
+        if(!isEmergency && FdnUtils.isNumberBlockedByFDN(mPhoneId, dialString, getCountryIso())) {
+            throw new CallStateException(CallStateException.ERROR_FDN_BLOCKED,
+                    "cannot dial number blocked by FDN");
+        }
+
         // Bypass WiFi Only WFC check if this is an emergency call - we should still try to
         // place over cellular if possible.
         if (!isEmergency) {
@@ -1559,10 +1582,13 @@
         }
         if (DBG) logd("Trying (non-IMS) CS call");
         if (isDialedNumberSwapped && isEmergency) {
-            // Triggers ECM when CS call ends only for test emergency calls using
-            // ril.test.emergencynumber.
-            mIsTestingEmergencyCallbackMode = true;
-            mCi.testingEmergencyCall();
+            // If domain selection is enabled, ECM testing is handled in EmergencyStateTracker
+            if (!DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+                // Triggers ECM when CS call ends only for test emergency calls using
+                // ril.test.emergencynumber.
+                mIsTestingEmergencyCallbackMode = true;
+                mCi.testingEmergencyCall();
+            }
         }
 
         chosenPhoneConsumer.accept(this);
@@ -1688,6 +1714,13 @@
             return true;
         }
 
+        // Perform FDN check
+        if(FdnUtils.isNumberBlockedByFDN(mPhoneId, ussdRequest, getCountryIso())) {
+            sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
+                    wrappedCallback );
+            return true;
+        }
+
         // Try over IMS if possible.
         Phone imsPhone = mImsPhone;
         if ((imsPhone != null)
@@ -1787,7 +1820,7 @@
     public void setRadioPower(boolean power, boolean forEmergencyCall,
             boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
         setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply,
-                Phone.RADIO_POWER_REASON_USER);
+                TelephonyManager.RADIO_POWER_REASON_USER);
     }
 
     @Override
@@ -1797,6 +1830,11 @@
                 forceApply, reason);
     }
 
+    @Override
+    public Set<Integer> getRadioPowerOffReasons() {
+        return mSST.getRadioPowerOffReasons();
+    }
+
     private void storeVoiceMailNumber(String number) {
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         SharedPreferences.Editor editor = sp.edit();
@@ -1946,6 +1984,11 @@
         return mImei;
     }
 
+    @Override
+    public int getImeiType() {
+        return mImeiType;
+    }
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     @Override
     public String getEsn() {
@@ -2331,6 +2374,15 @@
     @Override
     public void getCallForwardingOption(int commandInterfaceCFReason, int serviceClass,
             Message onComplete) {
+        // Perform FDN check
+        SsData.ServiceType serviceType = GsmMmiCode.cfReasonToServiceType(commandInterfaceCFReason);
+        if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, serviceType)) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
+            return;
+        }
+
         Phone imsPhone = mImsPhone;
         if (useSsOverIms(onComplete)) {
             imsPhone.getCallForwardingOption(commandInterfaceCFReason, serviceClass, onComplete);
@@ -2380,6 +2432,16 @@
             int serviceClass,
             int timerSeconds,
             Message onComplete) {
+        // Perform FDN check
+        SsData.RequestType requestType = GsmMmiCode.cfActionToRequestType(commandInterfaceCFAction);
+        SsData.ServiceType serviceType = GsmMmiCode.cfReasonToServiceType(commandInterfaceCFReason);
+        if(isRequestBlockedByFDN(requestType, serviceType)) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
+            return;
+        }
+
         Phone imsPhone = mImsPhone;
         if (useSsOverIms(onComplete)) {
             imsPhone.setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason,
@@ -2433,6 +2495,15 @@
     @Override
     public void getCallBarring(String facility, String password, Message onComplete,
             int serviceClass) {
+        // Perform FDN check
+        SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility);
+        if (isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, serviceType)) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
+            return;
+        }
+
         Phone imsPhone = mImsPhone;
         if (useSsOverIms(onComplete)) {
             imsPhone.getCallBarring(facility, password, onComplete, serviceClass);
@@ -2449,6 +2520,17 @@
     @Override
     public void setCallBarring(String facility, boolean lockState, String password,
             Message onComplete, int serviceClass) {
+        // Perform FDN check
+        SsData.RequestType requestType = lockState ? SsData.RequestType.SS_ACTIVATION :
+                SsData.RequestType.SS_DEACTIVATION;
+        SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility);
+        if (isRequestBlockedByFDN(requestType, serviceType)) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
+            return;
+        }
+
         Phone imsPhone = mImsPhone;
         if (useSsOverIms(onComplete)) {
             imsPhone.setCallBarring(facility, lockState, password, onComplete, serviceClass);
@@ -2472,6 +2554,18 @@
      */
     public void changeCallBarringPassword(String facility, String oldPwd, String newPwd,
             Message onComplete) {
+        // Perform FDN check
+        SsData.ServiceType serviceType = GsmMmiCode.cbFacilityToServiceType(facility);
+        ArrayList<String> controlStrings = GsmMmiCode.getControlStringsForPwd(
+                SsData.RequestType.SS_REGISTRATION,
+                serviceType);
+        if(FdnUtils.isSuppServiceRequestBlockedByFdn(mPhoneId, controlStrings, getCountryIso())) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
+            return;
+        }
+
         if (isPhoneTypeGsm()) {
             mCi.changeBarringPassword(facility, oldPwd, newPwd, onComplete);
         } else {
@@ -2481,7 +2575,16 @@
 
     @Override
     public void getOutgoingCallerIdDisplay(Message onComplete) {
+        // Perform FDN check
+        if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_CLIR)){
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
+            return;
+        }
+
         Phone imsPhone = mImsPhone;
+
         if (useSsOverIms(onComplete)) {
             imsPhone.getOutgoingCallerIdDisplay(onComplete);
             return;
@@ -2499,6 +2602,15 @@
 
     @Override
     public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) {
+        // Perform FDN check
+        SsData.RequestType requestType = GsmMmiCode.clirModeToRequestType(commandInterfaceCLIRMode);
+        if (isRequestBlockedByFDN(requestType, SsData.ServiceType.SS_CLIR)) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
+            return;
+        }
+
         Phone imsPhone = mImsPhone;
         if (useSsOverIms(onComplete)) {
             imsPhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode, onComplete);
@@ -2521,6 +2633,14 @@
 
     @Override
     public void queryCLIP(Message onComplete) {
+        // Perform FDN check
+        if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_CLIP)){
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
+            return;
+        }
+
         Phone imsPhone = mImsPhone;
         if (useSsOverIms(onComplete)) {
             imsPhone.queryCLIP(onComplete);
@@ -2539,6 +2659,16 @@
 
     @Override
     public void getCallWaiting(Message onComplete) {
+        // Perform FDN check
+        if(isRequestBlockedByFDN(SsData.RequestType.SS_INTERROGATION, SsData.ServiceType.SS_WAIT)){
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
+            return;
+        }
+
+        if (mCallWaitingController.getCallWaiting(onComplete)) return;
+
         Phone imsPhone = mImsPhone;
         if (useSsOverIms(onComplete)) {
             imsPhone.getCallWaiting(onComplete);
@@ -2580,6 +2710,18 @@
 
     @Override
     public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) {
+        // Perform FDN check
+        SsData.RequestType requestType = enable ? SsData.RequestType.SS_ACTIVATION :
+                SsData.RequestType.SS_DEACTIVATION;
+        if (isRequestBlockedByFDN(requestType, SsData.ServiceType.SS_WAIT)) {
+            AsyncResult.forMessage(onComplete, null,
+                    new CommandException(CommandException.Error.FDN_CHECK_FAILURE));
+            onComplete.sendToTarget();
+            return;
+        }
+
+        if (mCallWaitingController.setCallWaiting(enable, serviceClass, onComplete)) return;
+
         Phone imsPhone = mImsPhone;
         if (useSsOverIms(onComplete)) {
             imsPhone.setCallWaiting(enable, onComplete);
@@ -2610,6 +2752,23 @@
     }
 
     @Override
+    public int getTerminalBasedCallWaitingState(boolean forCsOnly) {
+        return mCallWaitingController.getTerminalBasedCallWaitingState(forCsOnly);
+    }
+
+    @Override
+    public void setTerminalBasedCallWaitingStatus(int state) {
+        if (mImsPhone != null) {
+            mImsPhone.setTerminalBasedCallWaitingStatus(state);
+        }
+    }
+
+    @Override
+    public void setTerminalBasedCallWaitingSupported(boolean supported) {
+        mCallWaitingController.setTerminalBasedCallWaitingSupported(supported);
+    }
+
+    @Override
     public void getAvailableNetworks(Message response) {
         if (isPhoneTypeGsm() || isPhoneTypeCdmaLte()) {
             Message msg = obtainMessage(EVENT_GET_AVAILABLE_NETWORKS_DONE, response);
@@ -2672,25 +2831,12 @@
 
     @Override
     public boolean getDataRoamingEnabled() {
-        if (isUsingNewDataStack()) {
-            return getDataSettingsManager().isDataRoamingEnabled();
-        }
-        if (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) {
-            return getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).getDataRoamingEnabled();
-        }
-        return false;
+        return getDataSettingsManager().isDataRoamingEnabled();
     }
 
     @Override
     public void setDataRoamingEnabled(boolean enable) {
-        if (isUsingNewDataStack()) {
-            getDataSettingsManager().setDataRoamingEnabled(enable);
-            return;
-        }
-        if (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) {
-            getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                    .setDataRoamingEnabledByUser(enable);
-        }
+        getDataSettingsManager().setDataRoamingEnabled(enable);
     }
 
     @Override
@@ -2735,21 +2881,12 @@
     }
 
     /**
-     * Whether data is enabled by user. Unlike isDataEnabled, this only
-     * checks user setting stored in {@link android.provider.Settings.Global#MOBILE_DATA}
-     * if not provisioning, or isProvisioningDataEnabled if provisioning.
+     * Whether data is enabled by user.
      */
     @Override
     public boolean isUserDataEnabled() {
-        if (isUsingNewDataStack()) {
-            return getDataSettingsManager().isDataEnabledForReason(
-                    TelephonyManager.DATA_ENABLED_REASON_USER);
-        }
-        if (mDataEnabledSettings.isProvisioning()) {
-            return mDataEnabledSettings.isProvisioningDataEnabled();
-        } else {
-            return mDataEnabledSettings.isUserDataEnabled();
-        }
+        return getDataSettingsManager().isDataEnabledForReason(
+                TelephonyManager.DATA_ENABLED_REASON_USER);
     }
 
     /**
@@ -2901,11 +3038,12 @@
 
     private void handleRadioAvailable() {
         mCi.getBasebandVersion(obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE));
-
+        mCi.getImei(obtainMessage(EVENT_GET_DEVICE_IMEI_DONE));
         mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
         mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY));
         mCi.areUiccApplicationsEnabled(obtainMessage(EVENT_GET_UICC_APPS_ENABLEMENT_DONE));
 
+        handleNullCipherEnabledChange();
         startLceAfterRadioIsAvailable();
     }
 
@@ -2951,7 +3089,21 @@
                 handleRadioAvailable();
             }
             break;
-
+            case EVENT_GET_DEVICE_IMEI_DONE :
+                ar = (AsyncResult)msg.obj;
+                if (ar.exception != null || ar.result == null) {
+                    loge("Exception received : " + ar.exception);
+                    break;
+                }
+                ImeiInfo imeiInfo = (ImeiInfo) ar.result;
+                if (!TextUtils.isEmpty(imeiInfo.imei)) {
+                    mImeiType = imeiInfo.type;
+                    mImei = imeiInfo.imei;
+                    mImeiSv = imeiInfo.svn;
+                } else {
+                    // TODO Report telephony anomaly
+                }
+                break;
             case EVENT_GET_DEVICE_IDENTITY_DONE:{
                 ar = (AsyncResult)msg.obj;
 
@@ -2959,8 +3111,10 @@
                     break;
                 }
                 String[] respId = (String[])ar.result;
-                mImei = respId[0];
-                mImeiSv = respId[1];
+                if (TextUtils.isEmpty(mImei)) {
+                    mImei = respId[0];
+                    mImeiSv = respId[1];
+                }
                 mEsn  =  respId[2];
                 mMeid =  respId[3];
                 // some modems return all 0's instead of null/empty string when MEID is unavailable
@@ -2985,12 +3139,16 @@
                 logd("Event EVENT_MODEM_RESET Received" + " isInEcm = " + isInEcm()
                         + " isPhoneTypeGsm = " + isPhoneTypeGsm() + " mImsPhone = " + mImsPhone);
                 if (isInEcm()) {
-                    if (isPhoneTypeGsm()) {
-                        if (mImsPhone != null) {
-                            mImsPhone.handleExitEmergencyCallbackMode();
-                        }
+                    if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+                        EmergencyStateTracker.getInstance().exitEmergencyCallbackMode();
                     } else {
-                        handleExitEmergencyCallbackMode(msg);
+                        if (isPhoneTypeGsm()) {
+                            if (mImsPhone != null) {
+                                mImsPhone.handleExitEmergencyCallbackMode();
+                            }
+                        } else {
+                            handleExitEmergencyCallbackMode(msg);
+                        }
                     }
                 }
             }
@@ -3283,24 +3441,9 @@
             case EVENT_SET_CARRIER_DATA_ENABLED:
                 ar = (AsyncResult) msg.obj;
                 boolean enabled = (boolean) ar.result;
-                if (isUsingNewDataStack()) {
-                    getDataSettingsManager().setDataEnabled(
-                            TelephonyManager.DATA_ENABLED_REASON_CARRIER, enabled,
-                            mContext.getOpPackageName());
-                    return;
-                }
-                mDataEnabledSettings.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_CARRIER,
-                        enabled);
-                break;
-            case EVENT_DEVICE_PROVISIONED_CHANGE:
-                if (!isUsingNewDataStack()) {
-                    mDataEnabledSettings.updateProvisionedChanged();
-                }
-                break;
-            case EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE:
-                if (!isUsingNewDataStack()) {
-                    mDataEnabledSettings.updateProvisioningDataEnabled();
-                }
+                getDataSettingsManager().setDataEnabled(
+                        TelephonyManager.DATA_ENABLED_REASON_CARRIER, enabled,
+                        mContext.getOpPackageName());
                 break;
             case EVENT_GET_AVAILABLE_NETWORKS_DONE:
                 ar = (AsyncResult) msg.obj;
@@ -3375,12 +3518,64 @@
                 logd("EVENT_SUBSCRIPTIONS_CHANGED");
                 updateUsageSetting();
                 break;
+            case EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE:
+                logd("EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE");
+                ar = (AsyncResult) msg.obj;
+                if (ar == null || ar.exception == null) {
+                    mIsNullCipherAndIntegritySupported = true;
+                    return;
+                }
+                CommandException.Error error = ((CommandException) ar.exception).getCommandError();
+                mIsNullCipherAndIntegritySupported = !error.equals(
+                        CommandException.Error.REQUEST_NOT_SUPPORTED);
+                break;
 
+            case EVENT_IMS_DEREGISTRATION_TRIGGERED:
+                logd("EVENT_IMS_DEREGISTRATION_TRIGGERED");
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception == null) {
+                    mImsPhone.triggerImsDeregistration(((int[]) ar.result)[0]);
+                } else {
+                    Rlog.e(LOG_TAG, "Unexpected unsol with exception", ar.exception);
+                }
+                break;
+
+            case EVENT_TRIGGER_NOTIFY_ANBR:
+                logd("EVENT_TRIGGER_NOTIFY_ANBR");
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception == null) {
+                    if (mImsPhone != null) {
+                        mImsPhone.triggerNotifyAnbr(((int[]) ar.result)[0], ((int[]) ar.result)[1],
+                                ((int[]) ar.result)[2]);
+                    }
+                }
+                break;
             default:
                 super.handleMessage(msg);
         }
     }
 
+    /**
+     * Check if a different SIM is inserted at this slot from the last time. Storing last subId
+     * in SharedPreference for now to detect SIM change.
+     *
+     * @return {@code true} if current slot mapping changed; {@code false} otherwise.
+     */
+    private boolean currentSlotSubIdChanged() {
+        SharedPreferences sp =
+                PreferenceManager.getDefaultSharedPreferences(mContext);
+        int storedSubId = sp.getInt(CURR_SUBID + mPhoneId, -1);
+        boolean changed = storedSubId != getSubId();
+        if (changed) {
+            // Update stored subId
+            SharedPreferences.Editor editor = sp.edit();
+            editor.putInt(CURR_SUBID + mPhoneId, getSubId());
+            editor.apply();
+        }
+        Rlog.d(LOG_TAG, "currentSlotSubIdChanged: changed=" + changed);
+        return changed;
+    }
+
     public UiccCardApplication getUiccCardApplication() {
         if (isPhoneTypeGsm()) {
             return mUiccController.getUiccCardApplication(mPhoneId, UiccController.APP_FAM_3GPP);
@@ -3747,6 +3942,10 @@
 
     //CDMA
     private void handleEnterEmergencyCallbackMode(Message msg) {
+        if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+            Rlog.d(LOG_TAG, "DomainSelection enabled: ignore ECBM enter event.");
+            return;
+        }
         if (DBG) {
             Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode, isInEcm()="
                     + isInEcm());
@@ -3770,6 +3969,10 @@
 
     //CDMA
     private void handleExitEmergencyCallbackMode(Message msg) {
+        if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+            Rlog.d(LOG_TAG, "DomainSelection enabled: ignore ECBM exit event.");
+            return;
+        }
         AsyncResult ar = (AsyncResult)msg.obj;
         if (DBG) {
             Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode,ar.exception , isInEcm="
@@ -3795,10 +3998,6 @@
 
             // send an Intent
             sendEmergencyCallbackModeChange();
-            // Re-initiate data connection
-            if (!isUsingNewDataStack()) {
-                mDataEnabledSettings.setInternalDataEnabled(true);
-            }
             notifyEmergencyCallRegistrants(false);
         }
         mIsTestingEmergencyCallbackMode = false;
@@ -3816,6 +4015,7 @@
      * otherwise, restart Ecm timer and notify apps the timer is restarted.
      */
     public void handleTimerInEmergencyCallbackMode(int action) {
+        if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) return;
         switch(action) {
             case CANCEL_ECM_TIMER:
                 removeCallbacks(mExitEcmRunnable);
@@ -4229,6 +4429,7 @@
     @Override
     public void setImsRegistrationState(boolean registered) {
         mSST.setImsRegistrationState(registered);
+        mCallWaitingController.setImsRegistrationState(registered);
     }
 
     @Override
@@ -4290,6 +4491,13 @@
                 " mTelecomVoiceServiceStateOverride=" + mTelecomVoiceServiceStateOverride + "("
                         + ServiceState.rilServiceStateToString(mTelecomVoiceServiceStateOverride)
                         + ")");
+        pw.println(" mUiccApplicationsEnabled=" + mUiccApplicationsEnabled);
+        pw.flush();
+        try {
+            mCallWaitingController.dump(pw);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
         pw.flush();
     }
 
@@ -4409,7 +4617,7 @@
         if (subInfo == null || TextUtils.isEmpty(subInfo.getCountryIso())) {
             return null;
         }
-        return subInfo.getCountryIso().toUpperCase();
+        return subInfo.getCountryIso().toUpperCase(Locale.ROOT);
     }
 
     public void notifyEcbmTimerReset(Boolean flag) {
@@ -4515,6 +4723,27 @@
         mVolteSilentRedialRegistrants.notifyRegistrants(ar);
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public void registerForEmergencyDomainSelected(
+            @NonNull Handler h, int what, @Nullable Object obj) {
+        mEmergencyDomainSelectedRegistrants.addUnique(h, what, obj);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void unregisterForEmergencyDomainSelected(@NonNull Handler h) {
+        mEmergencyDomainSelectedRegistrants.remove(h);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void notifyEmergencyDomainSelected(@TransportType int transportType) {
+        logd("notifyEmergencyDomainSelected transportType=" + transportType);
+        mEmergencyDomainSelectedRegistrants.notifyRegistrants(
+                new AsyncResult(null, transportType, null));
+    }
+
     /**
      * Sets the SIM voice message waiting indicator records.
      * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
@@ -4675,6 +4904,8 @@
         // If no card is present or we don't have mUiccApplicationsEnabled yet, do nothing.
         if (slot == null || slot.getCardState() != IccCardStatus.CardState.CARDSTATE_PRESENT
                 || mUiccApplicationsEnabled == null) {
+            loge("reapplyUiccAppsEnablementIfNeeded: slot state="
+                    + (slot != null ? slot.getCardState() : null));
             return;
         }
 
@@ -4687,18 +4918,24 @@
             return;
         }
 
-        SubscriptionInfo info = SubscriptionController.getInstance().getSubInfoForIccId(
-                IccUtils.stripTrailingFs(iccId));
+        SubscriptionInfo info = mSubscriptionManagerService
+                .getAllSubInfoList(mContext.getOpPackageName(), mContext.getAttributionTag())
+                .stream()
+                .filter(subInfo -> subInfo.getIccId().equals(IccUtils.stripTrailingFs(iccId)))
+                .findFirst()
+                .orElse(null);
+
+        logd("reapplyUiccAppsEnablementIfNeeded: retries=" + retries + ", subInfo=" + info);
 
         // If info is null, it could be a new subscription. By default we enable it.
-        boolean expectedValue = info == null ? true : info.areUiccApplicationsEnabled();
+        boolean expectedValue = info == null || info.areUiccApplicationsEnabled();
 
         // If for any reason current state is different from configured state, re-apply the
         // configured state.
         if (expectedValue != mUiccApplicationsEnabled) {
             mCi.enableUiccApplications(expectedValue, Message.obtain(
                     this, EVENT_REAPPLY_UICC_APPS_ENABLEMENT_DONE,
-                    new Pair<Boolean, Integer>(expectedValue, retries)));
+                    new Pair<>(expectedValue, retries)));
         }
     }
 
@@ -4745,24 +4982,7 @@
      * @return Currently bound data service package names.
      */
     public @NonNull List<String> getDataServicePackages() {
-        if (isUsingNewDataStack()) {
-            return getDataNetworkController().getDataServicePackages();
-        }
-        List<String> packages = new ArrayList<>();
-        int[] transports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
-                AccessNetworkConstants.TRANSPORT_TYPE_WLAN};
-
-        for (int transport : transports) {
-            DcTracker dct = getDcTracker(transport);
-            if (dct != null) {
-                String pkg = dct.getDataServiceManager().getDataServicePackageName();
-                if (!TextUtils.isEmpty(pkg)) {
-                    packages.add(pkg);
-                }
-            }
-        }
-
-        return packages;
+        return getDataNetworkController().getDataServicePackages();
     }
 
     private void updateBroadcastEmergencyCallStateChangesAfterCarrierConfigChanged(
@@ -4804,28 +5024,26 @@
 
         boolean mIsVonrEnabledByCarrier =
                 config.getBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL);
-
-        String result = SubscriptionController.getInstance().getSubscriptionProperty(
-                getSubId(),
-                SubscriptionManager.NR_ADVANCED_CALLING_ENABLED);
+        boolean mDefaultVonr =
+                config.getBoolean(CarrierConfigManager.KEY_VONR_ON_BY_DEFAULT_BOOL);
 
         int setting = -1;
-        if (result != null) {
-            setting = Integer.parseInt(result);
+        SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+                .getSubscriptionInfoInternal(getSubId());
+        if (subInfo != null) {
+            setting = subInfo.getNrAdvancedCallingEnabled();
         }
 
         logd("VoNR setting from telephony.db:"
                 + setting
                 + " ,vonr_enabled_bool:"
-                + mIsVonrEnabledByCarrier);
+                + mIsVonrEnabledByCarrier
+                + " ,vonr_on_by_default_bool:"
+                + mDefaultVonr);
 
-        if (!mIsVonrEnabledByCarrier) {
-            mCi.setVoNrEnabled(false, obtainMessage(EVENT_SET_VONR_ENABLED_DONE), null);
-        } else if (setting == 1 || setting == -1) {
-            mCi.setVoNrEnabled(true, obtainMessage(EVENT_SET_VONR_ENABLED_DONE), null);
-        } else if (setting == 0) {
-            mCi.setVoNrEnabled(false, obtainMessage(EVENT_SET_VONR_ENABLED_DONE), null);
-        }
+        boolean enbleVonr = mIsVonrEnabledByCarrier
+                && (setting == 1 || (setting == -1 && mDefaultVonr));
+        mCi.setVoNrEnabled(enbleVonr, obtainMessage(EVENT_SET_VONR_ENABLED_DONE), null);
     }
 
     private void updateCdmaRoamingSettingsAfterCarrierConfigChanged(PersistableBundle config) {
@@ -4887,4 +5105,49 @@
     public InboundSmsHandler getInboundSmsHandler(boolean is3gpp2) {
         return mIccSmsInterfaceManager.getInboundSmsHandler(is3gpp2);
     }
+
+    /**
+     * Return current cell broadcast ranges.
+     */
+    public List<CellBroadcastIdRange> getCellBroadcastIdRanges() {
+        return mCellBroadcastConfigTracker.getCellBroadcastIdRanges();
+    }
+
+    /**
+     * Set reception of cell broadcast messages with the list of the given ranges.
+     */
+    @Override
+    public void setCellBroadcastIdRanges(
+            @NonNull List<CellBroadcastIdRange> ranges, Consumer<Integer> callback) {
+        mCellBroadcastConfigTracker.setCellBroadcastIdRanges(ranges, callback);
+    }
+
+    /**
+     * The following function checks if supplementary service request is blocked due to FDN.
+     * @param requestType request type associated with the supplementary service
+     * @param serviceType supplementary service type
+     * @return {@code true} if request is blocked due to FDN.
+     */
+    private boolean isRequestBlockedByFDN(SsData.RequestType requestType,
+            SsData.ServiceType serviceType) {
+        ArrayList<String> controlStrings = GsmMmiCode.getControlStrings(requestType, serviceType);
+        return FdnUtils.isSuppServiceRequestBlockedByFdn(mPhoneId, controlStrings, getCountryIso());
+    }
+
+    @Override
+    public void handleNullCipherEnabledChange() {
+        if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CELLULAR_SECURITY,
+                TelephonyManager.PROPERTY_ENABLE_NULL_CIPHER_TOGGLE, true)) {
+            logi("Not handling null cipher update. Feature disabled by DeviceConfig.");
+            return;
+        }
+        mCi.setNullCipherAndIntegrityEnabled(
+                getNullCipherAndIntegrityEnabledPreference(),
+                obtainMessage(EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE));
+    }
+
+    @Override
+    public boolean isNullCipherAndIntegritySupported() {
+        return mIsNullCipherAndIntegritySupported;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/HalVersion.java b/src/java/com/android/internal/telephony/HalVersion.java
index f83d790..c05111b 100644
--- a/src/java/com/android/internal/telephony/HalVersion.java
+++ b/src/java/com/android/internal/telephony/HalVersion.java
@@ -25,6 +25,9 @@
  */
 public class HalVersion implements Comparable<HalVersion> {
 
+    /** The HAL Version indicating that the version is unsupported */
+    public static final HalVersion UNSUPPORTED = new HalVersion(-2, -2);
+
     /** The HAL Version indicating that the version is unknown or invalid */
     public static final HalVersion UNKNOWN = new HalVersion(-1, -1);
 
diff --git a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
index 742cc90..ab62aa4 100644
--- a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -39,6 +39,7 @@
 import com.android.telephony.Rlog;
 
 import java.util.List;
+import java.util.Locale;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -337,7 +338,10 @@
         }
 
         efid = updateEfForIccType(efid);
-        if (DBG) logd("getAdnRecordsInEF: efid=0x" + Integer.toHexString(efid).toUpperCase());
+        if (DBG) {
+            logd("getAdnRecordsInEF: efid=0x" + Integer.toHexString(efid)
+                    .toUpperCase(Locale.ROOT));
+        }
 
         checkThread();
         Request loadRequest = new Request();
diff --git a/src/java/com/android/internal/telephony/IccProvider.java b/src/java/com/android/internal/telephony/IccProvider.java
index 7a128c0..b7c7e7b 100644
--- a/src/java/com/android/internal/telephony/IccProvider.java
+++ b/src/java/com/android/internal/telephony/IccProvider.java
@@ -37,6 +37,7 @@
 import com.android.telephony.Rlog;
 
 import java.util.List;
+import java.util.Locale;
 
 /**
  * {@hide}
@@ -424,7 +425,7 @@
 
     private MatrixCursor loadFromEf(int efType, int subId) {
         if (DBG) log("loadFromEf: efType=0x" +
-                Integer.toHexString(efType).toUpperCase() + ", subscription=" + subId);
+                Integer.toHexString(efType).toUpperCase(Locale.ROOT) + ", subscription=" + subId);
 
         List<AdnRecord> adnRecords = null;
         try {
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index a329771..2d77631 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -176,6 +176,41 @@
         mSmsPermissions = smsPermissions;
     }
 
+    /**
+     * PhoneFactory Dependencies for testing.
+     */
+    @VisibleForTesting
+    public interface PhoneFactoryProxy {
+        Phone getPhone(int index);
+        Phone getDefaultPhone();
+        Phone[] getPhones();
+    }
+
+    private PhoneFactoryProxy mPhoneFactoryProxy = new PhoneFactoryProxy() {
+        @Override
+        public Phone getPhone(int index) {
+            return PhoneFactory.getPhone(index);
+        }
+
+        @Override
+        public Phone getDefaultPhone() {
+            return PhoneFactory.getDefaultPhone();
+        }
+
+        @Override
+        public Phone[] getPhones() {
+            return PhoneFactory.getPhones();
+        }
+    };
+
+    /**
+     * Overrides PhoneFactory dependencies for testing.
+     */
+    @VisibleForTesting
+    public void setPhoneFactoryProxy(PhoneFactoryProxy proxy) {
+        mPhoneFactoryProxy = proxy;
+    }
+
     private void enforceNotOnHandlerThread(String methodName) {
         if (Looper.myLooper() == mHandler.getLooper()) {
             throw new RuntimeException("This method " + methodName + " will deadlock if called from"
@@ -457,11 +492,11 @@
      */
     public void sendText(String callingPackage, String destAddr, String scAddr,
             String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
-            boolean persistMessageForNonDefaultSmsApp, long messageId) {
+            boolean persistMessageForNonDefaultSmsApp, long messageId, boolean skipShortCodeCheck) {
         sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
                 persistMessageForNonDefaultSmsApp, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
                 false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, false /* isForVvm */,
-                messageId);
+                messageId, skipShortCodeCheck);
     }
 
     /**
@@ -481,6 +516,16 @@
                 SMS_MESSAGE_PERIOD_NOT_SPECIFIED, isForVvm, 0L /* messageId */);
     }
 
+
+    private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
+            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore,
+            int validityPeriod, boolean isForVvm, long messageId) {
+        sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
+                persistMessageForNonDefaultSmsApp, priority, expectMore, validityPeriod, isForVvm,
+                messageId, false);
+    }
+
     /**
      * Send a text based SMS.
      *
@@ -527,12 +572,13 @@
      *  Any Other values including negative considered as Invalid Validity Period of the message.
      * @param messageId An id that uniquely identifies the message requested to be sent.
      *                 Used for logging and diagnostics purposes. The id may be 0.
+     * @param skipShortCodeCheck Skip check for short code type destination address.
      */
 
     private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
             String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
             boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore,
-            int validityPeriod, boolean isForVvm, long messageId) {
+            int validityPeriod, boolean isForVvm, long messageId, boolean skipShortCodeCheck) {
         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
             log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr
                     + " text='" + text + "' sentIntent=" + sentIntent + " deliveryIntent="
@@ -544,7 +590,7 @@
         destAddr = filterDestAddress(destAddr);
         mDispatchersController.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
                 null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp,
-                priority, expectMore, validityPeriod, isForVvm, messageId);
+                priority, expectMore, validityPeriod, isForVvm, messageId, skipShortCodeCheck);
     }
 
     /**
@@ -625,9 +671,9 @@
         }
 
         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
-            log("pdu: " + pdu +
-                "\n format=" + format +
-                "\n receivedIntent=" + receivedIntent);
+            log("pdu: " + IccUtils.bytesToHexString(pdu)
+                    + "\n format=" + format
+                    + "\n receivedIntent=" + receivedIntent);
         }
         mDispatchersController.injectSmsPdu(pdu, format, false /* isOverIms */,
                 result -> {
@@ -862,6 +908,7 @@
     public String getSmscAddressFromIccEf(String callingPackage) {
         if (!mSmsPermissions.checkCallingOrSelfCanGetSmscAddress(
                 callingPackage, "getSmscAddressFromIccEf")) {
+            loge("Caller do not have permission to call GetSmscAddress");
             return null;
         }
         enforceNotOnHandlerThread("getSmscAddressFromIccEf");
@@ -883,6 +930,7 @@
     public boolean setSmscAddressOnIccEf(String callingPackage, String smsc) {
         if (!mSmsPermissions.checkCallingOrSelfCanSetSmscAddress(
                 callingPackage, "setSmscAddressOnIccEf")) {
+            loge("Caller do not have permission to call SetSmscAddress");
             return false;
         }
         enforceNotOnHandlerThread("setSmscAddressOnIccEf");
@@ -1452,11 +1500,27 @@
         return null;
     }
 
-    private void notifyIfOutgoingEmergencySms(String destAddr) {
+    @VisibleForTesting
+    public void notifyIfOutgoingEmergencySms(String destAddr) {
+        Phone[] allPhones = mPhoneFactoryProxy.getPhones();
         EmergencyNumber emergencyNumber = mPhone.getEmergencyNumberTracker().getEmergencyNumber(
                 destAddr);
         if (emergencyNumber != null) {
             mPhone.notifyOutgoingEmergencySms(emergencyNumber);
+        } else if (allPhones.length > 1) {
+            // If there are multiple active SIMs, check all instances:
+            for (Phone phone : allPhones) {
+                // If the current iteration was already checked, skip:
+                if (phone.getPhoneId() == mPhone.getPhoneId()) {
+                    continue;
+                }
+                emergencyNumber = phone.getEmergencyNumberTracker()
+                        .getEmergencyNumber(destAddr);
+                if (emergencyNumber != null) {
+                    mPhone.notifyOutgoingEmergencySms(emergencyNumber);
+                    break;
+                }
+            }
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/ImsIndication.java b/src/java/com/android/internal/telephony/ImsIndication.java
new file mode 100644
index 0000000..00652f8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ImsIndication.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.TelephonyManager.HAL_SERVICE_IMS;
+
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CONNECTION_SETUP_FAILURE;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NOTIFY_ANBR;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION;
+
+import android.hardware.radio.ims.IRadioImsIndication;
+import android.os.AsyncResult;
+import android.telephony.ims.feature.ConnectionFailureInfo;
+
+/**
+ * Interface declaring unsolicited radio indications for IMS APIs.
+ */
+public class ImsIndication extends IRadioImsIndication.Stub {
+    private final RIL mRil;
+
+    public ImsIndication(RIL ril) {
+        mRil = ril;
+    }
+
+    @Override
+    public String getInterfaceHash() {
+        return IRadioImsIndication.HASH;
+    }
+
+    @Override
+    public int getInterfaceVersion() {
+        return IRadioImsIndication.VERSION;
+    }
+
+    /**
+     * Fired by radio when any IMS traffic is not sent to network due to any failure
+     * on cellular networks.
+     *
+     * @param indicationType Type of radio indication.
+     * @param token The token provided by {@link #startImsTraffic}.
+     * @param failureInfo Connection failure information.
+     */
+    public void onConnectionSetupFailure(int indicationType, int token,
+            android.hardware.radio.ims.ConnectionFailureInfo failureInfo) {
+        mRil.processIndication(HAL_SERVICE_IMS, indicationType);
+
+        Object[] response = new Object[2];
+        response[0] = token;
+        response[1] = new ConnectionFailureInfo(
+                RILUtils.convertHalConnectionFailureReason(failureInfo.failureReason),
+                failureInfo.causeCode, failureInfo.waitTimeMillis);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CONNECTION_SETUP_FAILURE, response);
+
+        mRil.mConnectionSetupFailureRegistrants.notifyRegistrants(
+                new AsyncResult(null, response, null));
+    }
+
+    /**
+     * Fired by radio when ANBR is received form the network.
+     *
+     * @param indicationType Type of radio indication.
+     * @param qosSessionId QoS session ID is used to identify media stream such as audio or video.
+     * @param imsdirection Direction of this packet stream (e.g. uplink or downlink).
+     * @param bitsPerSecond The recommended bit rate for the UE
+     *        for a specific logical channel and a specific direction by the network.
+     */
+    public void notifyAnbr(int indicationType, int qosSessionId, int imsdirection,
+            int bitsPerSecond) {
+        mRil.processIndication(HAL_SERVICE_IMS, indicationType);
+
+        int[] response = new int[3];
+        response[0] = qosSessionId;
+        response[1] = imsdirection;
+        response[2] = bitsPerSecond;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NOTIFY_ANBR, response);
+
+        mRil.mNotifyAnbrRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
+    }
+
+    /**
+     * Requests IMS stack to perform graceful IMS deregistration before radio performing
+     * network detach in the events of SIM remove, refresh or and so on. The radio waits for
+     * the IMS deregistration, which will be notified by telephony via
+     * {@link IRadioIms#updateImsRegistrationInfo()}, or a certain timeout interval to start
+     * the network detach procedure.
+     *
+     * @param indicationType Type of radio indication.
+     * @param reason the reason why the deregistration is triggered.
+     */
+    public void triggerImsDeregistration(int indicationType, int reason) {
+        mRil.processIndication(HAL_SERVICE_IMS, indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION, reason);
+
+        int[] response = new int[1];
+        response[0] = RILUtils.convertHalDeregistrationReason(reason);
+
+        mRil.mTriggerImsDeregistrationRegistrants.notifyRegistrants(
+                new AsyncResult(null, response, null));
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ImsResponse.java b/src/java/com/android/internal/telephony/ImsResponse.java
new file mode 100644
index 0000000..1adc000
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ImsResponse.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.TelephonyManager.HAL_SERVICE_IMS;
+import static android.telephony.ims.feature.MmTelFeature.ImsTrafficSessionCallbackWrapper.INVALID_TOKEN;
+
+import android.hardware.radio.RadioError;
+import android.hardware.radio.RadioResponseInfo;
+import android.hardware.radio.ims.IRadioImsResponse;
+import android.telephony.ims.feature.ConnectionFailureInfo;
+
+/**
+ * Interface declaring response functions to solicited radio requests for IMS APIs.
+ */
+public class ImsResponse extends IRadioImsResponse.Stub {
+    private final RIL mRil;
+
+    public ImsResponse(RIL ril) {
+        mRil = ril;
+    }
+
+    @Override
+    public String getInterfaceHash() {
+        return IRadioImsResponse.HASH;
+    }
+
+    @Override
+    public int getInterfaceVersion() {
+        return IRadioImsResponse.VERSION;
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void setSrvccCallInfoResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void updateImsRegistrationInfoResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error.
+     * @param failureInfo Failure information.
+     */
+    public void startImsTrafficResponse(RadioResponseInfo responseInfo,
+            android.hardware.radio.ims.ConnectionFailureInfo failureInfo) {
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_IMS, responseInfo);
+
+        if (rr != null) {
+            Object[] response = { new Integer(INVALID_TOKEN), null };
+            if (responseInfo.error == RadioError.NONE) {
+                if (failureInfo != null) {
+                    response[1] = new ConnectionFailureInfo(
+                            RILUtils.convertHalConnectionFailureReason(failureInfo.failureReason),
+                            failureInfo.causeCode, failureInfo.waitTimeMillis);
+                }
+                RadioResponse.sendMessageResponse(rr.mResult, response);
+            }
+            mRil.processResponseDone(rr, responseInfo, response);
+        }
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void stopImsTrafficResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void triggerEpsFallbackResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void sendAnbrQueryResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+    }
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error.
+     */
+    public void updateImsCallStatusResponse(RadioResponseInfo info) {
+        RadioResponse.responseVoid(HAL_SERVICE_IMS, mRil, info);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index 42fe7a7..90885fe 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import android.app.Activity;
 import android.content.Context;
 import android.os.Binder;
 import android.os.Message;
@@ -43,7 +44,9 @@
 import com.android.internal.telephony.util.SMSDispatcherUtil;
 import com.android.telephony.Rlog;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
@@ -58,6 +61,7 @@
 
     private static final String TAG = "ImsSmsDispatcher";
     private static final int CONNECT_DELAY_MS = 5000; // 5 seconds;
+    public static final int MAX_SEND_RETRIES_OVER_IMS = MAX_SEND_RETRIES;
 
     /**
      * Creates FeatureConnector instances for ImsManager, used during testing to inject mock
@@ -72,6 +76,7 @@
                 FeatureConnector.Listener<ImsManager> listener, Executor executor);
     }
 
+    public List<Integer> mMemoryAvailableNotifierList = new ArrayList<Integer>();
     @VisibleForTesting
     public Map<Integer, SmsTracker> mTrackers = new ConcurrentHashMap<>();
     @VisibleForTesting
@@ -140,6 +145,37 @@
 
     private final IImsSmsListener mImsSmsListener = new IImsSmsListener.Stub() {
         @Override
+        public void onMemoryAvailableResult(int token, @SendStatusResult int status,
+                int networkReasonCode) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                logd("onMemoryAvailableResult token=" + token + " status=" + status
+                        + " networkReasonCode=" + networkReasonCode);
+                if (!mMemoryAvailableNotifierList.contains(token)) {
+                    loge("onMemoryAvailableResult Invalid token");
+                    return;
+                }
+                mMemoryAvailableNotifierList.remove(Integer.valueOf(token));
+
+                /**
+                 * The Retrans flag is set and reset As per section 6.3.3.1.2 in TS 124011
+                 * Note: Assuming that SEND_STATUS_ERROR_RETRY is sent in case of temporary failure
+                 */
+                if (status ==  ImsSmsImplBase.SEND_STATUS_ERROR_RETRY) {
+                    if (!mRPSmmaRetried) {
+                        sendMessageDelayed(obtainMessage(EVENT_RETRY_SMMA), SEND_RETRY_DELAY);
+                        mRPSmmaRetried = true;
+                    } else {
+                        mRPSmmaRetried = false;
+                    }
+                } else {
+                    mRPSmmaRetried = false;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+        @Override
         public void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
                 @SmsManager.Result int reason, int networkReasonCode) {
             final long identity = Binder.clearCallingIdentity();
@@ -170,10 +206,19 @@
                         mTrackers.remove(token);
                         break;
                     case ImsSmsImplBase.SEND_STATUS_ERROR_RETRY:
-                        if (tracker.mRetryCount < MAX_SEND_RETRIES) {
+                        int maxRetryCountOverIms = getMaxRetryCountOverIms();
+                        if (tracker.mRetryCount < getMaxSmsRetryCount()) {
+                            if (maxRetryCountOverIms < getMaxSmsRetryCount()
+                                    && tracker.mRetryCount >= maxRetryCountOverIms) {
+                                tracker.mRetryCount += 1;
+                                mTrackers.remove(token);
+                                fallbackToPstn(tracker);
+                                break;
+                            }
                             tracker.mRetryCount += 1;
                             sendMessageDelayed(
-                                    obtainMessage(EVENT_SEND_RETRY, tracker), SEND_RETRY_DELAY);
+                                    obtainMessage(EVENT_SEND_RETRY, tracker),
+                                    getSmsRetryDelayValue());
                         } else {
                             tracker.onFailed(mContext, reason, networkReasonCode);
                             mTrackers.remove(token);
@@ -193,6 +238,7 @@
                         SmsConstants.FORMAT_3GPP2.equals(getFormat()),
                         status == ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK,
                         reason,
+                        networkReasonCode,
                         tracker.mMessageId,
                         tracker.isFromDefaultSmsApplication(mContext),
                         tracker.getInterval());
@@ -248,6 +294,10 @@
                             mappedResult =
                                     ImsSmsImplBase.DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED;
                             break;
+                        case Activity.RESULT_OK:
+                            // class2 message saving to SIM operation is in progress, defer ack
+                            // until saving to SIM is success/failure
+                            return;
                         default:
                             mappedResult = ImsSmsImplBase.DELIVER_STATUS_ERROR_GENERIC;
                             break;
@@ -263,7 +313,7 @@
                     } catch (ImsException e) {
                         loge("Failed to acknowledgeSms(). Error: " + e.getMessage());
                     }
-                }, true /* ignoreClass */, true /* isOverIms */);
+                }, true /* ignoreClass */, true /* isOverIms */, token);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -277,6 +327,10 @@
                 logd("SMS retry..");
                 sendSms((SmsTracker) msg.obj);
                 break;
+            case EVENT_RETRY_SMMA:
+                logd("SMMA Notification retry..");
+                onMemoryAvailable();
+                break;
             default:
                 super.handleMessage(msg);
         }
@@ -295,6 +349,9 @@
                             mImsManager = manager;
                             setListeners();
                             mIsImsServiceUp = true;
+
+                            /* set ImsManager */
+                            mSmsDispatchersController.setImsManager(mImsManager);
                         }
                     }
 
@@ -309,6 +366,9 @@
                         synchronized (mLock) {
                             mImsManager = null;
                             mIsImsServiceUp = false;
+
+                            /* unset ImsManager */
+                            mSmsDispatchersController.setImsManager(null);
                         }
                     }
                 }, this::post);
@@ -394,6 +454,81 @@
     }
 
     @Override
+    public int getMaxSmsRetryCount() {
+        int retryCount = MAX_SEND_RETRIES;
+        CarrierConfigManager mConfigManager;
+
+        mConfigManager = (CarrierConfigManager)  mContext.getSystemService(
+                Context.CARRIER_CONFIG_SERVICE);
+
+        if (mConfigManager != null) {
+            PersistableBundle carrierConfig = mConfigManager.getConfigForSubId(
+                    getSubId());
+
+            if (carrierConfig != null) {
+                retryCount = carrierConfig.getInt(
+                        CarrierConfigManager.ImsSms.KEY_SMS_MAX_RETRY_COUNT_INT);
+            }
+        }
+
+        Rlog.d(TAG, "Retry Count: " + retryCount);
+
+        return retryCount;
+    }
+
+    /**
+     * Returns the number of times SMS can be sent over IMS
+     *
+     * @return  retry count over Ims from  carrier configuration
+     */
+    @VisibleForTesting
+    public int getMaxRetryCountOverIms() {
+        int retryCountOverIms = MAX_SEND_RETRIES_OVER_IMS;
+        CarrierConfigManager mConfigManager;
+
+        mConfigManager = (CarrierConfigManager) mContext.getSystemService(Context
+                                                        .CARRIER_CONFIG_SERVICE);
+
+        if (mConfigManager != null) {
+            PersistableBundle carrierConfig = mConfigManager.getConfigForSubId(
+                    getSubId());
+
+
+            if (carrierConfig != null) {
+                retryCountOverIms = carrierConfig.getInt(
+                        CarrierConfigManager.ImsSms.KEY_SMS_MAX_RETRY_OVER_IMS_COUNT_INT);
+            }
+        }
+
+        Rlog.d(TAG, "Retry Count Over Ims: " + retryCountOverIms);
+
+        return retryCountOverIms;
+    }
+
+    @Override
+    public int getSmsRetryDelayValue() {
+        int retryDelay = SEND_RETRY_DELAY;
+        CarrierConfigManager mConfigManager;
+
+        mConfigManager = (CarrierConfigManager)  mContext.getSystemService(
+                Context.CARRIER_CONFIG_SERVICE);
+
+        if (mConfigManager != null) {
+            PersistableBundle carrierConfig = mConfigManager.getConfigForSubId(
+                    getSubId());
+
+            if (carrierConfig != null) {
+                retryDelay = carrierConfig.getInt(
+                        CarrierConfigManager.ImsSms.KEY_SMS_OVER_IMS_SEND_RETRY_DELAY_MILLIS_INT);
+            }
+        }
+
+        Rlog.d(TAG, "Retry delay: " + retryDelay);
+
+        return retryDelay;
+    }
+
+    @Override
     protected boolean shouldBlockSmsForEcbm() {
         // We should not block outgoing SMS during ECM on IMS. It only applies to outgoing CDMA
         // SMS.
@@ -416,10 +551,43 @@
     }
 
     @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            String message, boolean statusReportRequested, SmsHeader smsHeader, int priority,
+            int validityPeriod, int messageRef) {
+        return SMSDispatcherUtil.getSubmitPdu(isCdmaMo(), scAddr, destAddr, message,
+                statusReportRequested, smsHeader, priority, validityPeriod, messageRef);
+    }
+
+    @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            int destPort, byte[] message, boolean statusReportRequested, int messageRef) {
+        return SMSDispatcherUtil.getSubmitPdu(isCdmaMo(), scAddr, destAddr, destPort, message,
+                statusReportRequested, messageRef);
+    }
+
+    @Override
     protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
         return SMSDispatcherUtil.calculateLength(isCdmaMo(), messageBody, use7bitOnly);
     }
 
+    /**
+     * Send the Memory Available Event to the ImsService
+     */
+    public void onMemoryAvailable() {
+        logd("onMemoryAvailable ");
+        int token = mNextToken.incrementAndGet();
+        try {
+            logd("onMemoryAvailable: token = " + token);
+            mMemoryAvailableNotifierList.add(token);
+            getImsManager().onMemoryAvailable(token);
+        } catch (ImsException e) {
+            loge("onMemoryAvailable failed: " + e.getMessage());
+            if (mMemoryAvailableNotifierList.contains(token)) {
+                mMemoryAvailableNotifierList.remove(Integer.valueOf(token));
+            }
+        }
+    }
+
     @Override
     public void sendSms(SmsTracker tracker) {
         logd("sendSms: "
@@ -489,6 +657,7 @@
 
     @VisibleForTesting
     public void fallbackToPstn(SmsTracker tracker) {
+        tracker.mMessageRef = nextMessageRef();
         mSmsDispatchersController.sendRetrySms(tracker);
     }
 
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index eeda1f9..9166719 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -45,7 +45,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.database.SQLException;
@@ -53,11 +52,10 @@
 import android.os.AsyncResult;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.PowerWhitelistManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
@@ -89,7 +87,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
@@ -116,38 +113,6 @@
     protected static final boolean DBG = true;
     protected static final boolean VDBG = false; // STOPSHIP if true, logs user data
 
-    /** Query projection for checking for duplicate message segments. */
-    private static final String[] PDU_DELETED_FLAG_PROJECTION = {
-            "pdu",
-            "deleted"
-    };
-
-    /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */
-    private static final Map<Integer, Integer> PDU_DELETED_FLAG_PROJECTION_INDEX_MAPPING =
-            new HashMap<Integer, Integer>() {{
-            put(PDU_COLUMN, 0);
-            put(DELETED_FLAG_COLUMN, 1);
-            }};
-
-    /** Query projection for combining concatenated message segments. */
-    private static final String[] PDU_SEQUENCE_PORT_PROJECTION = {
-            "pdu",
-            "sequence",
-            "destination_port",
-            "display_originating_addr",
-            "date"
-    };
-
-    /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */
-    private static final Map<Integer, Integer> PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING =
-            new HashMap<Integer, Integer>() {{
-                put(PDU_COLUMN, 0);
-                put(SEQUENCE_COLUMN, 1);
-                put(DESTINATION_PORT_COLUMN, 2);
-                put(DISPLAY_ADDRESS_COLUMN, 3);
-                put(DATE_COLUMN, 4);
-    }};
-
     public static final int PDU_COLUMN = 0;
     public static final int SEQUENCE_COLUMN = 1;
     public static final int DESTINATION_PORT_COLUMN = 2;
@@ -161,6 +126,34 @@
     public static final int DELETED_FLAG_COLUMN = 10;
     public static final int SUBID_COLUMN = 11;
 
+    /** Query projection for checking for duplicate message segments. */
+    private static final String[] PDU_DELETED_FLAG_PROJECTION = {
+            "pdu",
+            "deleted"
+    };
+
+    /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */
+    private static final Map<Integer, Integer> PDU_DELETED_FLAG_PROJECTION_INDEX_MAPPING = Map.of(
+            PDU_COLUMN, 0,
+            DELETED_FLAG_COLUMN, 1);
+
+    /** Query projection for combining concatenated message segments. */
+    private static final String[] PDU_SEQUENCE_PORT_PROJECTION = {
+            "pdu",
+            "sequence",
+            "destination_port",
+            "display_originating_addr",
+            "date"
+    };
+
+    /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */
+    private static final Map<Integer, Integer> PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING = Map.of(
+            PDU_COLUMN, 0,
+            SEQUENCE_COLUMN, 1,
+            DESTINATION_PORT_COLUMN, 2,
+            DISPLAY_ADDRESS_COLUMN, 3,
+            DATE_COLUMN, 4);
+
     public static final String SELECT_BY_ID = "_id=?";
 
     /** New SMS received as an AsyncResult. */
@@ -190,6 +183,7 @@
     /** BroadcastReceiver timed out waiting for an intent */
     public static final int EVENT_RECEIVER_TIMEOUT = 9;
 
+
     /** Wakelock release delay when returning to idle state. */
     private static final int WAKELOCK_TIMEOUT = 3000;
 
@@ -294,8 +288,8 @@
      * @param storageMonitor the SmsStorageMonitor to check for storage availability
      */
     protected InboundSmsHandler(String name, Context context, SmsStorageMonitor storageMonitor,
-            Phone phone) {
-        super(name);
+            Phone phone, Looper looper) {
+        super(name, looper);
 
         mContext = context;
         mStorageMonitor = storageMonitor;
@@ -506,7 +500,6 @@
                 case EVENT_RETURN_TO_IDLE:
                     // already in idle state; ignore
                     return HANDLED;
-
                 case EVENT_BROADCAST_COMPLETE:
                 case EVENT_START_ACCEPTING_SMS:
                 default:
@@ -545,7 +538,8 @@
 
                 case EVENT_INJECT_SMS:
                     // handle new injected SMS
-                    handleInjectSms((AsyncResult) msg.obj, msg.arg1 == 1 /* isOverIms */);
+                    handleInjectSms((AsyncResult) msg.obj, msg.arg1 == 1 /* isOverIms */,
+                            msg.arg2 /* token */);
                     sendMessage(EVENT_RETURN_TO_IDLE);
                     return HANDLED;
 
@@ -668,7 +662,6 @@
             }
         }
     }
-
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private void handleNewSms(AsyncResult ar) {
         if (ar.exception != null) {
@@ -679,7 +672,7 @@
         int result;
         try {
             SmsMessage sms = (SmsMessage) ar.result;
-            result = dispatchMessage(sms.mWrappedSmsMessage, SOURCE_NOT_INJECTED);
+            result = dispatchMessage(sms.mWrappedSmsMessage, SOURCE_NOT_INJECTED, 0 /*unused*/);
         } catch (RuntimeException ex) {
             loge("Exception dispatching message", ex);
             result = RESULT_SMS_DISPATCH_FAILURE;
@@ -698,7 +691,7 @@
      * @param ar is the AsyncResult that has the SMS PDU to be injected.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private void handleInjectSms(AsyncResult ar, boolean isOverIms) {
+    private void handleInjectSms(AsyncResult ar, boolean isOverIms, int token) {
         int result;
         SmsDispatchersController.SmsInjectionCallback callback = null;
         try {
@@ -710,7 +703,7 @@
             } else {
                 @SmsSource int smsSource =
                         isOverIms ? SOURCE_INJECTED_FROM_IMS : SOURCE_INJECTED_FROM_UNKNOWN;
-                result = dispatchMessage(sms.mWrappedSmsMessage, smsSource);
+                result = dispatchMessage(sms.mWrappedSmsMessage, smsSource, token);
             }
         } catch (RuntimeException ex) {
             loge("Exception dispatching message", ex);
@@ -731,7 +724,7 @@
      * @return a result code from {@link android.provider.Telephony.Sms.Intents},
      *  or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
      */
-    private int dispatchMessage(SmsMessageBase smsb, @SmsSource int smsSource) {
+    private int dispatchMessage(SmsMessageBase smsb, @SmsSource int smsSource, int token) {
         // If sms is null, there was a parsing error.
         if (smsb == null) {
             loge("dispatchSmsMessage: message is null");
@@ -745,20 +738,7 @@
             return Intents.RESULT_SMS_HANDLED;
         }
 
-        // onlyCore indicates if the device is in cryptkeeper
-        boolean onlyCore = false;
-        try {
-            onlyCore = IPackageManager.Stub.asInterface(ServiceManager.getService("package"))
-                    .isOnlyCoreApps();
-        } catch (RemoteException e) {
-        }
-        if (onlyCore) {
-            // Device is unable to receive SMS in encrypted state
-            log("Received a short message in encrypted state. Rejecting.");
-            return Intents.RESULT_SMS_RECEIVED_WHILE_ENCRYPTED;
-        }
-
-        int result = dispatchMessageRadioSpecific(smsb, smsSource);
+        int result = dispatchMessageRadioSpecific(smsb, smsSource, token);
 
         // In case of error, add to metrics. This is not required in case of success, as the
         // data will be tracked when the message is processed (processMessagePart).
@@ -780,7 +760,7 @@
      *  or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
      */
     protected abstract int dispatchMessageRadioSpecific(SmsMessageBase smsb,
-            @SmsSource int smsSource);
+            @SmsSource int smsSource, int token);
 
     /**
      * Send an acknowledge message to the SMSC.
@@ -880,7 +860,6 @@
      * <code>RESULT_SMS_DISPATCH_FAILURE</code><br>
      * <code>RESULT_SMS_NULL_PDU</code><br>
      * <code>RESULT_SMS_NULL_MESSAGE</code><br>
-     * <code>RESULT_SMS_RECEIVED_WHILE_ENCRYPTED</code><br>
      * <code>RESULT_SMS_DATABASE_ERROR</code><br>
      * <code>RESULT_SMS_INVALID_URI</code><br>
      */
@@ -1164,7 +1143,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private void showNewMessageNotification() {
         // Do not show the notification on non-FBE devices.
-        if (!StorageManager.isFileEncryptedNativeOrEmulated()) {
+        if (!StorageManager.isFileEncrypted()) {
             return;
         }
         log("Show new message notification.");
@@ -1458,12 +1437,14 @@
             intent.putExtra("messageId", messageId);
         }
 
+        UserHandle userHandle = null;
         if (destPort == -1) {
             intent.setAction(Intents.SMS_DELIVER_ACTION);
             // Direct the intent to only the default SMS app. If we can't find a default SMS app
             // then sent it to all broadcast receivers.
-            // We are deliberately delivering to the primary user's default SMS App.
-            ComponentName componentName = SmsApplication.getDefaultSmsApplication(mContext, true);
+            userHandle = TelephonyUtils.getSubscriptionUserHandle(mContext, subId);
+            ComponentName componentName = SmsApplication.getDefaultSmsApplicationAsUser(mContext,
+                    true, userHandle);
             if (componentName != null) {
                 // Deliver SMS message only to this receiver.
                 intent.setComponent(componentName);
@@ -1487,9 +1468,12 @@
             intent.setComponent(null);
         }
 
+        if (userHandle == null) {
+            userHandle = UserHandle.SYSTEM;
+        }
         Bundle options = handleSmsWhitelisting(intent.getComponent(), isClass0);
         dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
-                AppOpsManager.OPSTR_RECEIVE_SMS, options, resultReceiver, UserHandle.SYSTEM, subId);
+                AppOpsManager.OPSTR_RECEIVE_SMS, options, resultReceiver, userHandle, subId);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
old mode 100755
new mode 100644
index 4e0f452..de854fa
--- a/src/java/com/android/internal/telephony/LocaleTracker.java
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -30,6 +30,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.UserHandle;
 import android.sysprop.TelephonyProperties;
 import android.telephony.CellInfo;
 import android.telephony.ServiceState;
@@ -486,20 +487,12 @@
         String countryIso = "";
         String countryIsoDebugInfo = "empty as default";
 
-        // For time zone detection we want the best geographical match we can get, which may differ
-        // from the countryIso.
-        String timeZoneCountryIso = null;
-        String timeZoneCountryIsoDebugInfo = null;
-
         if (!TextUtils.isEmpty(mOperatorNumeric)) {
             MccMnc mccMnc = MccMnc.fromOperatorNumeric(mOperatorNumeric);
             if (mccMnc != null) {
-                countryIso = MccTable.countryCodeForMcc(mccMnc.mcc);
+                countryIso = MccTable.geoCountryCodeForMccMnc(mccMnc);
                 countryIsoDebugInfo = "OperatorNumeric(" + mOperatorNumeric
-                        + "): MccTable.countryCodeForMcc(\"" + mccMnc.mcc + "\")";
-                timeZoneCountryIso = MccTable.geoCountryCodeForMccMnc(mccMnc);
-                timeZoneCountryIsoDebugInfo =
-                        "OperatorNumeric: MccTable.geoCountryCodeForMccMnc(" + mccMnc + ")";
+                        + "): MccTable.geoCountryCodeForMccMnc(\"" + mccMnc + "\")";
             } else {
                 loge("updateLocale: Can't get country from operator numeric. mOperatorNumeric = "
                         + mOperatorNumeric);
@@ -509,15 +502,19 @@
         // If for any reason we can't get country from operator numeric, try to get it from cell
         // info.
         if (TextUtils.isEmpty(countryIso)) {
+            // Find the most prevalent MCC from surrounding cell towers.
             String mcc = getMccFromCellInfo();
             if (mcc != null) {
                 countryIso = MccTable.countryCodeForMcc(mcc);
                 countryIsoDebugInfo = "CellInfo: MccTable.countryCodeForMcc(\"" + mcc + "\")";
 
+                // Some MCC+MNC combinations are known to be used in countries other than those
+                // that the MCC alone would suggest. Do a second pass of nearby cells that match
+                // the most frequently observed MCC to see if this could be one of those cases.
                 MccMnc mccMnc = getMccMncFromCellInfo(mcc);
                 if (mccMnc != null) {
-                    timeZoneCountryIso = MccTable.geoCountryCodeForMccMnc(mccMnc);
-                    timeZoneCountryIsoDebugInfo =
+                    countryIso = MccTable.geoCountryCodeForMccMnc(mccMnc);
+                    countryIsoDebugInfo =
                             "CellInfo: MccTable.geoCountryCodeForMccMnc(" + mccMnc + ")";
                 }
             }
@@ -526,8 +523,6 @@
         if (mCountryOverride != null) {
             countryIso = mCountryOverride;
             countryIsoDebugInfo = "mCountryOverride = \"" + mCountryOverride + "\"";
-            timeZoneCountryIso = countryIso;
-            timeZoneCountryIsoDebugInfo = countryIsoDebugInfo;
         }
 
         if (!mPhone.isRadioOn()) {
@@ -564,11 +559,13 @@
             intent.putExtra(TelephonyManager.EXTRA_LAST_KNOWN_NETWORK_COUNTRY,
                     getLastKnownCountryIso());
             SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
-            mPhone.getContext().sendBroadcast(intent);
+            mPhone.getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
         }
 
         // Pass the geographical country information to the telephony time zone detection code.
 
+        String timeZoneCountryIso = countryIso;
+        String timeZoneCountryIsoDebugInfo = countryIsoDebugInfo;
         boolean isTestMcc = false;
         if (!TextUtils.isEmpty(mOperatorNumeric)) {
             // For a test cell (MCC 001), the NitzStateMachine requires handleCountryDetected("") in
@@ -579,11 +576,6 @@
                 timeZoneCountryIsoDebugInfo = "Test cell: " + mOperatorNumeric;
             }
         }
-        if (timeZoneCountryIso == null) {
-            // After this timeZoneCountryIso may still be null.
-            timeZoneCountryIso = countryIso;
-            timeZoneCountryIsoDebugInfo = "Defaulted: " + countryIsoDebugInfo;
-        }
         log("updateLocale: timeZoneCountryIso = " + timeZoneCountryIso
                 + ", timeZoneCountryIsoDebugInfo = " + timeZoneCountryIsoDebugInfo);
 
diff --git a/src/java/com/android/internal/telephony/MessagingIndication.java b/src/java/com/android/internal/telephony/MessagingIndication.java
index 86e30b0..5814e3d 100644
--- a/src/java/com/android/internal/telephony/MessagingIndication.java
+++ b/src/java/com/android/internal/telephony/MessagingIndication.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING;
+
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_CDMA_NEW_SMS;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS;
@@ -47,9 +49,9 @@
      */
     public void cdmaNewSms(int indicationType,
             android.hardware.radio.messaging.CdmaSmsMessage msg) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CDMA_NEW_SMS);
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_CDMA_NEW_SMS);
 
         SmsMessage sms = new SmsMessage(RILUtils.convertHalCdmaSmsMessage(msg));
         if (mRil.mCdmaSmsRegistrant != null) {
@@ -63,9 +65,9 @@
      * @param indicationType Type of radio indication
      */
     public void cdmaRuimSmsStorageFull(int indicationType) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL);
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL);
 
         if (mRil.mIccSmsFullRegistrant != null) {
             mRil.mIccSmsFullRegistrant.notifyRegistrant();
@@ -82,11 +84,11 @@
      *        BTS as coded in 3GPP 23.041 Section 9.4.2.2
      */
     public void newBroadcastSms(int indicationType, byte[] data) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
 
-        if (RIL.RILJ_LOGD) {
-            mRil.unsljLogvRet(RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS,
-                    IccUtils.bytesToHexString(data));
+        if (mRil.isLogOrTrace()) {
+            mRil.unsljLogvRet(
+                    RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS, IccUtils.bytesToHexString(data));
         }
 
         if (mRil.mGsmBroadcastSmsRegistrant != null) {
@@ -101,8 +103,8 @@
      *        The PDU starts with the SMSC address per TS 27.005 (+CMT:)
      */
     public void newSms(int indicationType, byte[] pdu) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
-        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS);
 
         SmsMessageBase smsb = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
         if (mRil.mGsmSmsRegistrant != null) {
@@ -117,9 +119,9 @@
      * @param recordNumber Record number on the SIM
      */
     public void newSmsOnSim(int indicationType, int recordNumber) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM);
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM);
 
         if (mRil.mSmsOnSimRegistrant != null) {
             mRil.mSmsOnSimRegistrant.notifyRegistrant(new AsyncResult(null, recordNumber, null));
@@ -133,9 +135,9 @@
      *        The PDU starts with the SMSC address per TS 27.005 (+CMT:)
      */
     public void newSmsStatusReport(int indicationType, byte[] pdu) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT);
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT);
 
         if (mRil.mSmsStatusRegistrant != null) {
             mRil.mSmsStatusRegistrant.notifyRegistrant(new AsyncResult(null, pdu, null));
@@ -149,9 +151,9 @@
      * @param indicationType Type of radio indication
      */
     public void simSmsStorageFull(int indicationType) {
-        mRil.processIndication(RIL.MESSAGING_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_SIM_SMS_STORAGE_FULL);
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SIM_SMS_STORAGE_FULL);
 
         if (mRil.mIccSmsFullRegistrant != null) {
             mRil.mIccSmsFullRegistrant.notifyRegistrant();
diff --git a/src/java/com/android/internal/telephony/MessagingResponse.java b/src/java/com/android/internal/telephony/MessagingResponse.java
index 3dc1d1a..19211e1 100644
--- a/src/java/com/android/internal/telephony/MessagingResponse.java
+++ b/src/java/com/android/internal/telephony/MessagingResponse.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING;
+
 import android.hardware.radio.RadioError;
 import android.hardware.radio.RadioResponseInfo;
 import android.hardware.radio.messaging.IRadioMessagingResponse;
@@ -36,7 +38,7 @@
 
     private void responseSms(RadioResponseInfo responseInfo,
             android.hardware.radio.messaging.SendSmsResult sms) {
-        RILRequest rr = mRil.processResponse(RIL.MESSAGING_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MESSAGING, responseInfo);
 
         if (rr != null) {
             long messageId = RIL.getOutgoingSmsMessageId(rr.mResult);
@@ -62,35 +64,35 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void acknowledgeIncomingGsmSmsWithPduResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void acknowledgeLastIncomingCdmaSmsResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void acknowledgeLastIncomingGsmSmsResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void deleteSmsOnRuimResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void deleteSmsOnSimResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
@@ -99,7 +101,7 @@
      */
     public void getCdmaBroadcastConfigResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.messaging.CdmaBroadcastSmsConfigInfo[] configs) {
-        RILRequest rr = mRil.processResponse(RIL.MESSAGING_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MESSAGING, responseInfo);
 
         if (rr != null) {
             int[] ret;
@@ -148,7 +150,7 @@
      */
     public void getGsmBroadcastConfigResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.messaging.GsmBroadcastSmsConfigInfo[] configs) {
-        RILRequest rr = mRil.processResponse(RIL.MESSAGING_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MESSAGING, responseInfo);
 
         if (rr != null) {
             ArrayList<SmsBroadcastConfigInfo> ret = new ArrayList<>();
@@ -168,14 +170,14 @@
      * @param smsc Short Message Service Center address on the device
      */
     public void getSmscAddressResponse(RadioResponseInfo responseInfo, String smsc) {
-        RadioResponse.responseString(RIL.MESSAGING_SERVICE, mRil, responseInfo, smsc);
+        RadioResponse.responseString(HAL_SERVICE_MESSAGING, mRil, responseInfo, smsc);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void reportSmsMemoryStatusResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
@@ -227,35 +229,35 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setCdmaBroadcastActivationResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setCdmaBroadcastConfigResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setGsmBroadcastActivationResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setGsmBroadcastConfigResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setSmscAddressResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MESSAGING_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MESSAGING, mRil, responseInfo);
     }
 
     /**
@@ -263,7 +265,7 @@
      * @param index record index where the CDMA SMS message is stored
      */
     public void writeSmsToRuimResponse(RadioResponseInfo responseInfo, int index) {
-        RadioResponse.responseInts(RIL.MESSAGING_SERVICE, mRil, responseInfo, index);
+        RadioResponse.responseInts(HAL_SERVICE_MESSAGING, mRil, responseInfo, index);
     }
 
     /**
@@ -271,7 +273,7 @@
      * @param index record index where the message is stored
      */
     public void writeSmsToSimResponse(RadioResponseInfo responseInfo, int index) {
-        RadioResponse.responseInts(RIL.MESSAGING_SERVICE, mRil, responseInfo, index);
+        RadioResponse.responseInts(HAL_SERVICE_MESSAGING, mRil, responseInfo, index);
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/MissedIncomingCallSmsFilter.java b/src/java/com/android/internal/telephony/MissedIncomingCallSmsFilter.java
index 5932f9e..dce65af 100644
--- a/src/java/com/android/internal/telephony/MissedIncomingCallSmsFilter.java
+++ b/src/java/com/android/internal/telephony/MissedIncomingCallSmsFilter.java
@@ -23,12 +23,14 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.PersistableBundle;
+import android.os.UserHandle;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.SmsMessage;
+import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 
 import java.time.Instant;
@@ -152,9 +154,6 @@
      * @return {@code true} if the SMS message has been processed as a missed incoming call SMS.
      */
     private boolean processSms(@NonNull SmsMessage message) {
-        long missedCallTime = 0;
-        String callerId = null;
-
         String[] smsPatterns = mCarrierConfig.getStringArray(CarrierConfigManager
                 .KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY);
         if (smsPatterns == null || smsPatterns.length == 0) {
@@ -162,76 +161,91 @@
             return false;
         }
 
-        for (String smsPattern : smsPatterns) {
-            Pattern pattern;
-            try {
-                pattern = Pattern.compile(smsPattern, Pattern.DOTALL | Pattern.UNIX_LINES);
-            } catch (PatternSyntaxException e) {
-                Rlog.w(TAG, "Configuration error. Unexpected missed incoming call sms "
-                        + "pattern: " + smsPattern + ", e=" + e);
-                continue;
-            }
-
-            Matcher matcher = pattern.matcher(message.getMessageBody());
-            String year = null, month = null, day = null, hour = null, minute = null;
-            if (matcher.find()) {
-                try {
-                    month = matcher.group(SMS_MONTH_TAG);
-                    day = matcher.group(SMS_DAY_TAG);
-                    hour = matcher.group(SMS_HOUR_TAG);
-                    minute = matcher.group(SMS_MINUTE_TAG);
-                    if (VDBG) {
-                        Rlog.v(TAG, "month=" + month + ", day=" + day + ", hour=" + hour
-                                + ", minute=" + minute);
+        boolean result = false;
+        String[] missedCallMsgs = splitCalls(message.getMessageBody());
+        if (missedCallMsgs != null && missedCallMsgs.length > 0) {
+            for (String parsedMsg : missedCallMsgs) {
+                long missedCallTime = 0;
+                String callerId = null;
+                for (String smsPattern : smsPatterns) {
+                    Pattern pattern;
+                    try {
+                        pattern = Pattern.compile(smsPattern, Pattern.DOTALL | Pattern.UNIX_LINES);
+                    } catch (PatternSyntaxException e) {
+                        Rlog.w(TAG, "Configuration error. Unexpected missed incoming call sms "
+                                + "pattern: " + smsPattern + ", e=" + e);
+                        continue;
                     }
-                } catch (IllegalArgumentException e) {
-                    if (VDBG) {
-                        Rlog.v(TAG, "One of the critical date field is missing. Using the "
-                                + "current time for missed incoming call.");
-                    }
-                    missedCallTime = System.currentTimeMillis();
-                }
 
-                // Year is an optional field.
-                try {
-                    year = matcher.group(SMS_YEAR_TAG);
-                } catch (IllegalArgumentException e) {
-                    if (VDBG) Rlog.v(TAG, "Year is missing.");
-                }
-
-                try {
-                    if (missedCallTime == 0) {
-                        missedCallTime = getEpochTime(year, month, day, hour, minute);
-                        if (missedCallTime == 0) {
-                            Rlog.e(TAG, "Can't get the time. Use the current time.");
+                    Matcher matcher = pattern.matcher(parsedMsg);
+                    String year = null, month = null, day = null, hour = null, minute = null;
+                    if (matcher.find()) {
+                        try {
+                            month = matcher.group(SMS_MONTH_TAG);
+                            day = matcher.group(SMS_DAY_TAG);
+                            hour = matcher.group(SMS_HOUR_TAG);
+                            minute = matcher.group(SMS_MINUTE_TAG);
+                            if (VDBG) {
+                                Rlog.v(TAG, "month=" + month + ", day=" + day + ", hour=" + hour
+                                        + ", minute=" + minute);
+                            }
+                        } catch (IllegalArgumentException e) {
+                            if (VDBG) {
+                                Rlog.v(TAG, "One of the critical date field is missing. Using the "
+                                        + "current time for missed incoming call.");
+                            }
                             missedCallTime = System.currentTimeMillis();
                         }
+
+                        // Year is an optional field.
+                        try {
+                            year = matcher.group(SMS_YEAR_TAG);
+                        } catch (IllegalArgumentException e) {
+                            if (VDBG) Rlog.v(TAG, "Year is missing.");
+                        }
+
+                        try {
+                            if (missedCallTime == 0) {
+                                missedCallTime = getEpochTime(year, month, day, hour, minute);
+                                if (missedCallTime == 0) {
+                                    Rlog.e(TAG, "Can't get the time. Use the current time.");
+                                    missedCallTime = System.currentTimeMillis();
+                                }
+                            }
+
+                            if (VDBG) Rlog.v(TAG, "missedCallTime=" + missedCallTime);
+                        } catch (Exception e) {
+                            Rlog.e(TAG, "Can't get the time for missed incoming call");
+                        }
+
+                        try {
+                            callerId = matcher.group(SMS_CALLER_ID_TAG);
+                            if (VDBG) Rlog.v(TAG, "caller id=" + callerId);
+                        } catch (IllegalArgumentException e) {
+                            Rlog.d(TAG, "Caller id is not provided or can't be parsed.");
+                        }
+                        createMissedIncomingCallEvent(missedCallTime, callerId);
+                        result = true;
+                        break;
                     }
-
-                    if (VDBG) Rlog.v(TAG, "missedCallTime=" + missedCallTime);
-                } catch (Exception e) {
-                    Rlog.e(TAG, "Can't get the time for missed incoming call");
                 }
-
-                try {
-                    callerId = matcher.group(SMS_CALLER_ID_TAG);
-                    if (VDBG) Rlog.v(TAG, "caller id=" + callerId);
-                } catch (IllegalArgumentException e) {
-                    Rlog.d(TAG, "Caller id is not provided or can't be parsed.");
-                }
-                createMissedIncomingCallEvent(missedCallTime, callerId);
-                return true;
             }
         }
-
-        Rlog.d(TAG, "SMS did not match any missed incoming call SMS pattern.");
-        return false;
+        if (!result) {
+            Rlog.d(TAG, "SMS did not match any missed incoming call SMS pattern.");
+        }
+        return result;
     }
 
-    // Create phone account. The logic is copied from PhoneUtils.makePstnPhoneAccountHandle.
-    private static PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) {
-        return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT,
-                String.valueOf(phone.getFullIccSerialNumber()));
+    private String[] splitCalls(String messageBody) {
+        String[] messages = null;
+        if (messageBody != null) {
+            messages = messageBody.split("(\\n|\\s\\n)" + "(\\n|\\s\\n)");
+            Rlog.d(TAG,
+                    "splitTheMultipleCalls no of calls = " + ((messages != null) ? messages.length
+                            : 0));
+        }
+        return messages;
     }
 
     /**
@@ -260,4 +274,18 @@
             tm.addNewIncomingCall(makePstnPhoneAccountHandle(mPhone), bundle);
         }
     }
-}
+
+    // Create phone account. The logic is copied from PhoneUtils.makePstnPhoneAccountHandle.
+    private PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) {
+        SubscriptionManager subscriptionManager =
+                (SubscriptionManager) phone.getContext().getSystemService(
+                        Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        UserHandle userHandle = subscriptionManager.getSubscriptionUserHandle(phone.getSubId());
+        if (userHandle != null) {
+            return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT,
+                    String.valueOf(phone.getSubId()), userHandle);
+        }
+        return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT,
+                String.valueOf(phone.getSubId()));
+    }
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/MockModem.java b/src/java/com/android/internal/telephony/MockModem.java
index 4266a75..a20e748 100644
--- a/src/java/com/android/internal/telephony/MockModem.java
+++ b/src/java/com/android/internal/telephony/MockModem.java
@@ -16,6 +16,14 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+import static android.telephony.TelephonyManager.HAL_SERVICE_IMS;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -34,6 +42,7 @@
     private static final String BIND_IRADIODATA = "android.telephony.mockmodem.iradiodata";
     private static final String BIND_IRADIONETWORK = "android.telephony.mockmodem.iradionetwork";
     private static final String BIND_IRADIOVOICE = "android.telephony.mockmodem.iradiovoice";
+    private static final String BIND_IRADIOIMS = "android.telephony.mockmodem.iradioims";
     private static final String BIND_IRADIOCONFIG = "android.telephony.mockmodem.iradioconfig";
     private static final String PHONE_ID = "phone_id";
 
@@ -42,7 +51,7 @@
     static final int RADIOCONFIG_SERVICE = RIL.MAX_SERVICE_IDX + 1;
 
     static final int BINDER_RETRY_MILLIS = 3 * 100;
-    static final int BINDER_MAX_RETRY = 3;
+    static final int BINDER_MAX_RETRY = 10;
 
     private Context mContext;
     private String mServiceName;
@@ -54,6 +63,7 @@
     private IBinder mDataBinder;
     private IBinder mNetworkBinder;
     private IBinder mVoiceBinder;
+    private IBinder mImsBinder;
     private IBinder mConfigBinder;
     private ServiceConnection mModemServiceConnection;
     private ServiceConnection mSimServiceConnection;
@@ -61,9 +71,11 @@
     private ServiceConnection mDataServiceConnection;
     private ServiceConnection mNetworkServiceConnection;
     private ServiceConnection mVoiceServiceConnection;
+    private ServiceConnection mImsServiceConnection;
     private ServiceConnection mConfigServiceConnection;
 
     private byte mPhoneId;
+    private String mTag;
 
     MockModem(Context context, String serviceName) {
         this(context, serviceName, 0);
@@ -71,6 +83,7 @@
 
     MockModem(Context context, String serviceName, int phoneId) {
         mPhoneId = (byte) phoneId;
+        mTag = TAG + "-" + mPhoneId;
         mContext = context;
         String[] componentInfo = serviceName.split("/", 2);
         mPackageName = componentInfo[0];
@@ -86,20 +99,22 @@
 
         @Override
         public void onServiceConnected(ComponentName name, IBinder binder) {
-            Rlog.d(TAG, "IRadio " + getModuleName(mService) + "  - onServiceConnected");
+            Rlog.d(mTag, "IRadio " + getModuleName(mService) + "  - onServiceConnected");
 
-            if (mService == RIL.MODEM_SERVICE) {
+            if (mService == HAL_SERVICE_MODEM) {
                 mModemBinder = binder;
-            } else if (mService == RIL.SIM_SERVICE) {
+            } else if (mService == HAL_SERVICE_SIM) {
                 mSimBinder = binder;
-            } else if (mService == RIL.MESSAGING_SERVICE) {
+            } else if (mService == HAL_SERVICE_MESSAGING) {
                 mMessagingBinder = binder;
-            } else if (mService == RIL.DATA_SERVICE) {
+            } else if (mService == HAL_SERVICE_DATA) {
                 mDataBinder = binder;
-            } else if (mService == RIL.NETWORK_SERVICE) {
+            } else if (mService == HAL_SERVICE_NETWORK) {
                 mNetworkBinder = binder;
-            } else if (mService == RIL.VOICE_SERVICE) {
+            } else if (mService == HAL_SERVICE_VOICE) {
                 mVoiceBinder = binder;
+            } else if (mService == HAL_SERVICE_IMS) {
+                mImsBinder = binder;
             } else if (mService == RADIOCONFIG_SERVICE) {
                 mConfigBinder = binder;
             }
@@ -107,20 +122,22 @@
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
-            Rlog.d(TAG, "IRadio " + getModuleName(mService) + "  - onServiceDisconnected");
+            Rlog.d(mTag, "IRadio " + getModuleName(mService) + "  - onServiceDisconnected");
 
-            if (mService == RIL.MODEM_SERVICE) {
+            if (mService == HAL_SERVICE_MODEM) {
                 mModemBinder = null;
-            } else if (mService == RIL.SIM_SERVICE) {
+            } else if (mService == HAL_SERVICE_SIM) {
                 mSimBinder = null;
-            } else if (mService == RIL.MESSAGING_SERVICE) {
+            } else if (mService == HAL_SERVICE_MESSAGING) {
                 mMessagingBinder = null;
-            } else if (mService == RIL.DATA_SERVICE) {
+            } else if (mService == HAL_SERVICE_DATA) {
                 mDataBinder = null;
-            } else if (mService == RIL.NETWORK_SERVICE) {
+            } else if (mService == HAL_SERVICE_NETWORK) {
                 mNetworkBinder = null;
-            } else if (mService == RIL.VOICE_SERVICE) {
+            } else if (mService == HAL_SERVICE_VOICE) {
                 mVoiceBinder = null;
+            } else if (mService == HAL_SERVICE_IMS) {
+                mImsBinder = null;
             } else if (mService == RADIOCONFIG_SERVICE) {
                 mConfigBinder = null;
             }
@@ -138,7 +155,7 @@
 
         Intent intent = new Intent();
         intent.setComponent(new ComponentName(mPackageName, mServiceName));
-        intent.setAction(actionName);
+        intent.setAction(actionName + phoneId);
         intent.putExtra(PHONE_ID, phoneId);
 
         status = mContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
@@ -148,18 +165,20 @@
     /** waitForBinder */
     public IBinder getServiceBinder(int service) {
         switch (service) {
-            case RIL.MODEM_SERVICE:
+            case HAL_SERVICE_MODEM:
                 return mModemBinder;
-            case RIL.SIM_SERVICE:
+            case HAL_SERVICE_SIM:
                 return mSimBinder;
-            case RIL.MESSAGING_SERVICE:
+            case HAL_SERVICE_MESSAGING:
                 return mMessagingBinder;
-            case RIL.DATA_SERVICE:
+            case HAL_SERVICE_DATA:
                 return mDataBinder;
-            case RIL.NETWORK_SERVICE:
+            case HAL_SERVICE_NETWORK:
                 return mNetworkBinder;
-            case RIL.VOICE_SERVICE:
+            case HAL_SERVICE_VOICE:
                 return mVoiceBinder;
+            case HAL_SERVICE_IMS:
+                return mImsBinder;
             case RADIOCONFIG_SERVICE:
                 return mConfigBinder;
             default:
@@ -183,95 +202,109 @@
                 boolean status =
                         bindModuleToMockModemService(BIND_IRADIOCONFIG, mConfigServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Config bind fail");
+                    Rlog.d(mTag, "IRadio Config bind fail");
                     mConfigServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Config is bound");
+                Rlog.d(mTag, "IRadio Config is bound");
             }
-        } else if (service == RIL.MODEM_SERVICE) {
+        } else if (service == HAL_SERVICE_MODEM) {
             if (mModemBinder == null) {
-                mModemServiceConnection = new MockModemConnection(RIL.MODEM_SERVICE);
+                mModemServiceConnection = new MockModemConnection(HAL_SERVICE_MODEM);
 
                 boolean status =
                         bindModuleToMockModemService(
                                 mPhoneId, BIND_IRADIOMODEM, mModemServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Modem bind fail");
+                    Rlog.d(mTag, "IRadio Modem bind fail");
                     mModemServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Modem is bound");
+                Rlog.d(mTag, "IRadio Modem is bound");
             }
-        } else if (service == RIL.SIM_SERVICE) {
+        } else if (service == HAL_SERVICE_SIM) {
             if (mSimBinder == null) {
-                mSimServiceConnection = new MockModemConnection(RIL.SIM_SERVICE);
+                mSimServiceConnection = new MockModemConnection(HAL_SERVICE_SIM);
 
                 boolean status =
                         bindModuleToMockModemService(
                                 mPhoneId, BIND_IRADIOSIM, mSimServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Sim bind fail");
+                    Rlog.d(mTag, "IRadio Sim bind fail");
                     mSimServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Sim is bound");
+                Rlog.d(mTag, "IRadio Sim is bound");
             }
-        } else if (service == RIL.MESSAGING_SERVICE) {
+        } else if (service == HAL_SERVICE_MESSAGING) {
             if (mMessagingBinder == null) {
-                mMessagingServiceConnection = new MockModemConnection(RIL.MESSAGING_SERVICE);
+                mMessagingServiceConnection = new MockModemConnection(HAL_SERVICE_MESSAGING);
 
                 boolean status =
                         bindModuleToMockModemService(
                                 mPhoneId, BIND_IRADIOMESSAGING, mMessagingServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Messaging bind fail");
+                    Rlog.d(mTag, "IRadio Messaging bind fail");
                     mMessagingServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Messaging is bound");
+                Rlog.d(mTag, "IRadio Messaging is bound");
             }
-        } else if (service == RIL.DATA_SERVICE) {
+        } else if (service == HAL_SERVICE_DATA) {
             if (mDataBinder == null) {
-                mDataServiceConnection = new MockModemConnection(RIL.DATA_SERVICE);
+                mDataServiceConnection = new MockModemConnection(HAL_SERVICE_DATA);
 
                 boolean status =
                         bindModuleToMockModemService(
                                 mPhoneId, BIND_IRADIODATA, mDataServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Data bind fail");
+                    Rlog.d(mTag, "IRadio Data bind fail");
                     mDataServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Data is bound");
+                Rlog.d(mTag, "IRadio Data is bound");
             }
-        } else if (service == RIL.NETWORK_SERVICE) {
+        } else if (service == HAL_SERVICE_NETWORK) {
             if (mNetworkBinder == null) {
-                mNetworkServiceConnection = new MockModemConnection(RIL.NETWORK_SERVICE);
+                mNetworkServiceConnection = new MockModemConnection(HAL_SERVICE_NETWORK);
 
                 boolean status =
                         bindModuleToMockModemService(
                                 mPhoneId, BIND_IRADIONETWORK, mNetworkServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Network bind fail");
+                    Rlog.d(mTag, "IRadio Network bind fail");
                     mNetworkServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Network is bound");
+                Rlog.d(mTag, "IRadio Network is bound");
             }
-        } else if (service == RIL.VOICE_SERVICE) {
+        } else if (service == HAL_SERVICE_VOICE) {
             if (mVoiceBinder == null) {
-                mVoiceServiceConnection = new MockModemConnection(RIL.VOICE_SERVICE);
+                mVoiceServiceConnection = new MockModemConnection(HAL_SERVICE_VOICE);
 
                 boolean status =
                         bindModuleToMockModemService(
                                 mPhoneId, BIND_IRADIOVOICE, mVoiceServiceConnection);
                 if (!status) {
-                    Rlog.d(TAG, "IRadio Voice bind fail");
+                    Rlog.d(mTag, "IRadio Voice bind fail");
                     mVoiceServiceConnection = null;
                 }
             } else {
-                Rlog.d(TAG, "IRadio Voice is bound");
+                Rlog.d(mTag, "IRadio Voice is bound");
+            }
+        } else if (service == HAL_SERVICE_IMS) {
+            if (mImsBinder == null) {
+                mImsServiceConnection = new MockModemConnection(HAL_SERVICE_IMS);
+
+                boolean status =
+                        bindModuleToMockModemService(
+                                mPhoneId, BIND_IRADIOIMS, mImsServiceConnection);
+                if (!status) {
+                    Rlog.d(TAG, "IRadio Ims bind fail");
+                    mImsServiceConnection = null;
+                }
+            } else {
+                Rlog.d(TAG, "IRadio Ims is bound");
             }
         }
     }
@@ -284,49 +317,56 @@
                 mContext.unbindService(mConfigServiceConnection);
                 mConfigServiceConnection = null;
                 mConfigBinder = null;
-                Rlog.d(TAG, "unbind IRadio Config");
+                Rlog.d(mTag, "unbind IRadio Config");
             }
-        } else if (service == RIL.MODEM_SERVICE) {
+        } else if (service == HAL_SERVICE_MODEM) {
             if (mModemServiceConnection != null) {
                 mContext.unbindService(mModemServiceConnection);
                 mModemServiceConnection = null;
                 mModemBinder = null;
-                Rlog.d(TAG, "unbind IRadio Modem");
+                Rlog.d(mTag, "unbind IRadio Modem");
             }
-        } else if (service == RIL.SIM_SERVICE) {
+        } else if (service == HAL_SERVICE_SIM) {
             if (mSimServiceConnection != null) {
                 mContext.unbindService(mSimServiceConnection);
                 mSimServiceConnection = null;
                 mSimBinder = null;
-                Rlog.d(TAG, "unbind IRadio Sim");
+                Rlog.d(mTag, "unbind IRadio Sim");
             }
-        } else if (service == RIL.MESSAGING_SERVICE) {
+        } else if (service == HAL_SERVICE_MESSAGING) {
             if (mMessagingServiceConnection != null) {
                 mContext.unbindService(mMessagingServiceConnection);
                 mMessagingServiceConnection = null;
                 mMessagingBinder = null;
-                Rlog.d(TAG, "unbind IRadio Messaging");
+                Rlog.d(mTag, "unbind IRadio Messaging");
             }
-        } else if (service == RIL.DATA_SERVICE) {
+        } else if (service == HAL_SERVICE_DATA) {
             if (mDataServiceConnection != null) {
                 mContext.unbindService(mDataServiceConnection);
                 mDataServiceConnection = null;
                 mDataBinder = null;
-                Rlog.d(TAG, "unbind IRadio Data");
+                Rlog.d(mTag, "unbind IRadio Data");
             }
-        } else if (service == RIL.NETWORK_SERVICE) {
+        } else if (service == HAL_SERVICE_NETWORK) {
             if (mNetworkServiceConnection != null) {
                 mContext.unbindService(mNetworkServiceConnection);
                 mNetworkServiceConnection = null;
                 mNetworkBinder = null;
-                Rlog.d(TAG, "unbind IRadio Network");
+                Rlog.d(mTag, "unbind IRadio Network");
             }
-        } else if (service == RIL.VOICE_SERVICE) {
+        } else if (service == HAL_SERVICE_VOICE) {
             if (mVoiceServiceConnection != null) {
                 mContext.unbindService(mVoiceServiceConnection);
                 mVoiceServiceConnection = null;
                 mVoiceBinder = null;
-                Rlog.d(TAG, "unbind IRadio Voice");
+                Rlog.d(mTag, "unbind IRadio Voice");
+            }
+        } else if (service == HAL_SERVICE_IMS) {
+            if (mImsServiceConnection != null) {
+                mContext.unbindService(mImsServiceConnection);
+                mImsServiceConnection = null;
+                mImsBinder = null;
+                Rlog.d(TAG, "unbind IRadio Ims");
             }
         }
     }
@@ -337,18 +377,20 @@
 
     private String getModuleName(int service) {
         switch (service) {
-            case RIL.MODEM_SERVICE:
+            case HAL_SERVICE_MODEM:
                 return "modem";
-            case RIL.SIM_SERVICE:
+            case HAL_SERVICE_SIM:
                 return "sim";
-            case RIL.MESSAGING_SERVICE:
+            case HAL_SERVICE_MESSAGING:
                 return "messaging";
-            case RIL.DATA_SERVICE:
+            case HAL_SERVICE_DATA:
                 return "data";
-            case RIL.NETWORK_SERVICE:
+            case HAL_SERVICE_NETWORK:
                 return "network";
-            case RIL.VOICE_SERVICE:
+            case HAL_SERVICE_VOICE:
                 return "voice";
+            case HAL_SERVICE_IMS:
+                return "ims";
             case RADIOCONFIG_SERVICE:
                 return "config";
             default:
diff --git a/src/java/com/android/internal/telephony/ModemIndication.java b/src/java/com/android/internal/telephony/ModemIndication.java
index 8baa5fd..0ee40bb 100644
--- a/src/java/com/android/internal/telephony/ModemIndication.java
+++ b/src/java/com/android/internal/telephony/ModemIndication.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
+
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_HARDWARE_CONFIG_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_MODEM_RESTART;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RADIO_CAPABILITY;
@@ -44,11 +46,11 @@
      */
     public void hardwareConfigChanged(int indicationType,
             android.hardware.radio.modem.HardwareConfig[] configs) {
-        mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
 
         ArrayList<HardwareConfig> response = RILUtils.convertHalHardwareConfigList(configs);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_HARDWARE_CONFIG_CHANGED, response);
+        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_HARDWARE_CONFIG_CHANGED, response);
 
         mRil.mHardwareConfigChangeRegistrants.notifyRegistrants(
                 new AsyncResult(null, response, null));
@@ -62,9 +64,9 @@
      *        restart" that explains the cause of the modem restart
      */
     public void modemReset(int indicationType, String reason) {
-        mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_MODEM_RESTART, reason);
+        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_MODEM_RESTART, reason);
 
         mRil.writeMetricsModemRestartEvent(reason);
         mRil.mModemResetRegistrants.notifyRegistrants(new AsyncResult(null, reason, null));
@@ -78,11 +80,11 @@
      */
     public void radioCapabilityIndication(int indicationType,
             android.hardware.radio.modem.RadioCapability radioCapability) {
-        mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
 
         RadioCapability response = RILUtils.convertHalRadioCapability(radioCapability, mRil);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_RADIO_CAPABILITY, response);
+        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_RADIO_CAPABILITY, response);
 
         mRil.mPhoneRadioCapabilityChangedRegistrants.notifyRegistrants(
                 new AsyncResult(null, response, null));
@@ -94,12 +96,12 @@
      * @param radioState Current radio state
      */
     public void radioStateChanged(int indicationType, int radioState) {
-        mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
 
         int state = RILUtils.convertHalRadioState(radioState);
-        if (RIL.RILJ_LOGD) {
-            mRil.unsljLogMore(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, "radioStateChanged: "
-                    + state);
+        if (mRil.isLogOrTrace()) {
+            mRil.unsljLogMore(
+                    RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, "radioStateChanged: " + state);
         }
 
         mRil.setRadioState(state, false /* forceNotifyRegistrants */);
@@ -110,9 +112,9 @@
      * @param indicationType Type of radio indication
      */
     public void rilConnected(int indicationType) {
-        mRil.processIndication(RIL.MODEM_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_MODEM, indicationType);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RIL_CONNECTED);
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RIL_CONNECTED);
 
         // Initial conditions
         mRil.setRadioPower(false, null);
diff --git a/src/java/com/android/internal/telephony/ModemResponse.java b/src/java/com/android/internal/telephony/ModemResponse.java
index 6e44ddc..bd04d16 100644
--- a/src/java/com/android/internal/telephony/ModemResponse.java
+++ b/src/java/com/android/internal/telephony/ModemResponse.java
@@ -16,9 +16,12 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
+
 import android.hardware.radio.RadioError;
 import android.hardware.radio.RadioResponseInfo;
 import android.hardware.radio.modem.IRadioModemResponse;
+import android.hardware.radio.modem.ImeiInfo;
 import android.os.SystemClock;
 import android.telephony.ActivityStatsTechSpecificInfo;
 import android.telephony.AnomalyReporter;
@@ -51,7 +54,7 @@
      * @param responseInfo Response info struct containing response type, serial number and error.
      */
     public void enableModemResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
     }
 
     /**
@@ -59,7 +62,7 @@
      * @param version String containing version string for log reporting
      */
     public void getBasebandVersionResponse(RadioResponseInfo responseInfo, String version) {
-        RadioResponse.responseString(RIL.MODEM_SERVICE, mRil, responseInfo, version);
+        RadioResponse.responseString(HAL_SERVICE_MODEM, mRil, responseInfo, version);
     }
 
     /**
@@ -72,7 +75,21 @@
     public void getDeviceIdentityResponse(RadioResponseInfo responseInfo, String imei,
             String imeisv, String esn, String meid) {
         RadioResponse.responseStrings(
-                RIL.MODEM_SERVICE, mRil, responseInfo, imei, imeisv, esn, meid);
+                HAL_SERVICE_MODEM, mRil, responseInfo, imei, imeisv, esn, meid);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param imeiInfo object containing ImeiType, device IMEI and IMEISV
+     */
+    public void getImeiResponse(RadioResponseInfo responseInfo, ImeiInfo imeiInfo) {
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
+        if (rr != null) {
+            if (responseInfo.error == RadioError.NONE) {
+                RadioResponse.sendMessageResponse(rr.mResult, imeiInfo);
+            }
+            mRil.processResponseDone(rr, responseInfo, imeiInfo);
+        }
     }
 
     /**
@@ -81,7 +98,7 @@
      */
     public void getHardwareConfigResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.modem.HardwareConfig[] config) {
-        RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
 
         if (rr != null) {
             ArrayList<HardwareConfig> ret = RILUtils.convertHalHardwareConfigList(config);
@@ -98,7 +115,7 @@
      */
     public void getModemActivityInfoResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.modem.ActivityStatsInfo activityInfo) {
-        RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
 
         if (rr != null) {
             ModemActivityInfo ret = null;
@@ -141,7 +158,7 @@
      * @param isEnabled whether the modem stack is enabled.
      */
     public void getModemStackStatusResponse(RadioResponseInfo responseInfo, boolean isEnabled) {
-        RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
 
         if (rr != null) {
             if (responseInfo.error == RadioError.NONE) {
@@ -157,7 +174,7 @@
      */
     public void getRadioCapabilityResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.modem.RadioCapability radioCapability) {
-        RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
 
         if (rr != null) {
             RadioCapability ret = RILUtils.convertHalRadioCapability(radioCapability, mRil);
@@ -179,42 +196,42 @@
      * @param result String containing the contents of the NV item
      */
     public void nvReadItemResponse(RadioResponseInfo responseInfo, String result) {
-        RadioResponse.responseString(RIL.MODEM_SERVICE, mRil, responseInfo, result);
+        RadioResponse.responseString(HAL_SERVICE_MODEM, mRil, responseInfo, result);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void nvResetConfigResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void nvWriteCdmaPrlResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void nvWriteItemResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void requestShutdownResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void sendDeviceStateResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
     }
 
     /**
@@ -223,7 +240,7 @@
      */
     public void setRadioCapabilityResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.modem.RadioCapability radioCapability) {
-        RILRequest rr = mRil.processResponse(RIL.MODEM_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
 
         if (rr != null) {
             RadioCapability ret = RILUtils.convertHalRadioCapability(radioCapability, mRil);
@@ -238,7 +255,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error.
      */
     public void setRadioPowerResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.MODEM_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
         mRil.mLastRadioPowerResult = responseInfo.error;
         if (responseInfo.error == RadioError.RF_HARDWARE_ISSUE) {
             AnomalyReporter.reportAnomaly(
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index 3f5c23c..0acae4b 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -33,10 +33,8 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
@@ -54,6 +52,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.util.ArrayUtils;
 
 import java.lang.annotation.Retention;
@@ -83,7 +83,6 @@
     private static final int EVENT_SUBSCRIPTION_INFO_CHANGED         = 4;
     private static final int EVENT_SUBSCRIPTION_GROUP_CHANGED        = 5;
     private static final int EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED = 6;
-    private static final int EVENT_CARRIER_CONFIG_CHANGED            = 7;
     private static final int EVENT_MULTI_SIM_CONFIG_CHANGED          = 8;
     @VisibleForTesting
     public static final int EVENT_RADIO_STATE_CHANGED                = 9;
@@ -121,19 +120,15 @@
     private static final int PRIMARY_SUB_REMOVED_IN_GROUP       = 7;
 
     protected final Context mContext;
-    protected final SubscriptionController mSubController;
+    private final SubscriptionManagerService mSubscriptionManagerService;
+
     // Keep a record of active primary (non-opportunistic) subscription list.
     @NonNull private List<Integer> mPrimarySubList = new ArrayList<>();
 
     /** The singleton instance. */
     protected static MultiSimSettingController sInstance = null;
 
-    // This will be set true when handling EVENT_ALL_SUBSCRIPTIONS_LOADED. The reason of keeping
-    // a local variable instead of calling SubscriptionInfoUpdater#isSubInfoInitialized is, there
-    // might be a race condition that we receive EVENT_SUBSCRIPTION_INFO_CHANGED first, then
-    // EVENT_ALL_SUBSCRIPTIONS_LOADED. And calling SubscriptionInfoUpdater#isSubInfoInitialized
-    // will make us handle EVENT_SUBSCRIPTION_INFO_CHANGED unexpectedly and causing us to believe
-    // the SIMs are newly inserted instead of being initialized.
+    // This will be set true when handling EVENT_ALL_SUBSCRIPTIONS_LOADED.
     private boolean mSubInfoInitialized = false;
 
     // mInitialHandling is to make sure we don't always ask user to re-select data SIM after reboot.
@@ -158,19 +153,6 @@
 
     private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub";
 
-    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
-                int phoneId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX,
-                        SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-                int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
-                        SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-                notifyCarrierConfigChanged(phoneId, subId);
-            }
-        }
-    };
-
     private static class DataSettingsControllerCallback extends DataSettingsManagerCallback {
         private final Phone mPhone;
 
@@ -218,10 +200,10 @@
     /**
      * Init instance of MultiSimSettingController.
      */
-    public static MultiSimSettingController init(Context context, SubscriptionController sc) {
+    public static MultiSimSettingController init(Context context) {
         synchronized (MultiSimSettingController.class) {
             if (sInstance == null) {
-                sInstance = new MultiSimSettingController(context, sc);
+                sInstance = new MultiSimSettingController(context);
             } else {
                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
             }
@@ -230,9 +212,9 @@
     }
 
     @VisibleForTesting
-    public MultiSimSettingController(Context context, SubscriptionController sc) {
+    public MultiSimSettingController(Context context) {
         mContext = context;
-        mSubController = sc;
+        mSubscriptionManagerService = SubscriptionManagerService.getInstance();
 
         // Initialize mCarrierConfigLoadedSubIds and register to listen to carrier config change.
         final int phoneCount = ((TelephonyManager) mContext.getSystemService(
@@ -245,8 +227,12 @@
 
         mIsAskEverytimeSupportedForSms = mContext.getResources()
                 .getBoolean(com.android.internal.R.bool.config_sms_ask_every_time_support);
-        context.registerReceiver(mIntentReceiver, new IntentFilter(
-                CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+
+        CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class);
+        // Listener callback is executed on handler thread to directly handle config change
+        ccm.registerCarrierConfigChangeListener(this::post,
+                (slotIndex, subId, carrierId, specificCarrierId) ->
+                        onCarrierConfigChanged(slotIndex, subId));
     }
 
     /**
@@ -303,7 +289,7 @@
             case EVENT_USER_DATA_ENABLED: {
                 int subId = msg.arg1;
                 boolean enable = msg.arg2 != 0;
-                onUserDataEnabled(subId, enable);
+                onUserDataEnabled(subId, enable, true);
                 break;
             }
             case EVENT_ROAMING_DATA_ENABLED: {
@@ -325,11 +311,6 @@
             case EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
                 onDefaultDataSettingChanged();
                 break;
-            case EVENT_CARRIER_CONFIG_CHANGED:
-                int phoneId = msg.arg1;
-                int subId = msg.arg2;
-                onCarrierConfigChanged(phoneId, subId);
-                break;
             case EVENT_MULTI_SIM_CONFIG_CHANGED:
                 int activeModems = (int) ((AsyncResult) msg.obj).result;
                 onMultiSimConfigChanged(activeModems);
@@ -352,17 +333,21 @@
      * If user is enabling a non-default non-opportunistic subscription, make it default
      * data subscription.
      */
-    protected void onUserDataEnabled(int subId, boolean enable) {
-        if (DBG) log("onUserDataEnabled");
+    private void onUserDataEnabled(int subId, boolean enable, boolean setDefaultData) {
+        if (DBG) log("[onUserDataEnabled] subId=" + subId + " enable=" + enable +
+        " setDefaultData=" + setDefaultData);
         // Make sure MOBILE_DATA of subscriptions in same group are synced.
         setUserDataEnabledForGroup(subId, enable);
 
+        SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(subId);
+        int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId();
+
         // If user is enabling a non-default non-opportunistic subscription, make it default.
-        if (mSubController.getDefaultDataSubId() != subId && !mSubController.isOpportunistic(subId)
-                && enable && mSubController.isActiveSubId(subId)) {
-             android.provider.Settings.Global.putInt(mContext.getContentResolver(),
-                 SETTING_USER_PREF_DATA_SUB, subId);
-            mSubController.setDefaultDataSubId(subId);
+        if (defaultDataSubId != subId && subInfo != null && !subInfo.isOpportunistic() && enable
+                && subInfo.isActive() && setDefaultData) {
+            android.provider.Settings.Global.putInt(mContext.getContentResolver(),
+                    SETTING_USER_PREF_DATA_SUB, subId);
+            mSubscriptionManagerService.setDefaultDataSubId(subId);
         }
     }
 
@@ -373,8 +358,8 @@
         if (DBG) log("onRoamingDataEnabled");
         setRoamingDataEnabledForGroup(subId, enable);
 
-        // Also inform SubscriptionController as it keeps another copy of user setting.
-        mSubController.setDataRoaming(enable ? 1 : 0, subId);
+        // Also inform SubscriptionManagerService as it keeps another copy of user setting.
+        mSubscriptionManagerService.setDataRoaming(enable ? 1 : 0, subId);
     }
 
     /**
@@ -419,14 +404,6 @@
         reEvaluateAll();
     }
 
-    /**
-     * Called when carrier config changes on any phone.
-     */
-    @VisibleForTesting
-    public void notifyCarrierConfigChanged(int phoneId, int subId) {
-        obtainMessage(EVENT_CARRIER_CONFIG_CHANGED, phoneId, subId).sendToTarget();
-    }
-
     private void onCarrierConfigChanged(int phoneId, int subId) {
         log("onCarrierConfigChanged phoneId " + phoneId + " subId " + subId);
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
@@ -438,14 +415,12 @@
         // being specified in it. So here we do additional check to make sur we don't miss the
         // subId.
         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            int[] subIds = mSubController.getSubId(phoneId);
-            if (!ArrayUtils.isEmpty(subIds)) {
-                CarrierConfigManager cm = (CarrierConfigManager) mContext.getSystemService(
-                        mContext.CARRIER_CONFIG_SERVICE);
-                if (cm != null && cm.getConfigForSubId(subIds[0]) != null) {
-                    loge("onCarrierConfigChanged with invalid subId while subd "
-                            + subIds[0] + " is active and its config is loaded");
-                    subId = subIds[0];
+            subId = SubscriptionManager.getSubscriptionId(phoneId);
+            if (SubscriptionManager.isValidSubscriptionId(subId)) {
+                CarrierConfigManager cm = mContext.getSystemService(CarrierConfigManager.class);
+                if (cm != null && cm.getConfigForSubId(subId) != null) {
+                    loge("onCarrierConfigChanged with invalid subId while subId "
+                            + subId + " is active and its config is loaded");
                 }
             }
         }
@@ -459,7 +434,7 @@
      */
     @VisibleForTesting
     public boolean isCarrierConfigLoadedForAllSub() {
-        int[] activeSubIds = mSubController.getActiveSubIdList(false);
+        int[] activeSubIds = mSubscriptionManagerService.getActiveSubIdList(false);
         for (int activeSubId : activeSubIds) {
             boolean isLoaded = false;
             for (int configLoadedSub : mCarrierConfigLoadedSubIds) {
@@ -526,7 +501,7 @@
     private void onSubscriptionGroupChanged(ParcelUuid groupUuid) {
         if (DBG) log("onSubscriptionGroupChanged");
 
-        List<SubscriptionInfo> infoList = mSubController.getSubscriptionsInGroup(
+        List<SubscriptionInfo> infoList = mSubscriptionManagerService.getSubscriptionsInGroup(
                 groupUuid, mContext.getOpPackageName(), mContext.getAttributionTag());
         if (infoList == null || infoList.isEmpty()) return;
 
@@ -535,24 +510,36 @@
         int refSubId = infoList.get(0).getSubscriptionId();
         for (SubscriptionInfo info : infoList) {
             int subId = info.getSubscriptionId();
-            if (mSubController.isActiveSubId(subId) && !mSubController.isOpportunistic(subId)) {
+            if (info.isActive() && !info.isOpportunistic()) {
                 refSubId = subId;
                 break;
             }
         }
+
         if (DBG) log("refSubId is " + refSubId);
 
         boolean enable = false;
         try {
             enable = GlobalSettingsHelper.getBoolean(
                     mContext, Settings.Global.MOBILE_DATA, refSubId);
-            onUserDataEnabled(refSubId, enable);
         } catch (SettingNotFoundException exception) {
             //pass invalid refSubId to fetch the single-sim setting
             enable = GlobalSettingsHelper.getBoolean(
                     mContext, Settings.Global.MOBILE_DATA, INVALID_SUBSCRIPTION_ID, enable);
-            onUserDataEnabled(refSubId, enable);
         }
+        boolean setDefaultData = true;
+        List<SubscriptionInfo> activeSubList = mSubscriptionManagerService
+                .getActiveSubscriptionInfoList(mContext.getOpPackageName(),
+                        mContext.getAttributionTag());
+        for (SubscriptionInfo activeInfo : activeSubList) {
+            if (!(groupUuid.equals(activeInfo.getGroupUuid()))) {
+                // Do not set refSubId as defaultDataSubId if there are other active
+                // subscriptions which does not belong to this groupUuid
+                setDefaultData = false;
+                break;
+            }
+        }
+        onUserDataEnabled(refSubId, enable, setDefaultData);
 
         enable = false;
         try {
@@ -566,8 +553,7 @@
             onRoamingDataEnabled(refSubId, enable);
         }
 
-        // Sync settings in subscription database..
-        mSubController.syncGroupedSetting(refSubId);
+        mSubscriptionManagerService.syncGroupedSetting(refSubId);
     }
 
     /**
@@ -589,21 +575,24 @@
 
         if (!isReadyToReevaluate()) return;
 
-        List<SubscriptionInfo> activeSubInfos = mSubController
+        List<SubscriptionInfo> activeSubInfos = mSubscriptionManagerService
                 .getActiveSubscriptionInfoList(mContext.getOpPackageName(),
                         mContext.getAttributionTag());
 
         if (ArrayUtils.isEmpty(activeSubInfos)) {
             mPrimarySubList.clear();
-            if (DBG) log("[updateDefaultValues] No active sub. Setting default to INVALID sub.");
-            mSubController.setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-            mSubController.setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-            mSubController.setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            if (DBG) log("updateDefaults: No active sub. Setting default to INVALID sub.");
+            mSubscriptionManagerService.setDefaultDataSubId(
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            mSubscriptionManagerService.setDefaultVoiceSubId(
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            mSubscriptionManagerService.setDefaultSmsSubId(
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
             return;
         }
 
         int change = updatePrimarySubListAndGetChangeType(activeSubInfos);
-        if (DBG) log("[updateDefaultValues] change: " + change);
+        if (DBG) log("updateDefaultValues: change: " + change);
         if (change == PRIMARY_SUB_NO_CHANGE) return;
 
         // If there's only one primary subscription active, we trigger PREFERRED_PICK_DIALOG
@@ -615,33 +604,35 @@
                 || ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
                 .getActiveModemCount() == 1)) {
             int subId = mPrimarySubList.get(0);
-            if (DBG) log("[updateDefaultValues] to only primary sub " + subId);
-            mSubController.setDefaultDataSubId(subId);
-            mSubController.setDefaultVoiceSubId(subId);
-            mSubController.setDefaultSmsSubId(subId);
+            if (DBG) log("updateDefaultValues: to only primary sub " + subId);
+            mSubscriptionManagerService.setDefaultDataSubId(subId);
+            mSubscriptionManagerService.setDefaultVoiceSubId(subId);
+            mSubscriptionManagerService.setDefaultSmsSubId(subId);
             sendDefaultSubConfirmedNotification(subId);
             return;
         }
 
-        if (DBG) log("[updateDefaultValues] records: " + mPrimarySubList);
+        if (DBG) log("updateDefaultValues: records: " + mPrimarySubList);
+
+        boolean dataSelected, voiceSelected, smsSelected;
 
         // Update default data subscription.
-        if (DBG) log("[updateDefaultValues] Update default data subscription");
-        boolean dataSelected = updateDefaultValue(mPrimarySubList,
-                mSubController.getDefaultDataSubId(),
-                (newValue -> mSubController.setDefaultDataSubId(newValue)));
+        if (DBG) log("updateDefaultValues: Update default data subscription");
+        dataSelected = updateDefaultValue(mPrimarySubList,
+                mSubscriptionManagerService.getDefaultDataSubId(),
+                mSubscriptionManagerService::setDefaultDataSubId);
 
         // Update default voice subscription.
-        if (DBG) log("[updateDefaultValues] Update default voice subscription");
-        boolean voiceSelected = updateDefaultValue(mPrimarySubList,
-                mSubController.getDefaultVoiceSubId(),
-                (newValue -> mSubController.setDefaultVoiceSubId(newValue)));
+        if (DBG) log("updateDefaultValues: Update default voice subscription");
+        voiceSelected = updateDefaultValue(mPrimarySubList,
+                mSubscriptionManagerService.getDefaultVoiceSubId(),
+                mSubscriptionManagerService::setDefaultVoiceSubId);
 
         // Update default sms subscription.
-        if (DBG) log("[updateDefaultValues] Update default sms subscription");
-        boolean smsSelected = updateDefaultValue(mPrimarySubList,
-                mSubController.getDefaultSmsSubId(),
-                (newValue -> mSubController.setDefaultSmsSubId(newValue)),
+        if (DBG) log("updateDefaultValues: Update default sms subscription");
+        smsSelected = updateDefaultValue(mPrimarySubList,
+                mSubscriptionManagerService.getDefaultSmsSubId(),
+                mSubscriptionManagerService::setDefaultSmsSubId,
                 mIsAskEverytimeSupportedForSms);
 
         boolean autoFallbackEnabled = mContext.getResources().getBoolean(
@@ -693,7 +684,9 @@
             // any previous primary subscription becomes inactive, we consider it
             for (int subId : prevPrimarySubList) {
                 if (mPrimarySubList.contains(subId)) continue;
-                if (!mSubController.isActiveSubId(subId)) {
+                SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(subId);
+
+                if (subInfo == null || !subInfo.isActive()) {
                     for (int currentSubId : mPrimarySubList) {
                         if (areSubscriptionsInSameGroup(currentSubId, subId)) {
                             return PRIMARY_SUB_REMOVED_IN_GROUP;
@@ -701,10 +694,10 @@
                     }
                     return PRIMARY_SUB_REMOVED;
                 }
-                if (!mSubController.isOpportunistic(subId)) {
+                if (!subInfo.isOpportunistic()) {
                     // Should never happen.
-                    loge("[updatePrimarySubListAndGetChangeType]: missing active primary subId "
-                            + subId);
+                    loge("[updatePrimarySubListAndGetChangeType]: missing active primary "
+                            + "subId " + subId);
                 }
             }
             return PRIMARY_SUB_MARKED_OPPT;
@@ -729,6 +722,7 @@
         @TelephonyManager.DefaultSubscriptionSelectType
         int simSelectDialogType = getSimSelectDialogType(
                 change, dataSelected, voiceSelected, smsSelected);
+        log("sendSubChangeNotificationIfNeeded: simSelectDialogType=" + simSelectDialogType);
         SimCombinationWarningParams simCombinationParams = getSimCombinationWarningParams(change);
 
         if (simSelectDialogType != EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE
@@ -760,6 +754,12 @@
             boolean voiceSelected, boolean smsSelected) {
         int dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE;
 
+        // Do not show preference selection dialog during SuW as there is fullscreen activity to
+        // choose preference.
+        if (Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
+            return dialogType;
+        }
         // If a primary subscription is removed and only one is left active, ask user
         // for preferred sub selection if any default setting is not set.
         // If another primary subscription is added or default data is not selected, ask
@@ -768,12 +768,8 @@
                 && (!dataSelected || !smsSelected || !voiceSelected)) {
             dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
         } else if (mPrimarySubList.size() > 1 && (isUserVisibleChange(change)
-                || (change == PRIMARY_SUB_INITIALIZED && !dataSelected
-                && Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.DEVICE_PROVISIONED, 0) != 0))) {
+                || (change == PRIMARY_SUB_INITIALIZED && !dataSelected))) {
             // If change is SWAPPED_IN_GROUP or MARKED_OPPT, don't ask user again.
-            // In default DSDS devices, do not show data selection dialog during SuW as there is
-            // fullscreen activity to choose data preference.
             dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
         }
 
@@ -801,9 +797,12 @@
             // If a dual CDMA SIM combination warning is needed.
             if (phone != null && phone.isCdmaSubscriptionAppPresent()) {
                 cdmaPhoneCount++;
-                String simName = mSubController.getActiveSubscriptionInfo(
-                        subId, mContext.getOpPackageName(), mContext.getAttributionTag())
-                        .getDisplayName().toString();
+                String simName = null;
+                SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+                        .getSubscriptionInfoInternal(subId);
+                if (subInfo != null) {
+                    simName = subInfo.getDisplayName();
+                }
                 if (TextUtils.isEmpty(simName)) {
                     // Fall back to carrier name.
                     simName = phone.getCarrierName();
@@ -828,23 +827,21 @@
     protected void disableDataForNonDefaultNonOpportunisticSubscriptions() {
         if (!isReadyToReevaluate()) return;
 
-        int defaultDataSub = mSubController.getDefaultDataSubId();
+        int defaultDataSub = mSubscriptionManagerService.getDefaultDataSubId();
 
         for (Phone phone : PhoneFactory.getPhones()) {
+            SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+                    .getSubscriptionInfoInternal(phone.getSubId());
+            boolean isOpportunistic = subInfo != null && subInfo.isOpportunistic();
             if (phone.getSubId() != defaultDataSub
                     && SubscriptionManager.isValidSubscriptionId(phone.getSubId())
-                    && !mSubController.isOpportunistic(phone.getSubId())
+                    && !isOpportunistic
                     && phone.isUserDataEnabled()
                     && !areSubscriptionsInSameGroup(defaultDataSub, phone.getSubId())) {
                 log("setting data to false on " + phone.getSubId());
-                if (phone.isUsingNewDataStack()) {
-                    phone.getDataSettingsManager().setDataEnabled(
-                            TelephonyManager.DATA_ENABLED_REASON_USER, false,
-                            mContext.getOpPackageName());
-                } else {
-                    phone.getDataEnabledSettings().setDataEnabled(
-                            TelephonyManager.DATA_ENABLED_REASON_USER, false);
-                }
+                phone.getDataSettingsManager().setDataEnabled(
+                        TelephonyManager.DATA_ENABLED_REASON_USER, false,
+                        mContext.getOpPackageName());
             }
         }
     }
@@ -854,9 +851,13 @@
                 || !SubscriptionManager.isUsableSubscriptionId(subId2)) return false;
         if (subId1 == subId2) return true;
 
-        ParcelUuid groupUuid1 = mSubController.getGroupUuid(subId1);
-        ParcelUuid groupUuid2 = mSubController.getGroupUuid(subId2);
-        return groupUuid1 != null && groupUuid1.equals(groupUuid2);
+        SubscriptionInfoInternal subInfo1 =
+                mSubscriptionManagerService.getSubscriptionInfoInternal(subId1);
+        SubscriptionInfoInternal subInfo2 =
+                mSubscriptionManagerService.getSubscriptionInfoInternal(subId2);
+        return subInfo1 != null && subInfo2 != null
+                && !TextUtils.isEmpty(subInfo1.getGroupUuid())
+                && subInfo1.getGroupUuid().equals(subInfo2.getGroupUuid());
     }
 
     /**
@@ -865,28 +866,30 @@
      */
     protected void setUserDataEnabledForGroup(int subId, boolean enable) {
         log("setUserDataEnabledForGroup subId " + subId + " enable " + enable);
-        List<SubscriptionInfo> infoList = mSubController.getSubscriptionsInGroup(
-                mSubController.getGroupUuid(subId), mContext.getOpPackageName(),
-                mContext.getAttributionTag());
+        List<SubscriptionInfo> infoList = null;
+        SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+                .getSubscriptionInfoInternal(subId);
+        if (subInfo != null && !subInfo.getGroupUuid().isEmpty()) {
+            infoList = mSubscriptionManagerService.getSubscriptionsInGroup(
+                    ParcelUuid.fromString(subInfo.getGroupUuid()), mContext.getOpPackageName(),
+                    mContext.getAttributionTag());
+        }
 
         if (infoList == null) return;
 
         for (SubscriptionInfo info : infoList) {
             int currentSubId = info.getSubscriptionId();
             // TODO: simplify when setUserDataEnabled becomes singleton
-            if (mSubController.isActiveSubId(currentSubId)) {
+            if (info.isActive()) {
                 // For active subscription, call setUserDataEnabled through DataSettingsManager.
-                Phone phone = PhoneFactory.getPhone(mSubController.getPhoneId(currentSubId));
+                Phone phone = PhoneFactory.getPhone(mSubscriptionManagerService
+                        .getPhoneId(currentSubId));
                 // If enable is true and it's not opportunistic subscription, we don't enable it,
                 // as there can't be two
                 if (phone != null) {
-                    if (phone.isUsingNewDataStack()) {
-                        phone.getDataSettingsManager().setDataEnabled(
-                                TelephonyManager.DATA_ENABLED_REASON_USER, enable,
-                                mContext.getOpPackageName());
-                    } else {
-                        phone.getDataEnabledSettings().setUserDataEnabled(enable, false);
-                    }
+                    phone.getDataSettingsManager().setDataEnabled(
+                            TelephonyManager.DATA_ENABLED_REASON_USER, enable,
+                            mContext.getOpPackageName());
                 }
             } else {
                 // For inactive subscription, directly write into global settings.
@@ -901,11 +904,12 @@
      * are synced.
      */
     private void setRoamingDataEnabledForGroup(int subId, boolean enable) {
-        SubscriptionController subController = SubscriptionController.getInstance();
-        List<SubscriptionInfo> infoList = subController.getSubscriptionsInGroup(
-                mSubController.getGroupUuid(subId), mContext.getOpPackageName(),
-                mContext.getAttributionTag());
-
+        SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+                .getSubscriptionInfoInternal(subId);
+        if (subInfo == null || subInfo.getGroupUuid().isEmpty()) return;
+        List<SubscriptionInfo> infoList = SubscriptionManagerService.getInstance()
+                .getSubscriptionsInGroup(ParcelUuid.fromString(subInfo.getGroupUuid()),
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
         if (infoList == null) return;
 
         for (SubscriptionInfo info : infoList) {
@@ -958,16 +962,16 @@
     // subscription gets deactivated or removed, we need to automatically disable the grouped
     // opportunistic subscription, which will be marked isGroupDisabled as true by SubController.
     private void deactivateGroupedOpportunisticSubscriptionIfNeeded() {
-        if (!SubscriptionInfoUpdater.isSubInfoInitialized()) return;
-
-        List<SubscriptionInfo> opptSubList = mSubController.getOpportunisticSubscriptions(
-                mContext.getOpPackageName(), mContext.getAttributionTag());
+        List<SubscriptionInfo> opptSubList = mSubscriptionManagerService.getAllSubInfoList(
+                mContext.getOpPackageName(), mContext.getAttributionTag()).stream()
+                .filter(SubscriptionInfo::isOpportunistic)
+                .collect(Collectors.toList());
 
         if (ArrayUtils.isEmpty(opptSubList)) return;
 
         for (SubscriptionInfo info : opptSubList) {
-            if (info.isGroupDisabled() && mSubController.isActiveSubId(info.getSubscriptionId())) {
-                log("[deactivateGroupedOpptSubIfNeeded] "
+            if (info.isGroupDisabled() && info.isActive()) {
+                log("deactivateGroupedOpportunisticSubscriptionIfNeeded: "
                         + "Deactivating grouped opportunistic subscription "
                         + info.getSubscriptionId());
                 deactivateSubscription(info);
@@ -997,47 +1001,55 @@
     // would be selected as preferred voice/data/sms SIM.
     private void updateUserPreferences(List<Integer> primarySubList, boolean dataSelected,
             boolean voiceSelected, boolean smsSelected) {
-        // In Single SIM case or if there are no activated subs available, no need to update. EXIT.
-        if ((primarySubList.isEmpty()) || (mSubController.getActiveSubInfoCountMax() == 1)) return;
+        // In Single SIM case or if there are no activated subs available, no need to update.
+        // EXIT.
+        if ((primarySubList.isEmpty()) || (mSubscriptionManagerService
+                .getActiveSubInfoCountMax() == 1)) {
+            return;
+        }
 
         if (!isRadioAvailableOnAllSubs()) {
             log("Radio is in Invalid state, Ignore Updating User Preference!!!");
             return;
         }
-        final int defaultDataSubId = mSubController.getDefaultDataSubId();
+        final int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId();
 
-        if (DBG) log("updateUserPreferences:  dds = " + defaultDataSubId + " voice = "
-                + mSubController.getDefaultVoiceSubId() +
-                " sms = " + mSubController.getDefaultSmsSubId());
+        if (DBG) {
+            log("updateUserPreferences:  dds = " + defaultDataSubId + " voice = "
+                    + mSubscriptionManagerService.getDefaultVoiceSubId()
+                    + " sms = " + mSubscriptionManagerService.getDefaultSmsSubId());
+        }
 
         int autoDefaultSubId = primarySubList.get(0);
 
         if ((primarySubList.size() == 1) && !smsSelected) {
-            mSubController.setDefaultSmsSubId(autoDefaultSubId);
+            mSubscriptionManagerService.setDefaultSmsSubId(autoDefaultSubId);
         }
 
         if ((primarySubList.size() == 1) && !voiceSelected) {
-            mSubController.setDefaultVoiceSubId(autoDefaultSubId);
+            mSubscriptionManagerService.setDefaultVoiceSubId(autoDefaultSubId);
         }
 
         int userPrefDataSubId = getUserPrefDataSubIdFromDB();
 
-        if (DBG) log("User pref subId = " + userPrefDataSubId + " current dds " + defaultDataSubId
-                 + " next active subId " + autoDefaultSubId);
+        log("User pref subId = " + userPrefDataSubId + " current dds " + defaultDataSubId
+                + " next active subId " + autoDefaultSubId);
 
         // If earlier user selected DDS is now available, set that as DDS subId.
-        if (primarySubList.contains(userPrefDataSubId) &&
-                SubscriptionManager.isValidSubscriptionId(userPrefDataSubId) &&
-                (defaultDataSubId != userPrefDataSubId)) {
-            mSubController.setDefaultDataSubId(userPrefDataSubId);
+        if (primarySubList.contains(userPrefDataSubId)
+                && SubscriptionManager.isValidSubscriptionId(userPrefDataSubId)
+                && (defaultDataSubId != userPrefDataSubId)) {
+            mSubscriptionManagerService.setDefaultDataSubId(userPrefDataSubId);
         } else if (!dataSelected) {
-            mSubController.setDefaultDataSubId(autoDefaultSubId);
+            mSubscriptionManagerService.setDefaultDataSubId(autoDefaultSubId);
         }
 
-
-        if (DBG) log("updateUserPreferences: after dds = " + mSubController.getDefaultDataSubId() +
-                " voice = " + mSubController.getDefaultVoiceSubId() + " sms = " +
-                 mSubController.getDefaultSmsSubId());
+        if (DBG) {
+            log("updateUserPreferences: after dds = "
+                    + mSubscriptionManagerService.getDefaultDataSubId() + " voice = "
+                    + mSubscriptionManagerService.getDefaultVoiceSubId() + " sms = "
+                    + mSubscriptionManagerService.getDefaultSmsSubId());
+        }
     }
 
     private int getUserPrefDataSubIdFromDB() {
@@ -1061,10 +1073,8 @@
         // existing phone instance.
         Phone[] phones = PhoneFactory.getPhones();
         for (int i = mCallbacksCount; i < phones.length; i++) {
-            if (phones[i].isUsingNewDataStack()) {
-                phones[i].getDataSettingsManager().registerCallback(
-                        new DataSettingsControllerCallback(phones[i], this::post));
-            }
+            phones[i].getDataSettingsManager().registerCallback(
+                    new DataSettingsControllerCallback(phones[i], this::post));
         }
         mCallbacksCount = phones.length;
     }
diff --git a/src/java/com/android/internal/telephony/NetworkIndication.java b/src/java/com/android/internal/telephony/NetworkIndication.java
index 17d906e..7f9ff79 100644
--- a/src/java/com/android/internal/telephony/NetworkIndication.java
+++ b/src/java/com/android/internal/telephony/NetworkIndication.java
@@ -16,14 +16,17 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
 import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
 
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_PRL_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CELL_INFO_LIST;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_LCEDATA_RECV;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NETWORK_SCAN_RESULT;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NITZ_TIME_RECEIVED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_REGISTRATION_FAILED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESTRICTED_STATE_CHANGED;
@@ -39,6 +42,7 @@
 import android.telephony.BarringInfo;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
+import android.telephony.EmergencyRegResult;
 import android.telephony.LinkCapacityEstimate;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhysicalChannelConfig;
@@ -72,7 +76,7 @@
     public void barringInfoChanged(int indicationType,
             android.hardware.radio.network.CellIdentity cellIdentity,
             android.hardware.radio.network.BarringInfo[] barringInfos) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         if (cellIdentity == null || barringInfos == null) {
             reportAnomaly(UUID.fromString("645b16bb-c930-4c1c-9c5d-568696542e05"),
@@ -84,7 +88,7 @@
         BarringInfo cbi = new BarringInfo(RILUtils.convertHalCellIdentity(cellIdentity),
                 RILUtils.convertHalBarringInfoList(barringInfos));
 
-        mRil.mBarringInfoChangedRegistrants.notifyRegistrants(new AsyncResult(null, cbi, null));
+        mRil.notifyBarringInfoChanged(cbi);
     }
 
     /**
@@ -93,11 +97,11 @@
      * @param version PRL version after PRL changes
      */
     public void cdmaPrlChanged(int indicationType, int version) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         int[] response = new int[]{version};
 
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_PRL_CHANGED, response);
+        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CDMA_PRL_CHANGED, response);
 
         mRil.mCdmaPrlChangedRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
     }
@@ -109,9 +113,9 @@
      */
     public void cellInfoList(int indicationType,
             android.hardware.radio.network.CellInfo[] records) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
         ArrayList<CellInfo> response = RILUtils.convertHalCellInfoList(records);
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response);
+        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response);
         mRil.mRilCellInfoListRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
     }
 
@@ -122,11 +126,11 @@
      */
     public void currentLinkCapacityEstimate(int indicationType,
             android.hardware.radio.network.LinkCapacityEstimate lce) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         List<LinkCapacityEstimate> response = RILUtils.convertHalLceData(lce);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
+        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
 
         if (mRil.mLceInfoRegistrants != null) {
             mRil.mLceInfoRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
@@ -140,27 +144,34 @@
      */
     public void currentPhysicalChannelConfigs(int indicationType,
             android.hardware.radio.network.PhysicalChannelConfig[] configs) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
         List<PhysicalChannelConfig> response = new ArrayList<>(configs.length);
         try {
             for (android.hardware.radio.network.PhysicalChannelConfig config : configs) {
                 PhysicalChannelConfig.Builder builder = new PhysicalChannelConfig.Builder();
+                int band = PhysicalChannelConfig.BAND_UNKNOWN;
                 switch (config.band.getTag()) {
                     case android.hardware.radio.network.PhysicalChannelConfigBand.geranBand:
-                        builder.setBand(config.band.getGeranBand());
+                        band = config.band.getGeranBand();
                         break;
                     case android.hardware.radio.network.PhysicalChannelConfigBand.utranBand:
-                        builder.setBand(config.band.getUtranBand());
+                        band = config.band.getUtranBand();
                         break;
                     case android.hardware.radio.network.PhysicalChannelConfigBand.eutranBand:
-                        builder.setBand(config.band.getEutranBand());
+                        band = config.band.getEutranBand();
                         break;
                     case android.hardware.radio.network.PhysicalChannelConfigBand.ngranBand:
-                        builder.setBand(config.band.getNgranBand());
+                        band = config.band.getNgranBand();
                         break;
                     default:
                         mRil.riljLoge("Unsupported band type " + config.band.getTag());
                 }
+                if (band == PhysicalChannelConfig.BAND_UNKNOWN) {
+                    mRil.riljLoge("Unsupported unknown band.");
+                    return;
+                } else {
+                    builder.setBand(band);
+                }
                 response.add(builder.setCellConnectionStatus(
                         RILUtils.convertHalCellConnectionStatus(config.status))
                         .setDownlinkChannelNumber(config.downlinkChannelNumber)
@@ -178,7 +189,7 @@
             mRil.riljLoge("Invalid PhysicalChannelConfig " + iae);
             return;
         }
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response);
+        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response);
 
         mRil.mPhysicalChannelConfigurationRegistrants.notifyRegistrants(
                 new AsyncResult(null, response, null));
@@ -191,13 +202,13 @@
      */
     public void currentSignalStrength(int indicationType,
             android.hardware.radio.network.SignalStrength signalStrength) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         SignalStrength ssInitial = RILUtils.convertHalSignalStrength(signalStrength);
 
         SignalStrength ss = mRil.fixupSignalStrength10(ssInitial);
         // Note this is set to "verbose" because it happens frequently
-        if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
+        if (mRil.isLogvOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
 
         if (mRil.mSignalStrengthRegistrant != null) {
             mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult(null, ss, null));
@@ -209,9 +220,9 @@
      * @param indicationType Type of radio indication
      */
     public void imsNetworkStateChanged(int indicationType) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED);
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED);
 
         mRil.mImsNetworkStateChangedRegistrants.notifyRegistrants();
     }
@@ -223,11 +234,11 @@
      */
     public void networkScanResult(int indicationType,
             android.hardware.radio.network.NetworkScanResult result) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         ArrayList<CellInfo> cellInfos = RILUtils.convertHalCellInfoList(result.networkInfos);
         NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos);
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
+        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
         mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
     }
 
@@ -236,9 +247,9 @@
      * @param indicationType Type of radio indication
      */
     public void networkStateChanged(int indicationType) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED);
+        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED);
 
         mRil.mNetworkStateRegistrants.notifyRegistrants();
     }
@@ -256,9 +267,9 @@
      */
     public void nitzTimeReceived(int indicationType, String nitzTime,
         @ElapsedRealtimeLong long receivedTimeMs, long ageMs) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime);
+        if (mRil.isLogOrTrace()) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime);
 
         // Ignore the NITZ if receivedTimeMs or ageMs is not a valid time.
         // e.g. receivedTimeMs is non-positive, ageMs is negative or greater than receivedTimeMs.
@@ -280,7 +291,7 @@
         boolean ignoreNitz = TelephonyProperties.ignore_nitz().orElse(false);
 
         if (ignoreNitz) {
-            if (RIL.RILJ_LOGD) mRil.riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED");
+            if (mRil.isLogOrTrace()) mRil.riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED");
         } else {
             if (mRil.mNITZTimeRegistrant != null) {
                 mRil.mNITZTimeRegistrant.notifyRegistrant(new AsyncResult(null, result, null));
@@ -303,23 +314,32 @@
     public void registrationFailed(int indicationType,
             android.hardware.radio.network.CellIdentity cellIdentity, String chosenPlmn,
             @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
         CellIdentity ci = RILUtils.convertHalCellIdentity(cellIdentity);
         if (ci == null || TextUtils.isEmpty(chosenPlmn)
                 || (domain & NetworkRegistrationInfo.DOMAIN_CS_PS) == 0
                 || (domain & ~NetworkRegistrationInfo.DOMAIN_CS_PS) != 0
                 || causeCode < 0 || additionalCauseCode < 0
                 || (causeCode == Integer.MAX_VALUE && additionalCauseCode == Integer.MAX_VALUE)) {
-            reportAnomaly(UUID.fromString("f16e5703-6105-4341-9eb3-e68189156eb4"),
+            reportAnomaly(UUID.fromString("f16e5703-6105-4341-9eb3-e68189156eb5"),
                     "Invalid registrationFailed indication");
 
-            mRil.riljLoge("Invalid registrationFailed indication");
+            mRil.riljLoge("Invalid registrationFailed indication (ci is null)=" + (ci == null)
+                    + " (chosenPlmn is empty)=" + TextUtils.isEmpty(chosenPlmn)
+                    + " (is CS/PS)=" + ((domain & NetworkRegistrationInfo.DOMAIN_CS_PS) == 0)
+                    + " (only CS/PS)=" + ((domain & ~NetworkRegistrationInfo.DOMAIN_CS_PS) != 0)
+                    + " (causeCode)=" + causeCode
+                    + " (additionalCauseCode)=" + additionalCauseCode);
             return;
         }
 
+        RegistrationFailedEvent registrationFailedEvent = new RegistrationFailedEvent(
+                ci, chosenPlmn, domain, causeCode, additionalCauseCode);
+        if (mRil.isLogOrTrace()) {
+            mRil.unsljLogMore(RIL_UNSOL_REGISTRATION_FAILED, registrationFailedEvent.toString());
+        }
         mRil.mRegistrationFailedRegistrant.notifyRegistrant(
-                new AsyncResult(null, new RegistrationFailedEvent(
-                        ci, chosenPlmn, domain, causeCode, additionalCauseCode), null));
+                new AsyncResult(null, registrationFailedEvent, null));
     }
 
     /**
@@ -328,9 +348,9 @@
      * @param state Bitmask of restricted state as defined by PhoneRestrictedState
      */
     public void restrictedStateChanged(int indicationType, int state) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
-        if (RIL.RILJ_LOGD) mRil.unsljLogvRet(RIL_UNSOL_RESTRICTED_STATE_CHANGED, state);
+        if (mRil.isLogOrTrace()) mRil.unsljLogvRet(RIL_UNSOL_RESTRICTED_STATE_CHANGED, state);
 
         if (mRil.mRestrictedStateRegistrant != null) {
             mRil.mRestrictedStateRegistrant.notifyRegistrant(new AsyncResult(null, state, null));
@@ -344,7 +364,7 @@
      */
     public void suppSvcNotify(int indicationType,
             android.hardware.radio.network.SuppSvcNotification suppSvcNotification) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         SuppServiceNotification notification = new SuppServiceNotification();
         notification.notificationType = suppSvcNotification.isMT ? 1 : 0;
@@ -353,7 +373,9 @@
         notification.type = suppSvcNotification.type;
         notification.number = suppSvcNotification.number;
 
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SUPP_SVC_NOTIFICATION, notification);
+        if (mRil.isLogOrTrace()) {
+            mRil.unsljLogRet(RIL_UNSOL_SUPP_SVC_NOTIFICATION, notification);
+        }
 
         if (mRil.mSsnRegistrant != null) {
             mRil.mSsnRegistrant.notifyRegistrant(new AsyncResult(null, notification, null));
@@ -366,16 +388,37 @@
      * @param rat Current new voice rat
      */
     public void voiceRadioTechChanged(int indicationType, int rat) {
-        mRil.processIndication(RIL.NETWORK_SERVICE, indicationType);
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
 
         int[] response = new int[] {rat};
 
-        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, response);
+        if (mRil.isLogOrTrace()) {
+            mRil.unsljLogRet(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, response);
+        }
 
         mRil.mVoiceRadioTechChangedRegistrants.notifyRegistrants(
                 new AsyncResult(null, response, null));
     }
 
+    /**
+     * Emergency Scan Results.
+     * @param indicationType Type of radio indication
+     * @param result the result of the Emergency Network Scan
+     */
+    public void emergencyNetworkScanResult(int indicationType,
+            android.hardware.radio.network.EmergencyRegResult result) {
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
+
+        EmergencyRegResult response = RILUtils.convertHalEmergencyRegResult(result);
+
+        if (mRil.isLogOrTrace()) {
+            mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT, response);
+        }
+
+        mRil.mEmergencyNetworkScanRegistrants.notifyRegistrants(
+                new AsyncResult(null, response, null));
+    }
+
     @Override
     public String getInterfaceHash() {
         return IRadioNetworkIndication.HASH;
diff --git a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
index 3535678..91cd4ec 100644
--- a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
+++ b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
@@ -16,13 +16,10 @@
 
 package com.android.internal.telephony;
 
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.IBinder;
@@ -30,7 +27,6 @@
 import android.os.PersistableBundle;
 import android.os.RegistrantList;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.CarrierConfigManager;
@@ -58,9 +54,6 @@
     private final int mTransportType;
 
     private final Phone mPhone;
-
-    private final CarrierConfigManager mCarrierConfigManager;
-
     // Registrants who listens registration state change callback from this class.
     private final RegistrantList mRegStateChangeRegistrants = new RegistrantList();
 
@@ -72,22 +65,6 @@
 
     private NetworkServiceConnection mServiceConnection;
 
-    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)
-                    && mPhone.getPhoneId() == intent.getIntExtra(
-                    CarrierConfigManager.EXTRA_SLOT_INDEX, 0)) {
-                // We should wait for carrier config changed event because the target binding
-                // package name can come from the carrier config. Note that we still get this event
-                // even when SIM is absent.
-                logd("Carrier config changed. Try to bind network service.");
-                sendEmptyMessage(EVENT_BIND_NETWORK_SERVICE);
-            }
-        }
-    };
-
     public NetworkRegistrationManager(@TransportType int transportType, Phone phone) {
         mTransportType = transportType;
         mPhone = phone;
@@ -96,19 +73,20 @@
                 ? "C" : "I") + "-" + mPhone.getPhoneId();
         mTag = "NRM" + tagSuffix;
 
-        mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
-                Context.CARRIER_CONFIG_SERVICE);
+        CarrierConfigManager ccm = phone.getContext().getSystemService(CarrierConfigManager.class);
+        // Callback directly calls rebindService and should be executed in handler thread
+        ccm.registerCarrierConfigChangeListener(
+                this::post,
+                (slotIndex, subId, carrierId, specificCarrierId) -> {
+                    if (slotIndex == phone.getPhoneId()) {
+                        // We should wait for carrier config changed event because the target
+                        // binding package name can come from the carrier config. Note that
+                        // we still get this event even when SIM is absent.
+                        logd("Carrier config changed. Try to bind network service.");
+                        rebindService();
+                    }
+                });
 
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        try {
-            Context contextAsUser = phone.getContext().createPackageContextAsUser(
-                phone.getContext().getPackageName(), 0, UserHandle.ALL);
-            contextAsUser.registerReceiver(mBroadcastReceiver, intentFilter,
-                null /* broadcastPermission */, null);
-        } catch (PackageManager.NameNotFoundException e) {
-            loge("Package name not found: " + e.getMessage());
-        }
         PhoneConfigurationManager.registerForMultiSimConfigChange(
                 this, EVENT_BIND_NETWORK_SERVICE, null);
 
@@ -333,9 +311,10 @@
         // Read package name from resource overlay
         packageName = mPhone.getContext().getResources().getString(resourceId);
 
-        PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
-        if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
+        PersistableBundle b =
+                CarrierConfigManager.getCarrierConfigSubset(
+                        mPhone.getContext(), mPhone.getSubId(), carrierConfig);
+        if (!b.isEmpty() && !TextUtils.isEmpty(b.getString(carrierConfig))) {
             // If carrier config overrides it, use the one from carrier config
             packageName = b.getString(carrierConfig, packageName);
         }
@@ -367,15 +346,17 @@
         // Read class name from resource overlay
         className = mPhone.getContext().getResources().getString(resourceId);
 
-        PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
-
-        if (b != null && !TextUtils.isEmpty(b.getString(carrierConfig))) {
+        PersistableBundle b =
+                CarrierConfigManager.getCarrierConfigSubset(
+                        mPhone.getContext(), mPhone.getSubId(), carrierConfig);
+        if (!b.isEmpty() && !TextUtils.isEmpty(b.getString(carrierConfig))) {
             // If carrier config overrides it, use the one from carrier config
             className = b.getString(carrierConfig, className);
         }
 
         return className;
     }
+
     private void logd(String msg) {
         Rlog.d(mTag, msg);
     }
diff --git a/src/java/com/android/internal/telephony/NetworkResponse.java b/src/java/com/android/internal/telephony/NetworkResponse.java
index d9f70fd..b1eb926 100644
--- a/src/java/com/android/internal/telephony/NetworkResponse.java
+++ b/src/java/com/android/internal/telephony/NetworkResponse.java
@@ -16,12 +16,15 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+
 import android.hardware.radio.RadioError;
 import android.hardware.radio.RadioResponseInfo;
 import android.hardware.radio.network.IRadioNetworkResponse;
 import android.os.AsyncResult;
 import android.telephony.BarringInfo;
 import android.telephony.CellInfo;
+import android.telephony.EmergencyRegResult;
 import android.telephony.LinkCapacityEstimate;
 import android.telephony.RadioAccessSpecifier;
 import android.telephony.SignalStrength;
@@ -57,7 +60,7 @@
             int halRadioAccessFamilyBitmap) {
         int networkTypeBitmask = RILUtils.convertHalNetworkTypeBitMask(halRadioAccessFamilyBitmap);
         mRil.mAllowedNetworkTypesBitmask = networkTypeBitmask;
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, networkTypeBitmask);
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, networkTypeBitmask);
     }
 
     /**
@@ -65,7 +68,7 @@
      * @param bandModes List of RadioBandMode listing supported modes
      */
     public void getAvailableBandModesResponse(RadioResponseInfo responseInfo, int[] bandModes) {
-        RadioResponse.responseIntArrayList(RIL.NETWORK_SERVICE, mRil, responseInfo,
+        RadioResponse.responseIntArrayList(HAL_SERVICE_NETWORK, mRil, responseInfo,
                 RILUtils.primitiveArrayToArrayList(bandModes));
     }
 
@@ -75,7 +78,7 @@
      */
     public void getAvailableNetworksResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.OperatorInfo[] networkInfos) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             ArrayList<OperatorInfo> ret = new ArrayList<>();
@@ -98,7 +101,7 @@
     public void getBarringInfoResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.CellIdentity cellIdentity,
             android.hardware.radio.network.BarringInfo[] barringInfos) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             BarringInfo bi = new BarringInfo(RILUtils.convertHalCellIdentity(cellIdentity),
@@ -118,7 +121,7 @@
      * @param type CdmaRoamingType defined in types.hal
      */
     public void getCdmaRoamingPreferenceResponse(RadioResponseInfo responseInfo, int type) {
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, type);
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, type);
     }
 
     /**
@@ -127,7 +130,7 @@
      */
     public void getCellInfoListResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.CellInfo[] cellInfo) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             ArrayList<CellInfo> ret = RILUtils.convertHalCellInfoList(cellInfo);
@@ -144,7 +147,7 @@
      */
     public void getDataRegistrationStateResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.RegStateResult dataRegResponse) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             if (responseInfo.error == RadioError.NONE) {
@@ -161,7 +164,7 @@
      */
     public void getImsRegistrationStateResponse(RadioResponseInfo responseInfo,
             boolean isRegistered, int ratFamily) {
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, isRegistered ? 1 : 0,
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, isRegistered ? 1 : 0,
                 ratFamily == android.hardware.radio.RadioTechnologyFamily.THREE_GPP
                         ? PhoneConstants.PHONE_TYPE_GSM : PhoneConstants.PHONE_TYPE_CDMA);
     }
@@ -171,7 +174,7 @@
      * @param selection false for automatic selection, true for manual selection
      */
     public void getNetworkSelectionModeResponse(RadioResponseInfo responseInfo, boolean selection) {
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, selection ? 1 : 0);
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, selection ? 1 : 0);
     }
 
     /**
@@ -183,7 +186,7 @@
     public void getOperatorResponse(RadioResponseInfo responseInfo, String longName,
             String shortName, String numeric) {
         RadioResponse.responseStrings(
-                RIL.NETWORK_SERVICE, mRil, responseInfo, longName, shortName, numeric);
+                HAL_SERVICE_NETWORK, mRil, responseInfo, longName, shortName, numeric);
     }
 
     /**
@@ -192,7 +195,7 @@
      */
     public void getSignalStrengthResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.SignalStrength signalStrength) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             SignalStrength ret = RILUtils.convertHalSignalStrength(signalStrength);
@@ -209,7 +212,7 @@
      */
     public void getSystemSelectionChannelsResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.RadioAccessSpecifier[] halSpecifiers) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             ArrayList<RadioAccessSpecifier> specifiers = new ArrayList<>();
@@ -229,7 +232,7 @@
      * @param rat Current voice RAT
      */
     public void getVoiceRadioTechnologyResponse(RadioResponseInfo responseInfo, int rat) {
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, rat);
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, rat);
     }
 
     /**
@@ -238,7 +241,7 @@
      */
     public void getVoiceRegistrationStateResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.RegStateResult voiceRegResponse) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
         if (rr != null) {
             if (responseInfo.error == RadioError.NONE) {
                 RadioResponse.sendMessageResponse(rr.mResult, voiceRegResponse);
@@ -254,7 +257,7 @@
      */
     public void isNrDualConnectivityEnabledResponse(RadioResponseInfo responseInfo,
             boolean isEnabled) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             if (responseInfo.error == RadioError.NONE) {
@@ -270,7 +273,7 @@
      */
     public void pullLceDataResponse(RadioResponseInfo responseInfo,
             android.hardware.radio.network.LceDataInfo lceInfo) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
 
         if (rr != null) {
             List<LinkCapacityEstimate> ret = RILUtils.convertHalLceData(lceInfo);
@@ -285,105 +288,105 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setAllowedNetworkTypesBitmapResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setBandModeResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setBarringPasswordResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setCdmaRoamingPreferenceResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setCellInfoListRateResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setIndicationFilterResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setLinkCapacityReportingCriteriaResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setLocationUpdatesResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setNetworkSelectionModeAutomaticResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setNetworkSelectionModeManualResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setNrDualConnectivityStateResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setSignalStrengthReportingCriteriaResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setSuppServiceNotificationsResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial number and error.
      */
     public void setSystemSelectionChannelsResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void startNetworkScanResponse(RadioResponseInfo responseInfo) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
         if (rr != null) {
             NetworkScanResult nsr = null;
             if (responseInfo.error == RadioError.NONE) {
@@ -399,7 +402,7 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void stopNetworkScanResponse(RadioResponseInfo responseInfo) {
-        RILRequest rr = mRil.processResponse(RIL.NETWORK_SERVICE, responseInfo);
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
         if (rr != null) {
             NetworkScanResult nsr = null;
             if (responseInfo.error == RadioError.NONE) {
@@ -417,14 +420,14 @@
      */
     public void supplyNetworkDepersonalizationResponse(RadioResponseInfo responseInfo,
             int retriesRemaining) {
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, retriesRemaining);
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, retriesRemaining);
     }
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void setUsageSettingResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.NETWORK_SERVICE, mRil, responseInfo);
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     /**
@@ -433,7 +436,91 @@
      */
     public void getUsageSettingResponse(RadioResponseInfo responseInfo,
             /* @TelephonyManager.UsageSetting */ int usageSetting) {
-        RadioResponse.responseInts(RIL.NETWORK_SERVICE, mRil, responseInfo, usageSetting);
+        RadioResponse.responseInts(HAL_SERVICE_NETWORK, mRil, responseInfo, usageSetting);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param regState the current registration state of the modem.
+     */
+    public void setEmergencyModeResponse(RadioResponseInfo responseInfo,
+            android.hardware.radio.network.EmergencyRegResult regState) {
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
+
+        if (rr != null) {
+            EmergencyRegResult response = RILUtils.convertHalEmergencyRegResult(regState);
+            if (responseInfo.error == RadioError.NONE) {
+                RadioResponse.sendMessageResponse(rr.mResult, response);
+            }
+            mRil.processResponseDone(rr, responseInfo, response);
+        }
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void triggerEmergencyNetworkScanResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void exitEmergencyModeResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void cancelEmergencyNetworkScanResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setNullCipherAndIntegrityEnabledResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error.
+     * @param isEnabled Indicates whether null cipher and integrity is enabled, indicating
+     *                  potentially unencrypted communication
+     */
+    public void isNullCipherAndIntegrityEnabledResponse(RadioResponseInfo responseInfo,
+                    boolean isEnabled) {
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
+
+        if (rr != null) {
+            if (responseInfo.error == RadioError.NONE) {
+                RadioResponse.sendMessageResponse(rr.mResult, isEnabled);
+            }
+            mRil.processResponseDone(rr, responseInfo, isEnabled);
+        }
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error.
+     * @param isEnabled Indicates whether N1 mode is enabled or not.
+     */
+    public void isN1ModeEnabledResponse(RadioResponseInfo responseInfo, boolean isEnabled) {
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
+
+        if (rr != null) {
+            if (responseInfo.error == RadioError.NONE) {
+                RadioResponse.sendMessageResponse(rr.mResult, isEnabled);
+            }
+            mRil.processResponseDone(rr, responseInfo, isEnabled);
+        }
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error.
+     */
+    public void setN1ModeEnabledResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
     }
 
     @Override
@@ -445,4 +532,5 @@
     public int getInterfaceVersion() {
         return IRadioNetworkResponse.VERSION;
     }
+
 }
diff --git a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
index ea68cba..7567566 100644
--- a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
+++ b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -42,6 +42,8 @@
 import android.telephony.TelephonyScanManager;
 import android.util.Log;
 
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
@@ -195,7 +197,7 @@
     public static Set<String> getAllowedMccMncsForLocationRestrictedScan(Context context) {
         final long token = Binder.clearCallingIdentity();
         try {
-            return SubscriptionController.getInstance()
+            return SubscriptionManagerService.getInstance()
                     .getAvailableSubscriptionInfoList(context.getOpPackageName(),
                             context.getAttributionTag()).stream()
                     .flatMap(NetworkScanRequestTracker::getAllowableMccMncsFromSubscriptionInfo)
@@ -472,21 +474,21 @@
                     notifyMessenger(nsri, notifyMsg,
                             rilErrorToScanError(nsr.scanError), nsr.networkInfos);
                     if (nsr.scanStatus == NetworkScanResult.SCAN_STATUS_COMPLETE) {
-                        deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
                         nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+                        deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
                     }
                 } else {
                     if (nsr.networkInfos != null) {
                         notifyMessenger(nsri, notifyMsg,
                                 rilErrorToScanError(nsr.scanError), nsr.networkInfos);
                     }
-                    deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true);
                     nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+                    deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true);
                 }
             } else {
                 logEmptyResultOrException(ar);
-                deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RADIO_INTERFACE_ERROR, true);
                 nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+                deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RADIO_INTERFACE_ERROR, true);
             }
         }
 
@@ -514,6 +516,7 @@
                 Log.e(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: nsri is null");
                 return;
             }
+            nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
             if (ar.exception == null && ar.result != null) {
                 deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
             } else {
@@ -526,7 +529,6 @@
                     Log.wtf(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: ar.exception can not be null!");
                 }
             }
-            nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
         }
 
         // Interrupts the live scan is the scanId matches the mScanId of the mLiveRequestInfo.
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index 2260d34..beebf22 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -28,20 +30,16 @@
 import android.telephony.Annotation;
 import android.telephony.CarrierConfigManager;
 import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PcoData;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.ServiceState;
-import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataCallResponse.LinkStatus;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
-import com.android.internal.telephony.dataconnection.DataConnection;
-import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.data.DataUtils;
 import com.android.internal.telephony.util.ArrayUtils;
 import com.android.internal.util.IState;
 import com.android.internal.util.IndentingPrintWriter;
@@ -55,6 +53,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -86,31 +85,35 @@
     /** Stop all timers and go to current state. */
     public static final int EVENT_UPDATE = 0;
     /** Quit after processing all existing messages. */
-    public static final int EVENT_QUIT = 1;
-    private static final int EVENT_DATA_RAT_CHANGED = 2;
-    private static final int EVENT_NR_STATE_CHANGED = 3;
-    private static final int EVENT_NR_FREQUENCY_CHANGED = 4;
-    private static final int EVENT_PHYSICAL_LINK_STATUS_CHANGED = 5;
-    private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED = 6;
-    private static final int EVENT_CARRIER_CONFIG_CHANGED = 7;
-    private static final int EVENT_PRIMARY_TIMER_EXPIRED = 8;
-    private static final int EVENT_SECONDARY_TIMER_EXPIRED = 9;
-    private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 10;
-    private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 11;
-    private static final int EVENT_INITIALIZE = 12;
-    private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 13;
-    private static final int EVENT_PCO_DATA_CHANGED = 14;
-    private static final int EVENT_BANDWIDTH_CHANGED = 15;
-    private static final int EVENT_UPDATE_NR_ADVANCED_STATE = 16;
-    private static final int EVENT_DEVICE_IDLE_MODE_CHANGED = 17;
+    private static final int EVENT_QUIT = 1;
+    /** Initialize all events. */
+    private static final int EVENT_INITIALIZE = 2;
+    /** Event for service state changed (data rat, bandwidth, NR state, NR frequency, etc). */
+    private static final int EVENT_SERVICE_STATE_CHANGED = 3;
+    /** Event for physical link status changed. */
+    private static final int EVENT_PHYSICAL_LINK_STATUS_CHANGED = 4;
+    /** Event for physical channel config indications turned on/off. */
+    private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED = 5;
+    /** Event for carrier configs changed. */
+    private static final int EVENT_CARRIER_CONFIG_CHANGED = 6;
+    /** Event for primary timer expired. If a secondary timer exists, it will begin afterwards. */
+    private static final int EVENT_PRIMARY_TIMER_EXPIRED = 7;
+    /** Event for secondary timer expired. */
+    private static final int EVENT_SECONDARY_TIMER_EXPIRED = 8;
+    /** Event for radio off or unavailable. */
+    private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 9;
+    /** Event for preferred network mode changed. */
+    private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 10;
+    /** Event for physical channel configs changed. */
+    private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 11;
+    /** Event for device idle mode changed, when device goes to deep sleep and pauses all timers. */
+    private static final int EVENT_DEVICE_IDLE_MODE_CHANGED = 12;
 
     private static final String[] sEvents = new String[EVENT_DEVICE_IDLE_MODE_CHANGED + 1];
     static {
         sEvents[EVENT_UPDATE] = "EVENT_UPDATE";
         sEvents[EVENT_QUIT] = "EVENT_QUIT";
-        sEvents[EVENT_DATA_RAT_CHANGED] = "EVENT_DATA_RAT_CHANGED";
-        sEvents[EVENT_NR_STATE_CHANGED] = "EVENT_NR_STATE_CHANGED";
-        sEvents[EVENT_NR_FREQUENCY_CHANGED] = "EVENT_NR_FREQUENCY_CHANGED";
+        sEvents[EVENT_SERVICE_STATE_CHANGED] = "EVENT_SERVICE_STATE_CHANGED";
         sEvents[EVENT_PHYSICAL_LINK_STATUS_CHANGED] = "EVENT_PHYSICAL_LINK_STATUS_CHANGED";
         sEvents[EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED] =
                 "EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED";
@@ -121,26 +124,15 @@
         sEvents[EVENT_PREFERRED_NETWORK_MODE_CHANGED] = "EVENT_PREFERRED_NETWORK_MODE_CHANGED";
         sEvents[EVENT_INITIALIZE] = "EVENT_INITIALIZE";
         sEvents[EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED] = "EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED";
-        sEvents[EVENT_PCO_DATA_CHANGED] = "EVENT_PCO_DATA_CHANGED";
-        sEvents[EVENT_BANDWIDTH_CHANGED] = "EVENT_BANDWIDTH_CHANGED";
-        sEvents[EVENT_UPDATE_NR_ADVANCED_STATE] = "EVENT_UPDATE_NR_ADVANCED_STATE";
         sEvents[EVENT_DEVICE_IDLE_MODE_CHANGED] = "EVENT_DEVICE_IDLE_MODE_CHANGED";
     }
 
-    private final Phone mPhone;
-    private final DisplayInfoController mDisplayInfoController;
-    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+    private final @NonNull Phone mPhone;
+    private final @NonNull DisplayInfoController mDisplayInfoController;
+    private final @NonNull BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             switch (intent.getAction()) {
-                case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
-                    if (intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX,
-                            SubscriptionManager.INVALID_PHONE_INDEX) == mPhone.getPhoneId()
-                            && !intent.getBooleanExtra(
-                                    CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
-                        sendMessage(EVENT_CARRIER_CONFIG_CHANGED);
-                    }
-                    break;
                 case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
                     sendMessage(EVENT_DEVICE_IDLE_MODE_CHANGED);
                     break;
@@ -148,19 +140,33 @@
         }
     };
 
-    private Map<String, OverrideTimerRule> mOverrideTimerRules = new HashMap<>();
-    private String mLteEnhancedPattern = "";
-    private int mOverrideNetworkType;
+    private final @NonNull CarrierConfigManager.CarrierConfigChangeListener
+            mCarrierConfigChangeListener =
+            new CarrierConfigManager.CarrierConfigChangeListener() {
+                @Override
+                public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId,
+                        int specificCarrierId) {
+                    // CarrierConfigChangeListener wouldn't send notification on device unlock
+                    if (slotIndex == mPhone.getPhoneId()) {
+                        sendMessage(EVENT_CARRIER_CONFIG_CHANGED);
+                    }
+                }
+            };
+
+    private @NonNull Map<String, OverrideTimerRule> mOverrideTimerRules = new HashMap<>();
+    private @NonNull String mLteEnhancedPattern = "";
+    private @Annotation.OverrideNetworkType int mOverrideNetworkType;
     private boolean mIsPhysicalChannelConfigOn;
     private boolean mIsPrimaryTimerActive;
     private boolean mIsSecondaryTimerActive;
-    private boolean mIsTimerResetEnabledForLegacyStateRRCIdle;
+    private boolean mIsTimerResetEnabledForLegacyStateRrcIdle;
     private int mLtePlusThresholdBandwidth;
     private int mNrAdvancedThresholdBandwidth;
-    private int[] mAdditionalNrAdvancedBandsList;
-    private String mPrimaryTimerState;
-    private String mSecondaryTimerState;
-    private String mPreviousState;
+    private boolean mIncludeLteForNrAdvancedThresholdBandwidth;
+    private @NonNull int[] mAdditionalNrAdvancedBandsList;
+    private @NonNull String mPrimaryTimerState;
+    private @NonNull String mSecondaryTimerState;
+    private @NonNull String mPreviousState;
     private @LinkStatus int mPhysicalLinkStatus;
     private boolean mIsPhysicalChannelConfig16Supported;
     private boolean mIsNrAdvancedAllowedByPco = false;
@@ -169,6 +175,13 @@
     private boolean mEnableNrAdvancedWhileRoaming = true;
     private boolean mIsDeviceIdleMode = false;
 
+    private @Nullable DataNetworkControllerCallback mNrAdvancedCapableByPcoChangedCallback = null;
+    private @Nullable DataNetworkControllerCallback mNrPhysicalLinkStatusChangedCallback = null;
+
+    // Cached copies below to prevent race conditions
+    private @NonNull ServiceState mServiceState;
+    private @Nullable List<PhysicalChannelConfig> mPhysicalChannelConfigs;
+
     /**
      * NetworkTypeController constructor.
      *
@@ -181,13 +194,22 @@
         mDisplayInfoController = displayInfoController;
         mOverrideNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
         mIsPhysicalChannelConfigOn = true;
-        addState(mDefaultState);
-        addState(mLegacyState, mDefaultState);
-        addState(mIdleState, mDefaultState);
-        addState(mLteConnectedState, mDefaultState);
-        addState(mNrConnectedState, mDefaultState);
-        setInitialState(mDefaultState);
+        mPrimaryTimerState = "";
+        mSecondaryTimerState = "";
+        mPreviousState = "";
+        DefaultState defaultState = new DefaultState();
+        addState(defaultState);
+        addState(mLegacyState, defaultState);
+        addState(mIdleState, defaultState);
+        addState(mLteConnectedState, defaultState);
+        addState(mNrConnectedState, defaultState);
+        addState(mNrConnectedAdvancedState, defaultState);
+        setInitialState(defaultState);
         start();
+
+        mServiceState = mPhone.getServiceStateTracker().getServiceState();
+        mPhysicalChannelConfigs = mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+
         sendMessage(EVENT_INITIALIZE);
     }
 
@@ -200,10 +222,21 @@
     }
 
     /**
-     * @return True if either the primary or secondary 5G hysteresis timer is active,
-     * and false if neither are.
+     * @return The current data network type, used to create TelephonyDisplayInfo in
+     * DisplayInfoController.
      */
-    public boolean is5GHysteresisActive() {
+    public @Annotation.NetworkType int getDataNetworkType() {
+        NetworkRegistrationInfo nri = mServiceState.getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        return nri == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN
+                : nri.getAccessNetworkTechnology();
+    }
+
+    /**
+     * @return {@code true} if either the primary or secondary 5G icon timers are active,
+     * and {@code false} if neither are.
+     */
+    public boolean areAnyTimersActive() {
         return mIsPrimaryTimerActive || mIsSecondaryTimerActive;
     }
 
@@ -214,133 +247,101 @@
                 EVENT_PREFERRED_NETWORK_MODE_CHANGED, null);
         mPhone.registerForPhysicalChannelConfig(getHandler(),
                 EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, null);
-        mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(
-                AccessNetworkConstants.TRANSPORT_TYPE_WWAN, getHandler(),
-                EVENT_DATA_RAT_CHANGED, null);
-        mPhone.getServiceStateTracker().registerForBandwidthChanged(
-                getHandler(), EVENT_BANDWIDTH_CHANGED, null);
+        mPhone.getServiceStateTracker().registerForServiceStateChanged(getHandler(),
+                EVENT_SERVICE_STATE_CHANGED, null);
         mIsPhysicalChannelConfig16Supported = mPhone.getContext().getSystemService(
                 TelephonyManager.class).isRadioInterfaceCapabilitySupported(
                 TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
-        mPhone.getServiceStateTracker().registerForNrStateChanged(getHandler(),
-                EVENT_NR_STATE_CHANGED, null);
-        mPhone.getServiceStateTracker().registerForNrFrequencyChanged(getHandler(),
-                EVENT_NR_FREQUENCY_CHANGED, null);
         mPhone.getDeviceStateMonitor().registerForPhysicalChannelConfigNotifChanged(getHandler(),
                 EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED, null);
         IntentFilter filter = new IntentFilter();
-        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
         mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
-        if (!mPhone.isUsingNewDataStack()) {
-            mPhone.mCi.registerForPcoData(getHandler(), EVENT_PCO_DATA_CHANGED, null);
-        }
+        CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
+        ccm.registerCarrierConfigChangeListener(Runnable::run, mCarrierConfigChangeListener);
     }
 
     private void unRegisterForAllEvents() {
         mPhone.unregisterForRadioOffOrNotAvailable(getHandler());
         mPhone.unregisterForPreferredNetworkTypeChanged(getHandler());
-        mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged(
-                AccessNetworkConstants.TRANSPORT_TYPE_WWAN, getHandler());
-        mPhone.getServiceStateTracker().unregisterForNrStateChanged(getHandler());
-        mPhone.getServiceStateTracker().unregisterForNrFrequencyChanged(getHandler());
+        mPhone.getServiceStateTracker().unregisterForServiceStateChanged(getHandler());
         mPhone.getDeviceStateMonitor().unregisterForPhysicalChannelConfigNotifChanged(getHandler());
         mPhone.getContext().unregisterReceiver(mIntentReceiver);
-        if (!mPhone.isUsingNewDataStack()) {
-            mPhone.mCi.unregisterForPcoData(getHandler());
+        CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
+        if (mCarrierConfigChangeListener != null) {
+            ccm.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
         }
     }
 
     private void parseCarrierConfigs() {
-        String nrIconConfiguration = CarrierConfigManager.getDefaultConfig().getString(
-                CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING);
-        String overrideTimerRule = CarrierConfigManager.getDefaultConfig().getString(
-                CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING);
-        String overrideSecondaryTimerRule = CarrierConfigManager.getDefaultConfig().getString(
-                CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING);
-        mLteEnhancedPattern = CarrierConfigManager.getDefaultConfig().getString(
-                CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING);
-        mIsTimerResetEnabledForLegacyStateRRCIdle =
-                CarrierConfigManager.getDefaultConfig().getBoolean(
-                        CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL);
-        mLtePlusThresholdBandwidth = CarrierConfigManager.getDefaultConfig().getInt(
-                CarrierConfigManager.KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT);
-        mNrAdvancedThresholdBandwidth = CarrierConfigManager.getDefaultConfig().getInt(
-                CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT);
-        mEnableNrAdvancedWhileRoaming = CarrierConfigManager.getDefaultConfig().getBoolean(
-                CarrierConfigManager.KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL);
-
-        CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
-                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle config = CarrierConfigManager.getDefaultConfig();
+        CarrierConfigManager configManager =
+                mPhone.getContext().getSystemService(CarrierConfigManager.class);
         if (configManager != null) {
             PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
             if (b != null) {
-                if (b.getString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING) != null) {
-                    nrIconConfiguration = b.getString(
-                            CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING);
-                }
-                if (b.getString(CarrierConfigManager
-                        .KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING) != null) {
-                    overrideTimerRule = b.getString(
-                            CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING);
-                }
-                if (b.getString(CarrierConfigManager
-                        .KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING) != null) {
-                    overrideSecondaryTimerRule = b.getString(
-                            CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING);
-                }
-                if (b.getString(CarrierConfigManager
-                        .KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING) != null) {
-                    mLteEnhancedPattern = b.getString(
-                            CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING);
-                }
-                mIsTimerResetEnabledForLegacyStateRRCIdle = b.getBoolean(
-                        CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL);
-                mLtePlusThresholdBandwidth = b.getInt(
-                        CarrierConfigManager.KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT,
-                        mLtePlusThresholdBandwidth);
-                mNrAdvancedThresholdBandwidth = b.getInt(
-                        CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT,
-                        mNrAdvancedThresholdBandwidth);
-                mAdditionalNrAdvancedBandsList = b.getIntArray(
-                        CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY);
-                mNrAdvancedCapablePcoId = b.getInt(
-                        CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT);
-                if (mNrAdvancedCapablePcoId > 0 && mPhone.isUsingNewDataStack()) {
-                    mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
-                            new DataNetworkControllerCallback(getHandler()::post) {
-                                @Override
-                                public void onNrAdvancedCapableByPcoChanged(
-                                        boolean nrAdvancedCapable) {
-                                    log("mIsNrAdvancedAllowedByPco=" + nrAdvancedCapable);
-                                    mIsNrAdvancedAllowedByPco = nrAdvancedCapable;
-                                    sendMessage(EVENT_UPDATE_NR_ADVANCED_STATE);
-                                }
-                            });
-                }
-                mEnableNrAdvancedWhileRoaming = b.getBoolean(
-                        CarrierConfigManager.KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL);
-                mIsUsingUserDataForRrcDetection = b.getBoolean(
-                        CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL);
-                if (!mIsPhysicalChannelConfig16Supported || mIsUsingUserDataForRrcDetection) {
-                    if (mPhone.isUsingNewDataStack()) {
-                        mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
-                                new DataNetworkControllerCallback(getHandler()::post) {
-                                    @Override
-                                    public void onPhysicalLinkStatusChanged(
-                                            @LinkStatus int status) {
-                                        sendMessage(obtainMessage(
-                                                EVENT_PHYSICAL_LINK_STATUS_CHANGED,
-                                                new AsyncResult(null, status, null)));
-                                    }});
-                    } else {
-                        mPhone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                                .registerForPhysicalLinkStatusChanged(getHandler(),
-                                        EVENT_PHYSICAL_LINK_STATUS_CHANGED);
-                    }
-                }
+                config = b;
             }
         }
+        mLteEnhancedPattern = config.getString(
+                CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING);
+        mIsTimerResetEnabledForLegacyStateRrcIdle = config.getBoolean(
+                CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL);
+        mLtePlusThresholdBandwidth = config.getInt(
+                CarrierConfigManager.KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT);
+        mNrAdvancedThresholdBandwidth = config.getInt(
+                CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT);
+        mIncludeLteForNrAdvancedThresholdBandwidth = config.getBoolean(
+                CarrierConfigManager.KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL);
+        mEnableNrAdvancedWhileRoaming = config.getBoolean(
+                CarrierConfigManager.KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL);
+        mAdditionalNrAdvancedBandsList = config.getIntArray(
+                CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY);
+        mNrAdvancedCapablePcoId = config.getInt(
+                CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT);
+        if (mNrAdvancedCapablePcoId > 0 && mNrAdvancedCapableByPcoChangedCallback == null) {
+            mNrAdvancedCapableByPcoChangedCallback =
+                    new DataNetworkControllerCallback(getHandler()::post) {
+                        @Override
+                        public void onNrAdvancedCapableByPcoChanged(boolean nrAdvancedCapable) {
+                            log("mIsNrAdvancedAllowedByPco=" + nrAdvancedCapable);
+                            mIsNrAdvancedAllowedByPco = nrAdvancedCapable;
+                            sendMessage(EVENT_UPDATE);
+                        }
+                    };
+            mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
+                    mNrAdvancedCapableByPcoChangedCallback);
+        } else if (mNrAdvancedCapablePcoId == 0 && mNrAdvancedCapableByPcoChangedCallback != null) {
+            mPhone.getDataNetworkController().unregisterDataNetworkControllerCallback(
+                    mNrAdvancedCapableByPcoChangedCallback);
+            mNrAdvancedCapableByPcoChangedCallback = null;
+        }
+        mIsUsingUserDataForRrcDetection = config.getBoolean(
+                CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL);
+        if (!isUsingPhysicalChannelConfigForRrcDetection()) {
+            if (mNrPhysicalLinkStatusChangedCallback == null) {
+                mNrPhysicalLinkStatusChangedCallback =
+                        new DataNetworkControllerCallback(getHandler()::post) {
+                            @Override
+                            public void onPhysicalLinkStatusChanged(@LinkStatus int status) {
+                                sendMessage(obtainMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
+                                        new AsyncResult(null, status, null)));
+                            }
+                        };
+                mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
+                        mNrPhysicalLinkStatusChangedCallback);
+            }
+        } else if (mNrPhysicalLinkStatusChangedCallback != null) {
+            mPhone.getDataNetworkController().unregisterDataNetworkControllerCallback(
+                    mNrPhysicalLinkStatusChangedCallback);
+            mNrPhysicalLinkStatusChangedCallback = null;
+        }
+        String nrIconConfiguration = config.getString(
+                CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING);
+        String overrideTimerRule = config.getString(
+                CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING);
+        String overrideSecondaryTimerRule = config.getString(
+                CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING);
         createTimerRules(nrIconConfiguration, overrideTimerRule, overrideSecondaryTimerRule);
     }
 
@@ -349,7 +350,7 @@
         if (!TextUtils.isEmpty(icons)) {
             // Format: "STATE:ICON,STATE2:ICON2"
             for (String pair : icons.trim().split(",")) {
-                String[] kv = (pair.trim().toLowerCase()).split(":");
+                String[] kv = (pair.trim().toLowerCase(Locale.ROOT)).split(":");
                 if (kv.length != 2) {
                     if (DBG) loge("Invalid 5G icon configuration, config = " + pair);
                     continue;
@@ -376,7 +377,7 @@
         if (!TextUtils.isEmpty(timers)) {
             // Format: "FROM_STATE,TO_STATE,DURATION;FROM_STATE_2,TO_STATE_2,DURATION_2"
             for (String triple : timers.trim().split(";")) {
-                String[] kv = (triple.trim().toLowerCase()).split(",");
+                String[] kv = (triple.trim().toLowerCase(Locale.ROOT)).split(",");
                 if (kv.length != 3) {
                     if (DBG) loge("Invalid 5G icon timer configuration, config = " + triple);
                     continue;
@@ -402,7 +403,7 @@
         if (!TextUtils.isEmpty(secondaryTimers)) {
             // Format: "PRIMARY_STATE,TO_STATE,DURATION;PRIMARY_STATE_2,TO_STATE_2,DURATION_2"
             for (String triple : secondaryTimers.trim().split(";")) {
-                String[] kv = (triple.trim().toLowerCase()).split(",");
+                String[] kv = (triple.trim().toLowerCase(Locale.ROOT)).split(",");
                 if (kv.length != 3) {
                     if (DBG) {
                         loge("Invalid 5G icon secondary timer configuration, config = " + triple);
@@ -444,7 +445,7 @@
         int displayNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
         int dataNetworkType = getDataNetworkType();
         boolean nrNsa = isLte(dataNetworkType)
-                && mPhone.getServiceState().getNrState() != NetworkRegistrationInfo.NR_STATE_NONE;
+                && mServiceState.getNrState() != NetworkRegistrationInfo.NR_STATE_NONE;
         boolean nrSa = dataNetworkType == TelephonyManager.NETWORK_TYPE_NR;
 
         // NR display is not accurate when physical channel config notifications are off
@@ -475,7 +476,7 @@
                 keys.add(STATE_CONNECTED_NR_ADVANCED);
             }
         } else {
-            switch (mPhone.getServiceState().getNrState()) {
+            switch (mServiceState.getNrState()) {
                 case NetworkRegistrationInfo.NR_STATE_CONNECTED:
                     if (isNrAdvanced()) {
                         keys.add(STATE_CONNECTED_NR_ADVANCED);
@@ -505,9 +506,9 @@
     private @Annotation.OverrideNetworkType int getLteDisplayType() {
         int value = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
         if ((getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA
-                || mPhone.getServiceState().isUsingCarrierAggregation())
-                && (IntStream.of(mPhone.getServiceState().getCellBandwidths()).sum()
-                        > mLtePlusThresholdBandwidth)) {
+                || mServiceState.isUsingCarrierAggregation())
+                && IntStream.of(mServiceState.getCellBandwidths()).sum()
+                > mLtePlusThresholdBandwidth) {
             value = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA;
         }
         if (isLteEnhancedAvailable()) {
@@ -521,8 +522,8 @@
             return false;
         }
         Pattern stringPattern = Pattern.compile(mLteEnhancedPattern);
-        for (String opName : new String[] {mPhone.getServiceState().getOperatorAlphaLongRaw(),
-                mPhone.getServiceState().getOperatorAlphaShortRaw()}) {
+        for (String opName : new String[] {mServiceState.getOperatorAlphaLongRaw(),
+                mServiceState.getOperatorAlphaShortRaw()}) {
             if (!TextUtils.isEmpty(opName)) {
                 Matcher matcher = stringPattern.matcher(opName);
                 if (matcher.find()) {
@@ -542,8 +543,6 @@
             if (DBG) log("DefaultState: process " + getEventName(msg.what));
             switch (msg.what) {
                 case EVENT_UPDATE:
-                case EVENT_PREFERRED_NETWORK_MODE_CHANGED:
-                    if (DBG) log("Reset timers since preferred network mode changed.");
                     resetAllTimers();
                     transitionToCurrentState();
                     break;
@@ -555,26 +554,15 @@
                     break;
                 case EVENT_INITIALIZE:
                     // The reason that we do it here is because some of the works below requires
-                    // other modules (e.g. DcTracker, ServiceStateTracker), which is not created
-                    // yet when NetworkTypeController is created.
+                    // other modules (e.g. DataNetworkController, ServiceStateTracker), which is not
+                    // created yet when NetworkTypeController is created.
                     registerForAllEvents();
                     parseCarrierConfigs();
                     break;
-                case EVENT_DATA_RAT_CHANGED:
-                case EVENT_NR_STATE_CHANGED:
-                case EVENT_NR_FREQUENCY_CHANGED:
-                case EVENT_PCO_DATA_CHANGED:
-                case EVENT_UPDATE_NR_ADVANCED_STATE:
-                    // ignored
-                    break;
-                case EVENT_BANDWIDTH_CHANGED:
-                    // Update in case of LTE/LTE+ switch
-                    updateOverrideNetworkType();
-                    break;
-                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
-                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
-                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
-                    }
+                case EVENT_SERVICE_STATE_CHANGED:
+                    mServiceState = mPhone.getServiceStateTracker().getServiceState();
+                    if (DBG) log("ServiceState updated: " + mServiceState);
+                    transitionToCurrentState();
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
@@ -616,6 +604,20 @@
                     resetAllTimers();
                     transitionTo(mLegacyState);
                     break;
+                case EVENT_PREFERRED_NETWORK_MODE_CHANGED:
+                    if (DBG) log("Reset timers since preferred network mode changed.");
+                    resetAllTimers();
+                    transitionToCurrentState();
+                    break;
+                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    mPhysicalChannelConfigs =
+                            mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+                    if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
+                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
+                    }
+                    transitionToCurrentState();
+                    break;
                 case EVENT_DEVICE_IDLE_MODE_CHANGED:
                     PowerManager pm = mPhone.getContext().getSystemService(PowerManager.class);
                     mIsDeviceIdleMode = pm.isDeviceIdleMode();
@@ -635,8 +637,6 @@
         }
     }
 
-    private final DefaultState mDefaultState = new DefaultState();
-
     /**
      * Device does not have NR available, due to any of the below reasons:
      * <ul>
@@ -664,11 +664,19 @@
         public boolean processMessage(Message msg) {
             if (DBG) log("LegacyState: process " + getEventName(msg.what));
             updateTimers();
-            int rat = getDataNetworkType();
             switch (msg.what) {
-                case EVENT_DATA_RAT_CHANGED:
+                case EVENT_SERVICE_STATE_CHANGED:
+                    mServiceState = mPhone.getServiceStateTracker().getServiceState();
+                    if (DBG) log("ServiceState updated: " + mServiceState);
+                    // fallthrough
+                case EVENT_UPDATE:
+                    int rat = getDataNetworkType();
                     if (rat == TelephonyManager.NETWORK_TYPE_NR || isLte(rat) && isNrConnected()) {
-                        transitionTo(mNrConnectedState);
+                        if (isNrAdvanced()) {
+                            transitionTo(mNrConnectedAdvancedState);
+                        } else {
+                            transitionTo(mNrConnectedState);
+                        }
                     } else if (isLte(rat) && isNrNotRestricted()) {
                         transitionWithTimerTo(isPhysicalLinkActive()
                                 ? mLteConnectedState : mIdleState);
@@ -681,35 +689,22 @@
                     }
                     mIsNrRestricted = isNrRestricted();
                     break;
-                case EVENT_NR_STATE_CHANGED:
-                    if (isNrConnected()) {
-                        transitionTo(mNrConnectedState);
-                    } else if (isLte(rat) && isNrNotRestricted()) {
-                        transitionWithTimerTo(isPhysicalLinkActive()
-                                ? mLteConnectedState : mIdleState);
-                    } else if (isLte(rat) && isNrRestricted()) {
-                        updateOverrideNetworkType();
-                    }
-                    mIsNrRestricted = isNrRestricted();
-                    break;
-                case EVENT_NR_FREQUENCY_CHANGED:
-                    // ignored
-                    break;
                 case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    mPhysicalChannelConfigs =
+                            mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+                    if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
                     if (isUsingPhysicalChannelConfigForRrcDetection()) {
                         mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
-                        if (mIsTimerResetEnabledForLegacyStateRRCIdle && !isPhysicalLinkActive()) {
+                        if (mIsTimerResetEnabledForLegacyStateRrcIdle && !isPhysicalLinkActive()) {
                             if (DBG) log("Reset timers since timer reset is enabled for RRC idle.");
                             resetAllTimers();
                         }
                     }
-                    // Update in case of LTE/LTE+ switch
-                    updateOverrideNetworkType();
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
-                    if (mIsTimerResetEnabledForLegacyStateRRCIdle && !isPhysicalLinkActive()) {
+                    if (mIsTimerResetEnabledForLegacyStateRrcIdle && !isPhysicalLinkActive()) {
                         if (DBG) log("Reset timers since timer reset is enabled for RRC idle.");
                         resetAllTimers();
                         updateOverrideNetworkType();
@@ -751,52 +746,52 @@
             if (DBG) log("IdleState: process " + getEventName(msg.what));
             updateTimers();
             switch (msg.what) {
-                case EVENT_DATA_RAT_CHANGED:
+                case EVENT_SERVICE_STATE_CHANGED:
+                    mServiceState = mPhone.getServiceStateTracker().getServiceState();
+                    if (DBG) log("ServiceState updated: " + mServiceState);
+                    // fallthrough
+                case EVENT_UPDATE:
                     int rat = getDataNetworkType();
-                    if (rat == TelephonyManager.NETWORK_TYPE_NR) {
-                        transitionTo(mNrConnectedState);
+                    if (rat == TelephonyManager.NETWORK_TYPE_NR
+                            || (isLte(rat) && isNrConnected())) {
+                        if (isNrAdvanced()) {
+                            transitionTo(mNrConnectedAdvancedState);
+                        } else {
+                            transitionTo(mNrConnectedState);
+                        }
                     } else if (!isLte(rat) || !isNrNotRestricted()) {
                         transitionWithTimerTo(mLegacyState);
-                    }
-                    break;
-                case EVENT_NR_STATE_CHANGED:
-                    if (isNrConnected()) {
-                        transitionTo(mNrConnectedState);
-                    } else if (!isNrNotRestricted()) {
-                        transitionWithTimerTo(mLegacyState);
-                    }
-                    break;
-                case EVENT_NR_FREQUENCY_CHANGED:
-                    // ignore
-                    break;
-                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
-                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
-                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
-                        if (isNrNotRestricted()) {
-                            // NOT_RESTRICTED_RRC_IDLE -> NOT_RESTRICTED_RRC_CON
-                            if (isPhysicalLinkActive()) {
-                                transitionWithTimerTo(mLteConnectedState);
-                                break;
-                            }
+                    } else {
+                        if (isPhysicalLinkActive()) {
+                            transitionWithTimerTo(mLteConnectedState);
                         } else {
-                            log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
-                            sendMessage(EVENT_NR_STATE_CHANGED);
+                            // Update in case the override network type changed
+                            updateOverrideNetworkType();
                         }
                     }
-                    // Update in case of LTE/LTE+ switch
-                    updateOverrideNetworkType();
+                    break;
+                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    mPhysicalChannelConfigs =
+                            mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+                    if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
+                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
+                        if (isPhysicalLinkActive()) {
+                            transitionWithTimerTo(mLteConnectedState);
+                        } else {
+                            log("Reevaluating state due to link status changed.");
+                            sendMessage(EVENT_UPDATE);
+                        }
+                    }
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
-                    if (isNrNotRestricted()) {
-                        // NOT_RESTRICTED_RRC_IDLE -> NOT_RESTRICTED_RRC_CON
-                        if (isPhysicalLinkActive()) {
-                            transitionWithTimerTo(mLteConnectedState);
-                        }
+                    if (isPhysicalLinkActive()) {
+                        transitionWithTimerTo(mLteConnectedState);
                     } else {
-                        log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
-                        sendMessage(EVENT_NR_STATE_CHANGED);
+                        log("Reevaluating state due to link status changed.");
+                        sendMessage(EVENT_UPDATE);
                     }
                     break;
                 default:
@@ -835,52 +830,52 @@
             if (DBG) log("LteConnectedState: process " + getEventName(msg.what));
             updateTimers();
             switch (msg.what) {
-                case EVENT_DATA_RAT_CHANGED:
+                case EVENT_SERVICE_STATE_CHANGED:
+                    mServiceState = mPhone.getServiceStateTracker().getServiceState();
+                    if (DBG) log("ServiceState updated: " + mServiceState);
+                    // fallthrough
+                case EVENT_UPDATE:
                     int rat = getDataNetworkType();
-                    if (rat == TelephonyManager.NETWORK_TYPE_NR) {
-                        transitionTo(mNrConnectedState);
+                    if (rat == TelephonyManager.NETWORK_TYPE_NR
+                            || (isLte(rat) && isNrConnected())) {
+                        if (isNrAdvanced()) {
+                            transitionTo(mNrConnectedAdvancedState);
+                        } else {
+                            transitionTo(mNrConnectedState);
+                        }
                     } else if (!isLte(rat) || !isNrNotRestricted()) {
                         transitionWithTimerTo(mLegacyState);
-                    }
-                    break;
-                case EVENT_NR_STATE_CHANGED:
-                    if (isNrConnected()) {
-                        transitionTo(mNrConnectedState);
-                    } else if (!isNrNotRestricted()) {
-                        transitionWithTimerTo(mLegacyState);
-                    }
-                    break;
-                case EVENT_NR_FREQUENCY_CHANGED:
-                    // ignore
-                    break;
-                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
-                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
-                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
-                        if (isNrNotRestricted()) {
-                            // NOT_RESTRICTED_RRC_CON -> NOT_RESTRICTED_RRC_IDLE
-                            if (!isPhysicalLinkActive()) {
-                                transitionWithTimerTo(mIdleState);
-                                break;
-                            }
+                    } else {
+                        if (!isPhysicalLinkActive()) {
+                            transitionWithTimerTo(mIdleState);
                         } else {
-                            log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
-                            sendMessage(EVENT_NR_STATE_CHANGED);
+                            // Update in case the override network type changed
+                            updateOverrideNetworkType();
                         }
                     }
-                    // Update in case of LTE/LTE+ switch
-                    updateOverrideNetworkType();
+                    break;
+                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    mPhysicalChannelConfigs =
+                            mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+                    if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
+                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
+                        if (!isPhysicalLinkActive()) {
+                            transitionWithTimerTo(mIdleState);
+                        } else {
+                            log("Reevaluating state due to link status changed.");
+                            sendMessage(EVENT_UPDATE);
+                        }
+                    }
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
-                    if (isNrNotRestricted()) {
-                        // NOT_RESTRICTED_RRC_CON -> NOT_RESTRICTED_RRC_IDLE
-                        if (!isPhysicalLinkActive()) {
-                            transitionWithTimerTo(mIdleState);
-                        }
+                    if (!isPhysicalLinkActive()) {
+                        transitionWithTimerTo(mIdleState);
                     } else {
-                        log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
-                        sendMessage(EVENT_NR_STATE_CHANGED);
+                        log("Reevaluating state due to link status changed.");
+                        sendMessage(EVENT_UPDATE);
                     }
                     break;
                 default:
@@ -904,28 +899,35 @@
      * Device is connected to 5G NR as the primary or secondary cell.
      */
     private final class NrConnectedState extends State {
-        private boolean mIsNrAdvanced = false;
-
         @Override
         public void enter() {
-            if (DBG) log("Entering NrConnectedState(" + getName() + ")");
+            if (DBG) log("Entering NrConnectedState");
             updateTimers();
             updateOverrideNetworkType();
             if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
-                mIsNrAdvanced = isNrAdvanced();
                 mPreviousState = getName();
             }
         }
 
         @Override
         public boolean processMessage(Message msg) {
-            if (DBG) log("NrConnectedState(" + getName() + "): process " + getEventName(msg.what));
+            if (DBG) log("NrConnectedState: process " + getEventName(msg.what));
             updateTimers();
-            int rat = getDataNetworkType();
             switch (msg.what) {
-                case EVENT_DATA_RAT_CHANGED:
-                    if (rat == TelephonyManager.NETWORK_TYPE_NR || isLte(rat) && isNrConnected()) {
-                        updateOverrideNetworkType();
+                case EVENT_SERVICE_STATE_CHANGED:
+                    mServiceState = mPhone.getServiceStateTracker().getServiceState();
+                    if (DBG) log("ServiceState updated: " + mServiceState);
+                    // fallthrough
+                case EVENT_UPDATE:
+                    int rat = getDataNetworkType();
+                    if (rat == TelephonyManager.NETWORK_TYPE_NR
+                            || (isLte(rat) && isNrConnected())) {
+                        if (isNrAdvanced()) {
+                            transitionTo(mNrConnectedAdvancedState);
+                        } else {
+                            // Update in case the override network type changed
+                            updateOverrideNetworkType();
+                        }
                     } else if (isLte(rat) && isNrNotRestricted()) {
                         transitionWithTimerTo(isPhysicalLinkActive()
                                 ? mLteConnectedState : mIdleState);
@@ -933,37 +935,21 @@
                         transitionWithTimerTo(mLegacyState);
                     }
                     break;
-                case EVENT_NR_STATE_CHANGED:
-                    if (isLte(rat) && isNrNotRestricted()) {
-                        transitionWithTimerTo(isPhysicalLinkActive()
-                                ? mLteConnectedState : mIdleState);
-                    } else if (rat != TelephonyManager.NETWORK_TYPE_NR && !isNrConnected()) {
-                        transitionWithTimerTo(mLegacyState);
-                    }
-                    break;
-                case EVENT_PCO_DATA_CHANGED:
-                    handlePcoData((AsyncResult) msg.obj);
-                    break;
-                case EVENT_UPDATE_NR_ADVANCED_STATE:
-                    updateNrAdvancedState();
-                    break;
-                case EVENT_NR_FREQUENCY_CHANGED:
                 case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    mPhysicalChannelConfigs =
+                            mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+                    if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
                     if (isUsingPhysicalChannelConfigForRrcDetection()) {
                         mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
                     }
-                    updateNrAdvancedState();
+                    // Check NR advanced in case NR advanced bands were added
+                    if (isNrAdvanced()) {
+                        transitionTo(mNrConnectedAdvancedState);
+                    }
                     break;
                 case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
                     AsyncResult ar = (AsyncResult) msg.obj;
                     mPhysicalLinkStatus = (int) ar.result;
-                    if (!isNrConnected()) {
-                        log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
-                        sendMessage(EVENT_NR_STATE_CHANGED);
-                    }
-                    break;
-                case EVENT_BANDWIDTH_CHANGED:
-                    updateNrAdvancedState();
                     break;
                 default:
                     return NOT_HANDLED;
@@ -976,55 +962,94 @@
 
         @Override
         public String getName() {
-            return mIsNrAdvanced ? STATE_CONNECTED_NR_ADVANCED : STATE_CONNECTED;
-        }
-
-        private void updateNrAdvancedState() {
-            if (!isNrConnected()) {
-                log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
-                sendMessage(EVENT_NR_STATE_CHANGED);
-                return;
-            }
-            if (!isNrAdvanced()) {
-                if (DBG) log("updateNrAdvancedState: CONNECTED_NR_ADVANCED -> CONNECTED");
-                transitionWithTimerTo(mNrConnectedState);
-            } else {
-                if (DBG) log("updateNrAdvancedState: CONNECTED -> CONNECTED_NR_ADVANCED");
-                transitionTo(mNrConnectedState);
-            }
-            mIsNrAdvanced = isNrAdvanced();
-        }
-
-        private void handlePcoData(AsyncResult ar) {
-            if (ar.exception != null) {
-                loge("PCO_DATA exception: " + ar.exception);
-                return;
-            }
-            PcoData pcodata = (PcoData) ar.result;
-            if (pcodata == null) {
-                return;
-            }
-            log("EVENT_PCO_DATA_CHANGED: pco data: " + pcodata);
-            DcTracker dcTracker = mPhone.getDcTracker(
-                    AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-            DataConnection dc =
-                    dcTracker != null ? dcTracker.getDataConnectionByContextId(pcodata.cid) : null;
-            ApnSetting apnSettings = dc != null ? dc.getApnSetting() : null;
-            if (apnSettings != null && apnSettings.canHandleType(ApnSetting.TYPE_DEFAULT)
-                    && mNrAdvancedCapablePcoId > 0
-                    && pcodata.pcoId == mNrAdvancedCapablePcoId
-            ) {
-                log("EVENT_PCO_DATA_CHANGED: NR_ADVANCED is allowed by PCO. length:"
-                        + pcodata.contents.length + ",value: " + Arrays.toString(pcodata.contents));
-                mIsNrAdvancedAllowedByPco = pcodata.contents.length > 0
-                        && pcodata.contents[pcodata.contents.length - 1] == 1;
-                updateNrAdvancedState();
-            }
+            return STATE_CONNECTED;
         }
     }
 
     private final NrConnectedState mNrConnectedState = new NrConnectedState();
 
+    /**
+     * Device is connected to 5G NR as the primary cell and the data rate is higher than
+     * the generic 5G data rate.
+     */
+    private final class NrConnectedAdvancedState extends State {
+        @Override
+        public void enter() {
+            if (DBG) log("Entering NrConnectedAdvancedState");
+            updateTimers();
+            updateOverrideNetworkType();
+            if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
+                mPreviousState = getName();
+            }
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (DBG) log("NrConnectedAdvancedState: process " + getEventName(msg.what));
+            updateTimers();
+            switch (msg.what) {
+                case EVENT_SERVICE_STATE_CHANGED:
+                    mServiceState = mPhone.getServiceStateTracker().getServiceState();
+                    if (DBG) log("ServiceState updated: " + mServiceState);
+                    // fallthrough
+                case EVENT_UPDATE:
+                    int rat = getDataNetworkType();
+                    if (rat == TelephonyManager.NETWORK_TYPE_NR
+                            || (isLte(rat) && isNrConnected())) {
+                        if (isNrAdvanced()) {
+                            // Update in case the override network type changed
+                            updateOverrideNetworkType();
+                        } else {
+                            if (rat == TelephonyManager.NETWORK_TYPE_NR && mOverrideNetworkType
+                                    != TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) {
+                                // manually override network type after data rat changes since
+                                // timer will prevent it from being updated
+                                mOverrideNetworkType =
+                                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
+                            }
+                            transitionWithTimerTo(mNrConnectedState);
+                        }
+                    } else if (isLte(rat) && isNrNotRestricted()) {
+                        transitionWithTimerTo(isPhysicalLinkActive()
+                                ? mLteConnectedState : mIdleState);
+                    } else {
+                        transitionWithTimerTo(mLegacyState);
+                    }
+                    break;
+                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    mPhysicalChannelConfigs =
+                            mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+                    if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+                    if (isUsingPhysicalChannelConfigForRrcDetection()) {
+                        mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
+                    }
+                    // Check NR advanced in case NR advanced bands were removed
+                    if (!isNrAdvanced()) {
+                        transitionWithTimerTo(mNrConnectedState);
+                    }
+                    break;
+                case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    mPhysicalLinkStatus = (int) ar.result;
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
+                mPreviousState = getName();
+            }
+            return HANDLED;
+        }
+
+        @Override
+        public String getName() {
+            return STATE_CONNECTED_NR_ADVANCED;
+        }
+    }
+
+    private final NrConnectedAdvancedState mNrConnectedAdvancedState =
+            new NrConnectedAdvancedState();
+
     private void transitionWithTimerTo(IState destState) {
         String destName = destState.getName();
         if (DBG) log("Transition with primary timer from " + mPreviousState + " to " + destName);
@@ -1062,22 +1087,23 @@
     private void transitionToCurrentState() {
         int dataRat = getDataNetworkType();
         IState transitionState;
-        if (dataRat == TelephonyManager.NETWORK_TYPE_NR || isNrConnected()) {
-            transitionState = mNrConnectedState;
-            mPreviousState = isNrAdvanced() ? STATE_CONNECTED_NR_ADVANCED : STATE_CONNECTED;
+        if (dataRat == TelephonyManager.NETWORK_TYPE_NR || (isLte(dataRat) && isNrConnected())) {
+            if (isNrAdvanced()) {
+                transitionState = mNrConnectedAdvancedState;
+            } else {
+                transitionState = mNrConnectedState;
+            }
         } else if (isLte(dataRat) && isNrNotRestricted()) {
             if (isPhysicalLinkActive()) {
                 transitionState = mLteConnectedState;
-                mPreviousState = STATE_NOT_RESTRICTED_RRC_CON;
             } else {
                 transitionState = mIdleState;
-                mPreviousState = STATE_NOT_RESTRICTED_RRC_IDLE;
             }
         } else {
             transitionState = mLegacyState;
-            mPreviousState = isNrRestricted() ? STATE_RESTRICTED : STATE_LEGACY;
         }
         if (!transitionState.equals(getCurrentState())) {
+            mPreviousState = getCurrentState().getName();
             transitionTo(transitionState);
         } else {
             updateOverrideNetworkType();
@@ -1103,6 +1129,8 @@
             removeMessages(EVENT_PRIMARY_TIMER_EXPIRED);
             mIsPrimaryTimerActive = false;
             mPrimaryTimerState = "";
+            transitionToCurrentState();
+            return;
         }
 
         if (mIsSecondaryTimerActive && !mSecondaryTimerState.equals(currentState)) {
@@ -1114,18 +1142,25 @@
             removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
             mIsSecondaryTimerActive = false;
             mSecondaryTimerState = "";
+            transitionToCurrentState();
+            return;
         }
 
         if (mIsPrimaryTimerActive || mIsSecondaryTimerActive) {
             if (currentState.equals(STATE_CONNECTED_NR_ADVANCED)) {
                 if (DBG) log("Reset timers since state is NR_ADVANCED.");
                 resetAllTimers();
-            }
-
-            int rat = getDataNetworkType();
-            if (!isLte(rat) && rat != TelephonyManager.NETWORK_TYPE_NR) {
-                if (DBG) log("Reset timers since 2G and 3G don't need NR timers.");
+            } else if (currentState.equals(STATE_CONNECTED)
+                    && !mPrimaryTimerState.equals(STATE_CONNECTED_NR_ADVANCED)
+                    && !mSecondaryTimerState.equals(STATE_CONNECTED_NR_ADVANCED)) {
+                if (DBG) log("Reset non-NR_ADVANCED timers since state is NR_CONNECTED");
                 resetAllTimers();
+            } else {
+                int rat = getDataNetworkType();
+                if (!isLte(rat) && rat != TelephonyManager.NETWORK_TYPE_NR) {
+                    if (DBG) log("Reset timers since 2G and 3G don't need NR timers.");
+                    resetAllTimers();
+                }
             }
         }
     }
@@ -1222,17 +1257,15 @@
     }
 
     private boolean isNrConnected() {
-        return mPhone.getServiceState().getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED;
+        return mServiceState.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED;
     }
 
     private boolean isNrNotRestricted() {
-        return mPhone.getServiceState().getNrState()
-                == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED;
+        return mServiceState.getNrState() == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED;
     }
 
     private boolean isNrRestricted() {
-        return mPhone.getServiceState().getNrState()
-                == NetworkRegistrationInfo.NR_STATE_RESTRICTED;
+        return mServiceState.getNrState() == NetworkRegistrationInfo.NR_STATE_RESTRICTED;
     }
 
     /**
@@ -1247,15 +1280,24 @@
 
         // Check if NR advanced is enabled when the device is roaming. Some carriers disable it
         // while the device is roaming.
-        if (mPhone.getServiceState().getDataRoaming() && !mEnableNrAdvancedWhileRoaming) {
+        if (mServiceState.getDataRoaming() && !mEnableNrAdvancedWhileRoaming) {
             return false;
         }
 
+        int bandwidths = 0;
+        if (mPhone.getServiceStateTracker().getPhysicalChannelConfigList() != null) {
+            bandwidths = mPhone.getServiceStateTracker().getPhysicalChannelConfigList()
+                    .stream()
+                    .filter(config -> mIncludeLteForNrAdvancedThresholdBandwidth
+                            || config.getNetworkType() == TelephonyManager.NETWORK_TYPE_NR)
+                    .map(PhysicalChannelConfig::getCellBandwidthDownlinkKhz)
+                    .mapToInt(Integer::intValue)
+                    .sum();
+        }
+
         // Check if meeting minimum bandwidth requirement. For most carriers, there is no minimum
         // bandwidth requirement and mNrAdvancedThresholdBandwidth is 0.
-        if (mNrAdvancedThresholdBandwidth > 0
-                && IntStream.of(mPhone.getServiceState().getCellBandwidths()).sum()
-                < mNrAdvancedThresholdBandwidth) {
+        if (mNrAdvancedThresholdBandwidth > 0 && bandwidths < mNrAdvancedThresholdBandwidth) {
             return false;
         }
 
@@ -1265,18 +1307,15 @@
     }
 
     private boolean isNrMmwave() {
-        return mPhone.getServiceState().getNrFrequencyRange()
-                == ServiceState.FREQUENCY_RANGE_MMWAVE;
+        return mServiceState.getNrFrequencyRange() == ServiceState.FREQUENCY_RANGE_MMWAVE;
     }
 
     private boolean isAdditionalNrAdvancedBand() {
-        List<PhysicalChannelConfig> physicalChannelConfigList =
-                mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
         if (ArrayUtils.isEmpty(mAdditionalNrAdvancedBandsList)
-                || physicalChannelConfigList == null) {
+                || mPhysicalChannelConfigs == null) {
             return false;
         }
-        for (PhysicalChannelConfig item : physicalChannelConfigList) {
+        for (PhysicalChannelConfig item : mPhysicalChannelConfigs) {
             if (item.getNetworkType() == TelephonyManager.NETWORK_TYPE_NR
                     && ArrayUtils.contains(mAdditionalNrAdvancedBandsList, item.getBand())) {
                 return true;
@@ -1295,19 +1334,10 @@
     }
 
     private int getPhysicalLinkStatusFromPhysicalChannelConfig() {
-        List<PhysicalChannelConfig> physicalChannelConfigList =
-                mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
-        return (physicalChannelConfigList == null || physicalChannelConfigList.isEmpty())
+        return (mPhysicalChannelConfigs == null || mPhysicalChannelConfigs.isEmpty())
                 ? DataCallResponse.LINK_STATUS_DORMANT : DataCallResponse.LINK_STATUS_ACTIVE;
     }
 
-    private int getDataNetworkType() {
-        NetworkRegistrationInfo nri =  mPhone.getServiceState().getNetworkRegistrationInfo(
-                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-        return nri == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN
-                : nri.getAccessNetworkTechnology();
-    }
-
     private String getEventName(int event) {
         try {
             return sEvents[event];
@@ -1354,16 +1384,16 @@
         pw.println("mIsPhysicalChannelConfigOn=" + mIsPhysicalChannelConfigOn);
         pw.println("mIsPrimaryTimerActive=" + mIsPrimaryTimerActive);
         pw.println("mIsSecondaryTimerActive=" + mIsSecondaryTimerActive);
-        pw.println("mIsTimerRestEnabledForLegacyStateRRCIdle="
-                + mIsTimerResetEnabledForLegacyStateRRCIdle);
+        pw.println("mIsTimerResetEnabledForLegacyStateRrcIdle="
+                + mIsTimerResetEnabledForLegacyStateRrcIdle);
         pw.println("mLtePlusThresholdBandwidth=" + mLtePlusThresholdBandwidth);
         pw.println("mNrAdvancedThresholdBandwidth=" + mNrAdvancedThresholdBandwidth);
+        pw.println("mAdditionalNrAdvancedBandsList="
+                + Arrays.toString(mAdditionalNrAdvancedBandsList));
         pw.println("mPrimaryTimerState=" + mPrimaryTimerState);
         pw.println("mSecondaryTimerState=" + mSecondaryTimerState);
         pw.println("mPreviousState=" + mPreviousState);
-        pw.println("mPhysicalLinkStatus=" + mPhysicalLinkStatus);
-        pw.println("mAdditionalNrAdvancedBandsList="
-                + Arrays.toString(mAdditionalNrAdvancedBandsList));
+        pw.println("mPhysicalLinkStatus=" + DataUtils.linkStatusToString(mPhysicalLinkStatus));
         pw.println("mIsPhysicalChannelConfig16Supported=" + mIsPhysicalChannelConfig16Supported);
         pw.println("mIsNrAdvancedAllowedByPco=" + mIsNrAdvancedAllowedByPco);
         pw.println("mNrAdvancedCapablePcoId=" + mNrAdvancedCapablePcoId);
diff --git a/src/java/com/android/internal/telephony/NitzData.java b/src/java/com/android/internal/telephony/NitzData.java
index 6b19e08..8430585 100644
--- a/src/java/com/android/internal/telephony/NitzData.java
+++ b/src/java/com/android/internal/telephony/NitzData.java
@@ -21,6 +21,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.telephony.Rlog;
 
+import java.time.DateTimeException;
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
 import java.util.Objects;
@@ -39,9 +40,6 @@
     private static final int MS_PER_QUARTER_HOUR = 15 * 60 * 1000;
     private static final int MS_PER_HOUR = 60 * 60 * 1000;
 
-    /* Time stamp after 19 January 2038 is not supported under 32 bit */
-    private static final int MAX_NITZ_YEAR = 2037;
-
     private static final Pattern NITZ_SPLIT_PATTERN = Pattern.compile("[/:,+-]");
 
     // Stored For logging / debugging only.
@@ -77,22 +75,37 @@
         try {
             String[] nitzSubs = NITZ_SPLIT_PATTERN.split(nitz);
 
-            int year = 2000 + Integer.parseInt(nitzSubs[0]);
-            if (year > MAX_NITZ_YEAR) {
-                if (ServiceStateTracker.DBG) {
-                    Rlog.e(LOG_TAG, "NITZ year: " + year + " exceeds limit, skip NITZ time update");
-                }
-                return null;
+            int year = Integer.parseInt(nitzSubs[0]);
+            if (year < 1 || year > 99) {
+                // 0 > year > 99 imply an invalid string.
+                //
+                // At the time of this comment (year 2023), a zero year is considered invalid and
+                // assumed to be the result of invalid data being converted to zero in the code that
+                // turns the binary NITZ into a string. For the next few decades at least, Android
+                // devices should not need to interpret zero. Hopefully, NITZ will be replaced by
+                // the time that's not true, or folks dealing the Y2K1 issue can handle it.
+                //
+                // DateTimeException is also thrown by LocalDateTime below if the values are out of
+                // range and will be handled in the catch block.
+                throw new DateTimeException("Invalid NITZ year == 0");
             }
 
+            // Values < {current year} could be considered invalid but are used in test code, so no
+            // window is applied to adjust low values < {current year} with "+ 2100" (and would also
+            // need to consider zero as valid). Code that processes the NitzData is in a better
+            // position to log and discard obviously invalid NITZ signals from past years.
+            year += 2000;
+
             int month = Integer.parseInt(nitzSubs[1]);
             int date = Integer.parseInt(nitzSubs[2]);
             int hour = Integer.parseInt(nitzSubs[3]);
             int minute = Integer.parseInt(nitzSubs[4]);
             int second = Integer.parseInt(nitzSubs[5]);
 
-            /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
-             * offset as well (which we won't worry about until later) */
+            // NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
+            // offset as well (which we won't worry about until later).
+            // The LocalDateTime.of() will throw DateTimeException for values outside the allowed
+            // range for the Gregorian calendar.
             long epochMillis = LocalDateTime.of(year, month, date, hour, minute, second)
                     .toInstant(ZoneOffset.UTC)
                     .toEpochMilli();
diff --git a/src/java/com/android/internal/telephony/NitzSignal.java b/src/java/com/android/internal/telephony/NitzSignal.java
index 889fe95..2619f3d 100644
--- a/src/java/com/android/internal/telephony/NitzSignal.java
+++ b/src/java/com/android/internal/telephony/NitzSignal.java
@@ -19,7 +19,7 @@
 import android.annotation.DurationMillisLong;
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
-import android.os.TimestampedValue;
+import android.app.time.UnixEpochTime;
 
 import java.time.Duration;
 import java.util.Objects;
@@ -88,13 +88,12 @@
     }
 
     /**
-     * Creates a {@link android.os.TimestampedValue} containing the UTC time as the number of
-     * milliseconds since the start of the Unix epoch. The reference time is the time according to
-     * the elapsed realtime clock when that would have been the time, accounting for receipt time
-     * and age.
+     * Creates a {@link UnixEpochTime} containing the UTC time as the number of milliseconds since
+     * the start of the Unix epoch. The reference time is the time according to the elapsed realtime
+     * clock when that would have been the time, accounting for receipt time and age.
      */
-    public TimestampedValue<Long> createTimeSignal() {
-        return new TimestampedValue<>(
+    public UnixEpochTime createTimeSignal() {
+        return new UnixEpochTime(
                 getAgeAdjustedElapsedRealtimeMillis(),
                 getNitzData().getCurrentTimeInMillis());
     }
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 4afccd1..4e62d20 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.BroadcastOptions;
@@ -24,6 +26,7 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
+import android.hardware.radio.modem.ImeiInfo;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Build;
@@ -35,17 +38,21 @@
 import android.os.RegistrantList;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.os.WorkSource;
 import android.preference.PreferenceManager;
 import android.sysprop.TelephonyProperties;
 import android.telecom.VideoProfile;
 import android.telephony.AccessNetworkConstants;
-import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.SrvccState;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CarrierRestrictionRules;
+import android.telephony.CellBroadcastIdRange;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
 import android.telephony.ClientRequestStats;
+import android.telephony.DomainSelectionService;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.LinkCapacityEstimate;
 import android.telephony.NetworkRegistrationInfo;
@@ -60,14 +67,15 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
+import android.telephony.TelephonyManager.HalService;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.satellite.SatelliteDatagram;
 import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.Log;
-import android.util.SparseArray;
 import android.util.Xml;
 
 import com.android.ims.ImsCall;
@@ -80,15 +88,17 @@
 import com.android.internal.telephony.data.DataNetworkController;
 import com.android.internal.telephony.data.DataSettingsManager;
 import com.android.internal.telephony.data.LinkBandwidthEstimator;
-import com.android.internal.telephony.dataconnection.DataConnectionReasons;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
-import com.android.internal.telephony.dataconnection.DcTracker;
-import com.android.internal.telephony.dataconnection.TransportManager;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
+import com.android.internal.telephony.emergency.EmergencyConstants;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
+import com.android.internal.telephony.imsphone.ImsCallInfo;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
 import com.android.internal.telephony.metrics.SmsStats;
 import com.android.internal.telephony.metrics.VoiceCallSessionStats;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
 import com.android.internal.telephony.uicc.IccFileHandler;
@@ -172,7 +182,8 @@
     protected static final int EVENT_RADIO_ON                    = 5;
     protected static final int EVENT_GET_BASEBAND_VERSION_DONE   = 6;
     protected static final int EVENT_USSD                        = 7;
-    protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE  = 8;
+    @VisibleForTesting
+    public static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE  = 8;
     private static final int EVENT_GET_SIM_STATUS_DONE           = 11;
     protected static final int EVENT_SET_CALL_FORWARD_DONE       = 12;
     protected static final int EVENT_GET_CALL_FORWARD_DONE       = 13;
@@ -222,11 +233,8 @@
     // Radio state change
     protected static final int EVENT_RADIO_STATE_CHANGED            = 47;
     protected static final int EVENT_SET_CARRIER_DATA_ENABLED       = 48;
-    protected static final int EVENT_DEVICE_PROVISIONED_CHANGE      = 49;
-    protected static final int EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE = 50;
     protected static final int EVENT_GET_AVAILABLE_NETWORKS_DONE    = 51;
 
-    private static final int EVENT_ALL_DATA_DISCONNECTED                  = 52;
     protected static final int EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED  = 53;
     protected static final int EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED = 54;
     protected static final int EVENT_GET_UICC_APPS_ENABLEMENT_DONE        = 55;
@@ -239,8 +247,12 @@
     protected static final int EVENT_SUBSCRIPTIONS_CHANGED = 62;
     protected static final int EVENT_GET_USAGE_SETTING_DONE = 63;
     protected static final int EVENT_SET_USAGE_SETTING_DONE = 64;
+    protected static final int EVENT_IMS_DEREGISTRATION_TRIGGERED = 65;
+    protected static final int EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE = 66;
+    protected static final int EVENT_GET_DEVICE_IMEI_DONE = 67;
+    protected static final int EVENT_TRIGGER_NOTIFY_ANBR = 68;
 
-    protected static final int EVENT_LAST = EVENT_SET_USAGE_SETTING_DONE;
+    protected static final int EVENT_LAST = EVENT_TRIGGER_NOTIFY_ANBR;
 
     // For shared prefs.
     private static final String GSM_ROAMING_LIST_OVERRIDE_PREFIX = "gsm_roaming_list_";
@@ -267,6 +279,10 @@
     // Integer used to let the calling application know that the we are ignoring auto mode switch.
     private static final int ALREADY_IN_AUTO_SELECTION = 1;
 
+    public static final String PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED =
+            "pref_null_cipher_and_integrity_enabled";
+    private final TelephonyAdminReceiver m2gAdminUpdater;
+
     /**
      * This method is invoked when the Phone exits Emergency Callback Mode.
      */
@@ -303,11 +319,6 @@
     public CommandsInterface mCi;
     protected int mVmCount = 0;
     private boolean mDnsCheckDisabled;
-    // Data connection trackers. For each transport type (e.g. WWAN, WLAN), there will be a
-    // corresponding DcTracker. The WWAN DcTracker is for cellular data connections while
-    // WLAN DcTracker is for IWLAN data connection. For IWLAN legacy mode, only one (WWAN) DcTracker
-    // will be created.
-    protected final SparseArray<DcTracker> mDcTrackers = new SparseArray<>();
     protected DataNetworkController mDataNetworkController;
     /* Used for dispatching signals to configured carrier apps */
     protected CarrierSignalAgent mCarrierSignalAgent;
@@ -340,15 +351,13 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     protected AtomicReference<UiccCardApplication> mUiccApplication =
             new AtomicReference<UiccCardApplication>();
-    TelephonyTester mTelephonyTester;
+    private TelephonyTester mTelephonyTester;
     private String mName;
     private final String mActionDetached;
     private final String mActionAttached;
     protected DeviceStateMonitor mDeviceStateMonitor;
     protected DisplayInfoController mDisplayInfoController;
-    protected TransportManager mTransportManager;
     protected AccessNetworksManager mAccessNetworksManager;
-    protected DataEnabledSettings mDataEnabledSettings;
     // Used for identify the carrier of current subscription
     protected CarrierResolver mCarrierResolver;
     protected SignalStrengthController mSignalStrengthController;
@@ -399,7 +408,7 @@
     public static final String EXTRA_KEY_ALERT_SHOW = "alertShow";
     public static final String EXTRA_KEY_NOTIFICATION_MESSAGE = "notificationMessage";
 
-    private final RegistrantList mPreciseCallStateRegistrants = new RegistrantList();
+    protected final RegistrantList mPreciseCallStateRegistrants = new RegistrantList();
 
     private final RegistrantList mHandoverRegistrants = new RegistrantList();
 
@@ -428,8 +437,6 @@
 
     protected final RegistrantList mEmergencyCallToggledRegistrants = new RegistrantList();
 
-    private final RegistrantList mAllDataDisconnectedRegistrants = new RegistrantList();
-
     private final RegistrantList mCellInfoRegistrants = new RegistrantList();
 
     private final RegistrantList mRedialRegistrants = new RegistrantList();
@@ -449,6 +456,8 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     protected final Context mContext;
 
+    protected SubscriptionManagerService mSubscriptionManagerService;
+
     /**
      * PhoneNotifier is an abstraction for all system-wide
      * state change notification. DefaultPhoneNotifier is
@@ -474,9 +483,9 @@
 
     protected LinkBandwidthEstimator mLinkBandwidthEstimator;
 
-    /** The flag indicating using the new data stack or not. */
-    // This flag and the old data stack code will be deleted in Android 14.
-    private final boolean mNewDataStackEnabled;
+    public static final int IMEI_TYPE_UNKNOWN = -1;
+    public static final int IMEI_TYPE_PRIMARY = ImeiInfo.ImeiType.PRIMARY;
+    public static final int IMEI_TYPE_SECONDARY = ImeiInfo.ImeiType.SECONDARY;
 
     public IccRecords getIccRecords() {
         return mIccRecords.get();
@@ -580,10 +589,6 @@
                 .makeAppSmsManager(context);
         mLocalLog = new LocalLog(64);
 
-        if (TelephonyUtils.IS_DEBUGGABLE) {
-            mTelephonyTester = new TelephonyTester(this);
-        }
-
         setUnitTestMode(unitTestMode);
 
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
@@ -619,13 +624,17 @@
         // Initialize SMS stats
         mSmsStats = new SmsStats(this);
 
-        mNewDataStackEnabled = !mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_force_disable_telephony_new_data_stack);
+        mSubscriptionManagerService = SubscriptionManagerService.getInstance();
+        m2gAdminUpdater = new TelephonyAdminReceiver(context, this);
 
         if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
             return;
         }
 
+        if (TelephonyUtils.IS_DEBUGGABLE) {
+            mTelephonyTester = new TelephonyTester(this);
+        }
+
         // Initialize device storage and outgoing SMS usage monitors for SMSDispatchers.
         mTelephonyComponentFactory = telephonyComponentFactory;
         mSmsStorageMonitor = mTelephonyComponentFactory.inject(SmsStorageMonitor.class.getName())
@@ -814,11 +823,6 @@
                 break;
             }
 
-            case EVENT_ALL_DATA_DISCONNECTED:
-                if (areAllDataDisconnected()) {
-                    mAllDataDisconnectedRegistrants.notifyRegistrants();
-                }
-                break;
             case EVENT_GET_USAGE_SETTING_DONE:
                 ar = (AsyncResult) msg.obj;
                 if (ar.exception == null) {
@@ -871,7 +875,11 @@
         return null;
     }
 
-    public void notifySrvccState(Call.SrvccState state) {
+    /**
+     * Notifies the change of the SRVCC state.
+     * @param state the new SRVCC state.
+     */
+    public void notifySrvccState(@SrvccState int state) {
     }
 
     public void registerForSilentRedial(Handler h, int what, Object obj) {
@@ -894,6 +902,9 @@
         Call.SrvccState srvccState = Call.SrvccState.NONE;
         if (ret != null && ret.length != 0) {
             int state = ret[0];
+            if (imsPhone != null) {
+                imsPhone.notifySrvccState(state);
+            }
             switch(state) {
                 case TelephonyManager.SRVCC_STATE_HANDOVER_STARTED:
                     srvccState = Call.SrvccState.STARTED;
@@ -906,11 +917,6 @@
                     break;
                 case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED:
                     srvccState = Call.SrvccState.COMPLETED;
-                    if (imsPhone != null) {
-                        imsPhone.notifySrvccState(srvccState);
-                    } else {
-                        Rlog.d(LOG_TAG, "HANDOVER_COMPLETED: mImsPhone null");
-                    }
                     break;
                 case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED:
                 case TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED:
@@ -985,17 +991,6 @@
     }
 
     /**
-     * Subclasses of Phone probably want to replace this with a
-     * version scoped to their packages
-     */
-    protected void notifyPreciseCallStateChangedP() {
-        AsyncResult ar = new AsyncResult(null, this, null);
-        mPreciseCallStateRegistrants.notifyRegistrants(ar);
-
-        mNotifier.notifyPreciseCallState(this);
-    }
-
-    /**
      * Notifies when a Handover happens due to SRVCC or Silent Redial
      */
     public void registerForHandoverStateChanged(Handler h, int what, Object obj) {
@@ -1442,8 +1437,8 @@
     @UnsupportedAppUsage
     public void setNetworkSelectionModeAutomatic(Message response) {
         Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic, querying current mode");
-        // we don't want to do this unecesarily - it acutally causes
-        // the radio to repeate network selection and is costly
+        // we don't want to do this unnecessarily - it actually causes
+        // the radio to repeat network selection and is costly
         // first check if we're already in automatic mode
         Message msg = obtainMessage(EVENT_CHECK_FOR_NETWORK_AUTOMATIC);
         msg.obj = response;
@@ -1476,6 +1471,7 @@
         nsm.operatorAlphaShort = "";
 
         if (doAutomatic) {
+            Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic - set network selection auto");
             Message msg = obtainMessage(EVENT_SET_NETWORK_AUTOMATIC_COMPLETE, nsm);
             mCi.setNetworkSelectionModeAutomatic(msg);
         } else {
@@ -1932,7 +1928,7 @@
     public boolean isRadioOffForThermalMitigation() {
         ServiceStateTracker sst = getServiceStateTracker();
         return sst != null && sst.getRadioPowerOffReasons()
-                .contains(Phone.RADIO_POWER_REASON_THERMAL);
+                .contains(TelephonyManager.RADIO_POWER_REASON_THERMAL);
     }
 
     /**
@@ -1951,13 +1947,6 @@
     }
 
     /**
-     * @return The instance of transport manager.
-     */
-    public TransportManager getTransportManager() {
-        return null;
-    }
-
-    /**
      * @return The instance of access networks manager.
      */
     public AccessNetworksManager getAccessNetworksManager() {
@@ -2326,6 +2315,12 @@
         if (!mIsCarrierNrSupported) {
             allowedNetworkTypes &= ~TelephonyManager.NETWORK_TYPE_BITMASK_NR;
         }
+        if (m2gAdminUpdater.isCellular2gDisabled()) {
+            logd("SubId " + getSubId()
+                    + " disabling 2g in getEffectiveAllowedNetworkTypes according to admin user "
+                    + "restriction");
+            allowedNetworkTypes &= ~TelephonyManager.NETWORK_CLASS_BITMASK_2G;
+        }
         logd("SubId" + getSubId() + ",getEffectiveAllowedNetworkTypes: "
                 + TelephonyManager.convertNetworkTypeBitmaskToString(allowedNetworkTypes));
         return allowedNetworkTypes;
@@ -2401,17 +2396,18 @@
      * Loads the allowed network type from subscription database.
      */
     public void loadAllowedNetworksFromSubscriptionDatabase() {
-        // Try to load ALLOWED_NETWORK_TYPES from SIMINFO.
-        if (SubscriptionController.getInstance() == null) {
-            return;
+        String result = null;
+        SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+                .getSubscriptionInfoInternal(getSubId());
+        if (subInfo != null) {
+            result = subInfo.getAllowedNetworkTypesForReasons();
         }
 
-        String result = SubscriptionController.getInstance().getSubscriptionProperty(
-                getSubId(),
-                SubscriptionManager.ALLOWED_NETWORK_TYPES);
         // After fw load network type from DB, do unlock if subId is valid.
-        mIsAllowedNetworkTypesLoadedFromDb = SubscriptionManager.isValidSubscriptionId(getSubId());
-        if (result == null) {
+        mIsAllowedNetworkTypesLoadedFromDb = SubscriptionManager.isValidSubscriptionId(
+                getSubId());
+
+        if (TextUtils.isEmpty(result)) {
             return;
         }
 
@@ -2421,14 +2417,14 @@
         try {
             // Format: "REASON=VALUE,REASON2=VALUE2"
             for (String pair : result.trim().split(",")) {
-                String[] networkTypesValues = (pair.trim().toLowerCase()).split("=");
+                String[] networkTypesValues = (pair.trim().toLowerCase(Locale.ROOT)).split("=");
                 if (networkTypesValues.length != 2) {
                     Rlog.e(LOG_TAG, "Invalid ALLOWED_NETWORK_TYPES from DB, value = " + pair);
                     continue;
                 }
                 int key = convertAllowedNetworkTypeDbNameToMapIndex(networkTypesValues[0]);
                 long value = Long.parseLong(networkTypesValues[1]);
-                if (key != INVALID_ALLOWED_NETWORK_TYPES
+                if (TelephonyManager.isValidAllowedNetworkTypesReason(key)
                         && value != INVALID_ALLOWED_NETWORK_TYPES) {
                     synchronized (mAllowedNetworkTypesForReasons) {
                         mAllowedNetworkTypesForReasons.put(key, value);
@@ -2466,7 +2462,15 @@
         }
     }
 
-    private String convertAllowedNetworkTypeMapIndexToDbName(int reason) {
+    /**
+     * Convert the allowed network types reason to string.
+     *
+     * @param reason The allowed network types reason.
+     *
+     * @return The converted string.
+     */
+    public static String convertAllowedNetworkTypeMapIndexToDbName(
+            @TelephonyManager.AllowedNetworkTypesReason int reason) {
         switch (reason) {
             case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER:
                 return ALLOWED_NETWORK_TYPES_TEXT_USER;
@@ -2477,7 +2481,10 @@
             case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
                 return ALLOWED_NETWORK_TYPES_TEXT_ENABLE_2G;
             default:
-                return Integer.toString(INVALID_ALLOWED_NETWORK_TYPES);
+                throw new IllegalArgumentException(
+                        "No DB name conversion available for allowed network type reason: " + reason
+                                + ". Did you forget to add an ALLOWED_NETWORK_TYPE_TEXT entry for"
+                                + " a new reason?");
         }
     }
 
@@ -2977,6 +2984,9 @@
     // This property is used to handle phone process crashes, and is the same for CDMA and IMS
     // phones
     protected static boolean getInEcmMode() {
+        if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+            return EmergencyStateTracker.getInstance().isInEcm();
+        }
         return TelephonyProperties.in_ecm_mode().orElse(false);
     }
 
@@ -2986,6 +2996,9 @@
      * emergency operator.
      */
     public boolean isInEcm() {
+        if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+            return EmergencyStateTracker.getInstance().isInEcm();
+        }
         return mIsPhoneInEcmState;
     }
 
@@ -2994,6 +3007,9 @@
     }
 
     public boolean isInCdmaEcm() {
+        if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+            return EmergencyStateTracker.getInstance().isInCdmaEcm();
+        }
         return getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA && isInEcm()
                 && (mImsPhone == null || !mImsPhone.isInImsEcm());
     }
@@ -3730,18 +3746,11 @@
      * @return true if there is a matching DUN APN.
      */
     public boolean hasMatchedTetherApnSetting() {
-        if (isUsingNewDataStack()) {
-            NetworkRegistrationInfo nrs = getServiceState().getNetworkRegistrationInfo(
-                    NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-            if (nrs != null) {
-                return getDataNetworkController().getDataProfileManager()
-                        .isTetheringDataProfileExisting(nrs.getAccessNetworkTechnology());
-            }
-            return false;
-        }
-        if (getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) != null) {
-            return getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
-                    .hasMatchedTetherApnSetting();
+        NetworkRegistrationInfo nrs = getServiceState().getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        if (nrs != null) {
+            return getDataNetworkController().getDataProfileManager()
+                    .isTetheringDataProfileExisting(nrs.getAccessNetworkTechnology());
         }
         return false;
     }
@@ -3752,31 +3761,10 @@
      * @return {@code true} if internet data is allowed to be established.
      */
     public boolean isDataAllowed() {
-        if (isUsingNewDataStack()) {
-            return getDataNetworkController().isInternetDataAllowed();
-        }
-        return isDataAllowed(ApnSetting.TYPE_DEFAULT, null);
+        return getDataNetworkController().isInternetDataAllowed();
     }
 
     /**
-     * Report on whether data connectivity is allowed.
-     *
-     * @param apnType APN type
-     * @param reasons The reasons that data can/can't be established. This is an output param.
-     * @return True if data is allowed to be established
-     */
-    public boolean isDataAllowed(@ApnType int apnType, DataConnectionReasons reasons) {
-        if (mAccessNetworksManager != null) {
-            int transport = mAccessNetworksManager.getCurrentTransport(apnType);
-            if (getDcTracker(transport) != null) {
-                return getDcTracker(transport).isDataAllowed(reasons);
-            }
-        }
-        return false;
-    }
-
-
-    /**
      * Action set from carrier signalling broadcast receivers to enable/disable metered apns.
      */
     public void carrierActionSetMeteredApnsEnabled(boolean enabled) {
@@ -3932,16 +3920,6 @@
         return null;
     }
 
-    /**
-     * Get the current for the default apn DataState. No change notification
-     * exists at this interface -- use
-     * {@link android.telephony.PhoneStateListener} instead.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public PhoneConstants.DataState getDataConnectionState() {
-        return getDataConnectionState(ApnSetting.TYPE_DEFAULT_STRING);
-    }
-
     public void notifyCallForwardingIndicator() {
     }
 
@@ -4117,14 +4095,7 @@
      */
     @UnsupportedAppUsage
     public int getSubId() {
-        if (SubscriptionController.getInstance() == null) {
-            // TODO b/78359408 getInstance sometimes returns null in Treehugger tests, which causes
-            // flakiness. Even though we haven't seen this crash in the wild we should keep this
-            // check in until we've figured out the root cause.
-            Rlog.e(LOG_TAG, "SubscriptionController.getInstance = null! Returning default subId");
-            return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
-        }
-        return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhoneId);
+        return mSubscriptionManagerService.getSubId(mPhoneId);
     }
 
     /**
@@ -4420,6 +4391,7 @@
         // When radio capability switch is done, query IMEI value and update it in Phone objects
         // to make it in sync with the IMEI value currently used by Logical-Modem.
         if (capabilitySwitched) {
+            mCi.getImei(obtainMessage(EVENT_GET_DEVICE_IMEI_DONE));
             mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
         }
     }
@@ -4438,7 +4410,12 @@
     }
 
     private int getResolvedUsageSetting(int subId) {
-        SubscriptionInfo subInfo = SubscriptionController.getInstance().getSubscriptionInfo(subId);
+        SubscriptionInfo subInfo = null;
+        SubscriptionInfoInternal subInfoInternal = mSubscriptionManagerService
+                .getSubscriptionInfoInternal(subId);
+        if (subInfoInternal != null) {
+            subInfo = subInfoInternal.toSubscriptionInfo();
+        }
 
         if (subInfo == null
                 || subInfo.getUsageSetting() == SubscriptionManager.USAGE_SETTING_UNKNOWN) {
@@ -4621,42 +4598,6 @@
         return false;
     }
 
-    /**
-     * @return True if all data connections are disconnected.
-     */
-    public boolean areAllDataDisconnected() {
-        if (mAccessNetworksManager != null) {
-            for (int transport : mAccessNetworksManager.getAvailableTransports()) {
-                if (getDcTracker(transport) != null
-                        && !getDcTracker(transport).areAllDataDisconnected()) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    public void registerForAllDataDisconnected(Handler h, int what) {
-        mAllDataDisconnectedRegistrants.addUnique(h, what, null);
-        if (mAccessNetworksManager != null) {
-            for (int transport : mAccessNetworksManager.getAvailableTransports()) {
-                if (getDcTracker(transport) != null
-                        && !getDcTracker(transport).areAllDataDisconnected()) {
-                    getDcTracker(transport).registerForAllDataDisconnected(
-                            this, EVENT_ALL_DATA_DISCONNECTED);
-                }
-            }
-        }
-    }
-
-    public void unregisterForAllDataDisconnected(Handler h) {
-        mAllDataDisconnectedRegistrants.remove(h);
-    }
-
-    public DataEnabledSettings getDataEnabledSettings() {
-        return mDataEnabledSettings;
-    }
-
     @UnsupportedAppUsage
     public IccSmsInterfaceManager getIccSmsInterfaceManager(){
         return null;
@@ -4787,16 +4728,6 @@
         return isEmergencyCallOnly;
     }
 
-    /**
-     * Get data connection tracker based on the transport type
-     *
-     * @param transportType Transport type defined in AccessNetworkConstants.TransportType
-     * @return The data connection tracker. Null if not found.
-     */
-    public @Nullable DcTracker getDcTracker(int transportType) {
-        return mDcTrackers.get(transportType);
-    }
-
     // Return true if either CSIM or RUIM app is present. By default it returns false.
     public boolean isCdmaSubscriptionAppPresent() {
         return false;
@@ -4820,10 +4751,24 @@
      * Get the HAL version.
      *
      * @return the current HalVersion
+     *
+     * @deprecated Use {@link #getHalVersion(int service)} instead.
      */
+    @Deprecated
     public HalVersion getHalVersion() {
+        return getHalVersion(HAL_SERVICE_RADIO);
+    }
+
+    /**
+     * Get the HAL version with a specific service.
+     *
+     * @param service the service id to query
+     * @return the current HalVersion for a specific service
+     *
+     */
+    public HalVersion getHalVersion(@HalService int service) {
         if (mCi != null && mCi instanceof RIL) {
-            return ((RIL) mCi).getHalVersion();
+            return ((RIL) mCi).getHalVersion(service);
         }
         return RIL.RADIO_HAL_VERSION_UNKNOWN;
     }
@@ -4967,11 +4912,600 @@
     }
 
     /**
-     * @return {@code true} if using the new telephony data stack.
+     * Returns the user's last setting for terminal-based call waiting
+     * @param forCsOnly indicates the caller expects the result for CS calls only
      */
-    // This flag and the old data stack code will be deleted in Android 14.
-    public boolean isUsingNewDataStack() {
-        return mNewDataStackEnabled;
+    public int getTerminalBasedCallWaitingState(boolean forCsOnly) {
+        return CallWaitingController.TERMINAL_BASED_NOT_SUPPORTED;
+    }
+
+    /**
+     * Notifies the change of the user setting of the terminal-based call waiting service
+     * to IMS service.
+     */
+    public void setTerminalBasedCallWaitingStatus(int state) {
+    }
+
+    /**
+     * Notifies that the IMS service connected supports the terminal-based call waiting service
+     */
+    public void setTerminalBasedCallWaitingSupported(boolean supported) {
+    }
+
+    /**
+     * Notifies the NAS and RRC layers of the radio the type of upcoming IMS traffic.
+     *
+     * @param token A nonce to identify the request.
+     * @param trafficType IMS traffic type like registration, voice, video, SMS, emergency, and etc.
+     * @param accessNetworkType The type of the radio access network used.
+     * @param trafficDirection Indicates whether traffic is originated by mobile originated or
+     *        mobile terminated use case eg. MO/MT call/SMS etc.
+     * @param response is callback message.
+     */
+    public void startImsTraffic(int token,
+            @MmTelFeature.ImsTrafficType int trafficType,
+            @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
+            @MmTelFeature.ImsTrafficDirection int trafficDirection, Message response) {
+        mCi.startImsTraffic(token, trafficType, accessNetworkType, trafficDirection, response);
+    }
+
+    /**
+     * Notifies IMS traffic has been stopped.
+     *
+     * @param token The token assigned by startImsTraffic.
+     * @param response is callback message.
+     */
+    public void stopImsTraffic(int token, Message response) {
+        mCi.stopImsTraffic(token, response);
+    }
+
+    /**
+     * Register for notifications of connection setup failure
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForConnectionSetupFailure(Handler h, int what, Object obj) {
+        mCi.registerForConnectionSetupFailure(h, what, obj);
+    }
+
+    /**
+     * Unregister for notifications of connection setup failure
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    public void unregisterForConnectionSetupFailure(Handler h) {
+        mCi.unregisterForConnectionSetupFailure(h);
+    }
+
+    /**
+     * Triggers the UE initiated EPS fallback procedure.
+     *
+     * @param reason specifies the reason for EPS fallback.
+     * @param response is callback message.
+     */
+    public void triggerEpsFallback(@MmTelFeature.EpsFallbackReason int reason, Message response) {
+        mCi.triggerEpsFallback(reason, response);
+    }
+
+    /**
+     * Notifies the recommended bit rate for the indicated logical channel and direction.
+     *
+     * @param mediaType MediaType is used to identify media stream such as audio or video.
+     * @param direction Direction of this packet stream (e.g. uplink or downlink).
+     * @param bitsPerSecond The recommended bit rate for the UE for a specific logical channel and
+     *        a specific direction by NW.
+     */
+    public void triggerNotifyAnbr(int mediaType, int direction, int bitsPerSecond) {
+    }
+
+    /**
+     * Sets the emergency mode
+     *
+     * @param emcMode The radio emergency mode type.
+     * @param result Callback message.
+     */
+    public void setEmergencyMode(@EmergencyConstants.EmergencyMode int emcMode,
+            @Nullable Message result) {
+        mCi.setEmergencyMode(emcMode, result);
+    }
+
+    /**
+     * Triggers an emergency network scan.
+     *
+     * @param accessNetwork Contains the list of access network types to be prioritized
+     *        during emergency scan. The 1st entry has the highest priority.
+     * @param scanType Indicates the type of scans to be performed i.e. limited scan,
+     *        full service scan or any scan.
+     * @param result Callback message.
+     */
+    public void triggerEmergencyNetworkScan(
+            @NonNull @AccessNetworkConstants.RadioAccessNetworkType int[] accessNetwork,
+            @DomainSelectionService.EmergencyScanType int scanType, @Nullable Message result) {
+        mCi.triggerEmergencyNetworkScan(accessNetwork, scanType, result);
+    }
+
+    /**
+     * Cancels ongoing emergency network scan
+     * @param resetScan Indicates how the next {@link #triggerEmergencyNetworkScan} should work.
+     *        If {@code true}, then the modem shall start the new scan from the beginning,
+     *        otherwise the modem shall resume from the last search.
+     * @param result Callback message.
+     */
+    public void cancelEmergencyNetworkScan(boolean resetScan, @Nullable Message result) {
+        mCi.cancelEmergencyNetworkScan(resetScan, result);
+    }
+
+    /**
+     * Exits ongoing emergency mode
+     * @param result Callback message.
+     */
+    public void exitEmergencyMode(@Nullable Message result) {
+        mCi.exitEmergencyMode(result);
+    }
+
+    /**
+     * Registers for emergency network scan result.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForEmergencyNetworkScan(@NonNull Handler h,
+            int what, @Nullable Object obj) {
+        mCi.registerForEmergencyNetworkScan(h, what, obj);
+    }
+
+    /**
+     * Unregisters for emergency network scan result.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    public void unregisterForEmergencyNetworkScan(@NonNull Handler h) {
+        mCi.unregisterForEmergencyNetworkScan(h);
+    }
+
+    /**
+     * Notifies that IMS deregistration is triggered.
+     *
+     * @param reason the reason why the deregistration is triggered.
+     */
+    public void triggerImsDeregistration(
+            @ImsRegistrationImplBase.ImsDeregistrationReason int reason) {
+        if (mImsPhone != null) {
+            mImsPhone.triggerImsDeregistration(reason);
+        }
+    }
+
+    /**
+     * Registers for the domain selected for emergency calls.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForEmergencyDomainSelected(
+            @NonNull Handler h, int what, @Nullable Object obj) {
+    }
+
+    /**
+     * Unregisters for the domain selected for emergency calls.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    public void unregisterForEmergencyDomainSelected(@NonNull Handler h) {
+    }
+
+    /**
+     * Notifies the domain selected.
+     *
+     * @param transportType The preferred transport type.
+     */
+    public void notifyEmergencyDomainSelected(
+            @AccessNetworkConstants.TransportType int transportType) {
+    }
+
+    /**
+     * @return Telephony tester instance.
+     */
+    public @Nullable TelephonyTester getTelephonyTester() {
+        return mTelephonyTester;
+    }
+
+    /**
+     * @return User handle associated with the phone's subscription id. {@code null} if subscription
+     * is invalid or not found.
+     */
+    @Nullable
+    public UserHandle getUserHandle() {
+        int subId = getSubId();
+
+        UserHandle userHandle = null;
+        try {
+            SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
+            if (subManager != null) {
+                userHandle = subManager.getSubscriptionUserHandle(subId);
+            }
+        } catch (IllegalArgumentException ex) {
+            loge("getUserHandle: ex=" + ex);
+        }
+
+        return userHandle;
+    }
+
+    /**
+     * Checks if the context user is a managed profile.
+     *
+     * Note that this applies specifically to <em>managed</em> profiles.
+     *
+     * @return whether the context user is a managed profile.
+     */
+    public boolean isManagedProfile() {
+        UserHandle userHandle = getUserHandle();
+        UserManager userManager = mContext.getSystemService(UserManager.class);
+        if (userHandle == null || userManager == null) return false;
+        return userManager.isManagedProfile(userHandle.getIdentifier());
+    }
+
+    /**
+     * @return global null cipher and integrity enabled preference
+     */
+    public boolean getNullCipherAndIntegrityEnabledPreference() {
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+        return sp.getBoolean(PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED, true);
+    }
+
+    /**
+     * @return whether or not this Phone interacts with a modem that supports the null cipher
+     * and integrity feature.
+     */
+    public boolean isNullCipherAndIntegritySupported() {
+        return false;
+    }
+
+    /**
+     * Override to implement handling of an update to the enablement of the null cipher and
+     * integrity preference.
+     * {@see #PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED}
+     */
+    public void handleNullCipherEnabledChange() {
+    }
+
+    /**
+     * Notifies the IMS call status to the modem.
+     *
+     * @param imsCallInfo The list of {@link ImsCallInfo}.
+     * @param response A callback to receive the response.
+     */
+    public void updateImsCallStatus(@NonNull List<ImsCallInfo> imsCallInfo, Message response) {
+        mCi.updateImsCallStatus(imsCallInfo, response);
+    }
+
+    /**
+     * Enables or disables N1 mode (access to 5G core network) in accordance with
+     * 3GPP TS 24.501 4.9.
+     * @param enable {@code true} to enable N1 mode, {@code false} to disable N1 mode.
+     * @param result Callback message to receive the result.
+     */
+    public void setN1ModeEnabled(boolean enable, Message result) {
+        mCi.setN1ModeEnabled(enable, result);
+    }
+
+    /**
+     * Check whether N1 mode (access to 5G core network) is enabled or not.
+     * @param result Callback message to receive the result.
+     */
+    public void isN1ModeEnabled(Message result) {
+        mCi.isN1ModeEnabled(result);
+    }
+
+    /**
+     * Return current cell broadcast ranges.
+     */
+    public List<CellBroadcastIdRange> getCellBroadcastIdRanges() {
+        return new ArrayList<>();
+    }
+
+    /**
+     * Set reception of cell broadcast messages with the list of the given ranges.
+     */
+    public void setCellBroadcastIdRanges(
+            @NonNull List<CellBroadcastIdRange> ranges, Consumer<Integer> callback) {
+        callback.accept(TelephonyManager.CELL_BROADCAST_RESULT_UNSUPPORTED);
+    }
+
+    /**
+     * Start receiving satellite position updates.
+     * This can be called by the pointing UI when the user starts pointing to the satellite.
+     * Modem should continue to report the pointing input as the device or satellite moves.
+     *
+     * @param result The Message to send to result of the operation to.
+     **/
+    public void startSatellitePositionUpdates(Message result) {
+        mCi.startSendingSatellitePointingInfo(result);
+    }
+
+    /**
+     * Stop receiving satellite position updates.
+     * This can be called by the pointing UI when the user stops pointing to the satellite.
+     *
+     * @param result The Message to send to result of the operation to.
+     **/
+    public void stopSatellitePositionUpdates(Message result) {
+        mCi.stopSendingSatellitePointingInfo(result);
+    }
+
+    /**
+     * Get maximum number of characters per text message on satellite.
+     * @param result The Message to send the result of the operation to.
+     */
+    public void getMaxCharactersPerSatelliteTextMessage(Message result) {
+        mCi.getMaxCharactersPerSatelliteTextMessage(result);
+    }
+
+    /**
+     * Power on or off the satellite modem.
+     * @param result The Message to send the result of the operation to.
+     * @param powerOn {@code true} to power on the satellite modem and {@code false} to power off.
+     */
+    public void setSatellitePower(Message result, boolean powerOn) {
+        mCi.setSatellitePower(result, powerOn);
+    }
+
+    /**
+     * Check whether the satellite modem is powered on.
+     * @param result The Message to send the result of the operation to.
+     */
+    public void isSatellitePowerOn(Message result) {
+        mCi.getSatellitePowerState(result);
+    }
+
+    /**
+     * Check whether the satellite service is supported on the device.
+     * @param result The Message to send the result of the operation to.
+     */
+    public void isSatelliteSupported(Message result) {
+        mCi.isSatelliteSupported(result);
+    }
+
+    /**
+     * Check whether the satellite modem is provisioned.
+     * @param result The Message to send the result of the operation to.
+     */
+    public void isSatelliteProvisioned(Message result) {
+        mCi.getSatelliteProvisionState(result);
+    }
+
+    /**
+     * Get the satellite capabilities.
+     * @param result The Message to send the result of the operation to.
+     */
+    public void getSatelliteCapabilities(Message result) {
+        mCi.getSatelliteCapabilities(result);
+    }
+
+    /**
+     * Registers for pointing info changed from satellite modem.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForSatellitePositionInfoChanged(@NonNull Handler h,
+            int what, @Nullable Object obj) {
+        //TODO: Rename CommandsInterface and other modules when updating HAL APIs.
+        mCi.registerForSatellitePointingInfoChanged(h, what, obj);
+    }
+
+    /**
+     * Unregisters for pointing info changed from satellite modem.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    public void unregisterForSatellitePositionInfoChanged(@NonNull Handler h) {
+        //TODO: Rename CommandsInterface and other modules when updating HAL APIs.
+        mCi.unregisterForSatellitePointingInfoChanged(h);
+    }
+
+    /**
+     * Registers for datagrams delivered events from satellite modem.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForSatelliteDatagramsDelivered(@NonNull Handler h,
+            int what, @Nullable Object obj) {
+        //TODO: Remove.
+        mCi.registerForSatelliteMessagesTransferComplete(h, what, obj);
+    }
+
+    /**
+     * Unregisters for datagrams delivered events from satellite modem.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    public void unregisterForSatelliteDatagramsDelivered(@NonNull Handler h) {
+        //TODO: Remove.
+        mCi.unregisterForSatelliteMessagesTransferComplete(h);
+    }
+
+    /**
+     * Provision the subscription with a satellite provider.
+     * This is needed to register the device/subscription if the provider allows dynamic
+     * registration.
+     *
+     * @param result Callback message to receive the result.
+     * @param token The token of the device/subscription to be provisioned.
+     */
+    public void provisionSatelliteService(Message result, String token) {
+        // TODO: update parameters in HAL
+        // mCi.provisionSatelliteService(result, token);
+    }
+
+    /**
+     * Deprovision the device/subscription with a satellite provider.
+     * This is needed to unregister the device/subscription if the provider allows dynamic
+     * registration.
+     * If provisioning is in progress for the given SIM, cancel the request.
+     * If there is no request in progress, deprovision the given SIM.
+     *
+     * @param result Callback message to receive the result.
+     * @param token The token of the device/subscription to be deprovisioned.
+     */
+    public void deprovisionSatelliteService(Message result, String token) {
+        //TODO (b/266126070): add implementation.
+    }
+
+    /**
+     * Register for a satellite provision state changed event.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForSatelliteProvisionStateChanged(Handler h, int what, Object obj) {
+        mCi.registerForSatelliteProvisionStateChanged(h, what, obj);
+    }
+
+    /**
+     * Unregister for a satellite provision state changed event.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    public void unregisterForSatelliteProvisionStateChanged(Handler h) {
+        mCi.unregisterForSatelliteProvisionStateChanged(h);
+    }
+
+    /**
+     * Get the list of provisioned satellite features.
+     *
+     * @param result Callback message to receive the result.
+     */
+    public void getProvisionedSatelliteFeatures(Message result) {
+        //TODO (b/266126070): add implementation.
+    }
+
+    /**
+     * Registers for satellite state changed from satellite modem.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForSatelliteModemStateChanged(@NonNull Handler h, int what,
+            @Nullable Object obj) {
+        mCi.registerForSatelliteModeChanged(h, what, obj);
+    }
+
+    /**
+     * Unregisters for satellite state changed from satellite modem.
+     *
+     * @param h Handler to be removed from registrant list.
+     */
+    public void unregisterForSatelliteModemStateChanged(@NonNull Handler h) {
+        mCi.unregisterForSatelliteModeChanged(h);
+    }
+
+    /**
+     * Registers for pending datagram count info from satellite modem.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForPendingDatagramCount(@NonNull Handler h, int what,
+            @Nullable Object obj) {
+        mCi.registerForPendingSatelliteMessageCount(h, what, obj);
+    }
+
+    /**
+     * Unregisters for pending datagram count info from satellite modem.
+     *
+     * @param h Handler to be removed from registrant list.
+     */
+    public void unregisterForPendingDatagramCount(@NonNull Handler h) {
+        mCi.unregisterForPendingSatelliteMessageCount(h);
+    }
+
+    /**
+     * Register to receive incoming datagrams over satellite.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForSatelliteDatagramsReceived(@NonNull Handler h, int what,
+            @Nullable Object obj) {
+        // TODO: rename
+        mCi.registerForNewSatelliteMessages(h, what, obj);
+    }
+
+    /**
+     * Unregister to stop receiving incoming datagrams over satellite.
+     *
+     * @param h Handler to be removed from registrant list.
+     */
+    public void unregisterForSatelliteDatagramsReceived(@NonNull Handler h) {
+        // TODO: rename
+        mCi.unregisterForNewSatelliteMessages(h);
+    }
+
+    /**
+     * Poll pending datagrams over satellite.
+     * @param result The Message to send the result of the operation to.
+     */
+    public void pollPendingSatelliteDatagrams(Message result) {
+        //mCi.pollPendingSatelliteDatagrams(result);
+    }
+
+    /**
+     * Send datagram over satellite.
+     * @param result The Message to send the result of the operation to.
+     * @param datagram Datagram to send over satellite.
+     * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in
+     *                                 full screen mode.
+     */
+    public void sendSatelliteDatagram(Message result, SatelliteDatagram datagram,
+            boolean needFullScreenPointingUI) {
+        //mCi.sendSatelliteDatagram(result, datagram);
+    }
+
+    /**
+     * Check whether satellite communication is allowed for the current location.
+     * @param result The Message to send the result of the operation to.
+     */
+    public void isSatelliteCommunicationAllowedForCurrentLocation(Message result) {
+        mCi.isSatelliteCommunicationAllowedForCurrentLocation(result);
+    }
+
+    /**
+     * Get the time after which the satellite will be visible.
+     * @param result The Message to send the result of the operation to.
+     */
+    public void requestTimeForNextSatelliteVisibility(Message result) {
+        mCi.getTimeForNextSatelliteVisibility(result);
+    }
+
+    /**
+     * Start callback mode
+     * @param type for callback mode entry.
+     */
+    public void startCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type) {
+        Rlog.d(LOG_TAG, "startCallbackMode:type=" + type);
+        mNotifier.notifyCallbackModeStarted(this, type);
+    }
+
+    /**
+     * Stop callback mode
+     * @param type for callback mode exit.
+     * @param reason for stopping callback mode.
+     */
+    public void stopCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type,
+            @TelephonyManager.EmergencyCallbackModeStopReason int reason) {
+        Rlog.d(LOG_TAG, "stopCallbackMode:type=" + type + ", reason=" + reason);
+        mNotifier.notifyCallbackModeStopped(this, type, reason);
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -4996,7 +5530,7 @@
         pw.println(" isDnsCheckDisabled()=" + isDnsCheckDisabled());
         pw.println(" getUnitTestMode()=" + getUnitTestMode());
         pw.println(" getState()=" + getState());
-        pw.println(" getIccSerialNumber()=" + getIccSerialNumber());
+        pw.println(" getIccSerialNumber()=" + Rlog.pii(LOG_TAG, getIccSerialNumber()));
         pw.println(" getIccRecordsLoaded()=" + getIccRecordsLoaded());
         pw.println(" getMessageWaitingIndicator()=" + getMessageWaitingIndicator());
         pw.println(" getCallForwardingIndicator()=" + getCallForwardingIndicator());
@@ -5009,7 +5543,6 @@
         pw.println(" needsOtaServiceProvisioning=" + needsOtaServiceProvisioning());
         pw.println(" isInEmergencySmsMode=" + isInEmergencySmsMode());
         pw.println(" isEcmCanceledForEmergency=" + isEcmCanceledForEmergency());
-        pw.println(" isUsingNewDataStack=" + isUsingNewDataStack());
         pw.println(" service state=" + getServiceState());
         pw.flush();
         pw.println("++++++++++++++++++++++++++++++++");
@@ -5025,16 +5558,6 @@
             pw.println("++++++++++++++++++++++++++++++++");
         }
 
-        if (mAccessNetworksManager != null) {
-            for (int transport : mAccessNetworksManager.getAvailableTransports()) {
-                if (getDcTracker(transport) != null) {
-                    getDcTracker(transport).dump(fd, pw, args);
-                    pw.flush();
-                    pw.println("++++++++++++++++++++++++++++++++");
-                }
-            }
-        }
-
         if (mDataNetworkController != null) {
             try {
                 mDataNetworkController.dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index 05c325e..8b95824 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -35,6 +35,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.telephony.Rlog;
 
 import java.util.HashMap;
@@ -71,6 +72,7 @@
     private TelephonyManager mTelephonyManager;
     private static final RegistrantList sMultiSimConfigChangeRegistrants = new RegistrantList();
     private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
+    private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem";
     private static final boolean DEBUG = !"user".equals(Build.TYPE);
     /**
      * Init method to instantiate the object
@@ -122,6 +124,21 @@
         }
     }
 
+    // If virtual DSDA is enabled for this UE, then updates maxActiveVoiceSubscriptions to 2.
+    private PhoneCapability maybeUpdateMaxActiveVoiceSubscriptions(
+            final PhoneCapability staticCapability) {
+        boolean enableVirtualDsda = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_enable_virtual_dsda);
+
+        if (staticCapability.getLogicalModemList().size() > 1 && enableVirtualDsda) {
+            return new PhoneCapability.Builder(staticCapability)
+                    .setMaxActiveVoiceSubscriptions(2)
+                    .build();
+        } else {
+            return staticCapability;
+        }
+    }
+
     /**
      * Static method to get instance.
      */
@@ -180,6 +197,8 @@
                     ar = (AsyncResult) msg.obj;
                     if (ar != null && ar.exception == null) {
                         mStaticCapability = (PhoneCapability) ar.result;
+                        mStaticCapability =
+                                maybeUpdateMaxActiveVoiceSubscriptions(mStaticCapability);
                         notifyCapabilityChanged();
                     } else {
                         log(msg.what + " failure. Not getting phone capability." + ar.exception);
@@ -366,7 +385,7 @@
             // eg if we are going from 2 phones to 1 phone, we need to deregister RIL for the
             // second phone. This loop does nothing if numOfActiveModems is increasing.
             for (int phoneId = numOfActiveModems; phoneId < oldNumOfActiveModems; phoneId++) {
-                SubscriptionController.getInstance().clearSubInfoRecord(phoneId);
+                SubscriptionManagerService.getInstance().markSubscriptionsInactive(phoneId);
                 subInfoCleared = true;
                 mPhones[phoneId].mCi.onSlotActiveStatusChange(
                         SubscriptionManager.isValidPhoneId(phoneId));
@@ -400,7 +419,7 @@
                         + "setting VOICE & SMS subId to -1 (No Preference)");
 
                 //Set the default VOICE subId to -1 ("No Preference")
-                SubscriptionController.getInstance().setDefaultVoiceSubId(
+                SubscriptionManagerService.getInstance().setDefaultVoiceSubId(
                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
 
                 //TODO:: Set the default SMS sub to "No Preference". Tracking this bug (b/227386042)
@@ -465,34 +484,46 @@
      * @return true if the modem service is set successfully, false otherwise.
      */
     public boolean setModemService(String serviceName) {
-        if (mRadioConfig == null || mPhones[0] == null) {
-            return false;
-        }
-
         log("setModemService: " + serviceName);
         boolean statusRadioConfig = false;
         boolean statusRil = false;
         final boolean isAllowed = SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false);
+        final boolean isAllowedForBoot =
+                SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false);
 
-        // Check for ALLOW_MOCK_MODEM_PROPERTY on user builds
-        if (isAllowed || DEBUG) {
-            if (serviceName != null) {
+        // Check for ALLOW_MOCK_MODEM_PROPERTY and BOOT_ALLOW_MOCK_MODEM_PROPERTY on user builds
+        if (isAllowed || isAllowedForBoot || DEBUG) {
+            if (mRadioConfig != null) {
                 statusRadioConfig = mRadioConfig.setModemService(serviceName);
-
-                //TODO: consider multi-sim case (b/210073692)
-                statusRil = mPhones[0].mCi.setModemService(serviceName);
-            } else {
-                statusRadioConfig = mRadioConfig.setModemService(null);
-
-                //TODO: consider multi-sim case
-                statusRil = mPhones[0].mCi.setModemService(null);
             }
 
-            return statusRadioConfig && statusRil;
+            if (!statusRadioConfig) {
+                loge("setModemService: switching modem service for radioconfig fail");
+                return false;
+            }
+
+            for (int i = 0; i < getPhoneCount(); i++) {
+                if (mPhones[i] != null) {
+                    statusRil = mPhones[i].mCi.setModemService(serviceName);
+                }
+
+                if (!statusRil) {
+                    loge("setModemService: switch modem for radio " + i + " fail");
+
+                    // Disconnect the switched service
+                    mRadioConfig.setModemService(null);
+                    for (int t = 0; t < i; t++) {
+                        mPhones[t].mCi.setModemService(null);
+                    }
+                    return false;
+                }
+            }
         } else {
             loge("setModemService is not allowed");
             return false;
         }
+
+        return true;
     }
 
      /**
@@ -500,7 +531,6 @@
      * @return the service name of the connected service.
      */
     public String getModemService() {
-        //TODO: consider multi-sim case
         if (mPhones[0] == null) {
             return "";
         }
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 3361b74..57a375b 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
+
 import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA;
 import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA_LTE;
 
@@ -29,7 +31,6 @@
 import android.content.pm.PackageManager;
 import android.net.LocalServerSocket;
 import android.os.Build;
-import android.os.HandlerThread;
 import android.os.Looper;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
@@ -50,6 +51,7 @@
 import com.android.internal.telephony.imsphone.ImsPhoneFactory;
 import com.android.internal.telephony.metrics.MetricsCollector;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.uicc.UiccController;
 import com.android.internal.telephony.util.NotificationChannelController;
 import com.android.internal.util.IndentingPrintWriter;
@@ -83,8 +85,7 @@
     private static IntentBroadcaster sIntentBroadcaster;
     private static @Nullable EuiccController sEuiccController;
     private static @Nullable EuiccCardController sEuiccCardController;
-
-    static private SubscriptionInfoUpdater sSubInfoRecordUpdater = null;
+    private static SubscriptionManagerService sSubscriptionManagerService;
 
     @UnsupportedAppUsage
     static private boolean sMadeDefaults = false;
@@ -117,6 +118,7 @@
         synchronized (sLockProxyPhones) {
             if (!sMadeDefaults) {
                 sContext = context;
+
                 // create the telephony device controller.
                 TelephonyDevController.create();
 
@@ -179,7 +181,7 @@
 
                 if (numPhones > 0) {
                     final RadioConfig radioConfig = RadioConfig.make(context,
-                            sCommandsInterfaces[0].getHalVersion());
+                            sCommandsInterfaces[0].getHalVersion(HAL_SERVICE_RADIO));
                     sRadioHalCapabilities = RadioInterfaceCapabilityController.init(radioConfig,
                             sCommandsInterfaces[0]);
                 } else {
@@ -194,12 +196,12 @@
                 // call getInstance()
                 sUiccController = UiccController.make(context);
 
-                Rlog.i(LOG_TAG, "Creating SubscriptionController");
-                TelephonyComponentFactory.getInstance().inject(SubscriptionController.class.
-                        getName()).initSubscriptionController(context);
+                Rlog.i(LOG_TAG, "Creating SubscriptionManagerService");
+                sSubscriptionManagerService = new SubscriptionManagerService(context,
+                        Looper.myLooper());
+
                 TelephonyComponentFactory.getInstance().inject(MultiSimSettingController.class.
-                        getName()).initMultiSimSettingController(context,
-                        SubscriptionController.getInstance());
+                        getName()).initMultiSimSettingController(context);
 
                 if (context.getPackageManager().hasSystemFeature(
                         PackageManager.FEATURE_TELEPHONY_EUICC)) {
@@ -231,14 +233,6 @@
 
                 sMadeDefaults = true;
 
-                Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater ");
-                HandlerThread pfhandlerThread = new HandlerThread("PhoneFactoryHandlerThread");
-                pfhandlerThread.start();
-                sSubInfoRecordUpdater = TelephonyComponentFactory.getInstance().inject(
-                        SubscriptionInfoUpdater.class.getName()).
-                        makeSubscriptionInfoUpdater(pfhandlerThread.
-                        getLooper(), context, SubscriptionController.getInstance());
-
                 // Only bring up IMS if the device supports having an IMS stack.
                 if (context.getPackageManager().hasSystemFeature(
                         PackageManager.FEATURE_TELEPHONY_IMS)) {
@@ -376,10 +370,6 @@
         }
     }
 
-    public static SubscriptionInfoUpdater getSubscriptionInfoUpdater() {
-        return sSubInfoRecordUpdater;
-    }
-
     /**
      * Get the network factory associated with a given phone ID.
      * @param phoneId the phone id
@@ -416,7 +406,6 @@
      * @param phoneId The phone's id.
      * @return the preferred network mode bitmask that should be set.
      */
-    // TODO: Fix when we "properly" have TelephonyDevController/SubscriptionController ..
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public static int calculatePreferredNetworkType(int phoneId) {
         if (getPhone(phoneId) == null) {
@@ -433,7 +422,7 @@
     /* Gets the default subscription */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public static int getDefaultSubscription() {
-        return SubscriptionController.getInstance().getDefaultSubId();
+        return SubscriptionManagerService.getInstance().getDefaultSubId();
     }
 
     /* Returns User SMS Prompt property,  enabled or not */
@@ -461,19 +450,7 @@
     }
 
     /**
-     * Request a refresh of the embedded subscription list.
-     *
-     * @param cardId the card ID of the eUICC.
-     * @param callback Optional callback to execute after the refresh completes. Must terminate
-     *     quickly as it will be called from SubscriptionInfoUpdater's handler thread.
-     */
-    public static void requestEmbeddedSubscriptionInfoListRefresh(
-            int cardId, @Nullable Runnable callback) {
-        sSubInfoRecordUpdater.requestEmbeddedSubscriptionInfoListRefresh(cardId, callback);
-    }
-
-    /**
-     * Get a the SmsController.
+     * Get the instance of {@link SmsController}.
      */
     public static SmsController getSmsController() {
         synchronized (sLockProxyPhones) {
@@ -576,21 +553,14 @@
         pw.decreaseIndent();
         pw.println("++++++++++++++++++++++++++++++++");
 
-        pw.println("SubscriptionController:");
-        pw.increaseIndent();
-        try {
-            SubscriptionController.getInstance().dump(fd, pw, args);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
         pw.flush();
         pw.decreaseIndent();
         pw.println("++++++++++++++++++++++++++++++++");
 
-        pw.println("SubInfoRecordUpdater:");
+        pw.println("sRadioHalCapabilities:");
         pw.increaseIndent();
         try {
-            sSubInfoRecordUpdater.dump(fd, pw, args);
+            sRadioHalCapabilities.dump(fd, pw, args);
         } catch (Exception e) {
             e.printStackTrace();
         }
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
index 5b4d5e5..32c0c73 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.telephony;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
@@ -36,9 +35,9 @@
 
 import com.android.internal.telephony.PhoneConstants.DataState;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.function.Consumer;
 
 /**
@@ -211,16 +210,6 @@
     static final String REASON_DATA_UNTHROTTLED = "dataUnthrottled";
     static final String REASON_TRAFFIC_DESCRIPTORS_UPDATED = "trafficDescriptorsUpdated";
 
-    // Reasons for Radio being powered off
-    int RADIO_POWER_REASON_USER = 0;
-    int RADIO_POWER_REASON_THERMAL = 1;
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"RADIO_POWER_REASON_"},
-        value = {
-            RADIO_POWER_REASON_USER,
-            RADIO_POWER_REASON_THERMAL})
-    public @interface RadioPowerReason {}
-
     // Used for band mode selection methods
     static final int BM_UNSPECIFIED = RILConstants.BAND_MODE_UNSPECIFIED; // automatic
     static final int BM_EURO_BAND   = RILConstants.BAND_MODE_EURO;
@@ -593,8 +582,9 @@
      * getServiceState().getState() will not change immediately after this call.
      * registerForServiceStateChanged() to find out when the
      * request is complete. This will set the reason for radio power state as {@link
-     * #RADIO_POWER_REASON_USER}. This will not guarantee that the requested radio power state will
-     * actually be set. See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
+     * android.telephony.TelephonyManager#RADIO_POWER_REASON_USER}. This will not guarantee that the
+     * requested radio power state will actually be set.
+     * See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
      * for details.
      *
      * @param power true means "on", false means "off".
@@ -620,8 +610,9 @@
      * getServiceState().getState() will not change immediately after this call.
      * registerForServiceStateChanged() to find out when the
      * request is complete. This will set the reason for radio power state as {@link
-     * #RADIO_POWER_REASON_USER}. This will not guarantee that the requested radio power state will
-     * actually be set. See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
+     * android.telephony.TelephonyManager#RADIO_POWER_REASON_USER}. This will not guarantee that the
+     * requested radio power state will actually be set.
+     * See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
      * for details.
      *
      * @param power true means "on", false means "off".
@@ -638,7 +629,7 @@
     default void setRadioPower(boolean power, boolean forEmergencyCall,
             boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
         setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply,
-                RADIO_POWER_REASON_USER);
+                TelephonyManager.RADIO_POWER_REASON_USER);
     }
 
     /**
@@ -656,11 +647,19 @@
      * @param power true means "on", false means "off".
      * @param reason RadioPowerReason constant defining the reason why the radio power was set.
      */
-    default void setRadioPowerForReason(boolean power, @RadioPowerReason int reason) {
+    default void setRadioPowerForReason(boolean power,
+            @TelephonyManager.RadioPowerReason int reason) {
         setRadioPowerForReason(power, false, false, false, reason);
     }
 
     /**
+     * @return reasons for powering off radio.
+     */
+    default Set<Integer> getRadioPowerOffReasons() {
+        return new HashSet<>();
+    }
+
+    /**
      * Sets the radio power on/off state with option to specify whether it's for emergency call
      * (off is sometimes called "airplane mode") and option to set the reason for setting the power
      * state. Current state can be gotten via {@link #getServiceState()}.
@@ -686,7 +685,7 @@
      */
     default void setRadioPowerForReason(boolean power, boolean forEmergencyCall,
             boolean isSelectedPhoneForEmergencyCall, boolean forceApply,
-            @RadioPowerReason int reason) {}
+            @TelephonyManager.RadioPowerReason int reason) {}
 
     /**
      * Get the line 1 phone number (MSISDN). For CDMA phones, the MDN is returned
@@ -1036,6 +1035,10 @@
     String getImei();
 
     /**
+     * Retrieves IMEI type for phones.
+     */
+    int getImeiType();
+    /**
      * Retrieves the IccPhoneBookInterfaceManager of the Phone
      */
     public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager();
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index 701a157..20d6702 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.telephony.Annotation;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SrvccState;
 import android.telephony.BarringInfo;
@@ -31,8 +32,11 @@
 import android.telephony.ServiceState;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager.DataEnabledReason;
+import android.telephony.TelephonyManager.EmergencyCallbackModeStopReason;
+import android.telephony.TelephonyManager.EmergencyCallbackModeType;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.MediaQualityStatus;
 
 import java.util.List;
 
@@ -78,7 +82,10 @@
 
     void notifyCellInfo(Phone sender, List<CellInfo> cellInfo);
 
-    void notifyPreciseCallState(Phone sender);
+    /** Send a notification that precise call state changed. */
+    void notifyPreciseCallState(Phone sender, String[] imsCallIds,
+            @Annotation.ImsCallServiceType int[] imsCallServiceTypes,
+            @Annotation.ImsCallType int[] imsCallTypes);
 
     void notifyDisconnectCause(Phone sender, int cause, int preciseCause);
 
@@ -113,6 +120,9 @@
     /** Notify of a change to the call quality of an active foreground call. */
     void notifyCallQualityChanged(Phone sender, CallQuality callQuality, int callNetworkType);
 
+    /** Notify of a change to the media quality status of an active foreground call. */
+    void notifyMediaQualityStatusChanged(Phone sender, MediaQualityStatus status);
+
     /** Notify registration failed */
     void notifyRegistrationFailed(Phone sender, @NonNull CellIdentity cellIdentity,
             @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
@@ -132,4 +142,11 @@
     /** Notify link capacity estimate has changed. */
     void notifyLinkCapacityEstimateChanged(Phone sender,
             List<LinkCapacityEstimate> linkCapacityEstimateList);
+
+    /** Notify callback mode started. */
+    void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type);
+
+    /** Notify callback mode stopped. */
+    void notifyCallbackModeStopped(Phone sender, @EmergencyCallbackModeType int type,
+            @EmergencyCallbackModeStopReason int reason);
 }
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index b1dfa5f..d30a73c 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -27,22 +27,29 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.RemoteException;
 import android.os.TelephonyServiceManager.ServiceRegisterer;
-import android.os.UserHandle;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
+import android.text.TextUtils;
 import android.util.EventLog;
 
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.uicc.IsimRecords;
+import com.android.internal.telephony.uicc.SIMRecords;
 import com.android.internal.telephony.uicc.UiccCardApplication;
 import com.android.internal.telephony.uicc.UiccPort;
 import com.android.telephony.Rlog;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
     private static final String TAG = "PhoneSubInfoController";
     private static final boolean DBG = true;
@@ -147,14 +154,13 @@
     public String getSubscriberIdForSubscriber(int subId, String callingPackage,
             String callingFeatureId) {
         String message = "getSubscriberIdForSubscriber";
-
-        enforceCallingPackage(callingPackage, Binder.getCallingUid(), message);
+        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
 
         long identity = Binder.clearCallingIdentity();
         boolean isActive;
         try {
-            isActive = SubscriptionController.getInstance().isActiveSubId(subId, callingPackage,
-                    callingFeatureId);
+            isActive = SubscriptionManagerService.getInstance().isActiveSubId(subId,
+                    callingPackage, callingFeatureId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -168,7 +174,12 @@
             }
             identity = Binder.clearCallingIdentity();
             try {
-                return SubscriptionController.getInstance().getImsiPrivileged(subId);
+                SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+                        .getSubscriptionInfoInternal(subId);
+                if (subInfo != null && !TextUtils.isEmpty(subInfo.getImsi())) {
+                    return subInfo.getImsi();
+                }
+                return null;
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -316,28 +327,6 @@
                 "Requires MODIFY_PHONE_STATE");
     }
 
-    /**
-     * Make sure the caller is the calling package itself
-     *
-     * @throws SecurityException if the caller is not the calling package
-     */
-    private void enforceCallingPackage(String callingPackage, int callingUid, String message) {
-        int packageUid = -1;
-        PackageManager pm = mContext.createContextAsUser(
-                UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
-        if (pm != null) {
-            try {
-                packageUid = pm.getPackageUid(callingPackage, 0);
-            } catch (PackageManager.NameNotFoundException e) {
-                // packageUid is -1
-            }
-        }
-        if (packageUid != callingUid) {
-            throw new SecurityException(message + ": Package " + callingPackage
-                    + " does not belong to " + callingUid);
-        }
-    }
-
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private int getDefaultSubscription() {
         return  PhoneFactory.getDefaultSubscription();
@@ -359,6 +348,34 @@
     }
 
     /**
+     * Fetches the IMS private user identity (EF_IMPI) based on subscriptionId.
+     *
+     * @param subId subscriptionId
+     * @return IMPI (IMS private user identity) of type string.
+     * @throws IllegalArgumentException if the subscriptionId is not valid
+     * @throws IllegalStateException in case the ISIM hasn’t been loaded.
+     * @throws SecurityException if the caller does not have the required permission
+     */
+    public String getImsPrivateUserIdentity(int subId, String callingPackage,
+            String callingFeatureId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid SubscriptionID  = " + subId);
+        }
+        if (!TelephonyPermissions.checkCallingOrSelfUseIccAuthWithDeviceIdentifier(mContext,
+                callingPackage, callingFeatureId, "getImsPrivateUserIdentity")) {
+            throw (new SecurityException("No permissions to the caller"));
+        }
+        Phone phone = getPhone(subId);
+        assert phone != null;
+        IsimRecords isim = phone.getIsimRecords();
+        if (isim != null) {
+            return isim.getIsimImpi();
+        } else {
+            throw new IllegalStateException("ISIM is not loaded");
+        }
+    }
+
+    /**
     * get the Isim Domain based on subId
     */
     public String getIsimDomain(int subId) {
@@ -389,6 +406,42 @@
     }
 
     /**
+     * Fetches the ISIM public user identities (EF_IMPU) from UICC based on subId
+     *
+     * @param subId subscriptionId
+     * @param callingPackage package name of the caller
+     * @param callingFeatureId feature Id of the caller
+     * @return List of public user identities of type android.net.Uri or empty list  if
+     * EF_IMPU is not available.
+     * @throws IllegalArgumentException if the subscriptionId is not valid
+     * @throws IllegalStateException in case the ISIM hasn’t been loaded.
+     * @throws SecurityException if the caller does not have the required permission
+     */
+    public List<Uri> getImsPublicUserIdentities(int subId, String callingPackage,
+            String callingFeatureId) {
+        if (TelephonyPermissions.
+                checkCallingOrSelfReadPrivilegedPhoneStatePermissionOrReadPhoneNumber(
+                mContext, subId, callingPackage, callingFeatureId, "getImsPublicUserIdentities")) {
+            Phone phone = getPhone(subId);
+            assert phone != null;
+            IsimRecords isimRecords = phone.getIsimRecords();
+            if (isimRecords != null) {
+                String[] impus = isimRecords.getIsimImpu();
+                List<Uri> impuList = new ArrayList<>();
+                for (String impu : impus) {
+                    if (impu != null && impu.trim().length() > 0) {
+                        impuList.add(Uri.parse(impu));
+                    }
+                }
+                return impuList;
+            }
+            throw new IllegalStateException("ISIM is not loaded");
+        } else {
+            throw new IllegalArgumentException("Invalid SubscriptionID  = " + subId);
+        }
+    }
+
+    /**
     * get the Isim Ist based on subId
     */
     public String getIsimIst(int subId) throws RemoteException {
@@ -418,6 +471,29 @@
                 });
     }
 
+    /**
+     * Returns the USIM service table that fetched from EFUST elementary field that are loaded
+     * based on the appType.
+     */
+    public String getSimServiceTable(int subId, int appType) throws RemoteException {
+        return callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getSimServiceTable",
+                (phone) -> {
+                    UiccPort uiccPort = phone.getUiccPort();
+                    if (uiccPort == null || uiccPort.getUiccProfile() == null) {
+                        loge("getSimServiceTable(): uiccPort or uiccProfile is null");
+                        return null;
+                    }
+                    UiccCardApplication uiccApp = uiccPort.getUiccProfile().getApplicationByType(
+                            appType);
+                    if (uiccApp == null) {
+                        loge("getSimServiceTable(): no app with specified apptype="
+                                + appType);
+                        return null;
+                    }
+                    return ((SIMRecords)uiccApp.getIccRecords()).getSimServiceTable();
+                });
+    }
+
     @Override
     public String getIccSimChallengeResponse(int subId, int appType, int authType, String data,
             String callingPackage, String callingFeatureId) throws RemoteException {
@@ -438,7 +514,9 @@
             }
 
             if (authType != UiccCardApplication.AUTH_CONTEXT_EAP_SIM
-                    && authType != UiccCardApplication.AUTH_CONTEXT_EAP_AKA) {
+                    && authType != UiccCardApplication.AUTH_CONTEXT_EAP_AKA
+                    && authType != UiccCardApplication.AUTH_CONTEXT_GBA_BOOTSTRAP
+                    && authType != UiccCardApplication.AUTHTYPE_GBA_NAF_KEY_EXTERNAL) {
                 loge("getIccSimChallengeResponse() unsupported authType: " + authType);
                 return null;
             }
@@ -491,7 +569,7 @@
             if (phone != null) {
                 return callMethodHelper.callMethod(phone);
             } else {
-                loge(message + " phone is null for Subscription:" + subId);
+                if (VDBG) loge(message + " phone is null for Subscription:" + subId);
                 return null;
             }
         } finally {
@@ -581,6 +659,37 @@
         }
     }
 
+    /**
+     * Returns SIP URI or tel URI of the Public Service Identity of the SM-SC fetched from
+     * EF_PSISMSC elementary field as defined in Section 4.5.9 (3GPP TS 31.102).
+     * @throws IllegalStateException in case if phone or UiccApplication is not available.
+     */
+    public Uri getSmscIdentity(int subId, int appType) throws RemoteException {
+        Uri smscIdentityUri = callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getSmscIdentity",
+                (phone) -> {
+                    try {
+                        String smscIdentity = null;
+                        UiccPort uiccPort = phone.getUiccPort();
+                        UiccCardApplication uiccApp =
+                                uiccPort.getUiccProfile().getApplicationByType(
+                                        appType);
+                        smscIdentity = (uiccApp != null) ? uiccApp.getIccRecords().getSmscIdentity()
+                                : null;
+                        if (TextUtils.isEmpty(smscIdentity)) {
+                            return Uri.EMPTY;
+                        }
+                        return Uri.parse(smscIdentity);
+                    } catch (NullPointerException ex) {
+                        Rlog.e(TAG, "getSmscIdentity(): Exception = " + ex);
+                        return null;
+                    }
+                });
+        if (smscIdentityUri == null) {
+            throw new IllegalStateException("Telephony service error");
+        }
+        return smscIdentityUri;
+    }
+
     private void log(String s) {
         Rlog.d(TAG, s);
     }
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index 76f0041..ed9982e 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -28,7 +28,6 @@
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 import android.telephony.RadioAccessFamily;
-import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -48,8 +47,10 @@
     @VisibleForTesting
     static final int EVENT_START_RC_RESPONSE                = 2;
     private static final int EVENT_APPLY_RC_RESPONSE        = 3;
-    private static final int EVENT_FINISH_RC_RESPONSE       = 4;
-    private static final int EVENT_TIMEOUT                  = 5;
+    @VisibleForTesting
+    public static final int EVENT_FINISH_RC_RESPONSE        = 4;
+    @VisibleForTesting
+    public static final int EVENT_TIMEOUT                   = 5;
     @VisibleForTesting
     public static final int EVENT_MULTI_SIM_CONFIG_CHANGED  = 6;
 
@@ -157,34 +158,6 @@
         logd("Constructor - Exit");
     }
 
-    public void registerForAllDataDisconnected(int subId, Handler h, int what) {
-        int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
-
-        if (SubscriptionManager.isValidPhoneId(phoneId)) {
-            mPhones[phoneId].registerForAllDataDisconnected(h, what);
-        }
-    }
-
-    public void unregisterForAllDataDisconnected(int subId, Handler h) {
-        int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
-
-        if (SubscriptionManager.isValidPhoneId(phoneId)) {
-            mPhones[phoneId].unregisterForAllDataDisconnected(h);
-        }
-    }
-
-
-    public boolean areAllDataDisconnected(int subId) {
-        int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
-
-        if (SubscriptionManager.isValidPhoneId(phoneId)) {
-            return mPhones[phoneId].areAllDataDisconnected();
-        } else {
-            // if we can't find a phone for the given subId, it is disconnected.
-            return true;
-        }
-    }
-
     /**
      * Get phone radio type and access technology.
      *
@@ -241,6 +214,7 @@
         clearTransaction();
 
         // Keep a wake lock until we finish radio capability changed
+        logd("Acquiring wake lock for setting radio capability");
         mWakeLock.acquire();
 
         return doSetRadioCapabilities(rafs);
@@ -386,19 +360,25 @@
                 }
             }
             RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result;
-            if ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId)) {
+            // Added exception condition  to continue to mark as transaction fail case.
+            // Checking session validity during exception is not valid
+            if (ar.exception == null
+                    && ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId))) {
                 logd("onStartRadioCapabilityResponse: Ignore session=" + mRadioCapabilitySessionId
                         + " rc=" + rc);
                 return;
             }
             mRadioAccessFamilyStatusCounter--;
-            int id = rc.getPhoneId();
+            //rc.getPhoneId() moved to avoid Null Pointer Exception, since when exception occurs
+            //its expected rc is null.
             if (ar.exception != null) {
-                logd("onStartRadioCapabilityResponse: Error response session=" + rc.getSession());
-                logd("onStartRadioCapabilityResponse: phoneId=" + id + " status=FAIL");
-                mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_FAIL;
+                logd("onStartRadioCapabilityResponse got exception=" + ar.exception);
+                //mSetRadioAccessFamilyStatus will be set anyway to SET_RC_STATUS_FAIL
+                // if either of them fail at issueFinish() method below,i.e. both phone id count
+                // is set to SET_RC_STATUS_FAIL.
                 mTransactionFailed = true;
             } else {
+                int id = rc.getPhoneId();
                 logd("onStartRadioCapabilityResponse: phoneId=" + id + " status=STARTED");
                 mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_STARTED;
             }
@@ -510,13 +490,20 @@
      * @param msg obj field isa RadioCapability
      */
     void onFinishRadioCapabilityResponse(Message msg) {
-        RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result;
-        if ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId)) {
-            logd("onFinishRadioCapabilityResponse: Ignore session=" + mRadioCapabilitySessionId
-                    + " rc=" + rc);
-            return;
-        }
         synchronized (mSetRadioAccessFamilyStatus) {
+            AsyncResult ar = (AsyncResult)  msg.obj;
+            RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result;
+            // Added exception condition on finish to continue to revert if exception occurred.
+            // Checking session validity during exception is not valid
+            if (ar.exception == null
+                    && ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId))) {
+                logd("onFinishRadioCapabilityResponse: Ignore session="
+                        + mRadioCapabilitySessionId + " rc=" + rc);
+                return;
+            }
+            if (ar.exception != null) {
+                logd("onFinishRadioCapabilityResponse got exception=" + ar.exception);
+            }
             logd(" onFinishRadioCapabilityResponse mRadioAccessFamilyStatusCounter="
                     + mRadioAccessFamilyStatusCounter);
             mRadioAccessFamilyStatusCounter--;
@@ -604,7 +591,6 @@
             clearTransaction();
         } else {
             intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_FAILED);
-
             // now revert.
             mTransactionFailed = false;
             RadioAccessFamily[] rafs = new RadioAccessFamily[mPhones.length];
@@ -631,6 +617,7 @@
             }
 
             if (isWakeLockHeld()) {
+                logd("clearTransaction:checking wakelock held and releasing");
                 mWakeLock.release();
             }
         }
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 49e8297..5ecdfcb 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -16,6 +16,15 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.TelephonyManager.HAL_SERVICE_DATA;
+import static android.telephony.TelephonyManager.HAL_SERVICE_IMS;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MESSAGING;
+import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;
+import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
+import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
+import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
+import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
+
 import static com.android.internal.telephony.RILConstants.*;
 
 import android.annotation.NonNull;
@@ -41,10 +50,13 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.sysprop.TelephonyProperties;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.BarringInfo;
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.CellInfo;
 import android.telephony.CellSignalStrengthCdma;
@@ -54,6 +66,7 @@
 import android.telephony.CellSignalStrengthTdscdma;
 import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.ClientRequestStats;
+import android.telephony.DomainSelectionService;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.ModemActivityInfo;
 import android.telephony.NeighboringCellInfo;
@@ -66,18 +79,24 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyHistogram;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.HalService;
 import android.telephony.TelephonyManager.PrefNetworkMode;
 import android.telephony.data.DataProfile;
 import android.telephony.data.NetworkSliceInfo;
 import android.telephony.data.TrafficDescriptor;
 import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.feature.ConnectionFailureInfo;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.cdma.CdmaInformationRecords;
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.emergency.EmergencyConstants;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.telephony.imsphone.ImsCallInfo;
 import com.android.internal.telephony.metrics.ModemRestartStats;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
@@ -91,8 +110,10 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
@@ -132,6 +153,9 @@
     private final ClientWakelockTracker mClientWakelockTracker = new ClientWakelockTracker();
 
     /** @hide */
+    public static final HalVersion RADIO_HAL_VERSION_UNSUPPORTED = HalVersion.UNSUPPORTED;
+
+    /** @hide */
     public static final HalVersion RADIO_HAL_VERSION_UNKNOWN = HalVersion.UNKNOWN;
 
     /** @hide */
@@ -158,8 +182,11 @@
     /** @hide */
     public static final HalVersion RADIO_HAL_VERSION_2_0 = new HalVersion(2, 0);
 
-    // IRadio version
-    private HalVersion mRadioVersion = RADIO_HAL_VERSION_UNKNOWN;
+    /** @hide */
+    public static final HalVersion RADIO_HAL_VERSION_2_1 = new HalVersion(2, 1);
+
+    // Hal version
+    private Map<Integer, HalVersion> mHalVersion = new HashMap<>();
 
     //***** Instance Variables
 
@@ -197,15 +224,9 @@
 
     private static final String PROPERTY_IS_VONR_ENABLED = "persist.radio.is_vonr_enabled_";
 
-    static final int RADIO_SERVICE = 0;
-    static final int DATA_SERVICE = 1;
-    static final int MESSAGING_SERVICE = 2;
-    static final int MODEM_SERVICE = 3;
-    static final int NETWORK_SERVICE = 4;
-    static final int SIM_SERVICE = 5;
-    static final int VOICE_SERVICE = 6;
-    static final int MIN_SERVICE_IDX = RADIO_SERVICE;
-    static final int MAX_SERVICE_IDX = VOICE_SERVICE;
+    public static final int MIN_SERVICE_IDX = HAL_SERVICE_RADIO;
+
+    public static final int MAX_SERVICE_IDX = HAL_SERVICE_IMS;
 
     /**
      * An array of sets that records if services are disabled in the HAL for a specific phone ID
@@ -232,6 +253,8 @@
     private volatile IRadio mRadioProxy = null;
     private DataResponse mDataResponse;
     private DataIndication mDataIndication;
+    private ImsResponse mImsResponse;
+    private ImsIndication mImsIndication;
     private MessagingResponse mMessagingResponse;
     private MessagingIndication mMessagingIndication;
     private ModemResponse mModemResponse;
@@ -361,11 +384,11 @@
 
                 case EVENT_AIDL_PROXY_DEAD:
                     int aidlService = msg.arg1;
-                    AtomicLong obj = (AtomicLong) msg.obj;
-                    riljLog("handleMessage: EVENT_AIDL_PROXY_DEAD cookie = " + msg.obj
+                    long msgCookie = (long) msg.obj;
+                    riljLog("handleMessage: EVENT_AIDL_PROXY_DEAD cookie = " + msgCookie
                             + ", service = " + serviceToString(aidlService) + ", cookie = "
                             + mServiceCookies.get(aidlService));
-                    if (obj.get() == mServiceCookies.get(aidlService).get()) {
+                    if (msgCookie == mServiceCookies.get(aidlService).get()) {
                         mIsRadioProxyInitialized = false;
                         resetProxyAndRequestList(aidlService);
                     }
@@ -412,8 +435,8 @@
         public void serviceDied(long cookie) {
             // Deal with service going away
             riljLog("serviceDied");
-            mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, RADIO_SERVICE,
-                    0 /* ignored arg2 */, cookie));
+            mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
+                    HAL_SERVICE_RADIO, 0 /* ignored arg2 */, cookie));
         }
     }
 
@@ -427,8 +450,11 @@
 
         public void linkToDeath(IBinder service) throws RemoteException {
             if (service != null) {
+                riljLog("Linked to death for service " + serviceToString(mService));
                 mBinder = service;
                 mBinder.linkToDeath(this, (int) mServiceCookies.get(mService).incrementAndGet());
+            } else {
+