Merge 24Q4 (ab/12406339) into aosp-main-future

Bug: 370570306
Merged-In: I2b2acc84a673e12b49f7a6c9641f287c8f10f3d8
Change-Id: I05159634922a70fb48371b7cab86d8baeeaf115b
diff --git a/flags/calling.aconfig b/flags/calling.aconfig
index c1dc7e7..a5757d8 100644
--- a/flags/calling.aconfig
+++ b/flags/calling.aconfig
@@ -4,7 +4,6 @@
 # OWNER=breadley TARGET=24Q3
 flag {
   name: "simultaneous_calling_indications"
-  is_exported: true
   namespace: "telephony"
   description: "APIs that are used to notify simultaneous calling changes to other applications."
   bug: "297446980"
@@ -21,6 +20,17 @@
 
 # OWNER=stevestatia TARGET=24Q4
 flag {
+    name: "national_country_code_formatting_for_local_calls"
+    namespace: "telephony"
+    description: "Make requests and bug fixes for formatting local calls based on country codes easier with a more scalable solution."
+    bug: "293993310"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=stevestatia TARGET=24Q4
+flag {
     name: "remove_country_code_from_local_singapore_calls"
     namespace: "telephony"
     description: "Fix bug where the country code is being shown when merging in local Singapore numbers to conference calls."
@@ -29,3 +39,26 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+# OWNER=grantmenke TARGET=25Q1
+flag {
+    name: "remap_disconnect_cause_sip_request_cancelled"
+    namespace: "telephony"
+    description: "Fix dialer UI bug by remapping disconnect CODE_SIP_REQUEST_CANCELLED to DisconnectCause.NORMAL"
+    bug: "351258918"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+    is_exported: true
+}
+
+# OWNER=breadley TARGET=24Q4
+flag {
+    name: "delay_phone_account_registration"
+    namespace: "telephony"
+    description: "Fix bug where telephony would try to register for PhoneAccounts when Telecom isn't ready yet"
+    bug: "349731543"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/flags/data.aconfig b/flags/data.aconfig
index d956104..0fd094d 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -34,53 +34,6 @@
   }
 }
 
-# OWNER=linggm TARGET=24Q2
-flag {
-  name: "use_alarm_callback"
-  namespace: "telephony"
-  description: "Use alarm callback instead of broadcast."
-  bug: "311476875"
-}
-
-# OWNER=linggm TARGET=24Q2
-flag {
-  name: "refine_preferred_data_profile_selection"
-  namespace: "telephony"
-  description: "Upon internet network connect, refine selection of preferred data profile."
-  bug: "311476883"
-}
-
-# OWNER=linggm TARGET=24Q2
-flag {
-  name: "unthrottle_check_transport"
-  namespace: "telephony"
-  description: "Check transport when unthrottle."
-  bug: "303922311"
-}
-
-# OWNER=linggm TARGET=24Q1
-flag {
-  name: "relax_ho_teardown"
-  namespace: "telephony"
-  description: "Relax handover tear down if the device is currently in voice call."
-  bug: "270895912"
-}
-
-# OWNER=linggm TARGET=24Q2
-flag {
-  name: "allow_mmtel_in_non_vops"
-  namespace: "telephony"
-  description: "Allow bring up MMTEL in nonVops area specified by carrier config."
-  bug: "241198464"
-}
-
-# OWNER=jackyu TARGET=24Q2
-flag {
-  name: "metered_embb_urlcc"
-  namespace: "telephony"
-  description: "Force networks that have PRIORITIZE_BANDWIDTH or PRIORITIZE_LATENCY to be metered."
-  bug: "301310451"
-  }
 
 # OWNER=sarahchin TARGET=24Q3
 flag {
@@ -109,14 +62,6 @@
   bug:"286171724"
 }
 
-# OWNER=nagendranb TARGET=24Q2
-flag {
- name: "notify_data_activity_changed_with_slot"
-  namespace: "telephony"
-  description: "notify data activity changed for slot id"
-  bug: "309896936"
-}
-
 # OWNER=qingqi TARGET=24Q3
 flag {
   name: "vonr_enabled_metric"
@@ -125,14 +70,6 @@
   bug:"288449751"
 }
 
-# OWNER=willycwhu TARGET=24Q2
-flag {
-  name: "ignore_existing_networks_for_internet_allowed_checking"
-  namespace: "telephony"
-  description: "Ignore existing networks when checking if internet is allowed"
-  bug: "284420611"
-}
-
 # OWNER=apsankar TARGET=24Q3
 flag {
   name: "data_call_session_stats_captures_cross_sim_calling"
@@ -141,14 +78,6 @@
   bug: "313956117"
 }
 
-# OWNER=jackyu TARGET=24Q2
-flag {
-  name: "force_iwlan_mms"
-  namespace: "telephony"
-  description: "When QNS prefers MMS on IWLAN, MMS will be attempted on IWLAN if it can, even though if existing cellular network already supports MMS."
-  bug: "316211526"
-}
-
 # OWNER=sewook TARGET=24Q3
 flag {
   name: "reconnect_qualified_network"
@@ -172,3 +101,34 @@
   description: "Write DataRatStateChanged atom"
   bug:"318519337"
 }
+
+# OWNER=jackyu TARGET=24Q4
+flag {
+  name: "dds_callback"
+  namespace: "telephony"
+  description: "Adding new callback when DDS changed"
+  bug:"353723350"
+}
+
+# OWNER=jackyu TARGET=25Q1
+flag {
+  name: "support_network_provider"
+  namespace: "telephony"
+  description: "Deprecate network factory and adapt the new network provider model from connectivity service"
+  bug: "343370895"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+# OWNER=jackyu TARGET=25Q1
+flag {
+  name: "sim_disabled_graceful_tear_down"
+  namespace: "telephony"
+  description: "Gracefully tear down the networks when SIM is disabled."
+  bug: "362372940"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
diff --git a/flags/domainselection.aconfig b/flags/domainselection.aconfig
index 623c3b6..4b1ad4a 100644
--- a/flags/domainselection.aconfig
+++ b/flags/domainselection.aconfig
@@ -33,3 +33,11 @@
     description: "This flag controls domain selection metrics."
     bug:"258112541"
 }
+
+# OWNER=jdyou TARGET=24Q4
+flag {
+    name: "hangup_emergency_call_for_cross_sim_redialing"
+    namespace: "telephony"
+    description: "This flag controls the behavior of terminating an emergency call for cross SIM redialing."
+    bug:"336398541"
+}
diff --git a/flags/ims.aconfig b/flags/ims.aconfig
index 4eff505..d2401fe 100644
--- a/flags/ims.aconfig
+++ b/flags/ims.aconfig
@@ -115,3 +115,25 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+# OWNER=joonhunshin TARGET=24Q4
+flag {
+    name: "prevent_hangup_during_call_merge"
+    namespace: "telephony"
+    description: "This flag prevents hangup call during call merge"
+    bug:"317070933"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=joonhunshin TARGET=24Q4
+flag {
+    name: "avoid_deleting_ims_object_from_cache"
+    namespace: "telephony"
+    description: "This flag controls deleting cached object to synchronize part of application callback and part of ImsFeature behavior"
+    bug:"353577279"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/flags/messaging.aconfig b/flags/messaging.aconfig
index 1030ba7..905dc94 100644
--- a/flags/messaging.aconfig
+++ b/flags/messaging.aconfig
@@ -38,4 +38,12 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
+}
+
+# OWNER=nykkumar TARGET=25Q2
+flag {
+  name: "sms_mms_deliver_broadcasts_redirect_to_main_user"
+  namespace: "telephony"
+  description: "This flag controls the redirection of SMS_DELIVER AND WAP_PUSH_DELIVER broadcasts to the MAIN user."
+  bug: "314321617"
 }
\ No newline at end of file
diff --git a/flags/misc.aconfig b/flags/misc.aconfig
index 862aa33..860c864 100644
--- a/flags/misc.aconfig
+++ b/flags/misc.aconfig
@@ -96,28 +96,6 @@
     bug:"271921464"
 }
 
-# OWNER=rambowang TARGET=24Q3
-flag {
-    name: "add_anomaly_when_notify_config_changed_with_invalid_phone"
-    namespace: "telephony"
-    description: "Report anomaly when CarrierConfigLoader received config change with sub that maps to invalid phoneId"
-    bug:"270757342"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-# OWNER=rambowang TARGET=24Q3
-flag {
-    name: "hide_preinstalled_carrier_app_at_most_once"
-    namespace: "telephony"
-    description: "Fix bug when preloaded carrier app is uninstalled and lose provisioning data"
-    bug:"158028151"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
 # OWNER=sangyun TARGET=24Q3
 flag {
     name: "roaming_notification_for_single_data_network"
@@ -206,3 +184,48 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+# OWNER=joonhunshin TARGET=24Q4
+flag {
+    name: "use_carrier_config_for_cfnry_time_via_mmi"
+    namespace: "telephony"
+    description: "This flag allows the no reply timer to be referenced in the carrier config when setting up call forward via MMI code and there is no timer value."
+    bug:"342346827"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=jackyu TARGET=25Q2
+flag {
+    name: "hsum_broadcast"
+    namespace: "telephony"
+    description: "Fixed the bug that broadcast intent is only sent to the system user."
+    bug:"362554272"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=rambowang TARGET=25Q2
+flag {
+    name: "support_carrier_services_for_hsum"
+    namespace: "telephony"
+    description: "Support Carrier Services (APIs) for HSUM."
+    bug:"345522246"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=jackyu TARGET=25Q2
+flag {
+    name: "hsum_package_manager"
+    namespace: "telephony"
+    description: "Fixed the bug that package manager is not for the right user"
+    bug:"356827794"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
diff --git a/flags/satellite.aconfig b/flags/satellite.aconfig
index 2eea80a..4806789 100644
--- a/flags/satellite.aconfig
+++ b/flags/satellite.aconfig
@@ -33,4 +33,32 @@
     namespace: "telephony"
     description: "This flag enables satellite persistent logging"
     bug:"339877723"
+}
+
+# OWNER=hyosunkim TARGET=24Q3
+flag {
+    name: "carrier_roaming_nb_iot_ntn"
+    namespace: "telephony"
+    description: "This flag enables satellite carrier roaming to nb iot ntn."
+    bug:"348253735"
+}
+
+# OWNER=tnd TARGET=24Q4
+flag {
+    name: "oem_enabled_satellite_phase_2"
+    is_exported: true
+    namespace: "telephony"
+    description: "This flag controls satellite communication supported by OEMs in phase 2."
+    bug:"349624547"
+}
+
+# OWNER=youngtaecha TARGET=24Q4
+flag {
+  name: "geofence_enhancement_for_better_ux"
+  namespace: "telephony"
+  description: "Enhance geofence to improve UX experience"
+  bug: "347711329"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
\ No newline at end of file
diff --git a/flags/subscription.aconfig b/flags/subscription.aconfig
index 9a5dabc..76485be 100644
--- a/flags/subscription.aconfig
+++ b/flags/subscription.aconfig
@@ -28,14 +28,6 @@
   bug: "296097429"
 }
 
-# OWNER=rambowang TARGET=24Q3
-flag {
-  name: "data_only_service_allow_emergency_call_only"
-  namespace: "telephony"
-  description: "Support emergency call only for data only cellular service."
-  bug: "296097429"
-}
-
 # OWNER=hhshin TARGET=24Q3
 flag {
   name: "support_psim_to_esim_conversion"
@@ -77,3 +69,15 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+# OWNER=jackyu TARGET=24Q4
+flag {
+  name: "uicc_phone_number_fix"
+  namespace: "telephony"
+  description: "Fixed that empty phone number when getLine1Number returns empty"
+  bug: "302437869"
+
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/flags/uicc.aconfig b/flags/uicc.aconfig
index 2679cfe..14341d9 100644
--- a/flags/uicc.aconfig
+++ b/flags/uicc.aconfig
@@ -43,14 +43,6 @@
     bug:"318348580"
 }
 
-# OWNER=rambowang TARGET=24Q3
-flag {
-    name: "cleanup_open_logical_channel_record_on_dispose"
-    namespace: "telephony"
-    description: "This flag cleans up the OpenLogicalChannelRecord once SIM is removed"
-    bug:"335046531"
-}
-
 # OWNER=arunvoddu TARGET=24Q4
 flag {
     name: "set_carrier_restriction_status"
@@ -58,3 +50,22 @@
     description: "This flag controls the visibility of the setCarrierRestrictionStatus API in carrierRestrictionRules class."
     bug:"342411308"
 }
+
+# OWNER=arunvoddu TARGET=24Q4
+flag {
+    name: "uicc_app_count_check_to_create_channel"
+    namespace: "telephony"
+    description: "This flag controls to create the open channel when uicc application count is greater than 0."
+    bug:"349966950"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=mewan TARGET=24Q4
+flag {
+    name: "optimization_apdu_sender"
+    namespace: "telephony"
+    description: "This flag controls optimization of apdu sender class."
+    bug:"335257880"
+}
diff --git a/proto/Android.bp b/proto/Android.bp
index 15c0aea..adc9b5e 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -31,3 +31,12 @@
     // Pin java_version until jarjar is certified to support later versions. http://b/72703434
     java_version: "1.8",
 }
+
+java_library_static {
+    name: "telephony-config-update-proto-lite",
+    proto: {
+        type: "lite",
+    },
+    srcs: ["src/**/telephony_config_update.proto"],
+    host_supported: true,
+}
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index 48e7b0d..9dbdcb0 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -320,6 +320,7 @@
     optional bool is_ntn = 41;
     optional bool supports_business_call_composer = 42;
     optional int32 call_composer_status = 43;
+    optional int32 precise_call_state_on_setup = 44;
 
     // Internal use only
     optional int64 setup_begin_millis = 10001;
@@ -717,6 +718,11 @@
     optional int32 count_of_allowed_satellite_access = 26;
     optional int32 count_of_disallowed_satellite_access = 27;
     optional int32 count_of_satellite_access_check_fail = 28;
+    optional bool is_provisioned = 29;
+    optional int32 carrier_id = 30;
+    optional int32 count_of_satellite_allowed_state_changed_events = 31;
+    optional int32 count_of_successful_location_queries = 32;
+    optional int32 count_of_failed_location_queries = 33;
 }
 
 message SatelliteSession {
@@ -733,6 +739,10 @@
     optional int32 count_of_incoming_datagram_failed = 11;
     optional bool is_demo_mode = 12;
     optional int32 max_ntn_signal_strength_level = 13;
+    optional int32 carrier_id = 14;
+    optional int32 count_of_satellite_notification_displayed = 15;
+    optional int32 count_of_auto_exit_due_to_screen_off = 16;
+    optional int32 count_of_auto_exit_due_to_tn_network = 17;
 }
 
 message SatelliteIncomingDatagram {
@@ -740,6 +750,7 @@
     optional int32 datagram_size_bytes = 2;
     optional int64 datagram_transfer_time_millis = 3;
     optional bool is_demo_mode = 4;
+    optional int32 carrier_id = 5;
 }
 
 message SatelliteOutgoingDatagram {
@@ -748,6 +759,7 @@
     optional int32 datagram_size_bytes = 3;
     optional int64 datagram_transfer_time_millis = 4;
     optional bool is_demo_mode = 5;
+    optional int32 carrier_id = 6;
 }
 
 message SatelliteProvision {
@@ -755,6 +767,7 @@
     optional int32 provisioning_time_sec = 2;
     optional bool is_provision_request = 3;
     optional bool is_canceled = 4;
+    optional int32 carrier_id = 5;
 }
 
 message SatelliteSosMessageRecommender {
@@ -766,6 +779,8 @@
     optional bool is_multi_sim = 6;
     optional int32 recommending_handover_type = 7;
     optional bool is_satellite_allowed_in_current_location = 8;
+    optional bool is_wifi_connected = 9;
+    optional int32 carrier_id = 10;
 }
 
 message DataNetworkValidation {
@@ -805,6 +820,8 @@
     optional int32 satellite_session_gap_min_sec = 5;
     optional int32 satellite_session_gap_avg_sec = 6;
     optional int32 satellite_session_gap_max_sec = 7;
+    optional int32 carrier_id = 8;
+    optional bool is_device_entitled = 9;
 }
 
 message SatelliteEntitlement {
@@ -832,4 +849,6 @@
     optional int32 result_code = 7;
     repeated string country_codes = 8;
     optional int32 config_data_source = 9;
+    optional int32 carrier_id = 10;
+    optional int32 triggering_event = 11;
 }
diff --git a/src/java/com/android/internal/telephony/CarrierActionAgent.java b/src/java/com/android/internal/telephony/CarrierActionAgent.java
index 6d74c18..c4ba77d 100644
--- a/src/java/com/android/internal/telephony/CarrierActionAgent.java
+++ b/src/java/com/android/internal/telephony/CarrierActionAgent.java
@@ -186,7 +186,8 @@
                         mPhone.getServiceStateTracker().registerForDataRoamingOff(
                                 this, EVENT_DATA_ROAMING_OFF, null, false);
                     }
-                } else if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(iccState)) {
+                } else if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(iccState)
+                        || IccCardConstants.INTENT_VALUE_ICC_NOT_READY.equals(iccState)) {
                     log("EVENT_SIM_STATE_CHANGED status: " + iccState);
                     carrierActionReset();
                     mSettingsObserver.unobserve();
@@ -257,6 +258,8 @@
                 return mCarrierActionOnRadioEnabled;
             case CARRIER_ACTION_REPORT_DEFAULT_NETWORK_STATUS:
                 return mCarrierActionReportDefaultNetworkStatus;
+            case EVENT_APN_SETTINGS_CHANGED:
+                return null;  // we don't know if it's enabled, but this is not "unsupported" action
             default:
                 loge("Unsupported action: " + action);
                 return null;
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
index 10a3a32..6f84521 100644
--- a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -120,7 +120,7 @@
     private boolean mAllowedOverMeteredNetwork = false;
     private boolean mDeleteOldKeyAfterDownload = false;
     private boolean mIsRequiredToHandleUnlock;
-    private final TelephonyManager mTelephonyManager;
+    private TelephonyManager mTelephonyManager;
     private UserManager mUserManager;
     @VisibleForTesting
     public String mMccMncForDownload = "";
@@ -152,7 +152,8 @@
         CarrierConfigManager carrierConfigManager = mContext.getSystemService(
                 CarrierConfigManager.class);
         // Callback which directly handle config change should be executed on handler thread
-        carrierConfigManager.registerCarrierConfigChangeListener(this::post,
+        if (carrierConfigManager != null) {
+            carrierConfigManager.registerCarrierConfigChangeListener(this::post,
                 (slotIndex, subId, carrierId, specificCarrierId) -> {
                     if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
                         logd("CarrierConfig changed slotIndex = " + slotIndex + " subId = " + subId
@@ -162,6 +163,13 @@
                         if ((slotIndex == mPhone.getPhoneId()) && (carrierId > 0
                                 || !TextUtils.isEmpty(
                                 mMccMncForDownload))) {
+                            if (mTelephonyManager == null
+                                    || mTelephonyManager.getSubscriptionId() != subId) {
+                                logd("recreating TelManager with SubId = " + subId);
+                                mTelephonyManager = mContext.getSystemService(
+                                                TelephonyManager.class)
+                                        .createForSubscriptionId(subId);
+                            }
                             mCarrierId = carrierId;
                             updateSimOperator();
                             // If device is screen locked do not proceed to handle
@@ -187,6 +195,7 @@
                         }
                     }
                 });
+        }
         mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
     }
 
diff --git a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
index 67be1b6..522cf77 100644
--- a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
@@ -31,6 +31,7 @@
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -66,6 +67,7 @@
 import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.uicc.IccUtils;
 import com.android.internal.telephony.uicc.UiccPort;
 import com.android.internal.telephony.uicc.UiccProfile;
@@ -93,6 +95,7 @@
  * Registered Telephony entities will receive notifications when the UIDs with these privileges
  * change.
  */
+@SuppressLint("MissingPermission")
 public class CarrierPrivilegesTracker extends Handler {
     private static final String TAG = CarrierPrivilegesTracker.class.getSimpleName();
 
@@ -223,6 +226,9 @@
             "mPrivilegedPackageInfoLock.writeLock()"})
     private boolean mSimIsReadyButNotLoaded = false;
 
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
     /** Small snapshot to hold package names and UIDs of privileged packages. */
     private static final class PrivilegedPackageInfo {
         @NonNull final Set<String> mPackageNames;
@@ -316,7 +322,7 @@
                             boolean notExist = false;
                             try {
                                 disabledByUser = action.equals(Intent.ACTION_PACKAGE_CHANGED)
-                                        && mPackageManager.getApplicationEnabledSetting(pkgName)
+                                        && getApplicationEnabledSetting(pkgName)
                                         == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
                             } catch (IllegalArgumentException iae) {
                                 // Very rare case when package changed race with package removed
@@ -336,19 +342,22 @@
                 }
             };
 
-    public CarrierPrivilegesTracker(
-            @NonNull Looper looper, @NonNull Phone phone, @NonNull Context context) {
+    public CarrierPrivilegesTracker(@NonNull Looper looper, @NonNull Phone phone,
+            @NonNull Context context, @NonNull FeatureFlags flags) {
         super(looper);
         mContext = context;
+        mFeatureFlags = flags;
         mPhone = phone;
         mPackageManager = mContext.getPackageManager();
         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));
+        if (mCarrierConfigManager != null) {
+            mCarrierConfigManager.registerCarrierConfigChangeListener(this::post,
+                    (slotIndex, subId, carrierId, specificCarrierId) -> handleCarrierConfigUpdated(
+                            subId, slotIndex));
+        }
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
         mTelephonyRegistryManager =
                 (TelephonyRegistryManager)
@@ -357,7 +366,13 @@
         IntentFilter certFilter = new IntentFilter();
         certFilter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
         certFilter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
-        mContext.registerReceiver(mIntentReceiver, certFilter);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            mContext.registerReceiverAsUser(
+                    mIntentReceiver, UserHandle.of(ActivityManager.getCurrentUser()), certFilter,
+                    /* broadcastPermission= */ null, /* scheduler= */ null);
+        } else {
+            mContext.registerReceiver(mIntentReceiver, certFilter);
+        }
 
         IntentFilter packageFilter = new IntentFilter();
         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -368,7 +383,13 @@
         // For package-related broadcasts, specify the data scheme for "package" to receive the
         // package name along with the broadcast
         packageFilter.addDataScheme("package");
-        mContext.registerReceiver(mIntentReceiver, packageFilter);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            mContext.registerReceiverAsUser(
+                    mIntentReceiver, UserHandle.of(ActivityManager.getCurrentUser()), packageFilter,
+                    /* broadcastPermission= */ null, /* scheduler= */ null);
+        } else {
+            mContext.registerReceiver(mIntentReceiver, packageFilter);
+        }
 
         sendMessage(obtainMessage(ACTION_INITIALIZE_TRACKER));
     }
@@ -444,7 +465,8 @@
                 CarrierConfigManager.getCarrierConfigSubset(
                         mContext, subId, KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
         // CarrierConfigManager#isConfigForIdentifiedCarrier can handle null or empty bundle
-        if (!mCarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfigs)) {
+        if (mCarrierConfigManager == null
+                || !mCarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfigs)) {
             return Collections.EMPTY_LIST;
         }
 
@@ -539,7 +561,14 @@
 
         PackageInfo pkg;
         try {
-            pkg = mPackageManager.getPackageInfo(pkgName, INSTALLED_PACKAGES_QUERY_FLAGS);
+            pkg =
+                    mFeatureFlags.supportCarrierServicesForHsum()
+                            ? mPackageManager.getPackageInfoAsUser(
+                                    pkgName,
+                                    INSTALLED_PACKAGES_QUERY_FLAGS,
+                                    ActivityManager.getCurrentUser())
+                            : mPackageManager.getPackageInfo(
+                                    pkgName, INSTALLED_PACKAGES_QUERY_FLAGS);
         } catch (NameNotFoundException e) {
             Rlog.e(TAG, "Error getting installed package: " + pkgName, e);
             return;
@@ -617,7 +646,10 @@
     private void refreshInstalledPackageCache() {
         List<PackageInfo> installedPackages =
                 mPackageManager.getInstalledPackagesAsUser(
-                        INSTALLED_PACKAGES_QUERY_FLAGS, UserHandle.SYSTEM.getIdentifier());
+                        INSTALLED_PACKAGES_QUERY_FLAGS,
+                        mFeatureFlags.supportCarrierServicesForHsum()
+                                ? ActivityManager.getCurrentUser()
+                                : UserHandle.SYSTEM.getIdentifier());
         for (PackageInfo pkg : installedPackages) {
             updateCertsForPackage(pkg);
             // This may be unnecessary before initialization, but invalidate the cache all the time
@@ -791,7 +823,9 @@
     private int getPackageUid(@Nullable String pkgName) {
         int uid = Process.INVALID_UID;
         try {
-            uid = mPackageManager.getPackageUid(pkgName, /* flags= */0);
+            uid = mFeatureFlags.supportCarrierServicesForHsum()
+                    ? mPackageManager.getPackageUidAsUser(pkgName, ActivityManager.getCurrentUser())
+                    : mPackageManager.getPackageUid(pkgName, /* flags= */0);
         } catch (NameNotFoundException e) {
             Rlog.e(TAG, "Unable to find uid for package " + pkgName);
         }
@@ -991,10 +1025,25 @@
         // Do the PackageManager queries before we take the lock, as these are the longest-running
         // pieces of this method and don't depend on the set of carrier apps.
         List<ResolveInfo> resolveInfos = new ArrayList<>();
-        resolveInfos.addAll(mPackageManager.queryBroadcastReceivers(intent, 0));
-        resolveInfos.addAll(mPackageManager.queryIntentActivities(intent, 0));
-        resolveInfos.addAll(mPackageManager.queryIntentServices(intent, 0));
-        resolveInfos.addAll(mPackageManager.queryIntentContentProviders(intent, 0));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            resolveInfos.addAll(
+                    mPackageManager.queryBroadcastReceiversAsUser(
+                            intent, /* flags= */ 0, ActivityManager.getCurrentUser()));
+            resolveInfos.addAll(
+                    mPackageManager.queryIntentActivitiesAsUser(
+                            intent, /* flags= */ 0, ActivityManager.getCurrentUser()));
+            resolveInfos.addAll(
+                    mPackageManager.queryIntentServicesAsUser(
+                            intent, /* flags= */ 0, ActivityManager.getCurrentUser()));
+            resolveInfos.addAll(
+                    mPackageManager.queryIntentContentProvidersAsUser(
+                            intent, /* flags= */ 0, ActivityManager.getCurrentUser()));
+        } else {
+            resolveInfos.addAll(mPackageManager.queryBroadcastReceivers(intent, 0));
+            resolveInfos.addAll(mPackageManager.queryIntentActivities(intent, 0));
+            resolveInfos.addAll(mPackageManager.queryIntentServices(intent, 0));
+            resolveInfos.addAll(mPackageManager.queryIntentContentProviders(intent, 0));
+        }
 
         // Now actually check which of the resolved packages have carrier privileges.
         mPrivilegedPackageInfoLock.readLock().lock();
@@ -1028,8 +1077,15 @@
 
     @NonNull
     private Pair<String, Integer> getCarrierService(@NonNull Set<String> simPrivilegedPackages) {
-        List<ResolveInfo> carrierServiceResolveInfos = mPackageManager.queryIntentServices(
-                new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), /* flags= */ 0);
+        List<ResolveInfo> carrierServiceResolveInfos =
+                mFeatureFlags.supportCarrierServicesForHsum()
+                        ? mPackageManager.queryIntentServicesAsUser(
+                                new Intent(CarrierService.CARRIER_SERVICE_INTERFACE),
+                                /* flags= */ 0,
+                                ActivityManager.getCurrentUser())
+                        : mPackageManager.queryIntentServices(
+                                new Intent(CarrierService.CARRIER_SERVICE_INTERFACE),
+                                /* flags= */ 0);
         String carrierServicePackageName = null;
         for (ResolveInfo resolveInfo : carrierServiceResolveInfos) {
             String packageName = getPackageName(resolveInfo);
@@ -1047,4 +1103,16 @@
                 ? new Pair<>(null, Process.INVALID_UID)
                 : new Pair<>(carrierServicePackageName, getPackageUid(carrierServicePackageName));
     }
+
+    private @PackageManager.EnabledState int getApplicationEnabledSetting(
+            @NonNull String packageName) {
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            return mContext.createContextAsUser(
+                            UserHandle.of(ActivityManager.getCurrentUser()), /* flags= */ 0)
+                    .getPackageManager()
+                    .getApplicationEnabledSetting(packageName);
+        } else {
+            return mPackageManager.getApplicationEnabledSetting(packageName);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/CarrierResolver.java b/src/java/com/android/internal/telephony/CarrierResolver.java
index 8a9b3e3..829bf6c 100644
--- a/src/java/com/android/internal/telephony/CarrierResolver.java
+++ b/src/java/com/android/internal/telephony/CarrierResolver.java
@@ -30,6 +30,7 @@
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
+import android.os.UserHandle;
 import android.provider.Telephony;
 import android.service.carrier.CarrierIdentifier;
 import android.telephony.CarrierConfigManager;
@@ -41,6 +42,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.CarrierIdMatchStats;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
@@ -112,6 +114,9 @@
     private final LocalLog mCarrierIdLocalLog = new LocalLog(16);
     private final TelephonyManager mTelephonyMgr;
 
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
     private final ContentObserver mContentObserver = new ContentObserver(this) {
         @Override
         public void onChange(boolean selfChange, Uri uri) {
@@ -172,9 +177,10 @@
         }
     };
 
-    public CarrierResolver(Phone phone) {
+    public CarrierResolver(Phone phone, @NonNull FeatureFlags flags) {
         logd("Creating CarrierResolver[" + phone.getPhoneId() + "]");
         mContext = phone.getContext();
+        mFeatureFlags = flags;
         mPhone = phone;
         mTelephonyMgr = TelephonyManager.from(mContext);
 
@@ -500,7 +506,11 @@
             intent.putExtra(TelephonyManager.EXTRA_SPECIFIC_CARRIER_ID, mSpecificCarrierId);
             intent.putExtra(TelephonyManager.EXTRA_SPECIFIC_CARRIER_NAME, mSpecificCarrierName);
             intent.putExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, mPhone.getSubId());
-            mContext.sendBroadcast(intent);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            } else {
+                mContext.sendBroadcast(intent);
+            }
 
             // notify content observers for specific carrier id change event.
             ContentValues cv = new ContentValues();
@@ -535,7 +545,11 @@
             intent.putExtra(TelephonyManager.EXTRA_CARRIER_ID, mCarrierId);
             intent.putExtra(TelephonyManager.EXTRA_CARRIER_NAME, mCarrierName);
             intent.putExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, mPhone.getSubId());
-            mContext.sendBroadcast(intent);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            } else {
+                mContext.sendBroadcast(intent);
+            }
 
             // notify content observers for carrier id change event
             ContentValues cv = new ContentValues();
diff --git a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
index 960d794..55baecc 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
@@ -45,6 +45,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.io.FileDescriptor;
@@ -171,7 +172,7 @@
                 context, mHandler.getLooper(), UserHandle.ALL);
         try {
             Context contextAsUser = mContext.createPackageContextAsUser(mContext.getPackageName(),
-                0, UserHandle.SYSTEM);
+                0, Flags.supportCarrierServicesForHsum() ? UserHandle.CURRENT : UserHandle.SYSTEM);
             contextAsUser.registerReceiver(mUserUnlockedReceiver,
                 new IntentFilter(Intent.ACTION_USER_UNLOCKED), null /* broadcastPermission */,
                 mHandler);
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index 6b99b56..d47c2c4 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -116,7 +116,8 @@
         mTelephonyManager = mPhone.getContext().getSystemService(
                 TelephonyManager.class).createForSubscriptionId(mPhone.getSubId());
         CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
-        ccm.registerCarrierConfigChangeListener(
+        if (ccm != null) {
+            ccm.registerCarrierConfigChangeListener(
                 mPhone.getContext().getMainExecutor(),
                 (slotIndex, subId, carrierId, specificCarrierId) -> {
                     if (slotIndex != mPhone.getPhoneId()) return;
@@ -143,6 +144,7 @@
                     }
                     handleConfigChanges();
                 });
+        }
 
         // Listen for subscriber changes
         SubscriptionManager.from(mPhone.getContext()).addOnSubscriptionsChangedListener(
diff --git a/src/java/com/android/internal/telephony/CarrierSignalAgent.java b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
index 3250cef..2abe643 100644
--- a/src/java/com/android/internal/telephony/CarrierSignalAgent.java
+++ b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
@@ -130,13 +130,15 @@
         CarrierConfigManager carrierConfigManager = mPhone.getContext().getSystemService(
                 CarrierConfigManager.class);
         loadCarrierConfig();
-        carrierConfigManager.registerCarrierConfigChangeListener(
-                mPhone.getContext().getMainExecutor(),
-                (slotIndex, subId, carrierId, specificCarrierId) -> {
-                    if (slotIndex == mPhone.getPhoneId()) {
-                        loadCarrierConfig();
-                    }
-                });
+        if (carrierConfigManager != null) {
+            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);
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index 82b4c2b8e..7a1c8fe 100644
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -692,8 +692,18 @@
                     && mEmergencyNumberInfo.getEmergencyCallRouting()
                         != EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY) {
                 int eccCategory = dialArgs.intentExtras.getInt(
-                        PhoneConstants.EXTRA_EMERGENCY_SERVICE_CATEGORY,
-                        mEmergencyNumberInfo.getEmergencyServiceCategoryBitmask());
+                    PhoneConstants.EXTRA_EMERGENCY_SERVICE_CATEGORY,
+                    mEmergencyNumberInfo.getEmergencyServiceCategoryBitmask());
+                // According to 3gpp 23.167 section 7.1.2, when CS domain is selected,
+                // emergency routing is performed only if the emergency category is provided.
+                if (this instanceof GsmCdmaConnection
+                        && dialArgs.intentExtras.getInt(
+                                PhoneConstants.EXTRA_EMERGENCY_SERVICE_CATEGORY,
+                                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED)
+                                == EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED) {
+                    Rlog.d(TAG, "setEmergencyCallInfo: specific eccCategory is required");
+                    return;
+                }
                 Rlog.d(TAG, "setEmergencyCallInfo: enforce emergency routing eccCategory="
                         + eccCategory);
                 List<String> emergencyUrns = dialArgs.intentExtras.getStringArrayList(
@@ -706,13 +716,32 @@
                         mEmergencyNumberInfo.getMnc(),
                         eccCategory,
                         emergencyUrns,
-                        mEmergencyNumberInfo.getEmergencyNumberSourceBitmask(),
+                        getEmergencyNumberSourceForEmergencyRouting(),
                         EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
             }
         }
     }
 
     /**
+     * Get the emergency number source to be used for emergency routing calls.
+     * This is not getting actual source, instead its forcing the source to
+     * EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING.
+     * Even when the source is EMERGENCY_NUMBER_SOURCE_DATABASE,
+     * to allow the category information delivered by the network to be used,
+     * the source is set to EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING.
+     */
+    private int getEmergencyNumberSourceForEmergencyRouting() {
+        int source = mEmergencyNumberInfo.getEmergencyNumberSourceBitmask();
+        Rlog.d(TAG, "getEmergencyNumberSourceForEmergencyRouting: source=" + source);
+
+        if (source != EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST) {
+            source = EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING;
+        }
+
+        return source;
+    }
+
+    /**
      * Set the non-detectable emergency number information.
      */
     public void setNonDetectableEmergencyCallInfo(int eccCategory,
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 9c7993b..da17f60 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -135,13 +135,9 @@
 
         int subId = sender.getSubId();
 
-        if (mFeatureFlags.notifyDataActivityChangedWithSlot()) {
-            int phoneId = sender.getPhoneId();
-            mTelephonyRegistryMgr.notifyDataActivityChanged(phoneId, subId,
-                    sender.getDataActivityState());
-        } else {
-            mTelephonyRegistryMgr.notifyDataActivityChanged(subId, sender.getDataActivityState());
-        }
+        int phoneId = sender.getPhoneId();
+        mTelephonyRegistryMgr.notifyDataActivityChanged(phoneId, subId,
+                sender.getDataActivityState());
     }
 
     @Override
@@ -324,6 +320,12 @@
         mTelephonyRegistryMgr.notifyCarrierRoamingNtnModeChanged(sender.getSubId(), active);
     }
 
+    @Override
+    public void notifyCarrierRoamingNtnEligibleStateChanged(Phone sender, boolean eligible) {
+        mTelephonyRegistryMgr.notifyCarrierRoamingNtnEligibleStateChanged(
+                sender.getSubId(), eligible);
+    }
+
     /**
      * 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 7bdf2ff..f572bf8 100644
--- a/src/java/com/android/internal/telephony/DeviceStateMonitor.java
+++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -102,6 +102,7 @@
     private final RegistrantList mPhysicalChannelConfigRegistrants = new RegistrantList();
     private final RegistrantList mSignalStrengthReportDecisionCallbackRegistrants =
             new RegistrantList();
+    private final RegistrantList mScreenStateRegistrants = new RegistrantList();
 
     private final NetworkRequest mWifiNetworkRequest =
             new NetworkRequest.Builder()
@@ -217,7 +218,13 @@
                 public void onDisplayAdded(int displayId) { }
 
                 @Override
-                public void onDisplayRemoved(int displayId) { }
+                public void onDisplayRemoved(int displayId) {
+                    /* adapter for virtual display removed */
+                    boolean screenOn = isScreenOn();
+                    Message msg = obtainMessage(EVENT_SCREEN_STATE_CHANGED);
+                    msg.arg1 = screenOn ? 1 : 0;
+                    sendMessage(msg);
+                }
 
                 @Override
                 public void onDisplayChanged(int displayId) {
@@ -509,6 +516,7 @@
     private void onUpdateDeviceState(int eventType, boolean state) {
         final boolean shouldEnableBarringInfoReportsOld = shouldEnableBarringInfoReports();
         final boolean wasHighPowerEnabled = shouldEnableHighPowerConsumptionIndications();
+        boolean wasScreenOn = mIsScreenOn;
         switch (eventType) {
             case EVENT_SCREEN_STATE_CHANGED:
                 if (mIsScreenOn == state) return;
@@ -618,6 +626,13 @@
                 mSignalStrengthReportDecisionCallbackRegistrants.notifyResult(false);
             }
         }
+
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+            // Determine whether to notify registrants about the screen on, off state change.
+            if (wasScreenOn != mIsScreenOn) {
+                mScreenStateRegistrants.notifyResult(mIsScreenOn);
+            }
+        }
     }
 
     /**
@@ -809,6 +824,37 @@
     }
 
     /**
+     * Unregister for Screen on, off notifications changed.
+     * @param h Handler to notify
+     */
+    public void unregisterForScreenStateChanged(Handler h) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            Rlog.d(TAG, "unregisterForScreenStateChanged: carrierRoamingNbIotNtn is disabled");
+            return;
+        }
+
+        mScreenStateRegistrants.remove(h);
+    }
+
+    /**
+     * Register a callback to receive the screen on or off.
+     * @param h Handler to notify
+     * @param what msg.what when the message is delivered
+     * @param obj AsyncResult.userObj when the message is delivered
+     */
+    public void registerForScreenStateChanged(Handler h, int what, Object obj) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            Rlog.d(TAG, "registerForScreenStateChanged: carrierRoamingNbIotNtn is disabled");
+            return;
+        }
+        Registrant r = new Registrant(h, what, obj);
+        mScreenStateRegistrants.add(r);
+
+        // Initial notification
+        mScreenStateRegistrants.notifyResult(mIsScreenOn);
+    }
+
+    /**
      * Register a callback to decide whether signal strength should be notified or not.
      * @param h Handler to notify
      */
diff --git a/src/java/com/android/internal/telephony/DisplayInfoController.java b/src/java/com/android/internal/telephony/DisplayInfoController.java
index e8a0566..d3f0264 100644
--- a/src/java/com/android/internal/telephony/DisplayInfoController.java
+++ b/src/java/com/android/internal/telephony/DisplayInfoController.java
@@ -87,23 +87,26 @@
         mLogTag = "DIC-" + mPhone.getPhoneId();
         mServiceState = mPhone.getServiceStateTracker().getServiceState();
         mConfigs = new PersistableBundle();
+        CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
         try {
-            mConfigs = mPhone.getContext().getSystemService(CarrierConfigManager.class)
-                    .getConfigForSubId(mPhone.getSubId(),
-                            CarrierConfigManager.KEY_SHOW_ROAMING_INDICATOR_BOOL);
+            if (ccm != null) {
+                mConfigs = ccm.getConfigForSubId(mPhone.getSubId(),
+                        CarrierConfigManager.KEY_SHOW_ROAMING_INDICATOR_BOOL);
+            }
         } catch (Exception ignored) {
             // CarrierConfigLoader might not be available yet.
             // Once it's available, configs will be updated through the listener.
         }
         mPhone.getServiceStateTracker()
                 .registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
-        mPhone.getContext().getSystemService(CarrierConfigManager.class)
-                .registerCarrierConfigChangeListener(Runnable::run,
-                        (slotIndex, subId, carrierId, specificCarrierId) -> {
-                            if (slotIndex == mPhone.getPhoneId()) {
-                                obtainMessage(EVENT_CARRIER_CONFIG_CHANGED).sendToTarget();
-                            }
-                        });
+        if (ccm != null) {
+            ccm.registerCarrierConfigChangeListener(Runnable::run,
+                    (slotIndex, subId, carrierId, specificCarrierId) -> {
+                        if (slotIndex == mPhone.getPhoneId()) {
+                            obtainMessage(EVENT_CARRIER_CONFIG_CHANGED).sendToTarget();
+                        }
+                    });
+        }
         mTelephonyDisplayInfo = new TelephonyDisplayInfo(
                 TelephonyManager.NETWORK_TYPE_UNKNOWN,
                 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index cc07047..d96663a 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -30,6 +30,7 @@
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ServiceState;
+import android.telephony.emergency.EmergencyNumber;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
@@ -186,7 +187,7 @@
 
         mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
         if (dialArgs.isEmergency) {
-            setEmergencyCallInfo(mOwner, null);
+            setEmergencyCallInfo(mOwner, dialArgs);
 
             // There was no emergency number info found for this call, however it is
             // still marked as an emergency number. This may happen if it was a redialed
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 93a0c2f..5d59327 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -390,8 +390,9 @@
                 .makeDataNetworkController(this, getLooper(), featureFlags);
 
         mCarrierResolver = mTelephonyComponentFactory.inject(CarrierResolver.class.getName())
-                .makeCarrierResolver(this);
-        mCarrierPrivilegesTracker = new CarrierPrivilegesTracker(Looper.myLooper(), this, context);
+                .makeCarrierResolver(this, featureFlags);
+        mCarrierPrivilegesTracker = new CarrierPrivilegesTracker(Looper.myLooper(), this, context,
+                featureFlags);
 
         getCarrierActionAgent().registerForCarrierAction(
                 CarrierActionAgent.CARRIER_ACTION_SET_METERED_APNS_ENABLED, this,
@@ -1119,6 +1120,7 @@
 
     @Override
     public GsmCdmaCall getForegroundCall() {
+        if (!hasCalling()) return null;
         return mCT.mForegroundCall;
     }
 
@@ -1396,6 +1398,8 @@
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isInCall() {
+        if (!hasCalling()) return false;
+
         GsmCdmaCall.State foregroundCallState = getForegroundCall().getState();
         GsmCdmaCall.State backgroundCallState = getBackgroundCall().getState();
         GsmCdmaCall.State ringingCallState = getRingingCall().getState();
@@ -5494,7 +5498,16 @@
     public void refreshSafetySources(String refreshBroadcastId) {
         if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()
                 || mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) {
-            mSafetySource.refresh(mContext, refreshBroadcastId);
+            post(() -> mSafetySource.refresh(mContext, refreshBroadcastId));
         }
     }
+
+    /**
+     * @return The sms dispatchers controller
+     */
+    @Override
+    @Nullable
+    public SmsDispatchersController getSmsDispatchersController() {
+        return mIccSmsInterfaceManager.mDispatchersController;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 7c1670c..3141406 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -96,7 +96,6 @@
     final protected Context mContext;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     final protected AppOpsManager mAppOps;
-    @VisibleForTesting
     public SmsDispatchersController mDispatchersController;
     private SmsPermissions mSmsPermissions;
 
@@ -407,15 +406,15 @@
      * A permissions check before passing to {@link IccSmsInterfaceManager#sendDataInternal}.
      * This method checks if the calling package or itself has the permission to send the data sms.
      */
-    public void sendDataWithSelfPermissions(String callingPackage, String callingAttributionTag,
-            String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, boolean isForVvm) {
+    public void sendDataWithSelfPermissions(String callingPackage, int callingUser,
+            String callingAttributionTag, String destAddr, String scAddr, int destPort, byte[] data,
+            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm) {
         if (!mSmsPermissions.checkCallingOrSelfCanSendSms(callingPackage, callingAttributionTag,
                 "Sending SMS message")) {
             returnUnspecifiedFailure(sentIntent);
             return;
         }
-        sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
+        sendDataInternal(callingPackage, callingUser, destAddr, scAddr, destPort, data, sentIntent,
                 deliveryIntent, isForVvm);
     }
 
@@ -425,9 +424,9 @@
      */
     @Deprecated
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
-            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
-        sendData(callingPackage, null, destAddr, scAddr, destPort, data,
+    public void sendData(String callingPackage, int callingUser, String destAddr, String scAddr,
+            int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        sendData(callingPackage, callingUser, null, destAddr, scAddr, destPort, data,
                 sentIntent, deliveryIntent);
     }
 
@@ -435,7 +434,7 @@
      * A permissions check before passing to {@link IccSmsInterfaceManager#sendDataInternal}.
      * This method checks only if the calling package has the permission to send the data sms.
      */
-    public void sendData(String callingPackage, String callingAttributionTag,
+    public void sendData(String callingPackage, int callingUser, String callingAttributionTag,
             String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
             PendingIntent deliveryIntent) {
         if (!mSmsPermissions.checkCallingCanSendSms(callingPackage, callingAttributionTag,
@@ -443,7 +442,7 @@
             returnUnspecifiedFailure(sentIntent);
             return;
         }
-        sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
+        sendDataInternal(callingPackage, callingUser, destAddr, scAddr, destPort, data, sentIntent,
                 deliveryIntent, false /* isForVvm */);
     }
 
@@ -474,17 +473,17 @@
      *  raw pdu of the status report is in the extended data ("pdu").
      */
 
-    private void sendDataInternal(String callingPackage, String destAddr, String scAddr,
-            int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent,
-            boolean isForVvm) {
+    private void sendDataInternal(String callingPackage, int callinUser, String destAddr,
+            String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
+            PendingIntent deliveryIntent, boolean isForVvm) {
         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
             log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort="
                     + destPort + " data='" + HexDump.toHexString(data)  + "' sentIntent="
                     + sentIntent + " deliveryIntent=" + deliveryIntent + " isForVVM=" + isForVvm);
         }
         destAddr = filterDestAddress(destAddr);
-        mDispatchersController.sendData(callingPackage, destAddr, scAddr, destPort, data,
-                sentIntent, deliveryIntent, isForVvm);
+        mDispatchersController.sendData(callingPackage, callinUser, destAddr, scAddr,
+                destPort, data, sentIntent, deliveryIntent, isForVvm);
     }
 
     /**
@@ -492,12 +491,13 @@
      * This method checks only if the calling package has the permission to send the sms.
      * Note: SEND_SMS permission should be checked by the caller of this method
      */
-    public void sendText(String callingPackage, String destAddr, String scAddr,
+    public void sendText(String callingPackage, int callingUser, String destAddr, String scAddr,
             String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
             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 */,
+        sendTextInternal(callingPackage, callingUser, destAddr, scAddr, text, sentIntent,
+                deliveryIntent, persistMessageForNonDefaultSmsApp,
+                SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, false /* expectMore */,
+                SMS_MESSAGE_PERIOD_NOT_SPECIFIED, false /* isForVvm */,
                 messageId, skipShortCodeCheck);
     }
 
@@ -505,27 +505,29 @@
      * A permissions check before passing to {@link IccSmsInterfaceManager#sendTextInternal}.
      * This method checks if the calling package or itself has the permission to send the sms.
      */
-    public void sendTextWithSelfPermissions(String callingPackage, String callingAttributeTag,
-            String destAddr, String scAddr, String text, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, boolean persistMessage, boolean isForVvm) {
+    public void sendTextWithSelfPermissions(String callingPackage, int callingUser,
+            String callingAttributeTag, String destAddr, String scAddr, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage,
+            boolean isForVvm) {
         if (!mSmsPermissions.checkCallingOrSelfCanSendSms(callingPackage, callingAttributeTag,
                 "Sending SMS message")) {
             returnUnspecifiedFailure(sentIntent);
             return;
         }
-        sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
-                persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, false /* expectMore */,
-                SMS_MESSAGE_PERIOD_NOT_SPECIFIED, isForVvm, 0L /* messageId */);
+        sendTextInternal(callingPackage, callingUser, destAddr, scAddr, text, sentIntent,
+                deliveryIntent, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
+                false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED,
+                isForVvm, 0L /* messageId */);
     }
 
 
-    private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
-            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+    private void sendTextInternal(String callingPackage, int callingUser, 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);
+        sendTextInternal(callingPackage, callingUser, destAddr, scAddr, text, sentIntent,
+                deliveryIntent, persistMessageForNonDefaultSmsApp, priority, expectMore,
+                validityPeriod, isForVvm, messageId, false);
     }
 
     /**
@@ -577,8 +579,8 @@
      * @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,
+    private void sendTextInternal(String callingPackage, int callingUser, String destAddr,
+            String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
             boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore,
             int validityPeriod, boolean isForVvm, long messageId, boolean skipShortCodeCheck) {
         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
@@ -591,7 +593,7 @@
         notifyIfOutgoingEmergencySms(destAddr);
         destAddr = filterDestAddress(destAddr);
         mDispatchersController.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
-                null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp,
+                null/*messageUri*/, callingPackage, callingUser, persistMessageForNonDefaultSmsApp,
                 priority, expectMore, validityPeriod, isForVvm, messageId, skipShortCodeCheck);
     }
 
@@ -641,18 +643,19 @@
      *  Any Other values including negative considered as Invalid Validity Period of the message.
      */
 
-    public void sendTextWithOptions(String callingPackage, String callingAttributionTag,
-            String destAddr, String scAddr, String text, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp, int priority,
+    public void sendTextWithOptions(String callingPackage, int callingUser,
+            String callingAttributionTag, String destAddr, String scAddr, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean persistMessageForNonDefaultSmsApp, int priority,
             boolean expectMore, int validityPeriod) {
         if (!mSmsPermissions.checkCallingCanSendText(persistMessageForNonDefaultSmsApp,
                     callingPackage, callingAttributionTag, "Sending SMS message")) {
             returnUnspecifiedFailure(sentIntent);
             return;
         }
-        sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
-                persistMessageForNonDefaultSmsApp, priority, expectMore, validityPeriod,
-                false /* isForVvm */, 0L /* messageId */);
+        sendTextInternal(callingPackage, callingUser, destAddr, scAddr, text, sentIntent,
+                deliveryIntent, persistMessageForNonDefaultSmsApp, priority, expectMore,
+                validityPeriod, false /* isForVvm */, 0L /* messageId */);
     }
 
     /**
@@ -718,12 +721,12 @@
      *                 Used for logging and diagnostics purposes. The id may be 0.
      */
 
-    public void sendMultipartText(String callingPackage, String callingAttributionTag,
-            String destAddr, String scAddr, List<String> parts, List<PendingIntent> sentIntents,
-            List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp,
-            long messageId) {
-        sendMultipartTextWithOptions(callingPackage, callingAttributionTag, destAddr, scAddr, parts,
-                sentIntents, deliveryIntents, persistMessageForNonDefaultSmsApp,
+    public void sendMultipartText(String callingPackage, int callingUser,
+            String callingAttributionTag, String destAddr, String scAddr, List<String> parts,
+            List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
+            boolean persistMessageForNonDefaultSmsApp, long messageId) {
+        sendMultipartTextWithOptions(callingPackage, callingUser, callingAttributionTag, destAddr,
+                scAddr, parts, sentIntents, deliveryIntents, persistMessageForNonDefaultSmsApp,
                 SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, false /* expectMore */,
                 SMS_MESSAGE_PERIOD_NOT_SPECIFIED,
                 messageId);
@@ -778,10 +781,11 @@
      *                 Used for logging and diagnostics purposes. The id may be 0.
      */
 
-    public void sendMultipartTextWithOptions(String callingPackage, String callingAttributionTag,
-            String destAddr, String scAddr, List<String> parts, List<PendingIntent> sentIntents,
-            List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp,
-            int priority, boolean expectMore, int validityPeriod, long messageId) {
+    public void sendMultipartTextWithOptions(String callingPackage, int callingUser,
+            String callingAttributionTag, String destAddr, String scAddr, List<String> parts,
+            List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
+            boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore,
+            int validityPeriod, long messageId) {
         if (!mSmsPermissions.checkCallingCanSendText(persistMessageForNonDefaultSmsApp,
                 callingPackage, callingAttributionTag, "Sending SMS message")) {
             returnUnspecifiedFailure(sentIntents);
@@ -821,7 +825,7 @@
                 }
 
                 mDispatchersController.sendText(destAddr, scAddr, singlePart, singleSentIntent,
-                        singleDeliveryIntent, null /* messageUri */, callingPackage,
+                        singleDeliveryIntent, null /* messageUri */, callingPackage, callingUser,
                         persistMessageForNonDefaultSmsApp, priority, expectMore, validityPeriod,
                         false /* isForVvm */, messageId);
             }
@@ -829,12 +833,12 @@
         }
 
         mDispatchersController.sendMultipartText(destAddr,
-                                      scAddr,
-                                      (ArrayList<String>) parts,
-                                      (ArrayList<PendingIntent>) sentIntents,
-                                      (ArrayList<PendingIntent>) deliveryIntents,
-                                      null, callingPackage, persistMessageForNonDefaultSmsApp,
-                                          priority, expectMore, validityPeriod, messageId);
+                scAddr,
+                (ArrayList<String>) parts,
+                (ArrayList<PendingIntent>) sentIntents,
+                (ArrayList<PendingIntent>) deliveryIntents,
+                null, callingPackage, callingUser, persistMessageForNonDefaultSmsApp,
+                priority, expectMore, validityPeriod, messageId);
 
     }
 
@@ -1292,12 +1296,13 @@
      */
     @Deprecated
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public void sendStoredText(String callingPkg, Uri messageUri, String scAddress,
+    public void sendStoredText(String callingPkg, int callingUser, Uri messageUri, String scAddress,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
-        sendStoredText(callingPkg, null, messageUri, scAddress, sentIntent, deliveryIntent);
+        sendStoredText(callingPkg, callingUser, null, messageUri,
+                scAddress, sentIntent, deliveryIntent);
     }
 
-    public void sendStoredText(String callingPkg, String callingAttributionTag,
+    public void sendStoredText(String callingPkg, int callingUser, String callingAttributionTag,
             Uri messageUri, String scAddress, PendingIntent sentIntent,
             PendingIntent deliveryIntent) {
         if (!mSmsPermissions.checkCallingCanSendSms(callingPkg, callingAttributionTag,
@@ -1324,7 +1329,7 @@
         notifyIfOutgoingEmergencySms(textAndAddress[1]);
         textAndAddress[1] = filterDestAddress(textAndAddress[1]);
         mDispatchersController.sendText(textAndAddress[1], scAddress, textAndAddress[0],
-                sentIntent, deliveryIntent, messageUri, callingPkg,
+                sentIntent, deliveryIntent, messageUri, callingPkg, callingUser,
                 true /* persistMessageForNonDefaultSmsApp */, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
                 false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, false /* isForVvm */,
                 0L /* messageId */);
@@ -1336,13 +1341,14 @@
      */
     @Deprecated
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public void sendStoredMultipartText(String callingPkg, Uri messageUri, String scAddress,
-            List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
-        sendStoredMultipartText(callingPkg, null, messageUri, scAddress, sentIntents,
-                deliveryIntents);
+    public void sendStoredMultipartText(String callingPkg, int callingUser,
+            Uri messageUri, String scAddress, List<PendingIntent> sentIntents,
+            List<PendingIntent> deliveryIntents) {
+        sendStoredMultipartText(callingPkg, callingUser, null,
+                messageUri, scAddress, sentIntents, deliveryIntents);
     }
 
-    public void sendStoredMultipartText(String callingPkg,
+    public void sendStoredMultipartText(String callingPkg, int callingUser,
             String callingAttributionTag, Uri messageUri, String scAddress,
             List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
         if (!mSmsPermissions.checkCallingCanSendSms(callingPkg, callingAttributionTag,
@@ -1395,7 +1401,7 @@
 
                 mDispatchersController.sendText(textAndAddress[1], scAddress, singlePart,
                         singleSentIntent, singleDeliveryIntent, messageUri, callingPkg,
-                        true  /* persistMessageForNonDefaultSmsApp */,
+                        callingUser, true  /* persistMessageForNonDefaultSmsApp */,
                         SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
                         false /* expectMore */, SMS_MESSAGE_PERIOD_NOT_SPECIFIED,
                         false /* isForVvm */, 0L /* messageId */);
@@ -1410,7 +1416,7 @@
                 (ArrayList<PendingIntent>) sentIntents,
                 (ArrayList<PendingIntent>) deliveryIntents,
                 messageUri,
-                callingPkg,
+                callingPkg, callingUser,
                 true  /* persistMessageForNonDefaultSmsApp */,
                 SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
                 false /* expectMore */,
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index 1a6bf2b..c94480e 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -202,9 +202,9 @@
                         tracker.onSent(mContext);
                         mTrackers.remove(token);
                         mPhone.notifySmsSent(tracker.mDestAddress);
-                        mSmsDispatchersController.notifySmsSentToEmergencyStateTracker(
+                        mSmsDispatchersController.notifySmsSent(
                                 tracker.mDestAddress, tracker.mMessageId, true,
-                                tracker.isSinglePartOrLastPart());
+                                tracker.isSinglePartOrLastPart(), true /*success*/);
                         break;
                     case ImsSmsImplBase.SEND_STATUS_ERROR:
                         tracker.onFailed(mContext, reason, networkReasonCode);
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 49d1adc..ca03f5d 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -30,6 +30,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
@@ -73,6 +74,8 @@
 import com.android.internal.telephony.SmsConstants.MessageClass;
 import com.android.internal.telephony.analytics.TelephonyAnalytics;
 import com.android.internal.telephony.analytics.TelephonyAnalytics.SmsMmsAnalytics;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.satellite.metrics.CarrierRoamingSatelliteSessionStats;
 import com.android.internal.telephony.util.NotificationChannelController;
@@ -284,6 +287,8 @@
 
     private List<SmsFilter> mSmsFilters;
 
+    protected final @NonNull FeatureFlags mFeatureFlags;
+
     /**
      * Create a new SMS broadcast helper.
      * @param name the class name for logging
@@ -291,18 +296,19 @@
      * @param storageMonitor the SmsStorageMonitor to check for storage availability
      */
     protected InboundSmsHandler(String name, Context context, SmsStorageMonitor storageMonitor,
-            Phone phone, Looper looper) {
+            Phone phone, Looper looper, FeatureFlags featureFlags) {
         super(name, looper);
 
+        mFeatureFlags = featureFlags;
         mContext = context;
         mStorageMonitor = storageMonitor;
         mPhone = phone;
         mResolver = context.getContentResolver();
-        mWapPush = new WapPushOverSms(context);
+        mWapPush = new WapPushOverSms(context, mFeatureFlags);
 
-        boolean smsCapable = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_sms_capable);
-        mSmsReceiveDisabled = !TelephonyManager.from(mContext).getSmsReceiveCapableForPhone(
+        TelephonyManager telephonyManager = TelephonyManager.from(mContext);
+        boolean smsCapable = telephonyManager.isDeviceSmsCapable();
+        mSmsReceiveDisabled = !telephonyManager.getSmsReceiveCapableForPhone(
                 mPhone.getPhoneId(), smsCapable);
 
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -801,7 +807,12 @@
             Intent intent = new Intent(Intents.SMS_REJECTED_ACTION);
             intent.putExtra("result", result);
             intent.putExtra("subId", mPhone.getSubId());
-            mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                        android.Manifest.permission.RECEIVE_SMS);
+            } else {
+                mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
+            }
         }
         acknowledgeLastIncomingSms(success, result, response);
     }
@@ -1064,7 +1075,7 @@
 
         SmsBroadcastReceiver resultReceiver = tracker.getSmsBroadcastReceiver(this);
 
-        if (!mUserManager.isUserUnlocked()) {
+        if (!isMainUserUnlocked()) {
             log("processMessagePart: !isUserUnlocked; calling processMessagePartWithUserLocked. "
                     + "Port: " + destPort, tracker.getMessageId());
             return processMessagePartWithUserLocked(
@@ -1182,6 +1193,15 @@
         return false;
     }
 
+    private boolean isMainUserUnlocked() {
+        UserHandle mainUser = mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser() ?
+                mUserManager.getMainUser() : null;
+        if (mainUser != null) {
+            return mUserManager.isUserUnlocked(mainUser);
+        }
+        return mUserManager.isUserUnlocked();
+    }
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private void showNewMessageNotification() {
         // Do not show the notification on non-FBE devices.
@@ -1205,8 +1225,15 @@
                 .setChannelId(NotificationChannelController.CHANNEL_ID_SMS);
         NotificationManager mNotificationManager =
             (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        mNotificationManager.notify(
-                NOTIFICATION_TAG, NOTIFICATION_ID_NEW_MESSAGE, mBuilder.build());
+        UserHandle mainUser = mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser() ?
+                mUserManager.getMainUser() : null;
+        if (mainUser != null) {
+            mNotificationManager.notifyAsUser(
+                    NOTIFICATION_TAG, NOTIFICATION_ID_NEW_MESSAGE, mBuilder.build(), mainUser);
+        } else {
+            mNotificationManager.notify(
+                    NOTIFICATION_TAG, NOTIFICATION_ID_NEW_MESSAGE, mBuilder.build());
+        }
     }
 
     static void cancelNewMessageNotification(Context context) {
@@ -1324,6 +1351,7 @@
      * @param user user to deliver the intent to
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @SuppressLint("MissingPermission")
     public void dispatchIntent(Intent intent, String permission, String appOp,
             Bundle opts, SmsBroadcastReceiver resultReceiver, UserHandle user, int subId) {
         intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
@@ -1352,12 +1380,17 @@
             // Get a list of currently started users.
             int[] users = null;
             final List<UserHandle> userHandles = mUserManager.getUserHandles(false);
+            final UserHandle mainUser = mUserManager.getMainUser();
             final List<UserHandle> runningUserHandles = new ArrayList();
             for (UserHandle handle : userHandles) {
                 if (mUserManager.isUserRunning(handle)) {
                     runningUserHandles.add(handle);
                 } else {
-                    if (handle.equals(UserHandle.SYSTEM)) {
+                    if (mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser()
+                            && handle.equals(mainUser)) {
+                        logeWithLocalLog("dispatchIntent: MAIN user is not running",
+                                resultReceiver.mInboundSmsTracker.getMessageId());
+                    } else if (handle.equals(UserHandle.SYSTEM)) {
                         logeWithLocalLog("dispatchIntent: SYSTEM user is not running",
                                 resultReceiver.mInboundSmsTracker.getMessageId());
                     }
@@ -1375,7 +1408,7 @@
             // by user policy.
             for (int i = users.length - 1; i >= 0; i--) {
                 UserHandle targetUser = UserHandle.of(users[i]);
-                if (users[i] != UserHandle.SYSTEM.getIdentifier()) {
+                if (!isMainUser(users[i])) {
                     // Is the user not allowed to use SMS?
                     if (hasUserRestriction(UserManager.DISALLOW_SMS, targetUser)) {
                         continue;
@@ -1385,14 +1418,14 @@
                         continue;
                     }
                 }
-                // Only pass in the resultReceiver when the user SYSTEM is processed.
+                // Only pass in the resultReceiver when the MAIN user is processed.
                 try {
-                    if (users[i] == UserHandle.SYSTEM.getIdentifier()) {
+                    if (isMainUser(users[i])) {
                         resultReceiver.setWaitingForIntent(intent);
                     }
                     mContext.createPackageContextAsUser(mContext.getPackageName(), 0, targetUser)
                             .sendOrderedBroadcast(intent, Activity.RESULT_OK, permission, appOp,
-                                    users[i] == UserHandle.SYSTEM.getIdentifier()
+                                    isMainUser(users[i])
                                             ? resultReceiver : null, getHandler(),
                                     null /* initialData */, null /* initialExtras */, opts);
                 } catch (PackageManager.NameNotFoundException ignored) {
@@ -1416,6 +1449,15 @@
         return (sources != null && !sources.isEmpty());
     }
 
+    @SuppressLint("MissingPermission")
+    private  boolean isMainUser(int userId) {
+        if (mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser()) {
+            return userId == mUserManager.getMainUser().getIdentifier();
+        } else {
+            return userId == UserHandle.SYSTEM.getIdentifier();
+        }
+    }
+
     /**
      * Helper for {@link SmsBroadcastUndelivered} to delete an old message in the raw table.
      */
@@ -1511,7 +1553,11 @@
         }
 
         if (userHandle == null) {
-            userHandle = UserHandle.SYSTEM;
+            if (mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser()) {
+                userHandle = mUserManager.getMainUser();
+            } else {
+                userHandle = UserHandle.SYSTEM;
+            }
         }
         Bundle options = handleSmsWhitelisting(intent.getComponent(), isClass0);
         dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
@@ -1739,6 +1785,7 @@
             handleAction(intent, true);
         }
 
+        @SuppressLint("MissingPermission")
         private synchronized void handleAction(@NonNull Intent intent, boolean onReceive) {
             String action = intent.getAction();
             if (mWaitingForIntent == null || !mWaitingForIntent.getAction().equals(action)) {
@@ -1795,9 +1842,15 @@
                 String mimeType = intent.getType();
 
                 setWaitingForIntent(intent);
-                dispatchIntent(intent, WapPushOverSms.getPermissionForType(mimeType),
-                        WapPushOverSms.getAppOpsStringPermissionForIntent(mimeType), options, this,
-                        UserHandle.SYSTEM, subId);
+                if (mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser()) {
+                    dispatchIntent(intent, WapPushOverSms.getPermissionForType(mimeType),
+                            WapPushOverSms.getAppOpsStringPermissionForIntent(mimeType), options,
+                            this, mUserManager.getMainUser(), subId);
+                } else {
+                    dispatchIntent(intent, WapPushOverSms.getPermissionForType(mimeType),
+                            WapPushOverSms.getAppOpsStringPermissionForIntent(mimeType), options,
+                            this, UserHandle.SYSTEM, subId);
+                }
             } else {
                 // Now that the intents have been deleted we can clean up the PDU data.
                 if (!Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
@@ -2098,11 +2151,16 @@
         public void onReceive(Context context, Intent intent) {
             if (ACTION_OPEN_SMS_APP.equals(intent.getAction())) {
                 // do nothing if the user had not unlocked the device yet
+                // TODO(b/355049884): This is looking at sms package of the wrong user!
                 UserManager userManager =
                         (UserManager) context.getSystemService(Context.USER_SERVICE);
+                PackageManager pm = context.getPackageManager();
+                if (Flags.hsumPackageManager()) {
+                    pm = context.createContextAsUser(UserHandle.CURRENT, 0).getPackageManager();
+                }
                 if (userManager.isUserUnlocked()) {
-                    context.startActivity(context.getPackageManager().getLaunchIntentForPackage(
-                            Telephony.Sms.getDefaultSmsPackage(context)));
+                    context.startActivityAsUser(pm.getLaunchIntentForPackage(
+                            Telephony.Sms.getDefaultSmsPackage(context)), UserHandle.CURRENT);
                 }
             }
         }
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
index 0afe119..42ec8d3 100644
--- a/src/java/com/android/internal/telephony/LocaleTracker.java
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -566,7 +566,7 @@
             }
 
             if (mFeatureFlags.oemEnabledSatelliteFlag()) {
-                TelephonyCountryDetector.getInstance(mPhone.getContext())
+                TelephonyCountryDetector.getInstance(mPhone.getContext(), mFeatureFlags)
                         .onNetworkCountryCodeChanged(mPhone, countryIso);
             }
             Intent intent = new Intent(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
diff --git a/src/java/com/android/internal/telephony/MockModem.java b/src/java/com/android/internal/telephony/MockModem.java
index a20e748..1fcdc2a 100644
--- a/src/java/com/android/internal/telephony/MockModem.java
+++ b/src/java/com/android/internal/telephony/MockModem.java
@@ -24,11 +24,13 @@
 import static android.telephony.TelephonyManager.HAL_SERVICE_SIM;
 import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
 
+import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.os.IBinder;
+import android.os.UserHandle;
 
 import com.android.telephony.Rlog;
 
@@ -158,7 +160,8 @@
         intent.setAction(actionName + phoneId);
         intent.putExtra(PHONE_ID, phoneId);
 
-        status = mContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+        status = mContext.bindServiceAsUser(intent, serviceConnection, Context.BIND_AUTO_CREATE,
+                UserHandle.of(ActivityManager.getCurrentUser()));
         return status;
     }
 
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index a14ae89..3c35f1a 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -43,6 +43,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelUuid;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.telephony.CarrierConfigManager;
@@ -247,9 +248,11 @@
 
         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));
+        if (ccm != null) {
+            ccm.registerCarrierConfigChangeListener(this::post,
+                    (slotIndex, subId, carrierId, specificCarrierId) ->
+                            onCarrierConfigChanged(slotIndex, subId));
+        }
 
         mConvertedPsimSubId = getConvertedPsimSubscriptionId();
     }
@@ -640,7 +643,7 @@
         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
+        // If there's only one primary subscription active, we trigger mobile data
         // dialog if and only if there were multiple primary SIM cards and one is removed.
         // Otherwise, if user just inserted their first SIM, or there's one primary and one
         // opportunistic subscription active (activeSubInfos.size() > 1), we automatically
@@ -656,7 +659,19 @@
             if (hasCalling()) mSubscriptionManagerService.setDefaultVoiceSubId(subId);
             if (hasMessaging()) mSubscriptionManagerService.setDefaultSmsSubId(subId);
             if (!mSubscriptionManagerService.isEsimBootStrapProvisioningActivated()) {
-                sendDefaultSubConfirmedNotification(subId);
+                // Determines the appropriate notification type
+                // Preconditions:
+                // - There is only one active primary subscription.
+                // - The eSIM bootstrap is NOT activated.
+                // Behavior:
+                // - If the primary subscription is not deactivated OR the device is in single SIM
+                //   mode, send a notification to dismiss the SIM dialog.
+                // - Otherwise, send a notification to trigger the preferred SIM/data pick dialog.
+                @TelephonyManager.DefaultSubscriptionSelectType
+                int type = (change != PRIMARY_SUB_REMOVED || mActiveModemCount == 1)
+                        ? EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS
+                        : EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
+                sendDefaultSubConfirmedNotification(type, subId);
             }
             return;
         }
@@ -768,17 +783,21 @@
         }
     }
 
-    private void sendDefaultSubConfirmedNotification(int defaultSubId) {
+    private void sendDefaultSubConfirmedNotification(
+            @TelephonyManager.DefaultSubscriptionSelectType int type, int defaultSubId) {
         Intent intent = new Intent();
         intent.setAction(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED);
         intent.setClassName("com.android.settings",
                 "com.android.settings.sim.SimSelectNotification");
 
-        intent.putExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE,
-                EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS);
+        intent.putExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, type);
         intent.putExtra(EXTRA_SUBSCRIPTION_ID, defaultSubId);
 
-        mContext.sendBroadcast(intent);
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(intent);
+        }
     }
 
     private void sendSubChangeNotificationIfNeeded(int change, boolean dataSelected,
@@ -816,7 +835,11 @@
             if (simCombinationParams.mWarningType == EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA) {
                 intent.putExtra(EXTRA_SIM_COMBINATION_NAMES, simCombinationParams.mSimNames);
             }
-            mContext.sendBroadcast(intent);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            } else {
+                mContext.sendBroadcast(intent);
+            }
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
index 91cd4ec..cc2e94d 100644
--- a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
+++ b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
@@ -75,17 +75,19 @@
 
         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();
-                    }
-                });
+        if (ccm != null) {
+            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();
+                        }
+                    });
+        }
 
         PhoneConfigurationManager.registerForMultiSimConfigChange(
                 this, EVENT_BIND_NETWORK_SERVICE, null);
diff --git a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
index c09f8fb..9b2b847 100644
--- a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
+++ b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -33,6 +33,7 @@
 import android.os.Messenger;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.telephony.CellInfo;
 import android.telephony.LocationAccessPolicy;
 import android.telephony.NetworkScan;
@@ -567,8 +568,8 @@
         //   3. The live scan is not requested by mobile network setting menu
         private synchronized boolean interruptLiveScan(NetworkScanRequestInfo nsri) {
             if (mLiveRequestInfo != null && mPendingRequestInfo == null
-                    && nsri.mUid == Process.SYSTEM_UID
-                            && mLiveRequestInfo.mUid != Process.SYSTEM_UID) {
+                    && UserHandle.isSameApp(nsri.mUid, Process.SYSTEM_UID)
+                            && !UserHandle.isSameApp(mLiveRequestInfo.mUid, Process.SYSTEM_UID)) {
                 doInterruptScan(mLiveRequestInfo.mScanId);
                 mPendingRequestInfo = nsri;
                 notifyMessenger(mLiveRequestInfo, TelephonyScanManager.CALLBACK_SCAN_ERROR,
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index 67ca1e1..6a331cf 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -315,7 +315,9 @@
         filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
         mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
         CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
-        ccm.registerCarrierConfigChangeListener(Runnable::run, mCarrierConfigChangeListener);
+        if (ccm != null) {
+            ccm.registerCarrierConfigChangeListener(Runnable::run, mCarrierConfigChangeListener);
+        }
     }
 
     private void unRegisterForAllEvents() {
@@ -327,7 +329,7 @@
                 mDataNetworkControllerCallback);
         mPhone.getContext().unregisterReceiver(mIntentReceiver);
         CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
-        if (mCarrierConfigChangeListener != null) {
+        if (ccm != null && mCarrierConfigChangeListener != null) {
             ccm.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
         }
     }
@@ -1022,7 +1024,10 @@
                     if (rat == TelephonyManager.NETWORK_TYPE_NR
                             || (isLte(rat) && isNrConnected())) {
                         if (isNrAdvanced()) {
-                            transitionTo(mNrConnectedAdvancedState);
+                            // Move into idle state because mPhysicalLinkStatus indicated idle,
+                            // ignored any advance reason because unless mPhysicalLinkStatus changed
+                            // again, shouldn't move back to advance.
+                            log("Ignore NR advanced from cached PCC/RatchetedNrBands while idle");
                         } else if (isPhysicalLinkActive()) {
                             transitionWithTimerTo(mNrConnectedState);
                         } else {
@@ -1309,6 +1314,7 @@
             mRatchetedNrBands.addAll(nrBands);
         } else {
             if (mFeatureFlags.supportNrSaRrcIdle() && mDoesPccListIndicateIdle
+                    && anchorNrCellId != mLastAnchorNrCellId
                     && isUsingPhysicalChannelConfigForRrcDetection()
                     && !mPrimaryCellChangedWhileIdle
                     && !isNrAdvancedForPccFields(nrBandwidths, nrBands)) {
@@ -1347,11 +1353,13 @@
         if (secondaryRule != null) {
             int secondaryDuration = secondaryRule.getSecondaryTimer(mSecondaryTimerState);
             long durationMillis = secondaryDuration * 1000L;
-            if ((mSecondaryTimerExpireTimestamp - SystemClock.uptimeMillis()) > durationMillis) {
+            long now = SystemClock.uptimeMillis();
+            if ((mSecondaryTimerExpireTimestamp - now) > durationMillis) {
                 if (DBG) log("Due to PCI change, reduce the secondary timer to " + durationMillis);
                 removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
                 sendMessageDelayed(EVENT_SECONDARY_TIMER_EXPIRED, mSecondaryTimerState,
                         durationMillis);
+                mSecondaryTimerExpireTimestamp = now + durationMillis;
             }
         } else {
             loge("!! Secondary timer is active, but found no rule for " + mPrimaryTimerState);
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index aa62acb..69b19aa 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -281,9 +281,6 @@
     // Key used to read/write the ID for storing the call forwarding status
     public static final String CF_ID = "cf_id_key";
 
-    // Key used to read/write "disable DNS server check" pref (used for testing)
-    private static final String DNS_SERVER_CHECK_DISABLED_KEY = "dns_server_check_disabled_key";
-
     // 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;
 
@@ -334,7 +331,6 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public CommandsInterface mCi;
     protected int mVmCount = 0;
-    private boolean mDnsCheckDisabled;
     protected DataNetworkController mDataNetworkController;
     /* Used for dispatching signals to configured carrier apps */
     protected CarrierSignalAgent mCarrierSignalAgent;
@@ -486,6 +482,7 @@
     private static final String ALLOWED_NETWORK_TYPES_TEXT_POWER = "power";
     private static final String ALLOWED_NETWORK_TYPES_TEXT_CARRIER = "carrier";
     private static final String ALLOWED_NETWORK_TYPES_TEXT_ENABLE_2G = "enable_2g";
+    private static final String ALLOWED_NETWORK_TYPES_TEXT_TEST = "test";
     private static final int INVALID_ALLOWED_NETWORK_TYPES = -1;
     protected boolean mIsCarrierNrSupported = false;
     protected boolean mIsAllowedNetworkTypesLoadedFromDb = false;
@@ -615,7 +612,6 @@
         setUnitTestMode(unitTestMode);
 
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
-        mDnsCheckDisabled = sp.getBoolean(DNS_SERVER_CHECK_DISABLED_KEY, false);
         mCi.setOnCallRing(this, EVENT_CALL_RING, null);
 
         /* "Voice capable" means that this device supports circuit-switched
@@ -661,7 +657,7 @@
         // Initialize device storage and outgoing SMS usage monitors for SMSDispatchers.
         mTelephonyComponentFactory = telephonyComponentFactory;
         mSmsStorageMonitor = mTelephonyComponentFactory.inject(SmsStorageMonitor.class.getName())
-                .makeSmsStorageMonitor(this);
+                .makeSmsStorageMonitor(this, mFeatureFlags);
         mSmsUsageMonitor = mTelephonyComponentFactory.inject(SmsUsageMonitor.class.getName())
                 .makeSmsUsageMonitor(context);
         mUiccController = UiccController.getInstance();
@@ -902,6 +898,8 @@
                         Rlog.e(mLogTag, "Invalid Exception for usage setting " + ar.exception);
                         break; // technically extraneous, but good hygiene
                     }
+                } else {
+                    mUsageSettingFromModem = msg.arg1;
                 }
                 break;
             default:
@@ -984,26 +982,6 @@
     protected abstract void onUpdateIccAvailability();
 
     /**
-     * Disables the DNS check (i.e., allows "0.0.0.0").
-     * Useful for lab testing environment.
-     * @param b true disables the check, false enables.
-     */
-    public void disableDnsCheck(boolean b) {
-        mDnsCheckDisabled = b;
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
-        SharedPreferences.Editor editor = sp.edit();
-        editor.putBoolean(DNS_SERVER_CHECK_DISABLED_KEY, b);
-        editor.apply();
-    }
-
-    /**
-     * Returns true if the DNS check is currently disabled.
-     */
-    public boolean isDnsCheckDisabled() {
-        return mDnsCheckDisabled;
-    }
-
-    /**
      * Register for getting notifications for change in the Call State {@link Call.State}
      * This is called PreciseCallState because the call state is more precise than the
      * {@link PhoneConstants.State} which can be obtained using the {@link PhoneStateListener}
@@ -2512,6 +2490,8 @@
                 return TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER;
             case ALLOWED_NETWORK_TYPES_TEXT_ENABLE_2G:
                 return TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G;
+            case ALLOWED_NETWORK_TYPES_TEXT_TEST:
+                return TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_TEST;
             default:
                 return INVALID_ALLOWED_NETWORK_TYPES;
         }
@@ -2535,6 +2515,8 @@
                 return ALLOWED_NETWORK_TYPES_TEXT_CARRIER;
             case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
                 return ALLOWED_NETWORK_TYPES_TEXT_ENABLE_2G;
+            case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_TEST:
+                return ALLOWED_NETWORK_TYPES_TEXT_TEST;
             default:
                 throw new IllegalArgumentException(
                         "No DB name conversion available for allowed network type reason: " + reason
@@ -3199,7 +3181,11 @@
             Intent intent = new Intent(TelephonyIntents.SECRET_CODE_ACTION,
                     Uri.parse("android_secret_code://" + code));
             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            mContext.sendBroadcast(intent, null, options.toBundle());
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, options.toBundle());
+            } else {
+                mContext.sendBroadcast(intent, null, options.toBundle());
+            }
 
             // {@link TelephonyManager.ACTION_SECRET_CODE} will replace {@link
             // TelephonyIntents#SECRET_CODE_ACTION} in the next Android version. Before
@@ -3207,7 +3193,12 @@
             Intent secrectCodeIntent = new Intent(TelephonyManager.ACTION_SECRET_CODE,
                     Uri.parse("android_secret_code://" + code));
             secrectCodeIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            mContext.sendBroadcast(secrectCodeIntent, null, options.toBundle());
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(secrectCodeIntent, UserHandle.ALL, null,
+                        options.toBundle());
+            } else {
+                mContext.sendBroadcast(secrectCodeIntent, null, options.toBundle());
+            }
         }
     }
 
@@ -4495,7 +4486,8 @@
             mCi.getUsageSetting(obtainMessage(EVENT_GET_USAGE_SETTING_DONE));
             // If the modem value is already known, and the value has changed, proceed to update.
         } else if (mPreferredUsageSetting != mUsageSettingFromModem) {
-            mCi.setUsageSetting(obtainMessage(EVENT_SET_USAGE_SETTING_DONE),
+            mCi.setUsageSetting(obtainMessage(EVENT_SET_USAGE_SETTING_DONE,
+                        mPreferredUsageSetting, 0 /* unused */),
                     mPreferredUsageSetting);
         }
         return true;
@@ -4921,6 +4913,14 @@
     }
 
     /**
+     * @return The sms dispatchers controller
+     */
+    @Nullable
+    public SmsDispatchersController getSmsDispatchersController() {
+        return null;
+    }
+
+    /**
      * @return The data settings manager
      */
     @NonNull
@@ -5326,11 +5326,31 @@
         mNotifier.notifyCarrierRoamingNtnModeChanged(this, active);
     }
 
+    /**
+     * Notify external listeners that the device eligibility to connect to carrier roaming
+     * non-terrestrial network changed.
+     *
+     * @param eligible {@code true} when the device is eligible for satellite
+     * communication if all the following conditions are met:
+     * <ul>
+     * <li>Any subscription on the device supports P2P satellite messaging which is defined by
+     * {@link CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} </li>
+     * <li>{@link CarrierConfigManager#KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT} set to
+     * {@link CarrierConfigManager#CARRIER_ROAMING_NTN_CONNECT_MANUAL} </li>
+     * <li>The device is in {@link ServiceState#STATE_OUT_OF_SERVICE}, not connected to Wi-Fi,
+     * and the hysteresis timer defined by {@link CarrierConfigManager
+     * #KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT} is expired. </li>
+     * </ul>
+     */
+    public void notifyCarrierRoamingNtnEligibleStateChanged(boolean eligible) {
+        logd("notifyCarrierRoamingNtnEligibleStateChanged eligible:" + eligible);
+        mNotifier.notifyCarrierRoamingNtnEligibleStateChanged(this, eligible);
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("Phone: subId=" + getSubId());
         pw.println(" mPhoneId=" + mPhoneId);
         pw.println(" mCi=" + mCi);
-        pw.println(" mDnsCheckDisabled=" + mDnsCheckDisabled);
         pw.println(" mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing);
         pw.println(" mCallRingContinueToken=" + mCallRingContinueToken);
         pw.println(" mCallRingDelay=" + mCallRingDelay);
@@ -5345,7 +5365,6 @@
         pw.println(" mNotifier=" + mNotifier);
         pw.println(" mSimulatedRadioControl=" + mSimulatedRadioControl);
         pw.println(" mUnitTestMode=" + mUnitTestMode);
-        pw.println(" isDnsCheckDisabled()=" + isDnsCheckDisabled());
         pw.println(" getUnitTestMode()=" + getUnitTestMode());
         pw.println(" getState()=" + getState());
         pw.println(" getIccSerialNumber()=" + Rlog.pii(mLogTag, getIccSerialNumber()));
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index ffa5b69..dd96594 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -29,6 +29,7 @@
 import android.os.PowerManager;
 import android.os.RegistrantList;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.sysprop.TelephonyProperties;
 import android.telephony.PhoneCapability;
@@ -391,9 +392,12 @@
                             }
                             mSlotsSupportingSimultaneousCellularCalls.add(i);
                         }
-                        // Ensure the slots supporting cellular DSDA does not exceed the phone count
-                        if (mSlotsSupportingSimultaneousCellularCalls.size() > getPhoneCount()) {
-                            loge("Invalid size of DSDA slots. Disabling cellular DSDA.");
+                        // Ensure the number of slots supporting cellular DSDA is valid:
+                        if (mSlotsSupportingSimultaneousCellularCalls.size() > getPhoneCount() ||
+                                mSlotsSupportingSimultaneousCellularCalls.size() < 2) {
+                            loge("Invalid size of DSDA slots. Disabling cellular DSDA. Size of "
+                                    + "mSlotsSupportingSimultaneousCellularCalls=" +
+                                    mSlotsSupportingSimultaneousCellularCalls.size());
                             mSlotsSupportingSimultaneousCellularCalls.clear();
                         }
                     } else {
@@ -770,7 +774,11 @@
 
         Intent intent = new Intent(ACTION_MULTI_SIM_CONFIG_CHANGED);
         intent.putExtra(EXTRA_ACTIVE_SIM_SUPPORTED_COUNT, numOfActiveModems);
-        mContext.sendBroadcast(intent);
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(intent);
+        }
     }
     /**
      * This is invoked from shell commands during CTS testing only.
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index d9c5c9c..c077ca9 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -46,6 +46,7 @@
 import com.android.internal.telephony.data.CellularNetworkValidator;
 import com.android.internal.telephony.data.PhoneSwitcher;
 import com.android.internal.telephony.data.TelephonyNetworkFactory;
+import com.android.internal.telephony.data.TelephonyNetworkProvider;
 import com.android.internal.telephony.euicc.EuiccCardController;
 import com.android.internal.telephony.euicc.EuiccController;
 import com.android.internal.telephony.flags.FeatureFlags;
@@ -101,6 +102,7 @@
     static private SimultaneousCallingTracker sSimultaneousCallingTracker;
     static private PhoneSwitcher sPhoneSwitcher;
     static private TelephonyNetworkFactory[] sTelephonyNetworkFactories;
+    private static TelephonyNetworkProvider sTelephonyNetworkProvider;
     static private NotificationChannelController sNotificationChannelController;
     static private CellularNetworkValidator sCellularNetworkValidator;
 
@@ -205,7 +207,7 @@
 
                 // Instantiate UiccController so that all other classes can just
                 // call getInstance()
-                sUiccController = UiccController.make(context);
+                sUiccController = UiccController.make(context, featureFlags);
 
                 Rlog.i(LOG_TAG, "Creating SubscriptionManagerService");
                 sSubscriptionManagerService = new SubscriptionManagerService(context,
@@ -239,6 +241,12 @@
                 }
                 Rlog.i(LOG_TAG, "defaultSmsApplication: " + packageName);
 
+                if (sFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser()) {
+                    // Explicitly call this, even if the user has no default Sms application, to
+                    // ensure that the System apps have the appropriate permissions.
+                    SmsApplication.grantPermissionsToSystemApps(context);
+                }
+
                 // Set up monitor to watch for changes to SMS packages
                 SmsApplication.initSmsPackageMonitor(context);
 
@@ -279,9 +287,15 @@
 
                 sNotificationChannelController = new NotificationChannelController(context);
 
-                for (int i = 0; i < numPhones; i++) {
-                    sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
-                            Looper.myLooper(), sPhones[i], featureFlags);
+                if (featureFlags.supportNetworkProvider()) {
+                    // Create the TelephonyNetworkProvider instance, which is a singleton.
+                    sTelephonyNetworkProvider = new TelephonyNetworkProvider(Looper.myLooper(),
+                            context, featureFlags);
+                } else {
+                    for (int i = 0; i < numPhones; i++) {
+                        sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
+                                Looper.myLooper(), sPhones[i], featureFlags);
+                    }
                 }
             }
         }
@@ -306,7 +320,10 @@
 
             sPhones = copyOf(sPhones, activeModemCount);
             sCommandsInterfaces = copyOf(sCommandsInterfaces, activeModemCount);
-            sTelephonyNetworkFactories = copyOf(sTelephonyNetworkFactories, activeModemCount);
+
+            if (!sFeatureFlags.supportNetworkProvider()) {
+                sTelephonyNetworkFactories = copyOf(sTelephonyNetworkFactories, activeModemCount);
+            }
 
             int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context);
             for (int i = prevActiveModemCount; i < activeModemCount; i++) {
@@ -318,8 +335,11 @@
                         PackageManager.FEATURE_TELEPHONY_IMS)) {
                     sPhones[i].createImsPhone();
                 }
-                sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
-                        Looper.myLooper(), sPhones[i], sFeatureFlags);
+
+                if (!sFeatureFlags.supportNetworkProvider()) {
+                    sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
+                            Looper.myLooper(), sPhones[i], sFeatureFlags);
+                }
             }
         }
     }
@@ -387,6 +407,10 @@
         }
     }
 
+    public static TelephonyNetworkProvider getNetworkProvider() {
+        return sTelephonyNetworkProvider;
+    }
+
     /**
      * Get the network factory associated with a given phone ID.
      * @param phoneId the phone id
@@ -573,13 +597,22 @@
             pw.flush();
             pw.println("++++++++++++++++++++++++++++++++");
 
-            sTelephonyNetworkFactories[i].dump(fd, pw, args);
+            if (!sFeatureFlags.supportNetworkProvider()) {
+                sTelephonyNetworkFactories[i].dump(fd, pw, args);
+            }
 
             pw.flush();
             pw.decreaseIndent();
             pw.println("++++++++++++++++++++++++++++++++");
         }
 
+        pw.increaseIndent();
+        if (sFeatureFlags.supportNetworkProvider()) {
+            sTelephonyNetworkProvider.dump(fd, pw, args);
+        }
+        pw.decreaseIndent();
+        pw.println("++++++++++++++++++++++++++++++++");
+
         pw.println("UiccController:");
         pw.increaseIndent();
         try {
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index 9f459f5..3f388fb 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -156,4 +156,7 @@
 
     /** Notify carrier roaming non-terrestrial network mode changed. **/
     void notifyCarrierRoamingNtnModeChanged(Phone sender, boolean active);
+
+    /** Notify eligibility to connect to carrier roaming non-terrestrial network changed. */
+    void notifyCarrierRoamingNtnEligibleStateChanged(Phone sender, boolean eligible);
 }
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index 073e242..7ee3de2 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -785,9 +785,16 @@
      */
     @Nullable
     private String getCurrentPackageName() {
+        if (mFeatureFlags.hsumPackageManager()) {
+            PackageManager pm = mContext.createContextAsUser(Binder.getCallingUserHandle(), 0)
+                    .getPackageManager();
+            if (pm == null) return null;
+            String[] callingPackageNames = pm.getPackagesForUid(Binder.getCallingUid());
+            return (callingPackageNames == null) ? null : callingPackageNames[0];
+        }
         if (mPackageManager == null) return null;
-        String[] callingUids = mPackageManager.getPackagesForUid(Binder.getCallingUid());
-        return (callingUids == null) ? null : callingUids[0];
+        String[] callingPackageNames = mPackageManager.getPackagesForUid(Binder.getCallingUid());
+        return (callingPackageNames == null) ? null : callingPackageNames[0];
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index 01aed07..3b30d2f 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -28,6 +28,7 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
+import android.os.UserHandle;
 import android.telephony.RadioAccessFamily;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -364,7 +365,11 @@
                     logd("onStartRadioCapabilityResponse got exception=" + ar.exception);
                     mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement();
                     Intent intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_FAILED);
-                    mContext.sendBroadcast(intent);
+                    if (mFlags.hsumBroadcast()) {
+                        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+                    } else {
+                        mContext.sendBroadcast(intent);
+                    }
                     clearTransaction();
                     return;
                 }
@@ -611,7 +616,12 @@
         }
 
         // Broadcast that we're done
-        mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
+        if (mFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                    android.Manifest.permission.READ_PHONE_STATE);
+        } else {
+            mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
+        }
     }
 
     // Clear this transaction
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index beeaaa7..34ac832 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -1050,10 +1050,16 @@
                     }
                 } else {
                     mDisabledRadioServices.get(service).add(mPhoneId);
-                    mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN);
-                    riljLoge("getRadioServiceProxy: set " + serviceToString(service) + " for "
-                            + HIDL_SERVICE_NAME[mPhoneId] + " as disabled\n"
-                            + android.util.Log.getStackTraceString(new RuntimeException()));
+                    if (isRadioServiceSupported(service)) {
+                        mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN);
+                        riljLoge("getRadioServiceProxy: set " + serviceToString(service) + " for "
+                                + HIDL_SERVICE_NAME[mPhoneId] + " as disabled\n"
+                                + android.util.Log.getStackTraceString(new RuntimeException()));
+                    } else {
+                        mHalVersion.put(service, RADIO_HAL_VERSION_UNSUPPORTED);
+                        riljLog("getRadioServiceProxy: set " + serviceToString(service) + " for "
+                                + HIDL_SERVICE_NAME[mPhoneId] + " as disabled (unsupported)");
+                    }
                 }
             }
         } catch (RemoteException e) {
@@ -4241,7 +4247,7 @@
             riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
                     + " with data profiles : ");
             for (DataProfile profile : dps) {
-                riljLog(profile.toString());
+                riljLog(Objects.toString(profile, "DataProfile is null"));
             }
         }
 
@@ -4377,9 +4383,9 @@
             riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
                     + " params: " + carrierRestrictionRules);
         }
-
         radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "setAllowedCarriers", () -> {
-            simProxy.setAllowedCarriers(rr.mSerial, carrierRestrictionRules);
+            simProxy.setAllowedCarriers(rr.mSerial, carrierRestrictionRules,
+                    getHalVersion(HAL_SERVICE_SIM));
         });
     }
 
diff --git a/src/java/com/android/internal/telephony/RILUtils.java b/src/java/com/android/internal/telephony/RILUtils.java
index 8897db4..a81dbc8 100644
--- a/src/java/com/android/internal/telephony/RILUtils.java
+++ b/src/java/com/android/internal/telephony/RILUtils.java
@@ -1944,6 +1944,31 @@
     }
 
     /**
+     * Convert a list of CarrierIdentifiers into an array of CarrierInfo.aidl
+     *
+     * @param carriers List of CarrierIdentifiers
+     * @return The converted array of CarrierInfos.
+     */
+    public static android.hardware.radio.sim.CarrierInfo[] convertToHalCarrierInfoListAidl(
+            List<CarrierIdentifier> carriers) {
+        android.hardware.radio.sim.CarrierInfo[] result =
+                new android.hardware.radio.sim.CarrierInfo[carriers.size()];
+        for (int i = 0; i < carriers.size(); i++) {
+            CarrierIdentifier ci = carriers.get(i);
+            android.hardware.radio.sim.CarrierInfo carrierInfo =
+                    new android.hardware.radio.sim.CarrierInfo();
+            carrierInfo.mcc = convertNullToEmptyString(ci.getMcc());
+            carrierInfo.mnc = convertNullToEmptyString(ci.getMnc());
+            carrierInfo.spn = ci.getSpn();
+            carrierInfo.imsiPrefix = ci.getImsi();
+            carrierInfo.gid1 = ci.getGid1();
+            carrierInfo.gid2 = ci.getGid2();
+            result[i] = carrierInfo;
+        }
+        return result;
+    }
+
+    /**
      * Convert to Dial defined in radio/1.0/types.hal
      * @param address Address
      * @param clirMode CLIR mode
diff --git a/src/java/com/android/internal/telephony/RadioConfig.java b/src/java/com/android/internal/telephony/RadioConfig.java
index da20639..b4db14c 100644
--- a/src/java/com/android/internal/telephony/RadioConfig.java
+++ b/src/java/com/android/internal/telephony/RadioConfig.java
@@ -466,7 +466,7 @@
         RILRequest rr = obtainRequest(RIL_REQUEST_SET_PREFERRED_DATA_MODEM,
                 result, mDefaultWorkSource);
         if (DBG) {
-            logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+            logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + " " + modemId);
         }
         try {
             proxy.setPreferredDataModem(rr.mSerial, modemId);
diff --git a/src/java/com/android/internal/telephony/RadioSimProxy.java b/src/java/com/android/internal/telephony/RadioSimProxy.java
index 5265692..1c864fe 100644
--- a/src/java/com/android/internal/telephony/RadioSimProxy.java
+++ b/src/java/com/android/internal/telephony/RadioSimProxy.java
@@ -24,6 +24,8 @@
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
 import com.android.internal.telephony.uicc.SimPhonebookRecord;
 
+import java.util.Collections;
+
 /**
  * A holder for IRadioSim.
  * Use getAidl to get IRadioSim and call the AIDL implementations of the HAL APIs.
@@ -512,27 +514,44 @@
 
     /**
      * Call IRadioSim#setAllowedCarriers
-     * @param serial Serial number of request
+     *
+     * @param serial                  Serial number of request
      * @param carrierRestrictionRules Allowed carriers
-     * @throws RemoteException
      */
-    public void setAllowedCarriers(int serial, CarrierRestrictionRules carrierRestrictionRules)
-            throws RemoteException {
+    public void setAllowedCarriers(int serial, CarrierRestrictionRules carrierRestrictionRules,
+            HalVersion halversion) throws RemoteException {
         if (isEmpty()) return;
         if (isAidl()) {
-            // Prepare structure with allowed list, excluded list and priority
             android.hardware.radio.sim.CarrierRestrictions carrierRestrictions =
                     new android.hardware.radio.sim.CarrierRestrictions();
-            carrierRestrictions.allowedCarriers = RILUtils.convertToHalCarrierRestrictionListAidl(
-                    carrierRestrictionRules.getAllowedCarriers());
-            carrierRestrictions.excludedCarriers = RILUtils.convertToHalCarrierRestrictionListAidl(
-                    carrierRestrictionRules.getExcludedCarriers());
+            if (halversion.greaterOrEqual(RIL.RADIO_HAL_VERSION_2_2)) {
+                carrierRestrictions.allowedCarrierInfoList =
+                        RILUtils.convertToHalCarrierInfoListAidl(
+                                carrierRestrictionRules.getAllowedCarriers());
+                carrierRestrictions.excludedCarrierInfoList =
+                        RILUtils.convertToHalCarrierInfoListAidl(
+                                carrierRestrictionRules.getExcludedCarriers());
+                carrierRestrictions.allowedCarriers =
+                        RILUtils.convertToHalCarrierRestrictionListAidl(Collections.EMPTY_LIST);
+                carrierRestrictions.excludedCarriers =
+                        RILUtils.convertToHalCarrierRestrictionListAidl(Collections.EMPTY_LIST);
+            } else {
+                // Prepare structure with allowed list, excluded list and priority
+                carrierRestrictions.allowedCarriers =
+                        RILUtils.convertToHalCarrierRestrictionListAidl(
+                                carrierRestrictionRules.getAllowedCarriers());
+                carrierRestrictions.excludedCarriers =
+                        RILUtils.convertToHalCarrierRestrictionListAidl(
+                                carrierRestrictionRules.getExcludedCarriers());
+            }
             carrierRestrictions.allowedCarriersPrioritized =
                     (carrierRestrictionRules.getDefaultCarrierRestriction()
                             == CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED);
+            carrierRestrictions.status = carrierRestrictionRules.getCarrierRestrictionStatus();
             mSimProxy.setAllowedCarriers(serial, carrierRestrictions,
                     RILUtils.convertToHalSimLockMultiSimPolicyAidl(
                             carrierRestrictionRules.getMultiSimPolicy()));
+            Rlog.d(TAG, "RadioSimProxy setAllowedCarriers params = " + carrierRestrictions);
         } else {
             // Prepare structure with allowed list, excluded list and priority
             android.hardware.radio.V1_4.CarrierRestrictionsWithPriority carrierRestrictions =
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 8764e02..2f2b62a 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -52,7 +52,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
-import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -89,6 +88,7 @@
 import com.android.internal.telephony.analytics.TelephonyAnalytics;
 import com.android.internal.telephony.analytics.TelephonyAnalytics.SmsMmsAnalytics;
 import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.uicc.IccRecords;
@@ -255,8 +255,7 @@
         mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
                 Settings.Global.SMS_SHORT_CODE_RULE), false, mSettingsObserver);
 
-        mSmsCapable = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_sms_capable);
+        mSmsCapable = mTelephonyManager.isDeviceSmsCapable();
         mSmsSendDisabled = !mTelephonyManager.getSmsSendCapableForPhone(
                 mPhone.getPhoneId(), mSmsCapable);
         IntentFilter intentFilter = new IntentFilter();
@@ -1017,8 +1016,8 @@
      */
     protected void notifySmsSentFailedToEmergencyStateTracker(SmsTracker tracker,
             boolean isOverIms) {
-        mSmsDispatchersController.notifySmsSentFailedToEmergencyStateTracker(
-                tracker.mDestAddress, tracker.mMessageId, isOverIms);
+        mSmsDispatchersController.notifySmsSent(tracker.mDestAddress, tracker.mMessageId,
+                isOverIms, true /*isLastSmsPart*/, false /*success*/);
     }
 
     /**
@@ -1053,9 +1052,9 @@
             }
             tracker.onSent(mContext);
             mPhone.notifySmsSent(tracker.mDestAddress);
-            mSmsDispatchersController.notifySmsSentToEmergencyStateTracker(
+            mSmsDispatchersController.notifySmsSent(
                     tracker.mDestAddress, tracker.mMessageId, false,
-                    tracker.isSinglePartOrLastPart());
+                    tracker.isSinglePartOrLastPart(), true /*success*/);
 
             mPhone.getSmsStats().onOutgoingSms(
                     tracker.mImsRetry > 0 /* isOverIms */,
@@ -1398,15 +1397,16 @@
      *  raw pdu of the status report is in the extended data ("pdu").
      */
     @UnsupportedAppUsage
-    protected void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
-            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm) {
+    protected void sendData(String callingPackage, int callingUser, String destAddr, String scAddr,
+            int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean isForVvm) {
         int messageRef = nextMessageRef();
         SmsMessageBase.SubmitPduBase pdu = getSubmitPdu(
                 scAddr, destAddr, destPort, data, (deliveryIntent != null), messageRef);
         if (pdu != null) {
             HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
-            SmsTracker tracker = getSmsTracker(callingPackage, map, sentIntent, deliveryIntent,
-                    getFormat(), null /*messageUri*/, false /*expectMore*/,
+            SmsTracker tracker = getSmsTracker(callingPackage, callingUser, map, sentIntent,
+                    deliveryIntent, getFormat(), null /*messageUri*/, false /*expectMore*/,
                     null /*fullMessageText*/, false /*isText*/,
                     true /*persistMessage*/, isForVvm, 0L /* messageId */, messageRef);
 
@@ -1521,11 +1521,12 @@
      */
     public void sendText(String destAddr, String scAddr, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
-            String callingPkg, boolean persistMessage, int priority,
+            String callingPkg, int callingUser, boolean persistMessage, int priority,
             boolean expectMore, int validityPeriod, boolean isForVvm,
             long messageId) {
         sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri, callingPkg,
-                persistMessage, priority, expectMore, validityPeriod, isForVvm, messageId, false);
+                callingUser, persistMessage, priority, expectMore, validityPeriod, isForVvm,
+                messageId, false);
     }
 
     /**
@@ -1630,10 +1631,10 @@
      * @param skipShortCodeCheck Skip check for short code type destination address.
      */
     public void sendText(String destAddr, String scAddr, String text,
-                         PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
-                         String callingPkg, boolean persistMessage, int priority,
-                         boolean expectMore, int validityPeriod, boolean isForVvm,
-                         long messageId, boolean skipShortCodeCheck) {
+            PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
+            String callingPkg, int callingUser, boolean persistMessage, int priority,
+            boolean expectMore, int validityPeriod, boolean isForVvm,
+            long messageId, boolean skipShortCodeCheck) {
         Rlog.d(TAG, "sendText id: " + SmsController.formatCrossStackMessageId(messageId));
         int messageRef = nextMessageRef();
         SmsMessageBase.SubmitPduBase pdu = getSubmitPdu(
@@ -1641,8 +1642,8 @@
                 messageRef);
         if (pdu != null) {
             HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
-            SmsTracker tracker = getSmsTracker(callingPkg, map, sentIntent, deliveryIntent,
-                    getFormat(), messageUri, expectMore, text, true /*isText*/,
+            SmsTracker tracker = getSmsTracker(callingPkg, callingUser, map, sentIntent,
+                    deliveryIntent, getFormat(), messageUri, expectMore, text, true /*isText*/,
                     persistMessage, priority, validityPeriod, isForVvm, messageId, messageRef,
                     skipShortCodeCheck);
 
@@ -1825,8 +1826,8 @@
     public void sendMultipartText(String destAddr, String scAddr,
             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
             ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
-            boolean persistMessage, int priority, boolean expectMore, int validityPeriod,
-            long messageId) {
+            int callingUser, boolean persistMessage, int priority, boolean expectMore,
+            int validityPeriod, long messageId) {
         final String fullMessageText = getMultipartMessageText(parts);
         int refNumber = getNextConcatenatedRef() & 0x00FF;
         int encoding = SmsConstants.ENCODING_UNKNOWN;
@@ -1885,8 +1886,8 @@
             }
             int messageRef = nextMessageRef();
             trackers[i] =
-                getNewSubmitPduTracker(callingPkg, destAddr, scAddr, parts.get(i), smsHeader,
-                        encoding, sentIntent, deliveryIntent, (i == (msgCount - 1)),
+                getNewSubmitPduTracker(callingPkg, callingUser, destAddr, scAddr, parts.get(i),
+                        smsHeader, encoding, sentIntent, deliveryIntent, (i == (msgCount - 1)),
                         unsentPartCount, anyPartFailed, messageUri,
                         fullMessageText, priority, expectMore, validityPeriod, messageId,
                         messageRef);
@@ -1920,9 +1921,9 @@
     /**
      * Create a new SubmitPdu and return the SMS tracker.
      */
-    private SmsTracker getNewSubmitPduTracker(String callingPackage, String destinationAddress,
-            String scAddress, String message, SmsHeader smsHeader, int encoding,
-            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
+    private SmsTracker getNewSubmitPduTracker(String callingPackage, int callingUser,
+            String destinationAddress, String scAddress, String message, SmsHeader smsHeader,
+            int encoding, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
             AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
             String fullMessageText, int priority, boolean expectMore, int validityPeriod,
             long messageId, int messageRef) {
@@ -1951,11 +1952,11 @@
             if (submitPdu != null) {
                 HashMap map = getSmsTrackerMap(destinationAddress, scAddress,
                         message, submitPdu);
-                return getSmsTracker(callingPackage, map, sentIntent, deliveryIntent,
-                        getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader,
-                        (!lastPart || expectMore), fullMessageText, true /*isText*/,
-                        true /*persistMessage*/, priority, validityPeriod, false /* isForVvm */,
-                        messageId, messageRef, false);
+                return getSmsTracker(callingPackage, callingUser, map, sentIntent,
+                        deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
+                        smsHeader, (!lastPart || expectMore), fullMessageText,  /*isText*/
+                        true,  /*persistMessage*/ true, priority, validityPeriod,  /* isForVvm */
+                        false, messageId, messageRef, false);
             } else {
                 Rlog.e(TAG, "CdmaSMSDispatcher.getNewSubmitPduTracker(): getSubmitPdu() returned "
                         + "null " + SmsController.formatCrossStackMessageId(messageId));
@@ -1969,11 +1970,12 @@
                             smsHeader.languageShiftTable, validityPeriod, messageRef);
             if (pdu != null) {
                 HashMap map = getSmsTrackerMap(destinationAddress, scAddress, message, pdu);
-                return getSmsTracker(callingPackage, map, sentIntent,
-                        deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
-                        smsHeader, (!lastPart || expectMore), fullMessageText, true /*isText*/,
-                        false /*persistMessage*/, priority, validityPeriod, false /* isForVvm */,
-                        messageId, messageRef, false);
+                return getSmsTracker(callingPackage, callingUser, map,
+                        sentIntent, deliveryIntent, getFormat(), unsentPartCount, anyPartFailed,
+                        messageUri, smsHeader, (!lastPart || expectMore),
+                        fullMessageText,  /*isText*/
+                        true,  /*persistMessage*/ false, priority, validityPeriod,  /* isForVvm */
+                        false, messageId, messageRef, false);
             } else {
                 Rlog.e(TAG, "GsmSMSDispatcher.getNewSubmitPduTracker(): getSubmitPdu() returned "
                         + "null " + SmsController.formatCrossStackMessageId(messageId));
@@ -2812,16 +2814,20 @@
         }
     }
 
-    protected SmsTracker getSmsTracker(String callingPackage, HashMap<String, Object> data,
+    protected SmsTracker getSmsTracker(String callingPackage, int callingUser,
+            HashMap<String, Object> data,
             PendingIntent sentIntent, PendingIntent deliveryIntent, String format,
             AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
             SmsHeader smsHeader, boolean expectMore, String fullMessageText, boolean isText,
             boolean persistMessage, int priority, int validityPeriod, boolean isForVvm,
             long messageId, int messageRef, boolean skipShortCodeCheck) {
+        if (!Flags.smsMmsDeliverBroadcastsRedirectToMainUser()) {
+            callingUser = UserHandle.getUserHandleForUid(Binder.getCallingUid()).getIdentifier();
+        }
+
         // Get package info via packagemanager
-        UserHandle callingUser = UserHandle.getUserHandleForUid(Binder.getCallingUid());
-        final int userId = callingUser.getIdentifier();
-        PackageManager pm = mContext.createContextAsUser(callingUser, 0).getPackageManager();
+        PackageManager pm = mContext.createContextAsUser(UserHandle.of(callingUser), 0)
+                .getPackageManager();
         PackageInfo appInfo = null;
         try {
             appInfo = pm.getPackageInfo(callingPackage, PackageManager.GET_SIGNATURES);
@@ -2833,31 +2839,33 @@
         String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr"));
         return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format,
                 unsentPartCount, anyPartFailed, messageUri, smsHeader, expectMore,
-                fullMessageText, getSubId(), isText, persistMessage, userId, priority,
+                fullMessageText, getSubId(), isText, persistMessage, callingUser, priority,
                 validityPeriod, isForVvm, messageId, mPhone.getCarrierId(), messageRef,
                 skipShortCodeCheck);
     }
 
-    protected SmsTracker getSmsTracker(String callingPackage, HashMap<String, Object> data,
-            PendingIntent sentIntent, PendingIntent deliveryIntent, String format, Uri messageUri,
-            boolean expectMore, String fullMessageText, boolean isText, boolean persistMessage,
-            boolean isForVvm, long messageId, int messageRef) {
-        return getSmsTracker(callingPackage, data, sentIntent, deliveryIntent, format,
-                null/*unsentPartCount*/, null/*anyPartFailed*/, messageUri, null/*smsHeader*/,
-                expectMore, fullMessageText, isText, persistMessage,
-                SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, isForVvm,
-                messageId, messageRef, false);
+    protected SmsTracker getSmsTracker(String callingPackage, int callingUser,
+            HashMap<String, Object> data, PendingIntent sentIntent, PendingIntent deliveryIntent,
+            String format, Uri messageUri, boolean expectMore, String fullMessageText,
+            boolean isText, boolean persistMessage, boolean isForVvm,
+            long messageId, int messageRef) {
+        return getSmsTracker(callingPackage, callingUser , data, sentIntent, deliveryIntent,
+                format, /*unsentPartCount*/ null, /*anyPartFailed*/ null, messageUri, /*smsHeader*/
+                null, expectMore, fullMessageText, isText,
+                persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
+                SMS_MESSAGE_PERIOD_NOT_SPECIFIED,
+                isForVvm, messageId, messageRef, false);
     }
 
-    protected SmsTracker getSmsTracker(String callingPackage, HashMap<String, Object> data,
-            PendingIntent sentIntent, PendingIntent deliveryIntent, String format, Uri messageUri,
-            boolean expectMore, String fullMessageText, boolean isText, boolean persistMessage,
-            int priority, int validityPeriod, boolean isForVvm, long messageId, int messageRef,
-            boolean skipShortCodeCheck) {
-        return getSmsTracker(callingPackage, data, sentIntent, deliveryIntent, format,
-                null/*unsentPartCount*/, null/*anyPartFailed*/, messageUri, null/*smsHeader*/,
-                expectMore, fullMessageText, isText, persistMessage, priority, validityPeriod,
-                isForVvm, messageId, messageRef, skipShortCodeCheck);
+    protected SmsTracker getSmsTracker(String callingPackage, int callingUser,
+            HashMap<String, Object> data, PendingIntent sentIntent, PendingIntent deliveryIntent,
+            String format, Uri messageUri, boolean expectMore, String fullMessageText,
+            boolean isText, boolean persistMessage, int priority, int validityPeriod,
+            boolean isForVvm, long messageId, int messageRef, boolean skipShortCodeCheck) {
+        return getSmsTracker(callingPackage, callingUser, data, sentIntent, deliveryIntent,
+                format, /*unsentPartCount*/ null, /*anyPartFailed*/ null, messageUri, /*smsHeader*/
+                null, expectMore, fullMessageText, isText, persistMessage, priority,
+                validityPeriod, isForVvm, messageId, messageRef, skipShortCodeCheck);
     }
 
     protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
@@ -3029,24 +3037,6 @@
         return SubscriptionManager.getSubscriptionId(mPhone.getPhoneId());
     }
 
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private void checkCallerIsPhoneOrCarrierApp() {
-        int uid = Binder.getCallingUid();
-        int appId = UserHandle.getAppId(uid);
-        if (appId == Process.PHONE_UID || uid == 0) {
-            return;
-        }
-        try {
-            PackageManager pm = mContext.getPackageManager();
-            ApplicationInfo ai = pm.getApplicationInfo(getCarrierAppPackageName(), 0);
-            if (UserHandle.getAppId(ai.uid) != UserHandle.getAppId(Binder.getCallingUid())) {
-                throw new SecurityException("Caller is not phone or carrier app!");
-            }
-        } catch (PackageManager.NameNotFoundException re) {
-            throw new SecurityException("Caller is not phone or carrier app!");
-        }
-    }
-
     protected boolean isCdmaMo() {
         return mSmsDispatchersController.isCdmaMo();
     }
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 2a9bf7f..e7500a2 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -666,7 +666,9 @@
         mCarrierConfig = getCarrierConfig();
         CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
         // Callback which directly handle config change should be executed in handler thread
-        ccm.registerCarrierConfigChangeListener(this::post, mCarrierConfigChangeListener);
+        if (ccm != null) {
+            ccm.registerCarrierConfigChangeListener(this::post, mCarrierConfigChangeListener);
+        }
 
         mAccessNetworksManager = mPhone.getAccessNetworksManager();
         mOutOfServiceSS = new ServiceState();
@@ -1162,6 +1164,9 @@
 
         mDesiredPowerState = power;
         setPowerStateToDesired(forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply);
+        if (mDesiredPowerState) {
+            SatelliteController.getInstance().onSetCellularRadioPowerStateRequested(true);
+        }
     }
 
     /**
@@ -1323,6 +1328,12 @@
                     // Hence, issuing shut down regardless of radio power response
                     mCi.requestShutdown(null);
                 }
+
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception != null) {
+                    loge("EVENT_RADIO_POWER_OFF_DONE: exception=" + ar.exception);
+                    SatelliteController.getInstance().onPowerOffCellularRadioFailed();
+                }
                 break;
 
             // GSM
@@ -2801,8 +2812,10 @@
         if (showPlmn) {
             carrierName = plmn;
             if (showSpn) {
-                // Need to show both plmn and spn if both are not same.
-                if (!Objects.equals(spn, plmn)) {
+                if (TextUtils.isEmpty(carrierName)) {
+                    carrierName = spn;
+                } else if (!TextUtils.isEmpty(spn) && !Objects.equals(spn, carrierName)) {
+                    // Need to show both plmn and spn if both are not same.
                     String separator = mPhone.getContext().getString(
                             com.android.internal.R.string.kg_text_message_separator).toString();
                     carrierName = new StringBuilder().append(carrierName).append(separator)
@@ -2895,10 +2908,8 @@
         }
 
         String crossSimSpnFormat = null;
-        if (mPhone.getImsPhone() != null
-                && (mPhone.getImsPhone() != null)
-                && (mPhone.getImsPhone().getImsRegistrationTech()
-                == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM)) {
+        if ((getImsRegistrationTech() == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM)
+                && mPhone.isImsRegistered()) {
             // In Cross SIM Calling mode show SPN or PLMN + Cross SIM Calling
             //
             // 1) Show SPN + Cross SIM Calling If SIM has SPN and SPN display condition
@@ -4977,7 +4988,7 @@
      */
     public void powerOffRadioSafely() {
         synchronized (this) {
-            SatelliteController.getInstance().onCellularRadioPowerOffRequested();
+            SatelliteController.getInstance().onSetCellularRadioPowerStateRequested(false);
             if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
                 EmergencyStateTracker.getInstance().onCellularRadioPowerOffRequested();
             }
diff --git a/src/java/com/android/internal/telephony/SignalStrengthController.java b/src/java/com/android/internal/telephony/SignalStrengthController.java
index 98f84b2..31e1950 100644
--- a/src/java/com/android/internal/telephony/SignalStrengthController.java
+++ b/src/java/com/android/internal/telephony/SignalStrengthController.java
@@ -164,9 +164,11 @@
         CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
         mCarrierConfig = getCarrierConfig();
         // Callback which directly handle config change should be executed on handler thread
-        ccm.registerCarrierConfigChangeListener(this::post,
-                (slotIndex, subId, carrierId, specificCarrierId) ->
-                        onCarrierConfigurationChanged(slotIndex));
+        if (ccm != null) {
+            ccm.registerCarrierConfigChangeListener(this::post,
+                    (slotIndex, subId, carrierId, specificCarrierId) ->
+                            onCarrierConfigurationChanged(slotIndex));
+        }
 
         mPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
     }
diff --git a/src/java/com/android/internal/telephony/SimResponse.java b/src/java/com/android/internal/telephony/SimResponse.java
index 59defc3..97692a0 100644
--- a/src/java/com/android/internal/telephony/SimResponse.java
+++ b/src/java/com/android/internal/telephony/SimResponse.java
@@ -112,7 +112,7 @@
             android.hardware.radio.sim.CarrierRestrictions carrierRestrictions,
             int multiSimPolicy) {
         RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
-        boolean carrierLockInfoSupported = mRil.getHalVersion(HAL_SERVICE_SIM).greater(
+        boolean carrierLockInfoSupported = mRil.getHalVersion(HAL_SERVICE_SIM).greaterOrEqual(
                 RIL.RADIO_HAL_VERSION_2_2);
         if (rr == null) {
             return;
diff --git a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
index 077ee0b..8c9a820 100644
--- a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
+++ b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
@@ -27,6 +27,7 @@
 import android.database.Cursor;
 import android.database.SQLException;
 import android.os.PersistableBundle;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
@@ -35,6 +36,7 @@
 import com.android.internal.telephony.analytics.TelephonyAnalytics;
 import com.android.internal.telephony.analytics.TelephonyAnalytics.SmsMmsAnalytics;
 import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
@@ -97,6 +99,10 @@
     /** Content resolver to use to access raw table from SmsProvider. */
     private final ContentResolver mResolver;
 
+    private final UserManager mUserManager;
+
+    private final FeatureFlags mFeatureFlags;
+
     /** Broadcast receiver that processes the raw table when the user unlocks the phone for the
      *  first time after reboot and the credential-encrypted storage is available.
      */
@@ -105,6 +111,12 @@
         public void onReceive(final Context context, Intent intent) {
             Rlog.d(TAG, "Received broadcast " + intent.getAction());
             if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+                if (mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser()) {
+                    int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+                    if (userId != getMainUser().getIdentifier()) {
+                        return;
+                    }
+                }
                 new ScanRawTableThread(context).start();
             }
         }
@@ -126,9 +138,9 @@
     }
 
     public static void initialize(Context context, GsmInboundSmsHandler gsmInboundSmsHandler,
-        CdmaInboundSmsHandler cdmaInboundSmsHandler) {
+        CdmaInboundSmsHandler cdmaInboundSmsHandler, FeatureFlags featureFlags) {
         if (instance == null) {
-            instance = new SmsBroadcastUndelivered(context);
+            instance = new SmsBroadcastUndelivered(context, featureFlags);
         }
 
         // Tell handlers to start processing new messages and transit from the startup state to the
@@ -143,20 +155,38 @@
     }
 
     @UnsupportedAppUsage
-    private SmsBroadcastUndelivered(Context context) {
+    private SmsBroadcastUndelivered(Context context, FeatureFlags featureFlags) {
         mResolver = context.getContentResolver();
 
-        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        mUserManager = context.getSystemService(UserManager.class);
+        mFeatureFlags = featureFlags;
 
-        if (userManager.isUserUnlocked()) {
+        UserHandle mainUser = getMainUser();
+        boolean isUserUnlocked = mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser() ?
+                        mUserManager.isUserUnlocked(mainUser) : mUserManager.isUserUnlocked();
+        if (isUserUnlocked) {
             new ScanRawTableThread(context).start();
         } else {
             IntentFilter userFilter = new IntentFilter();
             userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
-            context.registerReceiver(mBroadcastReceiver, userFilter);
+            if (mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser()) {
+                context.registerReceiverAsUser(
+                        mBroadcastReceiver, mainUser, userFilter, null, null);
+            } else {
+                context.registerReceiver(mBroadcastReceiver, userFilter);
+            }
         }
     }
 
+    /** Returns the MainUser, which is the user designated for sending SMS broadcasts. */
+    private UserHandle getMainUser() {
+        UserHandle mainUser = null;
+        if (mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser()) {
+            mainUser = mUserManager.getMainUser();
+        }
+        return mainUser != null ? mainUser : UserHandle.SYSTEM;
+    }
+
     /**
      * Scan the raw table for complete SMS messages to broadcast, and old PDUs to delete.
      */
@@ -296,7 +326,8 @@
         int subId = SubscriptionManager.getDefaultSmsSubscriptionId();
         CarrierConfigManager configManager =
                 (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        PersistableBundle bundle = configManager.getConfigForSubId(subId);
+        PersistableBundle bundle = null;
+        if (configManager != null) bundle = configManager.getConfigForSubId(subId);
 
         if (bundle != null) {
             return bundle.getLong(CarrierConfigManager.KEY_UNDELIVERED_SMS_MESSAGE_EXPIRATION_TIME,
diff --git a/src/java/com/android/internal/telephony/SmsController.java b/src/java/com/android/internal/telephony/SmsController.java
index 59184d8..e3c409d 100644
--- a/src/java/com/android/internal/telephony/SmsController.java
+++ b/src/java/com/android/internal/telephony/SmsController.java
@@ -176,11 +176,13 @@
         if (callingPackage == null) {
             callingPackage = getCallingPackage();
         }
+        UserHandle callingUser = Binder.getCallingUserHandle();
+
         Rlog.d(LOG_TAG, "sendDataForSubscriber caller=" + callingPackage);
 
         // Check if user is associated with the subscription
         if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId,
-                Binder.getCallingUserHandle(), destAddr)) {
+                callingUser, destAddr)) {
             TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, subId,
                     Binder.getCallingUid(), callingPackage);
             sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_USER_NOT_ALLOWED);
@@ -195,7 +197,8 @@
 
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
-            iccSmsIntMgr.sendData(callingPackage, callingAttributionTag, destAddr, scAddr, destPort,
+            iccSmsIntMgr.sendData(callingPackage, callingUser.getIdentifier(),
+                    callingAttributionTag, destAddr, scAddr, destPort,
                     data, sentIntent, deliveryIntent);
         } else {
             Rlog.e(LOG_TAG, "sendDataForSubscriber iccSmsIntMgr is null for"
@@ -206,12 +209,14 @@
     }
 
     private void sendDataForSubscriberWithSelfPermissionsInternal(int subId, String callingPackage,
-            String callingAttributionTag, String destAddr, String scAddr, int destPort, byte[] data,
-            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm) {
+            int callingUser, String callingAttributionTag, String destAddr, String scAddr,
+            int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean isForVvm) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
-            iccSmsIntMgr.sendDataWithSelfPermissions(callingPackage, callingAttributionTag,
-                    destAddr, scAddr, destPort, data, sentIntent, deliveryIntent, isForVvm);
+            iccSmsIntMgr.sendDataWithSelfPermissions(callingPackage, callingUser,
+                    callingAttributionTag, destAddr, scAddr, destPort, data, sentIntent,
+                    deliveryIntent, isForVvm);
         } else {
             Rlog.e(LOG_TAG, "sendText iccSmsIntMgr is null for"
                     + " Subscription: " + subId);
@@ -219,7 +224,15 @@
         }
     }
 
+    @NonNull
     private String getCallingPackage() {
+        if (mFlags.hsumPackageManager()) {
+            PackageManager pm = mContext.createContextAsUser(Binder.getCallingUserHandle(), 0)
+                    .getPackageManager();
+            String[] packages = pm.getPackagesForUid(Binder.getCallingUid());
+            if (packages == null || packages.length == 0) return "";
+            return packages[0];
+        }
         return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid())[0];
     }
 
@@ -261,6 +274,8 @@
         if (callingPackage == null) {
             callingPackage = getCallingPackage();
         }
+        UserHandle callingUser = Binder.getCallingUserHandle();
+
         Rlog.d(LOG_TAG, "sendTextForSubscriber caller=" + callingPackage);
 
         if (skipFdnCheck || skipShortCodeCheck) {
@@ -306,9 +321,9 @@
             if (isBluetoothSubscription(info)) {
                 sendBluetoothText(info, destAddr, text, sentIntent, deliveryIntent);
             } else {
-                sendIccText(subId, callingPackage, destAddr, scAddr, text, sentIntent,
-                        deliveryIntent, persistMessageForNonDefaultSmsApp, messageId,
-                        skipShortCodeCheck);
+                sendIccText(subId, callingPackage, callingUser.getIdentifier(), destAddr, scAddr,
+                        text, sentIntent, deliveryIntent, persistMessageForNonDefaultSmsApp,
+                        messageId, skipShortCodeCheck);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -326,14 +341,14 @@
         btSmsInterfaceManager.sendText(mContext, destAddr, text, sentIntent, deliveryIntent, info);
     }
 
-    private void sendIccText(int subId, String callingPackage, String destAddr,
+    private void sendIccText(int subId, String callingPackage, int callingUser, String destAddr,
             String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
             boolean persistMessageForNonDefaultSmsApp, long messageId, boolean skipShortCodeCheck) {
         Rlog.d(LOG_TAG, "sendTextForSubscriber iccSmsIntMgr"
                 + " Subscription: " + subId + " " + formatCrossStackMessageId(messageId));
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
-            iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent,
+            iccSmsIntMgr.sendText(callingPackage, callingUser, destAddr, scAddr, text, sentIntent,
                     deliveryIntent, persistMessageForNonDefaultSmsApp, messageId,
                     skipShortCodeCheck);
         } else {
@@ -344,13 +359,14 @@
     }
 
     private void sendTextForSubscriberWithSelfPermissionsInternal(int subId, String callingPackage,
-            String callingAttributeTag, String destAddr, String scAddr, String text,
-            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage,
-            boolean isForVvm) {
+            int callingUser, String callingAttributeTag, String destAddr, String scAddr,
+            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean persistMessage, boolean isForVvm) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
-            iccSmsIntMgr.sendTextWithSelfPermissions(callingPackage, callingAttributeTag, destAddr,
-                    scAddr, text, sentIntent, deliveryIntent, persistMessage, isForVvm);
+            iccSmsIntMgr.sendTextWithSelfPermissions(callingPackage, callingUser,
+                    callingAttributeTag, destAddr, scAddr, text, sentIntent, deliveryIntent,
+                    persistMessage, isForVvm);
         } else {
             Rlog.e(LOG_TAG, "sendText iccSmsIntMgr is null for"
                     + " Subscription: " + subId);
@@ -366,6 +382,8 @@
         if (callingPackage == null) {
             callingPackage = getCallingPackage();
         }
+        UserHandle callingUser = Binder.getCallingUserHandle();
+
         Rlog.d(LOG_TAG, "sendTextForSubscriberWithOptions caller=" + callingPackage);
 
         // Check if user is associated with the subscription
@@ -385,9 +403,9 @@
 
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
-            iccSmsIntMgr.sendTextWithOptions(callingPackage, callingAttributionTag, destAddr,
-                    scAddr, parts, sentIntent, deliveryIntent, persistMessage, priority, expectMore,
-                    validityPeriod);
+            iccSmsIntMgr.sendTextWithOptions(callingPackage, callingUser.getIdentifier(),
+                    callingAttributionTag, destAddr, scAddr, parts, sentIntent, deliveryIntent,
+                    persistMessage, priority, expectMore, validityPeriod);
         } else {
             Rlog.e(LOG_TAG, "sendTextWithOptions iccSmsIntMgr is null for"
                     + " Subscription: " + subId);
@@ -402,9 +420,9 @@
             boolean persistMessageForNonDefaultSmsApp, long messageId) {
         // This is different from the checking of other method. It prefers the package name
         // returned by getCallPackage() for backward-compatibility.
-        if (getCallingPackage() != null) {
-            callingPackage = getCallingPackage();
-        }
+        callingPackage = getCallingPackage();
+        UserHandle callingUser = Binder.getCallingUserHandle();
+
         Rlog.d(LOG_TAG, "sendMultipartTextForSubscriber caller=" + callingPackage);
 
         // Check if user is associated with the subscription
@@ -426,9 +444,9 @@
 
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
-            iccSmsIntMgr.sendMultipartText(callingPackage, callingAttributionTag, destAddr, scAddr,
-                    parts, sentIntents, deliveryIntents, persistMessageForNonDefaultSmsApp,
-                    messageId);
+            iccSmsIntMgr.sendMultipartText(callingPackage, callingUser.getIdentifier(),
+                    callingAttributionTag, destAddr, scAddr, parts, sentIntents, deliveryIntents,
+                    persistMessageForNonDefaultSmsApp, messageId);
         } else {
             Rlog.e(LOG_TAG, "sendMultipartTextForSubscriber iccSmsIntMgr is null for"
                     + " Subscription: " + subId + " " + formatCrossStackMessageId(messageId));
@@ -444,6 +462,8 @@
         if (callingPackage == null) {
             callingPackage = getCallingPackage();
         }
+        UserHandle callingUser = Binder.getCallingUserHandle();
+
         Rlog.d(LOG_TAG, "sendMultipartTextForSubscriberWithOptions caller=" + callingPackage);
 
         // Check if user is associated with the subscription
@@ -463,9 +483,9 @@
 
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
-            iccSmsIntMgr.sendMultipartTextWithOptions(callingPackage, callingAttributionTag,
-                    destAddr, scAddr, parts, sentIntents, deliveryIntents, persistMessage, priority,
-                    expectMore, validityPeriod, 0L /* messageId */);
+            iccSmsIntMgr.sendMultipartTextWithOptions(callingPackage, callingUser.getIdentifier(),
+                    callingAttributionTag, destAddr, scAddr, parts, sentIntents, deliveryIntents,
+                    persistMessage, priority, expectMore, validityPeriod, 0L /* messageId */);
         } else {
             Rlog.e(LOG_TAG, "sendMultipartTextWithOptions iccSmsIntMgr is null for"
                     + " Subscription: " + subId);
@@ -717,6 +737,7 @@
             Uri messageUri, String scAddress, PendingIntent sentIntent,
             PendingIntent deliveryIntent) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+        UserHandle callingUser = Binder.getCallingUserHandle();
         if (!getCallingPackage().equals(callingPkg)) {
             throw new SecurityException("sendStoredText: Package " + callingPkg
                     + "does not belong to " + Binder.getCallingUid());
@@ -724,8 +745,8 @@
         Rlog.d(LOG_TAG, "sendStoredText caller=" + callingPkg);
 
         if (iccSmsIntMgr != null) {
-            iccSmsIntMgr.sendStoredText(callingPkg, callingAttributionTag, messageUri, scAddress,
-                    sentIntent, deliveryIntent);
+            iccSmsIntMgr.sendStoredText(callingPkg, callingUser.getIdentifier(),
+                    callingAttributionTag, messageUri, scAddress, sentIntent, deliveryIntent);
         } else {
             Rlog.e(LOG_TAG, "sendStoredText iccSmsIntMgr is null for subscription: " + subId);
             sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
@@ -737,6 +758,8 @@
             Uri messageUri, String scAddress, List<PendingIntent> sentIntents,
             List<PendingIntent> deliveryIntents) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+        UserHandle callingUser = Binder.getCallingUserHandle();
+
         if (!getCallingPackage().equals(callingPkg)) {
             throw new SecurityException("sendStoredMultipartText: Package " + callingPkg
                     + " does not belong to " + Binder.getCallingUid());
@@ -744,8 +767,8 @@
         Rlog.d(LOG_TAG, "sendStoredMultipartText caller=" + callingPkg);
 
         if (iccSmsIntMgr != null) {
-            iccSmsIntMgr.sendStoredMultipartText(callingPkg, callingAttributionTag, messageUri,
-                    scAddress, sentIntents, deliveryIntents);
+            iccSmsIntMgr.sendStoredMultipartText(callingPkg, callingUser.getIdentifier(),
+                    callingAttributionTag, messageUri, scAddress, sentIntents, deliveryIntents);
         } else {
             Rlog.e(LOG_TAG, "sendStoredMultipartText iccSmsIntMgr is null for subscription: "
                     + subId);
@@ -979,7 +1002,7 @@
      * Internal API to send visual voicemail related SMS. This is not exposed outside the phone
      * process, and should be called only after verifying that the caller is the default VVM app.
      */
-    public void sendVisualVoicemailSmsForSubscriber(String callingPackage,
+    public void sendVisualVoicemailSmsForSubscriber(String callingPackage, int callingUser,
             String callingAttributionTag, int subId, String number, int port, String text,
             PendingIntent sentIntent) {
         Rlog.d(LOG_TAG, "sendVisualVoicemailSmsForSubscriber caller=" + callingPackage);
@@ -1001,12 +1024,12 @@
         }
 
         if (port == 0) {
-            sendTextForSubscriberWithSelfPermissionsInternal(subId, callingPackage,
+            sendTextForSubscriberWithSelfPermissionsInternal(subId, callingPackage, callingUser,
                     callingAttributionTag, number, null, text, sentIntent, null, false,
                     true /* isForVvm */);
         } else {
             byte[] data = text.getBytes(StandardCharsets.UTF_8);
-            sendDataForSubscriberWithSelfPermissionsInternal(subId, callingPackage,
+            sendDataForSubscriberWithSelfPermissionsInternal(subId, callingPackage, callingUser,
                     callingAttributionTag, number, null, (short) port, data, sentIntent, null,
                     true /* isForVvm */);
         }
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index bc1e1a8..7795955 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -35,6 +35,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Telephony.Sms;
 import android.provider.Telephony.Sms.Intents;
@@ -46,6 +47,7 @@
 import android.telephony.SmsMessage;
 import android.telephony.TelephonyManager;
 import android.telephony.emergency.EmergencyNumber;
+import android.telephony.satellite.SatelliteManager;
 import android.text.TextUtils;
 
 import com.android.ims.ImsManager;
@@ -61,6 +63,8 @@
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
 import com.android.internal.telephony.gsm.GsmSMSDispatcher;
+import com.android.internal.telephony.satellite.DatagramDispatcher;
+import com.android.internal.telephony.satellite.SatelliteController;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -115,6 +119,12 @@
     /** Called when the domain selection should be performed. */
     private static final int EVENT_REQUEST_DOMAIN_SELECTION = 22;
 
+    /** Called when {@link DatagramDispatcher} informs to send carrier roaming nb iot ntn sms. */
+    private static final int CMD_SEND_TEXT = 23;
+
+    /** Called when {@link DatagramDispatcher} informs sms cannot be sent over ntn due to error. */
+    private static final int EVENT_SEND_TEXT_OVER_NTN_ERROR = 24;
+
     /** Delete any partial message segments after being IN_SERVICE for 1 day. */
     private static final long PARTIAL_SEGMENT_WAIT_DURATION = (long) (60 * 60 * 1000) * 24;
     /** Constant for invalid time */
@@ -205,7 +215,7 @@
             };
 
     /** Stores the sending SMS information for a pending request. */
-    private static class PendingRequest {
+    public static class PendingRequest {
         public static final int TYPE_DATA = 1;
         public static final int TYPE_TEXT = 2;
         public static final int TYPE_MULTIPART_TEXT = 3;
@@ -214,6 +224,7 @@
         public final int type;
         public final SMSDispatcher.SmsTracker tracker;
         public final String callingPackage;
+        public final int callingUser;
         public final String destAddr;
         public final String scAddr;
         public final ArrayList<PendingIntent> sentIntents;
@@ -232,15 +243,16 @@
         public final long messageId;
         public final boolean skipShortCodeCheck;
 
-        PendingRequest(int type, SMSDispatcher.SmsTracker tracker, String callingPackage,
-                String destAddr, String scAddr, ArrayList<PendingIntent> sentIntents,
-                ArrayList<PendingIntent> deliveryIntents, boolean isForVvm, byte[] data,
-                int destPort, ArrayList<String> texts, Uri messageUri, boolean persistMessage,
-                int priority, boolean expectMore, int validityPeriod, long messageId,
-                boolean skipShortCodeCheck) {
+        public PendingRequest(int type, SMSDispatcher.SmsTracker tracker, String callingPackage,
+                int callingUser, String destAddr, String scAddr,
+                ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents,
+                boolean isForVvm, byte[] data, int destPort, ArrayList<String> texts,
+                Uri messageUri, boolean persistMessage, int priority, boolean expectMore,
+                int validityPeriod, long messageId, boolean skipShortCodeCheck) {
             this.type = type;
             this.tracker = tracker;
             this.callingPackage = callingPackage;
+            this.callingUser = callingUser;
             this.destAddr = destAddr;
             this.scAddr = scAddr;
             this.sentIntents = sentIntents;
@@ -381,12 +393,12 @@
         mImsSmsDispatcher = new ImsSmsDispatcher(phone, this, ImsManager::getConnector);
         mCdmaDispatcher = new CdmaSMSDispatcher(phone, this);
         mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
-                storageMonitor, phone, looper);
+                storageMonitor, phone, looper, mFeatureFlags);
         mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
-                storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher, looper);
+                storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher, looper, mFeatureFlags);
         mGsmDispatcher = new GsmSMSDispatcher(phone, this, mGsmInboundSmsHandler);
         SmsBroadcastUndelivered.initialize(phone.getContext(),
-                mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
+                mGsmInboundSmsHandler, mCdmaInboundSmsHandler, mFeatureFlags);
         InboundSmsHandler.registerNewMessageNotificationActionHandler(phone.getContext());
 
         mCi.registerForOn(this, EVENT_RADIO_ON, null);
@@ -525,6 +537,26 @@
                 }
                 break;
             }
+            case CMD_SEND_TEXT: {
+                PendingRequest request = (PendingRequest) msg.obj;
+                if (request.type == PendingRequest.TYPE_TEXT) {
+                    sendTextInternal(request);
+                } else if (request.type == PendingRequest.TYPE_MULTIPART_TEXT) {
+                    sendMultipartTextInternal(request);
+                } else {
+                    logd("CMD_SEND_TEXT: type=" + request.type
+                            + " messageId=" + request.messageId);
+                }
+                break;
+            }
+            case EVENT_SEND_TEXT_OVER_NTN_ERROR: {
+                PendingRequest request = (PendingRequest) msg.obj;
+                logd("EVENT_SEND_TEXT_OVER_NTN_ERROR: type=" + request.type
+                        + " messageId=" + request.messageId);
+                triggerSentIntentForFailure(request.sentIntents);
+                break;
+            }
+
             default:
                 if (isCdmaMo()) {
                     mCdmaDispatcher.handleMessage(msg);
@@ -786,7 +818,9 @@
                 SomeArgs args = SomeArgs.obtain();
                 args.arg1 = getDomainSelectionConnectionHolder(isEmergency);
                 args.arg2 = new PendingRequest(PendingRequest.TYPE_RETRY_SMS, tracker,
-                        null, null, null, null, null, false, null, 0, null, null, false,
+                        null, UserHandle.USER_NULL, null, null,
+                        null, null, false, null, 0,
+                        null, null, false,
                         0, false, 0, 0L, false);
                 args.arg3 = "sendRetrySms";
                 sendMessage(obtainMessage(EVENT_REQUEST_DOMAIN_SELECTION, args));
@@ -833,8 +867,8 @@
                 // should never come here...
                 Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!");
                 tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE);
-                notifySmsSentFailedToEmergencyStateTracker(
-                        tracker.mDestAddress, tracker.mMessageId, !retryUsingImsService);
+                notifySmsSent(tracker.mDestAddress, tracker.mMessageId,
+                        !retryUsingImsService, true /*isLastSmsPart*/, false /*success*/);
                 return;
             }
             String scAddr = (String) map.get("scAddr");
@@ -842,8 +876,8 @@
             if (destAddr == null) {
                 Rlog.e(TAG, "sendRetrySms failed due to null destAddr");
                 tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE);
-                notifySmsSentFailedToEmergencyStateTracker(
-                        tracker.mDestAddress, tracker.mMessageId, !retryUsingImsService);
+                notifySmsSent(tracker.mDestAddress, tracker.mMessageId,
+                        !retryUsingImsService, true /*isLastSmsPart*/, false /*success*/);
                 return;
             }
 
@@ -884,8 +918,8 @@
                         + "scAddr: %s, "
                         + "destPort: %s", scAddr, map.get("destPort")));
                 tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE);
-                notifySmsSentFailedToEmergencyStateTracker(
-                        tracker.mDestAddress, tracker.mMessageId, !retryUsingImsService);
+                notifySmsSent(tracker.mDestAddress, tracker.mMessageId,
+                        !retryUsingImsService, true /*isLastSmsPart*/, false /*success*/);
                 return;
             }
             // replace old smsc and pdu with newly encoded ones
@@ -1178,36 +1212,35 @@
     }
 
     /**
-     * Called when MO SMS is successfully sent.
+     * Called when MO SMS is sent.
      */
-    protected void notifySmsSentToEmergencyStateTracker(@NonNull String destAddr, long messageId,
-            boolean isOverIms, boolean isLastSmsPart) {
+    protected void notifySmsSent(@NonNull String destAddr, long messageId, boolean isOverIms,
+            boolean isLastSmsPart, boolean success) {
+        notifySmsSentToEmergencyStateTracker(
+                destAddr, messageId, isOverIms, isLastSmsPart, success);
+        notifySmsSentToDatagramDispatcher(messageId, success);
+    }
+
+    /**
+     * Called when MO SMS is sent.
+     */
+    private void notifySmsSentToEmergencyStateTracker(@NonNull String destAddr, long messageId,
+            boolean isOverIms, boolean isLastSmsPart, boolean success) {
         if (isSmsDomainSelectionEnabled()) {
             // Run on main thread for interworking with EmergencyStateTracker.
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = destAddr;
             args.arg2 = Long.valueOf(messageId);
-            args.arg3 = Boolean.TRUE;
+            args.arg3 = Boolean.valueOf(success);
             args.arg4 = Boolean.valueOf(isOverIms);
             args.arg5 = Boolean.valueOf(isLastSmsPart);
             sendMessage(obtainMessage(EVENT_SMS_SENT_COMPLETED_USING_DOMAIN_SELECTION, args));
         }
     }
 
-    /**
-     * Called when sending MO SMS is failed.
-     */
-    protected void notifySmsSentFailedToEmergencyStateTracker(@NonNull String destAddr,
-            long messageId, boolean isOverIms) {
-        if (isSmsDomainSelectionEnabled()) {
-            // Run on main thread for interworking with EmergencyStateTracker.
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = destAddr;
-            args.arg2 = Long.valueOf(messageId);
-            args.arg3 = Boolean.FALSE;
-            args.arg4 = Boolean.valueOf(isOverIms);
-            args.arg5 = Boolean.TRUE; // Ignored when sending SMS is failed.
-            sendMessage(obtainMessage(EVENT_SMS_SENT_COMPLETED_USING_DOMAIN_SELECTION, args));
+    private void notifySmsSentToDatagramDispatcher(long messageId, boolean success) {
+        if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn()) {
+            DatagramDispatcher.getInstance().onSendSmsDone(mPhone.getSubId(), messageId, success);
         }
     }
 
@@ -1367,17 +1400,17 @@
     private void sendData(@NetworkRegistrationInfo.Domain int domain,
             @NonNull PendingRequest request) {
         if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
-            mImsSmsDispatcher.sendData(request.callingPackage, request.destAddr, request.scAddr,
-                    request.destPort, request.data, request.sentIntents.get(0),
-                    request.deliveryIntents.get(0), request.isForVvm);
+            mImsSmsDispatcher.sendData(request.callingPackage, request.callingUser,
+                    request.destAddr, request.scAddr, request.destPort, request.data,
+                    request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm);
         } else if (isCdmaMo(domain)) {
-            mCdmaDispatcher.sendData(request.callingPackage, request.destAddr, request.scAddr,
-                    request.destPort, request.data, request.sentIntents.get(0),
-                    request.deliveryIntents.get(0), request.isForVvm);
+            mCdmaDispatcher.sendData(request.callingPackage, request.callingUser, request.destAddr,
+                    request.scAddr, request.destPort, request.data,
+                    request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm);
         } else {
-            mGsmDispatcher.sendData(request.callingPackage, request.destAddr, request.scAddr,
-                    request.destPort, request.data, request.sentIntents.get(0),
-                    request.deliveryIntents.get(0), request.isForVvm);
+            mGsmDispatcher.sendData(request.callingPackage, request.callingUser, request.destAddr,
+                    request.scAddr, request.destPort, request.data,
+                    request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm);
         }
     }
 
@@ -1394,22 +1427,25 @@
         if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
             mImsSmsDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0),
                     request.sentIntents.get(0), request.deliveryIntents.get(0),
-                    request.messageUri, request.callingPackage, request.persistMessage,
-                    request.priority, false /*request.expectMore*/, request.validityPeriod,
-                    request.isForVvm, request.messageId, request.skipShortCodeCheck);
+                    request.messageUri, request.callingPackage, request.callingUser,
+                    request.persistMessage, request.priority,  /*request.expectMore*/ false,
+                    request.validityPeriod, request.isForVvm, request.messageId,
+                    request.skipShortCodeCheck);
         } else {
             if (isCdmaMo(domain)) {
                 mCdmaDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0),
                         request.sentIntents.get(0), request.deliveryIntents.get(0),
-                        request.messageUri, request.callingPackage, request.persistMessage,
-                        request.priority, request.expectMore, request.validityPeriod,
-                        request.isForVvm, request.messageId, request.skipShortCodeCheck);
+                        request.messageUri, request.callingPackage, request.callingUser,
+                        request.persistMessage, request.priority, request.expectMore,
+                        request.validityPeriod, request.isForVvm, request.messageId,
+                        request.skipShortCodeCheck);
             } else {
                 mGsmDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0),
                         request.sentIntents.get(0), request.deliveryIntents.get(0),
-                        request.messageUri, request.callingPackage, request.persistMessage,
-                        request.priority, request.expectMore, request.validityPeriod,
-                        request.isForVvm, request.messageId, request.skipShortCodeCheck);
+                        request.messageUri, request.callingPackage, request.callingUser,
+                        request.persistMessage, request.priority, request.expectMore,
+                        request.validityPeriod, request.isForVvm, request.messageId,
+                        request.skipShortCodeCheck);
             }
         }
     }
@@ -1427,19 +1463,22 @@
         if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
             mImsSmsDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts,
                     request.sentIntents, request.deliveryIntents, request.messageUri,
-                    request.callingPackage, request.persistMessage, request.priority,
-                    false /*request.expectMore*/, request.validityPeriod, request.messageId);
+                    request.callingPackage, request.callingUser, request.persistMessage,
+                    request.priority, false /*request.expectMore*/, request.validityPeriod,
+                    request.messageId);
         } else {
             if (isCdmaMo(domain)) {
                 mCdmaDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts,
                         request.sentIntents, request.deliveryIntents, request.messageUri,
-                        request.callingPackage, request.persistMessage, request.priority,
-                        request.expectMore, request.validityPeriod, request.messageId);
+                        request.callingPackage, request.callingUser, request.persistMessage,
+                        request.priority, request.expectMore, request.validityPeriod,
+                        request.messageId);
             } else {
                 mGsmDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts,
                         request.sentIntents, request.deliveryIntents, request.messageUri,
-                        request.callingPackage, request.persistMessage, request.priority,
-                        request.expectMore, request.validityPeriod, request.messageId);
+                        request.callingPackage, request.callingUser, request.persistMessage,
+                        request.priority, request.expectMore, request.validityPeriod,
+                        request.messageId);
             }
         }
     }
@@ -1546,31 +1585,33 @@
      *  broadcast when the message is delivered to the recipient.  The
      *  raw pdu of the status report is in the extended data ("pdu").
      */
-    protected void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
-            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm) {
+    protected void sendData(String callingPackage, int callingUser, String destAddr, String scAddr,
+            int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean isForVvm) {
         if (TextUtils.isEmpty(scAddr)) {
             scAddr = getSmscAddressFromUSIMWithPhoneIdentity(callingPackage);
         }
 
         if (isSmsDomainSelectionEnabled()) {
             sendSmsUsingDomainSelection(getDomainSelectionConnectionHolder(false),
-                    new PendingRequest(PendingRequest.TYPE_DATA, null, callingPackage,
+                    new PendingRequest(PendingRequest.TYPE_DATA, null, callingPackage, callingUser,
                             destAddr, scAddr, asArrayList(sentIntent),
-                            asArrayList(deliveryIntent), isForVvm, data, destPort, null, null,
-                            false, 0, false, 0, 0L, false),
+                            asArrayList(deliveryIntent), isForVvm, data, destPort, null,
+                            null, false, 0, false, 0,
+                            0L, false),
                     "sendData");
             return;
         }
 
         if (mImsSmsDispatcher.isAvailable()) {
-            mImsSmsDispatcher.sendData(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
-                    deliveryIntent, isForVvm);
+            mImsSmsDispatcher.sendData(callingPackage, callingUser, destAddr, scAddr, destPort,
+                    data, sentIntent, deliveryIntent, isForVvm);
         } else if (isCdmaMo()) {
-            mCdmaDispatcher.sendData(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
-                    deliveryIntent, isForVvm);
+            mCdmaDispatcher.sendData(callingPackage, callingUser, destAddr, scAddr, destPort, data,
+                    sentIntent, deliveryIntent, isForVvm);
         } else {
-            mGsmDispatcher.sendData(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
-                    deliveryIntent, isForVvm);
+            mGsmDispatcher.sendData(callingPackage, callingUser, destAddr, scAddr, destPort, data,
+                    sentIntent, deliveryIntent, isForVvm);
         }
     }
 
@@ -1672,11 +1713,12 @@
      *  Any Other values included Negative considered as Invalid Validity Period of the message.
      */
     public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage,
-            int priority, boolean expectMore, int validityPeriod, boolean isForVvm,
-            long messageId) {
+            PendingIntent deliveryIntent, Uri messageUri, String callingPkg, int callingUser,
+            boolean persistMessage, int priority, boolean expectMore, int validityPeriod,
+            boolean isForVvm, long messageId) {
         sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri, callingPkg,
-                persistMessage, priority, expectMore, validityPeriod, isForVvm, messageId, false);
+                callingUser, persistMessage, priority, expectMore, validityPeriod, isForVvm,
+                messageId, false);
     }
 
     /**
@@ -1778,39 +1820,61 @@
      * @param skipShortCodeCheck Skip check for short code type destination address.
      */
     public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage,
-            int priority, boolean expectMore, int validityPeriod, boolean isForVvm,
-            long messageId, boolean skipShortCodeCheck) {
+            PendingIntent deliveryIntent, Uri messageUri, String callingPkg, int callingUser,
+            boolean persistMessage, int priority, boolean expectMore, int validityPeriod,
+            boolean isForVvm, long messageId, boolean skipShortCodeCheck) {
         if (TextUtils.isEmpty(scAddr)) {
             scAddr = getSmscAddressFromUSIMWithPhoneIdentity(callingPkg);
         }
 
-        if (isSmsDomainSelectionEnabled()) {
-            TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
-            boolean isEmergency = tm.isEmergencyNumber(destAddr);
-            sendSmsUsingDomainSelection(getDomainSelectionConnectionHolder(isEmergency),
-                    new PendingRequest(PendingRequest.TYPE_TEXT, null, callingPkg,
-                            destAddr, scAddr, asArrayList(sentIntent),
-                            asArrayList(deliveryIntent), isForVvm, null, 0, asArrayList(text),
-                            messageUri, persistMessage, priority, expectMore, validityPeriod,
-                            messageId, skipShortCodeCheck),
-                    "sendText");
+        PendingRequest pendingRequest = new PendingRequest(PendingRequest.TYPE_TEXT, null,
+                callingPkg, callingUser, destAddr, scAddr, asArrayList(sentIntent),
+                asArrayList(deliveryIntent), isForVvm, null, 0, asArrayList(text),
+                messageUri, persistMessage, priority, expectMore, validityPeriod, messageId,
+                skipShortCodeCheck);
+
+        if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn()) {
+            // Send P2P SMS using carrier roaming NB IOT NTN
+            DatagramDispatcher.getInstance().sendSms(pendingRequest);
             return;
         }
 
-        if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(destAddr)) {
-            mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
-                    messageUri, callingPkg, persistMessage, priority, false /*expectMore*/,
-                    validityPeriod, isForVvm, messageId, skipShortCodeCheck);
+        sendTextInternal(pendingRequest);
+    }
+
+    private void sendTextInternal(PendingRequest request) {
+        logd("sendTextInternal: messageId=" + request.messageId);
+        if (isSmsDomainSelectionEnabled()) {
+            TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+            boolean isEmergency = tm.isEmergencyNumber(request.destAddr);
+            sendSmsUsingDomainSelection(getDomainSelectionConnectionHolder(isEmergency),
+                    request, "sendText");
+            return;
+        }
+
+        if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(
+                request.destAddr)) {
+            mImsSmsDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0),
+                    request.sentIntents.get(0), request.deliveryIntents.get(0),
+                    request.messageUri, request.callingPackage, request.callingUser,
+                    request.persistMessage, request.priority, false /*expectMore*/,
+                    request.validityPeriod, request.isForVvm, request.messageId,
+                    request.skipShortCodeCheck);
         } else {
             if (isCdmaMo()) {
-                mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
-                        messageUri, callingPkg, persistMessage, priority, expectMore,
-                        validityPeriod, isForVvm, messageId, skipShortCodeCheck);
+                mCdmaDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0),
+                        request.sentIntents.get(0), request.deliveryIntents.get(0),
+                        request.messageUri, request.callingPackage, request.callingUser,
+                        request.persistMessage, request.priority, request.expectMore,
+                        request.validityPeriod, request.isForVvm, request.messageId,
+                        request.skipShortCodeCheck);
             } else {
-                mGsmDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
-                        messageUri, callingPkg, persistMessage, priority, expectMore,
-                        validityPeriod, isForVvm, messageId, skipShortCodeCheck);
+                mGsmDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0),
+                        request.sentIntents.get(0), request.deliveryIntents.get(0),
+                        request.messageUri, request.callingPackage, request.callingUser,
+                        request.persistMessage, request.priority, request.expectMore,
+                        request.validityPeriod, request.isForVvm, request.messageId,
+                        request.skipShortCodeCheck);
             }
         }
     }
@@ -1922,37 +1986,55 @@
     protected void sendMultipartText(String destAddr, String scAddr,
             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
             ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
-            boolean persistMessage, int priority, boolean expectMore, int validityPeriod,
-            long messageId) {
+            int callingUser, boolean persistMessage, int priority, boolean expectMore,
+            int validityPeriod, long messageId) {
         if (TextUtils.isEmpty(scAddr)) {
             scAddr = getSmscAddressFromUSIMWithPhoneIdentity(callingPkg);
         }
 
+        PendingRequest pendingRequest = new PendingRequest(PendingRequest.TYPE_MULTIPART_TEXT, null,
+                callingPkg, callingUser, destAddr, scAddr, sentIntents, deliveryIntents, false,
+                null, 0, parts, messageUri, persistMessage, priority, expectMore,
+                validityPeriod, messageId, false);
+
+        if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn()) {
+            // Send multipart P2P SMS using carrier roaming NB IOT NTN
+            DatagramDispatcher.getInstance().sendSms(pendingRequest);
+            return;
+        }
+
+        sendMultipartTextInternal(pendingRequest);
+    }
+
+    private void sendMultipartTextInternal(PendingRequest request) {
+        logd("sendMultipartTextInternal: messageId=" + request.messageId);
         if (isSmsDomainSelectionEnabled()) {
             TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
-            boolean isEmergency = tm.isEmergencyNumber(destAddr);
+            boolean isEmergency = tm.isEmergencyNumber(request.destAddr);
             sendSmsUsingDomainSelection(getDomainSelectionConnectionHolder(isEmergency),
-                    new PendingRequest(PendingRequest.TYPE_MULTIPART_TEXT, null,
-                            callingPkg, destAddr, scAddr, sentIntents, deliveryIntents, false,
-                            null, 0, parts, messageUri, persistMessage, priority, expectMore,
-                            validityPeriod, messageId, false),
-                    "sendMultipartText");
+                    request, "sendMultipartText");
             return;
         }
 
         if (mImsSmsDispatcher.isAvailable()) {
-            mImsSmsDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
-                    deliveryIntents, messageUri, callingPkg, persistMessage, priority,
-                    false /*expectMore*/, validityPeriod, messageId);
+            mImsSmsDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts,
+                    request.sentIntents, request.deliveryIntents, request.messageUri,
+                    request.callingPackage, request.callingUser, request.persistMessage,
+                    request.priority, false /*expectMore*/, request.validityPeriod,
+                    request.messageId);
         } else {
             if (isCdmaMo()) {
-                mCdmaDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
-                        deliveryIntents, messageUri, callingPkg, persistMessage, priority,
-                        expectMore, validityPeriod, messageId);
+                mCdmaDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts,
+                        request.sentIntents, request.deliveryIntents, request.messageUri,
+                        request.callingPackage, request.callingUser, request.persistMessage,
+                        request.priority, request.expectMore, request.validityPeriod,
+                        request.messageId);
             } else {
-                mGsmDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
-                        deliveryIntents, messageUri, callingPkg, persistMessage, priority,
-                        expectMore, validityPeriod, messageId);
+                mGsmDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts,
+                        request.sentIntents, request.deliveryIntents, request.messageUri,
+                        request.callingPackage, request.callingUser, request.persistMessage,
+                        request.priority, request.expectMore, request.validityPeriod,
+                        request.messageId);
             }
         }
     }
@@ -2081,6 +2163,44 @@
         else return mGsmInboundSmsHandler;
     }
 
+    /**
+     * This API should be used only by {@link DatagramDispatcher} to send SMS over
+     * non-terrestrial network.
+     *
+     * @param request {@link PendingRequest} object that contains all the information required to
+     *                send MO SMS.
+     */
+    public void sendCarrierRoamingNbIotNtnText(@NonNull PendingRequest request) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            logd("onSendCarrierRoamingNbIotNtnTextError: carrier roaming nb iot ntn "
+                    + "feature flag is disabled");
+            return;
+        }
+
+        sendMessage(obtainMessage(CMD_SEND_TEXT, request));
+    }
+
+    /**
+     * Send error code to pending MO SMS request.
+     *
+     * @param pendingRequest {@link PendingRequest} object that contains all the information
+     *                       related to MO SMS.
+     * @param errorCode error code to be returned.
+     */
+    public void onSendCarrierRoamingNbIotNtnTextError(@NonNull PendingRequest pendingRequest,
+            @SatelliteManager.SatelliteResult int errorCode) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            logd("onSendCarrierRoamingNbIotNtnTextError: carrier roaming nb iot ntn "
+                    + "feature flag is disabled");
+            return;
+        }
+
+        logd("onSendCarrierRoamingNbIotNtnTextError: messageId=" + pendingRequest.messageId
+                + " errorCode=" + errorCode);
+        sendMessage(obtainMessage(EVENT_SEND_TEXT_OVER_NTN_ERROR, pendingRequest));
+    }
+
+
     public interface SmsInjectionCallback {
         void onSmsInjectedResult(int result);
     }
diff --git a/src/java/com/android/internal/telephony/SmsStorageMonitor.java b/src/java/com/android/internal/telephony/SmsStorageMonitor.java
index 2736f7a..07b7a18 100644
--- a/src/java/com/android/internal/telephony/SmsStorageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsStorageMonitor.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;
@@ -33,6 +34,7 @@
 import android.telephony.SubscriptionManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.telephony.Rlog;
 
@@ -91,6 +93,9 @@
 
     private boolean mMemoryStatusOverrideFlag = false;
 
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
     /**
      * Hold the wake lock for 5 seconds, which should be enough time for
      * any receiver(s) to grab its own wake lock.
@@ -100,9 +105,11 @@
     /**
      * Creates an SmsStorageMonitor and registers for events.
      * @param phone the Phone to use
+     * @param flags The Android feature flags
      */
-    public SmsStorageMonitor(Phone phone) {
+    public SmsStorageMonitor(@NonNull Phone phone, @NonNull FeatureFlags flags) {
         mPhone = phone;
+        mFeatureFlags = flags;
         mContext = phone.getContext();
         mCi = phone.mCi;
 
@@ -278,7 +285,12 @@
         intent.setComponent(componentName);
         mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
-        mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                    android.Manifest.permission.RECEIVE_SMS);
+        } else {
+            mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
+        }
     }
 
     /** Returns whether or not there is storage available for an incoming SMS. */
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 0b0f9d3..10e97b6 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -289,8 +289,16 @@
         return new GsmCdmaCallTracker(phone, featureFlags);
     }
 
-    public SmsStorageMonitor makeSmsStorageMonitor(Phone phone) {
-        return new SmsStorageMonitor(phone);
+    /**
+     * Create {@link SmsStorageMonitor} instance.
+     *
+     * @param phone Phone instance
+     * @param flags Android feature flags
+     *
+     * @return The created instance
+     */
+    public SmsStorageMonitor makeSmsStorageMonitor(Phone phone, @NonNull FeatureFlags flags) {
+        return new SmsStorageMonitor(phone, flags);
     }
 
     public SmsUsageMonitor makeSmsUsageMonitor(Context context) {
@@ -331,8 +339,16 @@
         return new CarrierActionAgent(phone);
     }
 
-    public CarrierResolver makeCarrierResolver(Phone phone) {
-        return new CarrierResolver(phone);
+    /**
+     * Create {@link CarrierResolver} instance
+     *
+     * @param phone The phone instance
+     * @param flags Android feature flags
+     *
+     * @return The created instance
+     */
+    public CarrierResolver makeCarrierResolver(Phone phone, FeatureFlags flags) {
+        return new CarrierResolver(phone, flags);
     }
 
     public IccPhoneBookInterfaceManager makeIccPhoneBookInterfaceManager(Phone phone) {
diff --git a/src/java/com/android/internal/telephony/TelephonyCountryDetector.java b/src/java/com/android/internal/telephony/TelephonyCountryDetector.java
index 56e8b46..c2d6018 100644
--- a/src/java/com/android/internal/telephony/TelephonyCountryDetector.java
+++ b/src/java/com/android/internal/telephony/TelephonyCountryDetector.java
@@ -35,6 +35,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RegistrantList;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.telephony.Rlog;
@@ -42,6 +43,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.FeatureFlags;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -83,6 +85,8 @@
     @NonNull private final Geocoder mGeocoder;
     @NonNull private final LocationManager mLocationManager;
     @NonNull private final ConnectivityManager mConnectivityManager;
+    @NonNull private final RegistrantList mWifiConnectivityStateChangedRegistrantList =
+            new RegistrantList();
     @NonNull private final Object mLock = new Object();
     @NonNull
     @GuardedBy("mLock")
@@ -106,6 +110,10 @@
     private Map<String, Long> mOverriddenCachedNetworkCountryCodes = new HashMap<>();
     @GuardedBy("mLock")
     private boolean mIsCountryCodesOverridden = false;
+    private final RegistrantList mCountryCodeChangedRegistrants = new RegistrantList();
+
+    private FeatureFlags mFeatureFlags = null;
+
     @NonNull private final LocationListener mLocationListener = new LocationListener() {
         @Override
         public void onLocationChanged(Location location) {
@@ -183,22 +191,26 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected TelephonyCountryDetector(@NonNull Looper looper, @NonNull Context context,
             @NonNull LocationManager locationManager,
-            @NonNull ConnectivityManager connectivityManager) {
+            @NonNull ConnectivityManager connectivityManager,
+            FeatureFlags featureFlags) {
         super(looper);
         mLocationManager = locationManager;
         mGeocoder = new Geocoder(context);
         mConnectivityManager = connectivityManager;
+        mFeatureFlags = featureFlags;
         initialize();
     }
 
     /** @return the singleton instance of the {@link TelephonyCountryDetector} */
-    public static synchronized TelephonyCountryDetector getInstance(@NonNull Context context) {
+    public static synchronized TelephonyCountryDetector getInstance(@NonNull Context context,
+            FeatureFlags featureFlags) {
         if (sInstance == null) {
             HandlerThread handlerThread = new HandlerThread("TelephonyCountryDetector");
             handlerThread.start();
             sInstance = new TelephonyCountryDetector(handlerThread.getLooper(), context,
                     context.getSystemService(LocationManager.class),
-                    context.getSystemService(ConnectivityManager.class));
+                    context.getSystemService(ConnectivityManager.class),
+                    featureFlags);
         }
         return sInstance;
     }
@@ -286,6 +298,8 @@
                 handleNetworkCountryCodeChangedEvent((NetworkCountryCodeInfo) msg.obj);
                 break;
             case EVENT_WIFI_CONNECTIVITY_STATE_CHANGED:
+                handleEventWifiConnectivityStateChanged();
+                break;
             case EVENT_LOCATION_UPDATE_REQUEST_QUOTA_RESET:
                 evaluateRequestingLocationUpdates();
                 break;
@@ -468,6 +482,17 @@
             }
         }
         evaluateRequestingLocationUpdates();
+        if (mFeatureFlags.oemEnabledSatelliteFlag()) {
+            logd("mCountryCodeChangedRegistrants.notifyRegistrants()");
+            mCountryCodeChangedRegistrants.notifyRegistrants();
+        } else {
+            logd("mCountryCodeChangedRegistrants.notifyRegistrants() is not called");
+        }
+    }
+
+    private void handleEventWifiConnectivityStateChanged() {
+        mWifiConnectivityStateChangedRegistrantList.notifyResult(isWifiNetworkConnected());
+        evaluateRequestingLocationUpdates();
     }
 
     private void setLocationCountryCode(@NonNull Pair<String, Long> countryCodeInfo) {
@@ -533,7 +558,11 @@
         }
     }
 
-    private boolean isWifiNetworkConnected() {
+    /**
+     * Check whether Wi-Fi network is connected or not.
+     * @return {@code true} is Wi-Fi is connected, {@code false} otherwise.
+     */
+    public boolean isWifiNetworkConnected() {
         Network activeNetwork = mConnectivityManager.getActiveNetwork();
         NetworkCapabilities networkCapabilities =
                 mConnectivityManager.getNetworkCapabilities(activeNetwork);
@@ -543,6 +572,25 @@
     }
 
     /**
+     * Register a callback to receive Wi-Fi connectivity state changes.
+     * @param h Handler for notification message
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForWifiConnectivityStateChanged(@NonNull Handler h, int what,
+            @Nullable Object obj) {
+        mWifiConnectivityStateChangedRegistrantList.add(h, what, obj);
+    }
+
+    /**
+     * Unregisters for Wi-Fi connectivity state changes.
+     * @param h Handler to be removed from the registrant list.
+     */
+    public void unregisterForWifiConnectivityStateChanged(@NonNull Handler h) {
+        mWifiConnectivityStateChangedRegistrantList.remove(h);
+    }
+
+    /**
      * Check whether this is a valid country code.
      *
      * @param countryCode A 2-Character alphanumeric country code.
@@ -558,6 +606,26 @@
                 || SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false));
     }
 
+    /**
+     * Register a callback for country code changed events
+     *
+     * @param h    Handler to notify
+     * @param what msg.what when the message is delivered
+     * @param obj  AsyncResult.userObj when the message is delivered
+     */
+    public void registerForCountryCodeChanged(Handler h, int what, Object obj) {
+        mCountryCodeChangedRegistrants.add(h, what, obj);
+    }
+
+    /**
+     * Unregister a callback for country code changed events
+     *
+     * @param h Handler to notifyf
+     */
+    public void unregisterForCountryCodeChanged(Handler h) {
+        mCountryCodeChangedRegistrants.remove(h);
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
index e374811..64ecdc9 100644
--- a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
+++ b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
@@ -35,6 +35,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.VisualVoicemailSmsParser.WrappedMessageData;
+import com.android.internal.telephony.flags.Flags;
 
 import java.nio.ByteBuffer;
 import java.nio.charset.CharacterCodingException;
@@ -286,7 +287,11 @@
         intent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS, builder.build());
         intent.putExtra(VoicemailContract.EXTRA_TARGET_PACKAGE, filterSettings.packageName);
         intent.setPackage(TELEPHONY_SERVICE_PACKAGE);
-        context.sendBroadcast(intent);
+        if (Flags.hsumBroadcast()) {
+            context.sendBroadcastAsUser(intent, UserHandle.ALL);
+        } else {
+            context.sendBroadcast(intent);
+        }
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/WapPushOverSms.java b/src/java/com/android/internal/telephony/WapPushOverSms.java
index 7669411..9bede69 100644
--- a/src/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/src/java/com/android/internal/telephony/WapPushOverSms.java
@@ -23,6 +23,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
@@ -45,6 +46,7 @@
 import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.uicc.IccUtils;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.telephony.Rlog;
@@ -67,8 +69,14 @@
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private final Context mContext;
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private UserManager mUserManager;
+
     PowerWhitelistManager mPowerWhitelistManager;
 
+    protected final @NonNull FeatureFlags mFeatureFlags;
+
     private String mWapPushManagerPackage;
 
     /** Assigned from ServiceConnection callback on main threaad. */
@@ -128,11 +136,11 @@
         if (DBG) Rlog.v(TAG, "wappush manager disconnected.");
     }
 
-    public WapPushOverSms(Context context) {
+    public WapPushOverSms(Context context, FeatureFlags featureFlags) {
+        mFeatureFlags = featureFlags;
         mContext = context;
         mPowerWhitelistManager = mContext.getSystemService(PowerWhitelistManager.class);
-
-        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mUserManager = mContext.getSystemService(UserManager.class);
 
         bindWapPushManagerService(mContext);
     }
@@ -325,6 +333,7 @@
      *         {@link Activity#RESULT_OK} if the message has been broadcast
      *         to applications
      */
+    @SuppressLint("MissingPermission")
     public int dispatchWapPdu(byte[] pdu, InboundSmsHandler.SmsBroadcastReceiver receiver,
             InboundSmsHandler handler, String address, int subId, long messageId) {
         DecodedResult result = decodeWapPdu(pdu, handler);
@@ -422,7 +431,11 @@
         }
 
         if (userHandle == null) {
-            userHandle = UserHandle.SYSTEM;
+            if (mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser()) {
+                userHandle = mUserManager.getMainUser();
+            } else {
+                userHandle = UserHandle.SYSTEM;
+            }
         }
         handler.dispatchIntent(intent, getPermissionForType(result.mimeType),
                 getAppOpsStringPermissionForIntent(result.mimeType), options, receiver,
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 4da1622..5fdb8ce 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -40,6 +40,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -712,7 +713,12 @@
         intent.putExtra("SLOT_ID", mSlotId);
         intent.setComponent(AppInterface.getDefaultSTKApplication());
         CatLog.d(this, "Sending CmdMsg: " + cmdMsg+ " on slotid:" + mSlotId);
-        mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+
+        if (sFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL, AppInterface.STK_PERMISSION);
+        } else {
+            mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        }
     }
 
     /**
@@ -726,7 +732,11 @@
         Intent intent = new Intent(AppInterface.CAT_SESSION_END_ACTION);
         intent.putExtra("SLOT_ID", mSlotId);
         intent.setComponent(AppInterface.getDefaultSTKApplication());
-        mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        if (sFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL, AppInterface.STK_PERMISSION);
+        } else {
+            mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        }
     }
 
 
@@ -1090,7 +1100,11 @@
         intent.putExtra("SLOT_ID", mSlotId);
         CatLog.d(this, "Sending Card Status: "
                 + cardState + " " + "cardPresent: " + cardPresent +  "SLOT_ID: " +  mSlotId);
-        mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        if (sFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL, AppInterface.STK_PERMISSION);
+        } else {
+            mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        }
     }
 
     private void broadcastAlphaMessage(String alphaString) {
@@ -1100,7 +1114,11 @@
         intent.putExtra(AppInterface.ALPHA_STRING, alphaString);
         intent.putExtra("SLOT_ID", mSlotId);
         intent.setComponent(AppInterface.getDefaultSTKApplication());
-        mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        if (sFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL, AppInterface.STK_PERMISSION);
+        } else {
+            mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        }
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
index 784c974..9a85329 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
@@ -41,6 +41,7 @@
 import com.android.internal.telephony.cdma.sms.BearerData;
 import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.util.HexDump;
 
 import java.io.ByteArrayOutputStream;
@@ -78,8 +79,9 @@
      * Create a new inbound SMS handler for CDMA.
      */
     private CdmaInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor,
-            Phone phone, CdmaSMSDispatcher smsDispatcher, Looper looper) {
-        super("CdmaInboundSmsHandler", context, storageMonitor, phone, looper);
+            Phone phone, CdmaSMSDispatcher smsDispatcher, Looper looper,
+            FeatureFlags featureFlags) {
+        super("CdmaInboundSmsHandler", context, storageMonitor, phone, looper, featureFlags);
         mSmsDispatcher = smsDispatcher;
         phone.mCi.setOnNewCdmaSms(getHandler(), EVENT_NEW_SMS, null);
 
@@ -171,9 +173,9 @@
      */
     public static CdmaInboundSmsHandler makeInboundSmsHandler(Context context,
             SmsStorageMonitor storageMonitor, Phone phone, CdmaSMSDispatcher smsDispatcher,
-            Looper looper) {
+            Looper looper, FeatureFlags featureFlags) {
         CdmaInboundSmsHandler handler = new CdmaInboundSmsHandler(context, storageMonitor,
-                phone, smsDispatcher, looper);
+                phone, smsDispatcher, looper, featureFlags);
         handler.start();
         return handler;
     }
diff --git a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameData.java b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameData.java
index 59745dc..597bad3 100644
--- a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameData.java
+++ b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameData.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.Rlog;
 
 import java.util.Objects;
 
@@ -25,6 +26,9 @@
  * A container of carrier display name.
  */
 public class CarrierDisplayNameData implements Parcelable {
+
+    private static final String LOG_TAG = "CarrierDisplayNameData";
+
     /** Service provider name. */
     private final String mSpn;
 
@@ -40,8 +44,18 @@
     /** {@code True} if display PLMN network name is required. */
     private final boolean mShowPlmn;
 
-    private CarrierDisplayNameData(String spn, String dataSpn, boolean showSpn, String plmn,
-            boolean showPlmn) {
+    private CarrierDisplayNameData(
+            String spn, String dataSpn, boolean showSpn, String plmn, boolean showPlmn) {
+        final String logString = "Data SPN must be provided if SPN is provided";
+        if (spn != null && dataSpn == null) {
+            Rlog.e(LOG_TAG, logString);
+            if (android.os.Build.isDebuggable()) {
+                throw new IllegalArgumentException(logString);
+            } else {
+                dataSpn = spn;
+            }
+        }
+
         this.mSpn = spn;
         this.mDataSpn = dataSpn;
         this.mShowSpn = showSpn;
diff --git a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
index 4b5eebc..3837790 100644
--- a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
+++ b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
@@ -340,6 +340,7 @@
 
         return new CarrierDisplayNameData.Builder()
                 .setSpn(spn)
+                .setDataSpn(spn)
                 .setShowSpn(showSpn)
                 .setPlmn(plmn)
                 .setShowPlmn(showPlmn)
@@ -477,7 +478,8 @@
         CarrierDisplayNameData data = getCarrierDisplayNameFromEf();
         if (DBG) Rlog.d(TAG, "CarrierName from EF: " + data);
         if ((mPhone.getImsPhone() != null) && (mPhone.getImsPhone().getImsRegistrationTech()
-                == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM)) {
+                == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM
+                && mPhone.isImsRegistered())) {
             data = getCarrierDisplayNameFromCrossSimCallingOverride(data);
             if (DBG) {
                 Rlog.d(TAG, "CarrierName override by Cross-SIM Calling " + data);
diff --git a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
index bc684af..1bbc692 100644
--- a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
+++ b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
@@ -414,15 +414,19 @@
 
         // bindQualifiedNetworksService posts real work to handler thread. So here we can
         // let the callback execute in binder thread to avoid post twice.
-        mCarrierConfigManager.registerCarrierConfigChangeListener(Runnable::run,
-                (slotIndex, subId, carrierId, specificCarrierId) -> {
-                    if (slotIndex != mPhone.getPhoneId()) return;
-                    // 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.
-                    if (DBG) log("Carrier config changed. Try to bind qualified network service.");
-                    bindQualifiedNetworksService();
-                });
+        if (mCarrierConfigManager != null) {
+            mCarrierConfigManager.registerCarrierConfigChangeListener(Runnable::run,
+                    (slotIndex, subId, carrierId, specificCarrierId) -> {
+                        if (slotIndex != mPhone.getPhoneId()) return;
+                        // 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.
+                        if (DBG) {
+                            log("Carrier config changed. Try to bind qualified network service.");
+                        }
+                        bindQualifiedNetworksService();
+                    });
+        }
         bindQualifiedNetworksService();
 
         // Using post to delay the registering because data retry manager and data config
diff --git a/src/java/com/android/internal/telephony/data/DataConfigManager.java b/src/java/com/android/internal/telephony/data/DataConfigManager.java
index 9f4fa11..89b0fec 100644
--- a/src/java/com/android/internal/telephony/data/DataConfigManager.java
+++ b/src/java/com/android/internal/telephony/data/DataConfigManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.StringDef;
 import android.content.res.Resources;
 import android.net.LinkProperties;
@@ -272,7 +273,7 @@
     private final String mLogTag;
     @NonNull
     private final FeatureFlags mFeatureFlags;
-    @NonNull
+    @Nullable
     private final CarrierConfigManager mCarrierConfigManager;
     @NonNull
     private PersistableBundle mCarrierConfig = null;
@@ -355,12 +356,14 @@
 
         mCarrierConfigManager = mPhone.getContext().getSystemService(CarrierConfigManager.class);
         // Callback send msg to handler thread, so callback itself can be executed in binder thread.
-        mCarrierConfigManager.registerCarrierConfigChangeListener(Runnable::run,
-                (slotIndex, subId, carrierId, specificCarrierId) -> {
-                    if (slotIndex == mPhone.getPhoneId()) {
-                        sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED);
-                    }
-                });
+        if (mCarrierConfigManager != null) {
+            mCarrierConfigManager.registerCarrierConfigChangeListener(Runnable::run,
+                    (slotIndex, subId, carrierId, specificCarrierId) -> {
+                        if (slotIndex == mPhone.getPhoneId()) {
+                            sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED);
+                        }
+                    });
+        }
 
         // Register for device config update
         DeviceConfig.addOnPropertiesChangedListener(
@@ -640,10 +643,8 @@
         // Consumer slices are the slices that are allowed to be accessed by regular application to
         // get better performance. They should be metered. This can be turned into configurations in
         // the future.
-        if (mFeatureFlags.meteredEmbbUrlcc()) {
-            meteredCapabilities.add(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH);
-            meteredCapabilities.add(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY);
-        }
+        meteredCapabilities.add(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH);
+        meteredCapabilities.add(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY);
 
         return Collections.unmodifiableSet(meteredCapabilities);
     }
@@ -758,7 +759,6 @@
      */
     public boolean allowBringUpNetworkInNonVops(@NetworkRegistrationInfo.RegistrationState
             int regState) {
-        if (!mFeatureFlags.allowMmtelInNonVops()) return false;
         int networkType = -1;
         if (regState == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
             networkType = CarrierConfigManager.Ims.NETWORK_TYPE_HOME;
@@ -1503,14 +1503,6 @@
     }
 
     /**
-     * {@code True} enables mms to be attempted on iwlan if possible, even if existing cellular
-     *  networks already supports iwlan.
-     */
-    public boolean isForceIwlanMmsFeatureEnabled() {
-        return mResources.getBoolean(com.android.internal.R.bool.force_iwlan_mms_feature_enabled);
-    }
-
-    /**
      * Log debug messages.
      * @param s debug messages
      */
diff --git a/src/java/com/android/internal/telephony/data/DataEvaluation.java b/src/java/com/android/internal/telephony/data/DataEvaluation.java
index f5eae91..40c0081 100644
--- a/src/java/com/android/internal/telephony/data/DataEvaluation.java
+++ b/src/java/com/android/internal/telephony/data/DataEvaluation.java
@@ -225,6 +225,8 @@
         SIM_LOADED(true),
         /** SIM is removed. */
         SIM_REMOVAL(true),
+        /** SIM is disabled. */
+        SIM_DISABLED(true),
         /** Data profiles changed. */
         DATA_PROFILES_CHANGED(true),
         /** When service state changes.(For now only considering data RAT and data registration). */
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index 87c392e..738c60f 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -100,6 +100,7 @@
 import com.android.internal.telephony.data.DataRetryManager.DataRetryEntry;
 import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
 import com.android.internal.telephony.data.LinkBandwidthEstimator.LinkBandwidthEstimatorCallback;
+import com.android.internal.telephony.data.PhoneSwitcher.PhoneSwitcherCallback;
 import com.android.internal.telephony.data.TelephonyNetworkAgent.TelephonyNetworkAgentCallback;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.DataCallSessionStats;
@@ -271,6 +272,9 @@
     /** Event for response to data network validation request. */
     private static final int EVENT_DATA_NETWORK_VALIDATION_RESPONSE = 29;
 
+    /** Event for preferred data subscription changed. */
+    private static final int EVENT_PREFERRED_DATA_SUBSCRIPTION_CHANGED = 30;
+
     /** Invalid context id. */
     private static final int INVALID_CID = -1;
 
@@ -460,7 +464,10 @@
             // Dynamically add and remove MMTEL capability when network transition between VoPS
             // and non-VoPS network if the request is not MMTEL. For MMTEL, we retain the capability
             // to prevent immediate tear down.
-            NetworkCapabilities.NET_CAPABILITY_MMTEL
+            NetworkCapabilities.NET_CAPABILITY_MMTEL,
+            // Dynamically add and remove MMS capability depending on QNS's preference if there is
+            // a transport specific APN alternative.
+            NetworkCapabilities.NET_CAPABILITY_MMS
     );
 
     /** The parent state. Any messages not handled by the child state fallback to this. */
@@ -588,6 +595,10 @@
     @NonNull
     private final DataNetworkController mDataNetworkController;
 
+    /** Phone switcher which is responsible to determine which phone to route network request. */
+    @NonNull
+    private final PhoneSwitcher mPhoneSwitcher;
+
     /** Data network controller callback. */
     @NonNull
     private final DataNetworkController.DataNetworkControllerCallback
@@ -673,6 +684,13 @@
     /** Whether the current data network is congested. */
     private boolean mCongested = false;
 
+    /**
+     * Whether the current data network is on preferred data modem.
+     *
+     * @see PhoneSwitcher#getPreferredDataPhoneId()
+     */
+    private boolean mOnPreferredDataPhone;
+
     /** The network requests associated with this data network */
     @NonNull
     private final NetworkRequestList mAttachedNetworkRequestList =
@@ -812,6 +830,12 @@
     private PreciseDataConnectionState mPreciseDataConnectionState;
 
     /**
+     * Callback to listen event from {@link PhoneSwitcher}.
+     */
+    @NonNull
+    private PhoneSwitcherCallback mPhoneSwitcherCallback;
+
+    /**
      * The network bandwidth.
      */
     public static class NetworkBandwidth {
@@ -1024,6 +1048,8 @@
         mAccessNetworksManager = phone.getAccessNetworksManager();
         mVcnManager = mPhone.getContext().getSystemService(VcnManager.class);
         mDataNetworkController = phone.getDataNetworkController();
+        mPhoneSwitcher = PhoneSwitcher.getInstance();
+        mOnPreferredDataPhone = phone.getPhoneId() == mPhoneSwitcher.getPreferredDataPhoneId();
         mDataNetworkControllerCallback = new DataNetworkController.DataNetworkControllerCallback(
                 getHandler()::post) {
             @Override
@@ -1153,14 +1179,25 @@
             configBuilder.setNat64DetectionEnabled(false);
         }
 
-        final NetworkFactory factory = PhoneFactory.getNetworkFactory(
-                mPhone.getPhoneId());
-        final NetworkProvider provider = (null == factory) ? null : factory.getProvider();
+        NetworkProvider provider;
+        if (mFlags.supportNetworkProvider()) {
+            provider = PhoneFactory.getNetworkProvider();
+        } else {
+            final NetworkFactory factory = PhoneFactory.getNetworkFactory(
+                    mPhone.getPhoneId());
+            provider = (null == factory) ? null : factory.getProvider();
+        }
 
-        mNetworkScore = new NetworkScore.Builder()
-               .setKeepConnectedReason(isHandoverInProgress()
+        NetworkScore.Builder builder = new NetworkScore.Builder()
+                .setKeepConnectedReason(isHandoverInProgress()
                         ? NetworkScore.KEEP_CONNECTED_FOR_HANDOVER
-                        : NetworkScore.KEEP_CONNECTED_NONE).build();
+                        : NetworkScore.KEEP_CONNECTED_NONE);
+        if (mFlags.supportNetworkProvider()) {
+            builder.setTransportPrimary(mOnPreferredDataPhone);
+        }
+        mNetworkScore = builder.build();
+        logl("mNetworkScore: isPrimary=" + mNetworkScore.isTransportPrimary()
+                + ", keepConnectedReason=" + mNetworkScore.getKeepConnectedReason());
 
         return new TelephonyNetworkAgent(mPhone, getHandler().getLooper(), this,
                 mNetworkScore, configBuilder.build(), provider,
@@ -1222,6 +1259,16 @@
             mDataNetworkController.getDataSettingsManager()
                     .registerCallback(mDataSettingsManagerCallback);
 
+            if (mFlags.supportNetworkProvider()) {
+                mPhoneSwitcherCallback = new PhoneSwitcherCallback(Runnable::run) {
+                    @Override
+                    public void onPreferredDataPhoneIdChanged(int phoneId) {
+                        sendMessage(EVENT_PREFERRED_DATA_SUBSCRIPTION_CHANGED, phoneId, 0);
+                    }
+                };
+                mPhoneSwitcher.registerCallback(mPhoneSwitcherCallback);
+            }
+
             mPhone.getDisplayInfoController().registerForTelephonyDisplayInfoChanged(
                     getHandler(), EVENT_DISPLAY_INFO_CHANGED, null);
             mPhone.getServiceStateTracker().registerForServiceStateChanged(getHandler(),
@@ -1263,21 +1310,19 @@
                         getHandler(), EVENT_VOICE_CALL_ENDED, null);
             }
 
-            if (mFlags.forceIwlanMms() && mDataConfigManager.isForceIwlanMmsFeatureEnabled()) {
-                if (mDataProfile.canSatisfy(NetworkCapabilities.NET_CAPABILITY_MMS)) {
-                    mAccessNetworksManagerCallback = new AccessNetworksManagerCallback(
-                            getHandler()::post) {
-                        @Override
-                        public void onPreferredTransportChanged(
-                                @NetCapability int networkCapability, boolean forceReconnect) {
-                            if (networkCapability == NetworkCapabilities.NET_CAPABILITY_MMS) {
-                                log("MMS preference changed.");
-                                updateNetworkCapabilities();
-                            }
+            if (mDataProfile.canSatisfy(NetworkCapabilities.NET_CAPABILITY_MMS)) {
+                mAccessNetworksManagerCallback = new AccessNetworksManagerCallback(
+                        getHandler()::post) {
+                    @Override
+                    public void onPreferredTransportChanged(
+                            @NetCapability int networkCapability, boolean forceReconnect) {
+                        if (networkCapability == NetworkCapabilities.NET_CAPABILITY_MMS) {
+                            log("MMS preference changed.");
+                            updateNetworkCapabilities();
                         }
-                    };
-                    mAccessNetworksManager.registerCallback(mAccessNetworksManagerCallback);
-                }
+                    }
+                };
+                mAccessNetworksManager.registerCallback(mAccessNetworksManagerCallback);
             }
 
             // Only add symmetric code here, for example, registering and unregistering.
@@ -1289,8 +1334,7 @@
         @Override
         public void exit() {
             logv("Unregistering all events.");
-            if (mFlags.forceIwlanMms() && mAccessNetworksManagerCallback != null
-                    && mDataConfigManager.isForceIwlanMmsFeatureEnabled()) {
+            if (mAccessNetworksManagerCallback != null) {
                 mAccessNetworksManager.unregisterCallback(mAccessNetworksManagerCallback);
             }
 
@@ -1316,6 +1360,9 @@
             mPhone.getServiceStateTracker().unregisterForServiceStateChanged(getHandler());
             mPhone.getDisplayInfoController().unregisterForTelephonyDisplayInfoChanged(
                     getHandler());
+            if (mFlags.supportNetworkProvider()) {
+                mPhoneSwitcher.unregisterCallback(mPhoneSwitcherCallback);
+            }
             mDataNetworkController.getDataSettingsManager()
                     .unregisterCallback(mDataSettingsManagerCallback);
             mRil.unregisterForPcoData(getHandler());
@@ -1349,13 +1396,13 @@
                 }
                 case EVENT_ATTACH_NETWORK_REQUEST: {
                     onAttachNetworkRequests((NetworkRequestList) msg.obj);
-                    updateNetworkScore(isHandoverInProgress());
+                    updateNetworkScore();
                     break;
                 }
                 case EVENT_DETACH_NETWORK_REQUEST: {
                     onDetachNetworkRequest((TelephonyNetworkRequest) msg.obj,
                             msg.arg1 != 0 /* shouldRetry */);
-                    updateNetworkScore(isHandoverInProgress());
+                    updateNetworkScore();
                     break;
                 }
                 case EVENT_DETACH_ALL_NETWORK_REQUESTS: {
@@ -1426,6 +1473,12 @@
                     // handle the resultCode in response for the request.
                     handleDataNetworkValidationRequestResultCode(msg.arg1 /* resultCode */);
                     break;
+                case EVENT_PREFERRED_DATA_SUBSCRIPTION_CHANGED:
+                    mOnPreferredDataPhone = mPhone.getPhoneId() == msg.arg1;
+                    logl("Preferred data phone id changed to " + msg.arg1
+                            + ", mOnPreferredDataPhone=" + mOnPreferredDataPhone);
+                    updateNetworkScore();
+                    break;
                 default:
                     loge("Unhandled event " + eventToString(msg.what));
                     break;
@@ -2353,14 +2406,7 @@
                     DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo();
                     // Check if the network is non-VoPS.
                     if (dsri != null && dsri.getVopsSupportInfo() != null
-                            && !dsri.getVopsSupportInfo().isVopsSupported()
-                            // Reflect the actual MMTEL if flag on.
-                            && (mFlags.allowMmtelInNonVops()
-                            // Deceive Connectivity service to satisfy an MMTEL request, this should
-                            // be useless because we reach here if no MMTEL request, then removing
-                            // MMTEL capability shouldn't have any impacts.
-                            || !mDataConfigManager.shouldKeepNetworkUpInNonVops(
-                                    nri.getNetworkRegistrationState()))) {
+                            && !dsri.getVopsSupportInfo().isVopsSupported()) {
                         builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL);
                     }
                     log("updateNetworkCapabilities: dsri=" + dsri);
@@ -2488,9 +2534,7 @@
         // Check if the feature force MMS on IWLAN is enabled. When the feature is enabled, MMS
         // will be attempted on IWLAN if possible, even if existing cellular networks already
         // supports IWLAN.
-        if (mFlags.forceIwlanMms() && builder.build()
-                .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
-                && mDataConfigManager.isForceIwlanMmsFeatureEnabled()) {
+        if (builder.build().hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
             // If QNS sets MMS preferred on IWLAN, and it is possible to setup an MMS network on
             // IWLAN, then we need to remove the MMS capability on the cellular network. This will
             // allow the new MMS network to be brought up on IWLAN when MMS network request arrives.
@@ -3313,6 +3357,12 @@
         return mLinkStatus;
     }
 
+    /**
+     * Update the network score and report to connectivity service if necessary.
+     */
+    private void updateNetworkScore() {
+        updateNetworkScore(isHandoverInProgress());
+    }
 
     /**
      * Update the network score and report to connectivity service if necessary.
@@ -3322,10 +3372,18 @@
     private void updateNetworkScore(boolean keepConnectedForHandover) {
         int connectedReason = keepConnectedForHandover
                 ? NetworkScore.KEEP_CONNECTED_FOR_HANDOVER : NetworkScore.KEEP_CONNECTED_NONE;
-        if (mNetworkScore.getKeepConnectedReason() != connectedReason) {
-            mNetworkScore = new NetworkScore.Builder()
-                    .setKeepConnectedReason(connectedReason).build();
+        if (mNetworkScore.getKeepConnectedReason() != connectedReason
+                || (mFlags.supportNetworkProvider()
+                && mNetworkScore.isTransportPrimary() != mOnPreferredDataPhone)) {
+            NetworkScore.Builder builder = new NetworkScore.Builder()
+                    .setKeepConnectedReason(connectedReason);
+            if (mFlags.supportNetworkProvider()) {
+                builder.setTransportPrimary(mOnPreferredDataPhone);
+            }
+            mNetworkScore = builder.build();
             mNetworkAgent.sendNetworkScore(mNetworkScore);
+            logl("updateNetworkScore: isPrimary=" + mNetworkScore.isTransportPrimary()
+                    + ", keepConnectedForHandover=" + keepConnectedForHandover);
         }
     }
 
@@ -3359,7 +3417,7 @@
     public int getApnTypeNetworkCapability() {
         if (!mAttachedNetworkRequestList.isEmpty()) {
             // The highest priority network request is always at the top of list.
-            return mAttachedNetworkRequestList.get(0).getApnTypeNetworkCapability();
+            return mAttachedNetworkRequestList.get(0).getHighestPriorityApnTypeNetworkCapability();
         } else {
             return Arrays.stream(getNetworkCapabilities().getCapabilities()).boxed()
                     .filter(cap -> DataUtils.networkCapabilityToApnType(cap)
@@ -4054,12 +4112,14 @@
         pw.println("Tag: " + name());
         pw.increaseIndent();
         pw.println("mSubId=" + mSubId);
+        pw.println("mOnPreferredDataPhone=" + mOnPreferredDataPhone);
         pw.println("mTransport=" + AccessNetworkConstants.transportTypeToString(mTransport));
         pw.println("mLastKnownDataNetworkType=" + TelephonyManager
                 .getNetworkTypeName(mLastKnownDataNetworkType));
         pw.println("WWAN cid=" + mCid.get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN));
         pw.println("WLAN cid=" + mCid.get(AccessNetworkConstants.TRANSPORT_TYPE_WLAN));
         pw.println("mNetworkScore=" + mNetworkScore);
+        pw.println("keepConnectedReason=" + mNetworkScore.getKeepConnectedReason());
         pw.println("mDataAllowedReason=" + mDataAllowedReason);
         pw.println("mPduSessionId=" + mPduSessionId);
         pw.println("mDataProfile=" + mDataProfile);
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 010e0fe..9e432e1 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -1299,6 +1299,13 @@
      * @param networkRequest The network request.
      */
     private void onAddNetworkRequest(@NonNull TelephonyNetworkRequest networkRequest) {
+        // TelephonyNetworkRequest at TelephonyNetworkProvider layer does not have config assigned
+        // (Because TelephonyNetworkProvider is a singleton across all SIMs. We are not able to
+        // retrieve the right carrier config for it.). So as soon as the request arrives
+        // DataNetworkController, we need to update the config in the request so it can update
+        // some of its config-dependent properties like request priority.
+        networkRequest.updateDataConfig(mDataConfigManager);
+
         // To detect IMS back-to-back release-request anomaly event
         if (mLastImsOperationIsRelease) {
             mLastImsOperationIsRelease = false;
@@ -1501,11 +1508,9 @@
                         .build(), mPhone, mFeatureFlags);
         // If we don't skip checking existing network, then we should check If one of the
         // existing networks can satisfy the internet request, then internet is allowed.
-        if ((!mFeatureFlags.ignoreExistingNetworksForInternetAllowedChecking()
-                || !ignoreExistingNetworks)
-                && mDataNetworkList.stream().anyMatch(
-                        dataNetwork -> internetRequest.canBeSatisfiedBy(
-                                dataNetwork.getNetworkCapabilities()))) {
+        if (!ignoreExistingNetworks && mDataNetworkList.stream().anyMatch(
+                dataNetwork -> internetRequest.canBeSatisfiedBy(
+                        dataNetwork.getNetworkCapabilities()))) {
             return new DataEvaluation(DataEvaluationReason.EXTERNAL_QUERY);
         }
 
@@ -1579,7 +1584,7 @@
             @NonNull TelephonyNetworkRequest networkRequest, DataEvaluationReason reason) {
         DataEvaluation evaluation = new DataEvaluation(reason);
         int transport = mAccessNetworksManager.getPreferredTransportByNetworkCapability(
-                networkRequest.getApnTypeNetworkCapability());
+                networkRequest.getHighestPriorityApnTypeNetworkCapability());
 
         // Check if the request can be satisfied by cellular network or satellite network.
         if (mFeatureFlags.satelliteInternet()
@@ -1711,7 +1716,7 @@
 
         if (mDataSettingsManager.isDataInitialized()) {
             if (!mDataSettingsManager.isDataEnabled(DataUtils.networkCapabilityToApnType(
-                    networkRequest.getApnTypeNetworkCapability()))) {
+                    networkRequest.getHighestPriorityApnTypeNetworkCapability()))) {
                 evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_DISABLED);
             }
         } else {
@@ -1746,7 +1751,9 @@
                 // Check if request is unmetered (WiFi or unmetered APN).
                 evaluation.addDataAllowedReason(DataAllowedReason.UNMETERED_USAGE);
             } else if (transport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
-                if (!networkRequest.isMeteredRequest()) {
+                boolean isMeteredRequest = mDataConfigManager.isAnyMeteredCapability(
+                        networkRequest.getCapabilities(), mServiceState.getDataRoaming());
+                if (!isMeteredRequest) {
                     evaluation.addDataAllowedReason(DataAllowedReason.UNMETERED_USAGE);
                 }
             }
@@ -1985,7 +1992,7 @@
             if (mAllNetworkRequestList.stream()
                     .filter(request -> dataNetwork.getTransport()
                             == mAccessNetworksManager.getPreferredTransportByNetworkCapability(
-                                    request.getApnTypeNetworkCapability()))
+                                    request.getHighestPriorityApnTypeNetworkCapability()))
                     .filter(request
                             -> !hasCapabilityExemptsFromSinglePdnRule(request.getCapabilities()))
                     .anyMatch(request -> request.getPriority() > dataNetwork.getPriority())) {
@@ -2507,6 +2514,30 @@
     }
 
     private void onRemoveNetworkRequest(@NonNull TelephonyNetworkRequest request) {
+        if (mFeatureFlags.supportNetworkProvider()) {
+            if (!mAllNetworkRequestList.remove(request)) {
+                loge("onRemoveNetworkRequest: Network request does not exist. " + request);
+                return;
+            }
+
+            if (request.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
+                mImsThrottleCounter.addOccurrence();
+                mLastReleasedImsRequestCapabilities = request.getCapabilities();
+                mLastImsOperationIsRelease = true;
+            }
+
+            if (request.getAttachedNetwork() != null) {
+                request.getAttachedNetwork().detachNetworkRequest(
+                        request, false /* shouldRetry */);
+            }
+
+            request.setState(TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED);
+            request.setEvaluation(null);
+
+            log("onRemoveNetworkRequest: Removed " + request);
+            return;
+        }
+
         // The request generated from telephony network factory does not contain the information
         // the original request has, for example, attached data network. We need to find the
         // original one.
@@ -2527,7 +2558,7 @@
 
         if (networkRequest.getAttachedNetwork() != null) {
             networkRequest.getAttachedNetwork().detachNetworkRequest(
-                        networkRequest, false /* shouldRetry */);
+                    networkRequest, false /* shouldRetry */);
         }
         log("onRemoveNetworkRequest: Removed " + networkRequest);
     }
@@ -2869,7 +2900,7 @@
         }
 
         int transport = mAccessNetworksManager.getPreferredTransportByNetworkCapability(
-                networkRequestList.get(0).getApnTypeNetworkCapability());
+                networkRequestList.get(0).getHighestPriorityApnTypeNetworkCapability());
         logl("Creating data network on "
                 + AccessNetworkConstants.transportTypeToString(transport) + " with " + dataProfile
                 + ", and attaching " + networkRequestList.size() + " network requests to it.");
@@ -3141,7 +3172,8 @@
         log("onDataNetworkSetupRetry: Request list:" + requestList);
         TelephonyNetworkRequest telephonyNetworkRequest = requestList.get(0);
 
-        int networkCapability = telephonyNetworkRequest.getApnTypeNetworkCapability();
+        int networkCapability = telephonyNetworkRequest
+                .getHighestPriorityApnTypeNetworkCapability();
         int preferredTransport = mAccessNetworksManager.getPreferredTransportByNetworkCapability(
                 networkCapability);
         if (preferredTransport != dataSetupRetryEntry.transport) {
@@ -3163,7 +3195,7 @@
                 telephonyNetworkRequest, DataEvaluationReason.DATA_RETRY);
         if (!evaluation.containsDisallowedReasons()) {
             DataProfile dataProfile = dataSetupRetryEntry.dataProfile;
-            if (dataProfile == null) {
+            if (dataProfile == null || !mDataProfileManager.isDataProfileCompatible(dataProfile)) {
                 dataProfile = evaluation.getCandidateDataProfile();
             }
             if (dataProfile != null) {
@@ -3511,15 +3543,6 @@
     }
 
     /**
-     * Called when SIM is absent.
-     */
-    private void onSimAbsent() {
-        log("onSimAbsent");
-        sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
-                DataEvaluationReason.SIM_REMOVAL));
-    }
-
-    /**
      * Called when SIM state changes.
      *
      * @param simState SIM state. (Note this is mixed with card state and application state.)
@@ -3527,13 +3550,22 @@
     private void onSimStateChanged(@SimState int simState) {
         log("onSimStateChanged: state=" + TelephonyManager.simStateToString(simState));
         if (mSimState != simState) {
-            mSimState = simState;
             if (simState == TelephonyManager.SIM_STATE_ABSENT) {
-                onSimAbsent();
+                log("onSimStateChanged: SIM absent.");
+                sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
+                        DataEvaluationReason.SIM_REMOVAL));
+            } else if (simState == TelephonyManager.SIM_STATE_NOT_READY
+                    && mSimState == TelephonyManager.SIM_STATE_LOADED) {
+                if (mFeatureFlags.simDisabledGracefulTearDown()) {
+                    log("onSimStateChanged: SIM disabled.");
+                    sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
+                            DataEvaluationReason.SIM_DISABLED));
+                }
             } else if (simState == TelephonyManager.SIM_STATE_LOADED) {
                 sendMessage(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS,
                         DataEvaluationReason.SIM_LOADED));
             }
+            mSimState = simState;
             mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
                     () -> callback.onSimStateChanged(mSimState)));
         }
@@ -3609,8 +3641,7 @@
             dataNetwork.startHandover(targetTransport, dataHandoverRetryEntry);
         } else if (dataNetwork.shouldDelayImsTearDownDueToInCall()
                 && (dataEvaluation.containsOnly(DataDisallowedReason.NOT_IN_SERVICE)
-                || mFeatureFlags.relaxHoTeardown() && dataEvaluation.isSubsetOf(
-                        DataDisallowedReason.NOT_IN_SERVICE,
+                || dataEvaluation.isSubsetOf(DataDisallowedReason.NOT_IN_SERVICE,
                         DataDisallowedReason.NOT_ALLOWED_BY_POLICY))) {
             // We try our best to preserve the voice call by retrying later
             if (dataHandoverRetryEntry != null) {
diff --git a/src/java/com/android/internal/telephony/data/DataProfileManager.java b/src/java/com/android/internal/telephony/data/DataProfileManager.java
index c50d50a..28fe935 100644
--- a/src/java/com/android/internal/telephony/data/DataProfileManager.java
+++ b/src/java/com/android/internal/telephony/data/DataProfileManager.java
@@ -432,35 +432,18 @@
      */
     private void onInternetDataNetworkConnected(@NonNull Set<DataNetwork> internetNetworks) {
         DataProfile defaultProfile = null;
-        if (mFeatureFlags.refinePreferredDataProfileSelection()) {
-            // Most of the cases there should be only one.
-            // but in case there are multiple, find the default internet network, and choose the
-            // one which has longest life cycle.
-            defaultProfile = internetNetworks.stream()
-                    .filter(network -> mPreferredDataProfile == null
-                            // Find the one most resembles the current preferred profile,
-                            // avoiding e.g. DUN default network.
-                            || canPreferredDataProfileSatisfy(
-                            network.getAttachedNetworkRequestList()))
-                    .map(DataNetwork::getDataProfile)
-                    .min(Comparator.comparingLong(DataProfile::getLastSetupTimestamp))
-                    .orElse(null);
-        } else {
-            if (internetNetworks.size() == 1) {
-                // Most of the cases there should be only one.
-                defaultProfile = internetNetworks.stream().findFirst().get().getDataProfile();
-            } else if (internetNetworks.size() > 1) {
-                // but in case there are multiple, find the default internet network, and choose the
-                // one which has longest life cycle.
-                defaultProfile = internetNetworks.stream()
-                        .filter(network -> mPreferredDataProfile == null
-                                || canPreferredDataProfileSatisfy(
-                                network.getAttachedNetworkRequestList()))
-                        .map(DataNetwork::getDataProfile)
-                        .min(Comparator.comparingLong(DataProfile::getLastSetupTimestamp))
-                        .orElse(null);
-            }
-        }
+        // Most of the cases there should be only one.
+        // but in case there are multiple, find the default internet network, and choose the
+        // one which has longest life cycle.
+        defaultProfile = internetNetworks.stream()
+                .filter(network -> mPreferredDataProfile == null
+                        // Find the one most resembles the current preferred profile,
+                        // avoiding e.g. DUN default network.
+                        || canPreferredDataProfileSatisfy(
+                        network.getAttachedNetworkRequestList()))
+                .map(DataNetwork::getDataProfile)
+                .min(Comparator.comparingLong(DataProfile::getLastSetupTimestamp))
+                .orElse(null);
 
         // Update a working internet data profile by subid as a future candidate for preferred
         // data profile after APNs are reset to default
diff --git a/src/java/com/android/internal/telephony/data/DataRetryManager.java b/src/java/com/android/internal/telephony/data/DataRetryManager.java
index d35e9e8..318c4b5 100644
--- a/src/java/com/android/internal/telephony/data/DataRetryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataRetryManager.java
@@ -22,11 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.net.NetworkCapabilities;
 import android.os.AsyncResult;
 import android.os.Handler;
@@ -79,11 +74,6 @@
 public class DataRetryManager extends Handler {
     private static final boolean VDBG = false;
 
-    /** Intent of Alarm Manager for long retry timer. */
-    private static final String ACTION_RETRY = "com.android.internal.telephony.data.ACTION_RETRY";
-    /** The extra key for the hashcode of the retry entry for Alarm Manager. */
-    private static final String ACTION_RETRY_EXTRA_HASHCODE = "extra_retry_hashcode";
-
     /** Event for data setup retry. */
     private static final int EVENT_DATA_SETUP_RETRY = 3;
 
@@ -1078,22 +1068,6 @@
         mRil.registerForOn(this, EVENT_RADIO_ON, null);
         mRil.registerForModemReset(this, EVENT_MODEM_RESET, null);
 
-        if (!mFlags.useAlarmCallback()) {
-            // Register intent of alarm manager for long retry timer
-            IntentFilter intentFilter = new IntentFilter();
-            intentFilter.addAction(ACTION_RETRY);
-            mPhone.getContext().registerReceiver(new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    if (ACTION_RETRY.equals(intent.getAction())) {
-                        DataRetryManager.this.onAlarmIntentRetry(
-                                intent.getIntExtra(ACTION_RETRY_EXTRA_HASHCODE,
-                                        -1 /*Bad hashcode*/));
-                    }
-                }
-            }, intentFilter);
-        }
-
         if (mDataConfigManager.shouldResetDataThrottlingWhenTacChanges()) {
             mPhone.getServiceStateTracker().registerForAreaCodeChanged(this, EVENT_TAC_CHANGED,
                     null);
@@ -1277,7 +1251,8 @@
                 return;
             }
             for (NetworkRequestList networkRequestList : groupedNetworkRequestLists) {
-                int capability = networkRequestList.get(0).getApnTypeNetworkCapability();
+                int capability = networkRequestList.get(0)
+                        .getHighestPrioritySupportedNetworkCapability();
                 if (retryRule.canBeMatched(capability, cause)) {
                     // Check if there is already a similar network request retry scheduled.
                     if (isSimilarNetworkRequestRetryScheduled(
@@ -1492,7 +1467,8 @@
                                 mPhone.getCarrierId());
                         continue;
                     }
-                    if (entry.networkRequestList.get(0).getApnTypeNetworkCapability()
+                    if (entry.networkRequestList.get(0)
+                            .getHighestPrioritySupportedNetworkCapability()
                             == networkCapability
                             && entry.appliedDataRetryRule.equals(dataRetryRule)) {
                         if (entry.getState() == DataRetryEntry.RETRY_STATE_SUCCEEDED
@@ -1529,32 +1505,19 @@
                             ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, dataRetryEntry),
                     dataRetryEntry.retryDelayMillis);
         } else {
-            if (mFlags.useAlarmCallback()) {
-                // No need to wake up the device, the retry can wait util next time the device wake
-                // up to save power.
-                mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME,
-                        dataRetryEntry.retryElapsedTime,
-                        "dataRetryHash-" + dataRetryEntry.hashCode() /*debug tag*/,
-                        Runnable::run,
-                        null /*worksource*/,
-                        () -> {
-                            logl("onAlarm retry " + dataRetryEntry);
-                            sendMessage(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry
-                                            ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY,
-                                    dataRetryEntry));
-                        });
-            } else {
-                Intent intent = new Intent(ACTION_RETRY);
-                intent.putExtra(ACTION_RETRY_EXTRA_HASHCODE, dataRetryEntry.hashCode());
-                // No need to wake up the device, the retry can wait util next time the device wake
-                // up  to save power.
-                mAlarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME,
-                        dataRetryEntry.retryElapsedTime,
-                        PendingIntent.getBroadcast(mPhone.getContext(),
-                                dataRetryEntry.hashCode()/*Unique identifier of the retry attempt*/,
-                                intent,
-                                PendingIntent.FLAG_IMMUTABLE));
-            }
+            // No need to wake up the device, the retry can wait util next time the device wake
+            // up to save power.
+            mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME,
+                    dataRetryEntry.retryElapsedTime,
+                    "dataRetryHash-" + dataRetryEntry.hashCode() /*debug tag*/,
+                    Runnable::run,
+                    null /*worksource*/,
+                    () -> {
+                        logl("onAlarm retry " + dataRetryEntry);
+                        sendMessage(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry
+                                        ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY,
+                                dataRetryEntry));
+                    });
         }
     }
 
@@ -1599,8 +1562,7 @@
         // transport.
         mDataThrottlingEntries.removeIf(
                 throttlingEntry -> dataProfile.equals(throttlingEntry.dataProfile)
-                        && (!mFlags.unthrottleCheckTransport()
-                        || throttlingEntry.transport == transport));
+                        && (throttlingEntry.transport == transport));
 
         if (mDataThrottlingEntries.size() >= MAXIMUM_HISTORICAL_ENTRIES) {
             // If we don't see the anomaly report after U release, we should remove this check for
@@ -1649,7 +1611,7 @@
             // profile manager.
             Stream<DataThrottlingEntry> stream = mDataThrottlingEntries.stream();
             stream = stream.filter(entry -> entry.expirationTimeMillis > now
-                    && (!mFlags.unthrottleCheckTransport() || entry.transport == transport));
+                    && entry.transport == transport);
             if (dataProfile.getApnSetting() != null) {
                 stream = stream
                         .filter(entry -> entry.dataProfile.getApnSetting() != null)
@@ -1779,8 +1741,9 @@
                                 mPhone.getCarrierId());
                         continue;
                     }
-                    if (entry.networkRequestList.get(0).getApnTypeNetworkCapability()
-                            == networkRequest.getApnTypeNetworkCapability()
+                    if (entry.networkRequestList.get(0)
+                            .getHighestPrioritySupportedNetworkCapability()
+                            == networkRequest.getHighestPrioritySupportedNetworkCapability()
                             && entry.transport == transport) {
                         return true;
                     }
diff --git a/src/java/com/android/internal/telephony/data/DataSettingsManager.java b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
index fb112d9..0b9ac27 100644
--- a/src/java/com/android/internal/telephony/data/DataSettingsManager.java
+++ b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
@@ -326,7 +326,7 @@
                     @Override
                     public void onUserDataEnabledChanged(boolean enabled,
                             @NonNull String callingPackage) {
-                        log("phone" + phone.getPhoneId() + " onUserDataEnabledChanged "
+                        log("phone " + phone.getPhoneId() + " onUserDataEnabledChanged "
                                 + enabled + " by " + callingPackage
                                 + ", reevaluating mobile data policies");
                         DataSettingsManager.this.updateDataEnabledAndNotify(
@@ -335,6 +335,16 @@
                 });
             }
         }
+        SubscriptionManagerService.getInstance().registerCallback(
+                new SubscriptionManagerService.SubscriptionManagerServiceCallback(this::post) {
+                    @Override
+                    public void onDefaultDataSubscriptionChanged(int subId) {
+                        log((subId == mSubId ? "Became" : "Not")
+                                + " default data sub, reevaluating mobile data policies");
+                        DataSettingsManager.this.updateDataEnabledAndNotify(
+                                TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
+                    }
+                });
         updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_UNKNOWN);
     }
 
diff --git a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
index b9b60a0..42e1a78 100644
--- a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
@@ -32,6 +32,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.ValidationStatus;
@@ -714,7 +715,12 @@
         // Put the bundled stats extras on the intent.
         intent.putExtra("EXTRA_DSRS_STATS_BUNDLE", bundle);
 
-        mPhone.getContext().sendBroadcast(intent, READ_PRIVILEGED_PHONE_STATE);
+        if (mFeatureFlags.hsumBroadcast()) {
+            mPhone.getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+                    READ_PRIVILEGED_PHONE_STATE);
+        } else {
+            mPhone.getContext().sendBroadcast(intent, READ_PRIVILEGED_PHONE_STATE);
+        }
     }
 
     /** Recovery Action: RECOVERY_ACTION_GET_DATA_CALL_LIST */
diff --git a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
index 27b4331..1005bb7 100644
--- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -30,6 +30,7 @@
 
 import static java.util.Arrays.copyOf;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
@@ -63,6 +64,7 @@
 import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Log;
 import android.util.SparseIntArray;
@@ -89,6 +91,7 @@
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.OnDemandDataSwitch;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
+import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback;
 import com.android.internal.telephony.subscription.SubscriptionManagerService.WatchedInt;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.telephony.Rlog;
@@ -182,6 +185,27 @@
         }
     }
 
+    /**
+     * Callback from PhoneSwitcher
+     */
+    public static class PhoneSwitcherCallback extends DataCallback {
+        /**
+         * Constructor
+         *
+         * @param executor The executor of the callback.
+         */
+        public PhoneSwitcherCallback(@NonNull @CallbackExecutor Executor executor) {
+            super(executor);
+        }
+
+        /**
+         * Called when preferred data phone id changed.
+         *
+         * @param phoneId The phone id of the preferred data.
+         */
+        public void onPreferredDataPhoneIdChanged(int phoneId) {}
+    }
+
     @NonNull
     private final NetworkRequestList mNetworkRequestList = new NetworkRequestList();
     protected final RegistrantList mActivePhoneRegistrants;
@@ -260,6 +284,10 @@
 
     private ISetOpportunisticDataCallback mSetOpptSubCallback;
 
+    /** Phone switcher callbacks. */
+    @NonNull
+    private final Set<PhoneSwitcherCallback> mPhoneSwitcherCallbacks = new ArraySet<>();
+
     private static final int EVENT_PRIMARY_DATA_SUB_CHANGED       = 101;
     protected static final int EVENT_SUBSCRIPTION_CHANGED         = 102;
     private static final int EVENT_REQUEST_NETWORK                = 103;
@@ -467,6 +495,24 @@
         }
     }
 
+    /**
+     * Register the callback for receiving information from {@link PhoneSwitcher}.
+     *
+     * @param callback The callback.
+     */
+    public void registerCallback(@NonNull PhoneSwitcherCallback callback) {
+        mPhoneSwitcherCallbacks.add(callback);
+    }
+
+    /**
+     * Unregister the callback for receiving information from {@link PhoneSwitcher}.
+     *
+     * @param callback The callback.
+     */
+    public void unregisterCallback(@NonNull PhoneSwitcherCallback callback) {
+        mPhoneSwitcherCallbacks.remove(callback);
+    }
+
     private void evaluateIfImmediateDataSwitchIsNeeded(String evaluationReason, int switchReason) {
         if (onEvaluate(REQUESTS_UNCHANGED, evaluationReason)) {
             logDataSwitchEvent(mPreferredDataSubId.get(),
@@ -587,54 +633,54 @@
         };
         mAutoDataSwitchController = new AutoDataSwitchController(context, looper, this,
                 mFlags, mAutoDataSwitchCallback);
-
-        mContext.registerReceiver(mDefaultDataChangedReceiver,
-                new IntentFilter(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED));
+        if (!mFlags.ddsCallback()) {
+            mContext.registerReceiver(mDefaultDataChangedReceiver,
+                    new IntentFilter(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED));
+        } else {
+            mSubscriptionManagerService.registerCallback(new SubscriptionManagerServiceCallback(
+                    this::post) {
+                @Override
+                public void onDefaultDataSubscriptionChanged(int subId) {
+                    evaluateIfImmediateDataSwitchIsNeeded("default data sub changed to " + subId,
+                            DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL);
+                }
+            });
+        }
 
         PhoneConfigurationManager.registerForMultiSimConfigChange(
                 this, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
 
         mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this);
 
-        final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
-                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_IA)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_RCS)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_MCX)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH)
-                .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_1)
-                .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_2)
-                .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_3)
-                .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_4)
-                .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_5)
-                .setNetworkSpecifier(new MatchAllNetworkSpecifier());
+        if (!mFlags.supportNetworkProvider()) {
+            final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
+                    .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                    .addCapability(NetworkCapabilities.NET_CAPABILITY_IA)
+                    .addCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL)
+                    .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                    .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_1)
+                    .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_2)
+                    .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_3)
+                    .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_4)
+                    .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_5)
+                    .setNetworkSpecifier(new MatchAllNetworkSpecifier());
+            TelephonyNetworkRequest.getAllSupportedNetworkCapabilities()
+                    .forEach(builder::addCapability);
 
-        if (mFlags.satelliteInternet()) {
-            // TODO: b/328622096 remove the try/catch
-            try {
-                builder.addTransportType(NetworkCapabilities.TRANSPORT_SATELLITE);
-            } catch (IllegalArgumentException exception) {
-                loge("TRANSPORT_SATELLITE is not supported.");
+            if (mFlags.satelliteInternet()) {
+                // TODO: b/328622096 remove the try/catch
+                try {
+                    builder.addTransportType(NetworkCapabilities.TRANSPORT_SATELLITE);
+                } catch (IllegalArgumentException exception) {
+                    loge("TRANSPORT_SATELLITE is not supported.");
+                }
             }
-        }
 
-        NetworkFactory networkFactory = new PhoneSwitcherNetworkRequestListener(looper, context,
-                builder.build(), this);
-        // we want to see all requests
-        networkFactory.registerIgnoringScore();
+            NetworkFactory networkFactory = new PhoneSwitcherNetworkRequestListener(looper, context,
+                    builder.build(), this);
+            // we want to see all requests
+            networkFactory.registerIgnoringScore();
+        }
 
         updateHalCommandToUse();
 
@@ -1054,11 +1100,12 @@
         return false;
     }
 
+    // TODO: Remove after removing TelephonyNetworkFactory
     private static class PhoneSwitcherNetworkRequestListener extends NetworkFactory {
         private final PhoneSwitcher mPhoneSwitcher;
         public PhoneSwitcherNetworkRequestListener (Looper l, Context c,
                 NetworkCapabilities nc, PhoneSwitcher ps) {
-            super(l, c, "PhoneSwitcherNetworkRequstListener", nc);
+            super(l, c, "PhoneSwitcherNetworkRequestListener", nc);
             mPhoneSwitcher = ps;
         }
 
@@ -1079,7 +1126,13 @@
         }
     }
 
-    private void onRequestNetwork(NetworkRequest networkRequest) {
+    /**
+     * Called when receiving a network request.
+     *
+     * @param networkRequest The network request.
+     */
+    // TODO: Transform to TelephonyNetworkRequest after removing TelephonyNetworkFactory
+    public void onRequestNetwork(@NonNull NetworkRequest networkRequest) {
         TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest(
                 networkRequest, PhoneFactory.getDefaultPhone(), mFlags);
         if (!mNetworkRequestList.contains(telephonyNetworkRequest)) {
@@ -1088,7 +1141,13 @@
         }
     }
 
-    private void onReleaseNetwork(NetworkRequest networkRequest) {
+    /**
+     * Called when releasing a network request.
+     *
+     * @param networkRequest The network request to release.
+     */
+    // TODO: Transform to TelephonyNetworkRequest after removing TelephonyNetworkFactory
+    public void onReleaseNetwork(@NonNull NetworkRequest networkRequest) {
         TelephonyNetworkRequest telephonyNetworkRequest = new TelephonyNetworkRequest(
                 networkRequest, PhoneFactory.getDefaultPhone(), mFlags);
         if (mNetworkRequestList.remove(telephonyNetworkRequest)) {
@@ -1102,18 +1161,6 @@
         mDefaultNetworkCallback.mSwitchReason = reason;
     }
 
-    private void collectRequestNetworkMetrics(NetworkRequest networkRequest) {
-        // Request network for MMS will temporary disable the network on default data subscription,
-        // this only happen on multi-sim device.
-        if (mActiveModemCount > 1 && networkRequest.hasCapability(
-                NetworkCapabilities.NET_CAPABILITY_MMS)) {
-            OnDemandDataSwitch onDemandDataSwitch = new OnDemandDataSwitch();
-            onDemandDataSwitch.apn = TelephonyEvent.ApnType.APN_TYPE_MMS;
-            onDemandDataSwitch.state = TelephonyEvent.EventState.EVENT_STATE_START;
-            TelephonyMetrics.getInstance().writeOnDemandDataSwitch(onDemandDataSwitch);
-        }
-    }
-
     private void collectReleaseNetworkMetrics(NetworkRequest networkRequest) {
         // Release network for MMS will recover the network on default data subscription, this only
         // happen on multi-sim device.
@@ -1562,6 +1609,7 @@
      * If preferred phone changes, or phone activation status changes, registrants
      * will be notified.
      */
+    // TODO: Remove after removing TelephonyNetworkFactory
     public void registerForActivePhoneSwitch(Handler h, int what, Object o) {
         Registrant r = new Registrant(h, what, o);
         mActivePhoneRegistrants.add(r);
@@ -1983,6 +2031,10 @@
         // Notify all registrants
         mActivePhoneRegistrants.notifyRegistrants();
         notifyPreferredDataSubIdChanged();
+        if (mFlags.supportNetworkProvider()) {
+            mPhoneSwitcherCallbacks.forEach(callback -> callback.invokeFromExecutor(
+                    () -> callback.onPreferredDataPhoneIdChanged(phoneId)));
+        }
     }
 
     private boolean isPhoneIdValidForRetry(int phoneId) {
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
index ca34ca7..7d1746c 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
@@ -138,31 +138,19 @@
     public NetworkCapabilities makeNetworkFilter(int subscriptionId) {
         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_IA)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_RCS)
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_MCX)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH)
                 .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_1)
                 .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_2)
                 .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_3)
                 .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_4)
                 .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_5)
                 .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
-                .setSubscriptionId(subscriptionId).build());
+                                .setSubscriptionId(subscriptionId).build());
+        TelephonyNetworkRequest.getAllSupportedNetworkCapabilities()
+                .forEach(builder::addCapability);
 
         if (mFlags.satelliteInternet()) {
             // TODO: b/328622096 remove the try/catch
@@ -206,7 +194,7 @@
 
     private int getTransportTypeFromNetworkRequest(TelephonyNetworkRequest networkRequest) {
         int transport = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
-        int capability = networkRequest.getApnTypeNetworkCapability();
+        int capability = networkRequest.getHighestPriorityApnTypeNetworkCapability();
         if (capability >= 0) {
             transport = mAccessNetworksManager
                     .getPreferredTransportByNetworkCapability(capability);
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkProvider.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkProvider.java
new file mode 100644
index 0000000..63edefa
--- /dev/null
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkProvider.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.data;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.MatchAllNetworkSpecifier;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
+import android.net.NetworkProvider;
+import android.net.NetworkProvider.NetworkOfferCallback;
+import android.net.NetworkRequest;
+import android.net.NetworkScore;
+import android.os.Handler;
+import android.os.Looper;
+import android.telephony.SubscriptionManager;
+import android.util.ArrayMap;
+import android.util.LocalLog;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.data.PhoneSwitcher.PhoneSwitcherCallback;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.telephony.Rlog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Map;
+
+/**
+ * TelephonyNetworkProvider is a singleton network provider responsible for providing all
+ * telephony related networks including networks on cellular and IWLAN across all active SIMs.
+ */
+public class TelephonyNetworkProvider extends NetworkProvider implements NetworkOfferCallback {
+
+    public final String LOG_TAG = "TNP";
+
+    /** Android feature flags */
+    @NonNull
+    private final FeatureFlags mFlags;
+
+    /** The event handler */
+    @NonNull
+    private final Handler mHandler;
+
+    /** Phone switcher responsible to determine request routing on dual-SIM device */
+    @NonNull
+    private final PhoneSwitcher mPhoneSwitcher;
+
+    /** Network requests map. Key is the network request, value is the phone id it applies to. */
+    private final Map<TelephonyNetworkRequest, Integer> mNetworkRequests = new ArrayMap<>();
+
+    /** Persisted log */
+    @NonNull
+    private final LocalLog mLocalLog = new LocalLog(256);
+
+    /**
+     * Constructor
+     *
+     * @param looper The looper for event handling
+     * @param context The context
+     * @param featureFlags Android feature flags
+     */
+    public TelephonyNetworkProvider(@NonNull Looper looper, @NonNull Context context,
+                                    @NonNull FeatureFlags featureFlags) {
+        super(context, looper, TelephonyNetworkProvider.class.getSimpleName());
+
+        mFlags = featureFlags;
+        mHandler = new Handler(looper);
+        mPhoneSwitcher = PhoneSwitcher.getInstance();
+
+        // Register for subscription changed event.
+        context.getSystemService(SubscriptionManager.class)
+                .addOnSubscriptionsChangedListener(mHandler::post,
+                        new SubscriptionManager.OnSubscriptionsChangedListener() {
+                        @Override
+                        public void onSubscriptionsChanged() {
+                            logl("Subscription changed.");
+                            reevaluateNetworkRequests("subscription changed");
+                        }});
+
+        // Register for preferred data changed event
+        mPhoneSwitcher.registerCallback(new PhoneSwitcherCallback(mHandler::post) {
+                    @Override
+                    public void onPreferredDataPhoneIdChanged(int phoneId) {
+                        logl("Preferred data sub phone id changed to " + phoneId);
+                        reevaluateNetworkRequests("Preferred data subscription changed");
+                    }
+                });
+
+        // Register the provider and tell connectivity service what network offer telephony can
+        // provide
+        ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
+        if (cm != null) {
+            cm.registerNetworkProvider(this);
+            NetworkCapabilities caps = makeNetworkFilter();
+            registerNetworkOffer(new NetworkScore.Builder().build(), caps, mHandler::post, this);
+            logl("registerNetworkOffer: " + caps);
+        }
+    }
+
+    /**
+     * Get the phone id for the network request.
+     *
+     * @param request The network request
+     * @return The id of the phone where the network request should route to. If the network request
+     * can't be applied to any phone, {@link SubscriptionManager#INVALID_PHONE_INDEX} will be
+     * returned.
+     */
+    private int getPhoneIdForNetworkRequest(@NonNull TelephonyNetworkRequest request) {
+        for (Phone phone : PhoneFactory.getPhones()) {
+            int phoneId = phone.getPhoneId();
+            if (mPhoneSwitcher.shouldApplyNetworkRequest(request, phoneId)) {
+                // Return here because by design the network request can be only applied to *one*
+                // phone. It's not possible to have two DataNetworkController to attempt to setup
+                // data call for the same network request.
+                return phoneId;
+            }
+        }
+
+        return SubscriptionManager.INVALID_PHONE_INDEX;
+    }
+
+    /**
+     * Called when receiving a network request from connectivity service. This is the entry point
+     * that a network request arrives telephony.
+     *
+     * @param request The network request
+     */
+    @Override
+    public void onNetworkNeeded(@NonNull NetworkRequest request) {
+        TelephonyNetworkRequest networkRequest = new TelephonyNetworkRequest(request, mFlags);
+        if (mNetworkRequests.containsKey(networkRequest)) {
+            loge("Duplicate network request " + networkRequest);
+            return;
+        }
+
+        mPhoneSwitcher.onRequestNetwork(request);
+
+        // Check with PhoneSwitcher to see where to route the request.
+        int phoneId = getPhoneIdForNetworkRequest(networkRequest);
+        if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
+            logl("onNetworkNeeded: phoneId=" + phoneId + ", " + networkRequest);
+            PhoneFactory.getPhone(phoneId).getDataNetworkController()
+                    .addNetworkRequest(networkRequest);
+        } else {
+            logl("onNetworkNeeded: Not applied. " + networkRequest);
+        }
+
+        mNetworkRequests.put(networkRequest, phoneId);
+    }
+
+    /**
+     * Called when connectivity service remove the network request. Note this will not result in
+     * network tear down. Even there is no network request attached to the network, telephony still
+     * relies on {@link NetworkAgent#onNetworkUnwanted()} to tear down the network.
+     *
+     * @param request The released network request
+     *
+     * @see TelephonyNetworkAgent#onNetworkUnwanted()
+     */
+    @Override
+    public void onNetworkUnneeded(@NonNull NetworkRequest request) {
+        TelephonyNetworkRequest networkRequest = mNetworkRequests.keySet().stream()
+                .filter(r -> r.getNativeNetworkRequest().equals(request))
+                .findFirst()
+                .orElse(null);
+        if (networkRequest == null) {
+            loge("onNetworkUnneeded: Cannot find " + request);
+            return;
+        }
+
+        mPhoneSwitcher.onReleaseNetwork(request);
+        int phoneId = mNetworkRequests.remove(networkRequest);
+        Phone phone = PhoneFactory.getPhone(phoneId);
+        if (phone != null) {
+            logl("onNetworkUnneeded: phoneId=" + phoneId + ", " + networkRequest);
+            // Remove the network request from network controller. Note this will not result
+            // in disconnecting the data network.
+            phone.getDataNetworkController().removeNetworkRequest(networkRequest);
+        } else {
+            loge("onNetworkUnneeded: Unable to get phone. phoneId=" + phoneId);
+        }
+    }
+
+    /**
+     * Re-evaluate the existing networks and re-apply to the applicable phone.
+     *
+     * @param reason The reason for re-evaluating network request. Note this can be only used for
+     * debugging message purposes.
+     */
+    private void reevaluateNetworkRequests(@NonNull String reason) {
+        logl("reevaluateNetworkRequests: " + reason + ".");
+        mNetworkRequests.forEach((request, oldPhoneId) -> {
+            int newPhoneId = getPhoneIdForNetworkRequest(request);
+            if (newPhoneId != oldPhoneId) {
+                // We need to move the request from old phone to the new phone. This can happen
+                // when the user changes the default data subscription.
+
+                if (oldPhoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
+                    PhoneFactory.getPhone(oldPhoneId).getDataNetworkController()
+                            .removeNetworkRequest(request);
+                }
+
+                if (newPhoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
+                    PhoneFactory.getPhone(newPhoneId).getDataNetworkController()
+                            .addNetworkRequest(request);
+                }
+
+                logl("Request moved. phoneId " + oldPhoneId + " -> " + newPhoneId + " " + request);
+                mNetworkRequests.put(request, newPhoneId);
+            }
+        });
+    }
+
+    /**
+     * @return The maximal network capabilities that telephony can support.
+     */
+    @NonNull
+    private NetworkCapabilities makeNetworkFilter() {
+        final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_IA)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+                .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_1)
+                .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_2)
+                .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_3)
+                .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_4)
+                .addEnterpriseId(NetworkCapabilities.NET_ENTERPRISE_ID_5)
+                // Ideally TelephonyNetworkProvider should only accept TelephonyNetworkSpecifier,
+                // but this network provider is a singleton across all SIMs, and
+                // TelephonyNetworkSpecifier can't accept more than one subscription id, so we let
+                // the provider accepts all different kinds NetworkSpecifier.
+                .setNetworkSpecifier(new MatchAllNetworkSpecifier());
+        TelephonyNetworkRequest.getAllSupportedNetworkCapabilities()
+                .forEach(builder::addCapability);
+
+        // TODO: b/328622096 remove the try/catch
+        try {
+            builder.addTransportType(NetworkCapabilities.TRANSPORT_SATELLITE);
+        } catch (IllegalArgumentException exception) {
+            log("TRANSPORT_SATELLITE is not supported.");
+        }
+
+        return builder.build();
+    }
+
+    /**
+     * Log debug message to logcat.
+     *
+     * @param s The debug message to log
+     */
+    private void log(@NonNull String s) {
+        Rlog.d(LOG_TAG, s);
+    }
+
+    /**
+     * Log error debug messages to logcat.
+     * @param s The error debug messages
+     */
+    private void loge(@NonNull String s) {
+        Rlog.e(LOG_TAG, s);
+    }
+
+    /**
+     * Log to logcat and persisted local log.
+     *
+     * @param s The debug message to log
+     */
+    private void logl(@NonNull String s) {
+        log(s);
+        mLocalLog.log(s);
+    }
+
+    /**
+     * Dump the state of telephony network provider.
+     *
+     * @param fd File descriptor
+     * @param writer Print writer
+     * @param args Arguments
+     */
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
+        pw.println("TelephonyNetworkProvider:");
+        pw.increaseIndent();
+
+        pw.println("mPreferredDataPhoneId=" + mPhoneSwitcher.getPreferredDataPhoneId());
+        int defaultDataSubId = SubscriptionManagerService.getInstance().getDefaultDataSubId();
+        pw.println("DefaultDataSubId=" + defaultDataSubId);
+        pw.println("DefaultDataPhoneId=" + SubscriptionManagerService.getInstance()
+                .getPhoneId(defaultDataSubId));
+
+        pw.println("Registered capabilities: " + makeNetworkFilter());
+        pw.println("Network requests:");
+        pw.increaseIndent();
+        for (Phone phone : PhoneFactory.getPhones()) {
+            pw.println("Phone " + phone.getPhoneId() + ":");
+            pw.increaseIndent();
+            mNetworkRequests.forEach((request, phoneId) -> {
+                if (phoneId == phone.getPhoneId()) {
+                    pw.println(request);
+                }
+            });
+            pw.decreaseIndent();
+        }
+        pw.println("Not applied requests:");
+        pw.increaseIndent();
+        mNetworkRequests.forEach((request, phoneId) -> {
+            if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
+                pw.println(request);
+            }
+        });
+        pw.decreaseIndent();
+        pw.decreaseIndent();
+        pw.println();
+        pw.println("Local logs:");
+        pw.increaseIndent();
+        mLocalLog.dump(fd, pw, args);
+        pw.decreaseIndent();
+        pw.decreaseIndent();
+    }
+}
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
index 117eb36..ca1ca21 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkRequest.java
@@ -137,10 +137,6 @@
                 CAPABILITY_ATTRIBUTE_APN_SETTING | CAPABILITY_ATTRIBUTE_TRAFFIC_DESCRIPTOR_DNN)
     );
 
-    /** The phone instance. */
-    @NonNull
-    private final Phone mPhone;
-
     /**
      * Native network request from the clients. See {@link NetworkRequest};
      */
@@ -164,8 +160,8 @@
     /**
      * Data config manager for retrieving data config.
      */
-    @NonNull
-    private final DataConfigManager mDataConfigManager;
+    @Nullable
+    private DataConfigManager mDataConfigManager;
 
     /**
      * The attached data network. Note that the data network could be in any state. {@code null}
@@ -205,7 +201,19 @@
      */
     public TelephonyNetworkRequest(@NonNull NetworkRequest request, @NonNull Phone phone,
                                    @NonNull FeatureFlags featureFlags) {
-        mPhone = phone;
+        this(request, featureFlags);
+        mDataConfigManager = phone.getDataNetworkController().getDataConfigManager();
+        updatePriority();
+    }
+
+    /**
+     * Constructor
+     *
+     * @param request The native network request from the clients.
+     * @param featureFlags The feature flag
+     */
+    public TelephonyNetworkRequest(@NonNull NetworkRequest request,
+                                   @NonNull FeatureFlags featureFlags) {
         mNativeNetworkRequest = request;
         mFeatureFlags = featureFlags;
 
@@ -222,7 +230,15 @@
         // to satisfy it.
         mState = REQUEST_STATE_UNSATISFIED;
         mCreatedTimeMillis = SystemClock.elapsedRealtime();
-        mDataConfigManager = phone.getDataNetworkController().getDataConfigManager();
+    }
+
+    /**
+     * Update the associated data config manager.
+     *
+     * @param dataConfigManager Data config manager
+     */
+    public void updateDataConfig(@NonNull DataConfigManager dataConfigManager) {
+        mDataConfigManager = dataConfigManager;
         updatePriority();
     }
 
@@ -315,13 +331,15 @@
                 if (mNativeNetworkRequest.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
                         && !mNativeNetworkRequest.hasTransport(
                                 NetworkCapabilities.TRANSPORT_SATELLITE)) {
-                    if (Arrays.stream(getCapabilities()).noneMatch(mDataConfigManager
-                            .getForcedCellularTransportCapabilities()::contains)) {
-                        // If the request is explicitly for the cellular, then the data profile
-                        // needs to support cellular.
-                        if (!dataProfile.getApnSetting().isForInfrastructure(
-                                ApnSetting.INFRASTRUCTURE_CELLULAR)) {
-                            return false;
+                    if (mDataConfigManager != null) {
+                        if (Arrays.stream(getCapabilities()).noneMatch(mDataConfigManager
+                                .getForcedCellularTransportCapabilities()::contains)) {
+                            // If the request is explicitly for the cellular, then the data profile
+                            // needs to support cellular.
+                            if (!dataProfile.getApnSetting().isForInfrastructure(
+                                    ApnSetting.INFRASTRUCTURE_CELLULAR)) {
+                                return false;
+                            }
                         }
                     }
                 } else if (mNativeNetworkRequest.hasTransport(
@@ -371,10 +389,12 @@
      * Update the priority from data config manager.
      */
     public void updatePriority() {
-        mPriority = Arrays.stream(mNativeNetworkRequest.getCapabilities())
-                .map(mDataConfigManager::getNetworkCapabilityPriority)
-                .max()
-                .orElse(0);
+        if (mDataConfigManager != null) {
+            mPriority = Arrays.stream(mNativeNetworkRequest.getCapabilities())
+                    .map(mDataConfigManager::getNetworkCapabilityPriority)
+                    .max()
+                    .orElse(0);
+        }
     }
 
     /**
@@ -385,13 +405,41 @@
      * if there is no APN type capabilities in this network request.
      */
     @NetCapability
-    public int getApnTypeNetworkCapability() {
+    public int getHighestPriorityApnTypeNetworkCapability() {
         if (!hasAttribute(CAPABILITY_ATTRIBUTE_APN_SETTING)) return -1;
+        if (mDataConfigManager == null) return -1;
         return Arrays.stream(getCapabilities()).boxed()
                 .filter(cap -> DataUtils.networkCapabilityToApnType(cap) != ApnSetting.TYPE_NONE)
                 .max(Comparator.comparingInt(mDataConfigManager::getNetworkCapabilityPriority))
                 .orElse(-1);
     }
+
+    /**
+     * A parent set of {@link #getHighestPriorityApnTypeNetworkCapability()}.
+     * Get the network capability from the network request that can lead to data setup. If there are
+     * multiple capabilities, the highest priority one will be returned.
+     *
+     * @return The highest priority traffic descriptor type network capability from this network
+     * request. -1 if there is no traffic descriptor type capabilities in this network request.
+     */
+    @NetCapability
+    public int getHighestPrioritySupportedNetworkCapability() {
+        if (mDataConfigManager == null) return -1;
+        return Arrays.stream(getCapabilities()).boxed()
+                .filter(CAPABILITY_ATTRIBUTE_MAP::containsKey)
+                .max(Comparator.comparingInt(mDataConfigManager::getNetworkCapabilityPriority))
+                .orElse(-1);
+    }
+
+    /**
+     * @return Get all the network capabilities that can lead to data setup.
+     */
+    @NonNull
+    @NetCapability
+    public static List<Integer> getAllSupportedNetworkCapabilities() {
+        return CAPABILITY_ATTRIBUTE_MAP.keySet().stream().toList();
+    }
+
     /**
      * @return The native network request.
      */
@@ -440,7 +488,7 @@
      *
      * @param evaluation The data evaluation result.
      */
-    public void setEvaluation(@NonNull DataEvaluation evaluation) {
+    public void setEvaluation(@Nullable DataEvaluation evaluation) {
         mEvaluation = evaluation;
     }
 
@@ -461,14 +509,6 @@
     }
 
     /**
-     * @return {@code true} if this network request can result in bringing up a metered network.
-     */
-    public boolean isMeteredRequest() {
-        return mDataConfigManager.isAnyMeteredCapability(
-                getCapabilities(), mPhone.getServiceState().getDataRoaming());
-    }
-
-    /**
      * Get Os/App id from the network request.
      *
      * @return Os/App id. {@code null} if the request does not have traffic descriptor based network
@@ -521,7 +561,7 @@
         return "[" + mNativeNetworkRequest + ", mPriority=" + mPriority
                 + ", state=" + requestStateToString(mState)
                 + ", mAttachedDataNetwork=" + (mAttachedDataNetwork != null
-                ? mAttachedDataNetwork.name() : null) + ", isMetered=" + isMeteredRequest()
+                ? mAttachedDataNetwork.name() : null)
                 + ", created time=" + DataUtils.elapsedTimeToString(mCreatedTimeMillis)
                 + ", evaluation result=" + mEvaluation + "]";
     }
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
index fddeb06..677db87 100644
--- a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
@@ -43,6 +43,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.infra.AndroidFuture;
+import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.IDomainSelector;
 import com.android.internal.telephony.ITransportSelectorCallback;
 import com.android.internal.telephony.ITransportSelectorResultCallback;
@@ -50,6 +51,7 @@
 import com.android.internal.telephony.IWwanSelectorResultCallback;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.data.AccessNetworksManager.QualifiedNetworks;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.io.PrintWriter;
@@ -69,7 +71,9 @@
     protected static final int EVENT_SERVICE_CONNECTED = 3;
     protected static final int EVENT_SERVICE_BINDING_TIMEOUT = 4;
     protected static final int EVENT_RESET_NETWORK_SCAN_DONE = 5;
-    protected static final int EVENT_LAST = EVENT_RESET_NETWORK_SCAN_DONE;
+    protected static final int EVENT_TRIGGER_NETWORK_SCAN_DONE = 6;
+    protected static final int EVENT_MODEM_RESET = 7;
+    protected static final int EVENT_LAST = EVENT_MODEM_RESET;
 
     private static final int DEFAULT_BIND_RETRY_TIMEOUT_MS = 4 * 1000;
 
@@ -185,7 +189,10 @@
                     return;
                 }
                 DomainSelectionConnection.this.onSelectionTerminated(cause);
-                dispose();
+                if (!Flags.hangupEmergencyCallForCrossSimRedialing()
+                        || !mIsEmergency || !checkState(STATUS_DOMAIN_SELECTED)) {
+                    dispose();
+                }
             }
         }
     }
@@ -304,6 +311,23 @@
                                 mPendingScanRequest.mScanType, false);
                     }
                     break;
+                case EVENT_TRIGGER_NETWORK_SCAN_DONE:
+                    synchronized (mLock) {
+                        if (checkState(STATUS_DISPOSED) || !checkState(STATUS_WAIT_SCAN_RESULT)) {
+                            return;
+                        }
+                        ar = (AsyncResult) msg.obj;
+                        if (ar != null && ar.exception != null) {
+                            onTriggerNetworkScanError((Integer) ar.userObj,
+                                    ((CommandException) ar.exception).getCommandError());
+                        }
+                    }
+                    break;
+                case EVENT_MODEM_RESET:
+                    synchronized (mLock) {
+                        onModemReset();
+                    }
+                    break;
                 default:
                     loge("handleMessage unexpected msg=" + msg.what);
                     break;
@@ -528,10 +552,13 @@
             if (!mRegisteredRegistrant) {
                 mPhone.registerForEmergencyNetworkScan(mHandler,
                         EVENT_EMERGENCY_NETWORK_SCAN_RESULT, null);
+                mPhone.mCi.registerForModemReset(mHandler, EVENT_MODEM_RESET, null);
                 mRegisteredRegistrant = true;
             }
             setState(STATUS_WAIT_SCAN_RESULT);
-            mPhone.triggerEmergencyNetworkScan(preferredNetworks, scanType, null);
+            mPhone.triggerEmergencyNetworkScan(preferredNetworks, scanType,
+                    mHandler.obtainMessage(EVENT_TRIGGER_NETWORK_SCAN_DONE,
+                            Integer.valueOf(scanType)));
             mPendingScanRequest = null;
         }
     }
@@ -721,6 +748,7 @@
         setState(STATUS_DISPOSED);
         if (mRegisteredRegistrant) {
             mPhone.unregisterForEmergencyNetworkScan(mHandler);
+            mPhone.mCi.unregisterForModemReset(mHandler);
             mRegisteredRegistrant = false;
         }
         onCancel(true);
@@ -745,6 +773,34 @@
         }
     }
 
+    private void onTriggerNetworkScanError(int scanType, CommandException.Error error) {
+        loge("onTriggerNetworkScanError scanType=" + scanType + ", error=" + error);
+
+        if (shouldTerminateCallOnRadioNotAvailable()
+                && error == CommandException.Error.RADIO_NOT_AVAILABLE) {
+            clearState(STATUS_WAIT_SCAN_RESULT);
+            onSelectionTerminated(DisconnectCause.POWER_OFF);
+            dispose();
+            return;
+        }
+    }
+
+    private void onModemReset() {
+        loge("onModemReset status=" + mStatus);
+        if (!shouldTerminateCallOnRadioNotAvailable()) {
+            return;
+        }
+        if (checkState(STATUS_DISPOSED) || checkState(STATUS_DOMAIN_SELECTED)) {
+            return;
+        }
+        onSelectionTerminated(DisconnectCause.POWER_OFF);
+        dispose();
+    }
+
+    private boolean shouldTerminateCallOnRadioNotAvailable() {
+        return mIsEmergency && mSelectorType == DomainSelectionService.SELECTOR_TYPE_CALLING;
+    }
+
     /**
      * Get the  preferred transport.
      *
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
index 410f89b..1b66e54 100644
--- a/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
@@ -64,8 +64,7 @@
      *                               to be bound to the domain selection controller.
      */
     public static void make(Context context, String flattenedComponentName) {
-        Log.i(TAG, "make flag=" + Flags.apDomainSelectionEnabled()
-                + ", useOem=" + Flags.useOemDomainSelectionService());
+        Log.i(TAG, "make useOem=" + Flags.useOemDomainSelectionService());
         if (sInstance == null) {
             sInstance = new DomainSelectionResolver(context, flattenedComponentName);
         }
diff --git a/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
index 66b977d..4153c93 100644
--- a/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
@@ -47,6 +47,7 @@
 import com.android.internal.telephony.data.AccessNetworksManager;
 import com.android.internal.telephony.data.AccessNetworksManager.QualifiedNetworks;
 import com.android.internal.telephony.emergency.EmergencyStateTracker;
+import com.android.internal.telephony.flags.Flags;
 
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
@@ -198,6 +199,32 @@
         super.cancelSelection();
     }
 
+    @Override
+    public @NonNull CompletableFuture<Integer> reselectDomain(
+        @NonNull DomainSelectionService.SelectionAttributes attr) {
+        if (Flags.hangupEmergencyCallForCrossSimRedialing()) {
+            int disconnectCause = getDisconnectCause();
+            int preciseDisconnectCause = attr.getCsDisconnectCause();
+            if (disconnectCause == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE) {
+                preciseDisconnectCause = PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE;
+            } else if (disconnectCause
+                    == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE) {
+                preciseDisconnectCause = PreciseDisconnectCause.EMERGENCY_PERM_FAILURE;
+            }
+            if (preciseDisconnectCause != attr.getCsDisconnectCause()) {
+                attr = EmergencyCallDomainSelectionConnection.getSelectionAttributes(
+                        attr.getSlotIndex(), attr.getSubscriptionId(),
+                        attr.isExitedFromAirplaneMode(), attr.getCallId(),
+                        (attr.getAddress() != null)
+                        ? attr.getAddress().getSchemeSpecificPart() : "",
+                        attr.isTestEmergencyNumber(), preciseDisconnectCause,
+                        attr.getPsDisconnectCause(),
+                        attr.getEmergencyRegistrationResult());
+            }
+        }
+        return super.reselectDomain(attr);
+    }
+
     /**
      * Returns the attributes required to determine the domain for a telephony service.
      *
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
index 167062f..f983108 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
@@ -1661,7 +1661,8 @@
                     return phone.getServiceStateTracker().isRadioOn()
                             && !satelliteController.isSatelliteEnabled();
                 }
-            }, !isTestEmergencyNumber, phone, isTestEmergencyNumber, waitForInServiceTimeout);
+            }, !isTestEmergencyNumber, phone, isTestEmergencyNumber, waitForInServiceTimeout,
+                    /* forNormalRoutingEmergencyCall */ false);
         } else {
             switchDdsAndSetEmergencyMode(phone, emergencyType);
         }
diff --git a/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
index 306f6bb..26da1c2 100644
--- a/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
+++ b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
@@ -85,7 +85,7 @@
      */
     public void triggerRadioOnAndListen(RadioOnStateListener.Callback callback,
             boolean forEmergencyCall, Phone phoneForEmergencyCall, boolean isTestEmergencyNumber,
-            int emergencyTimeoutIntervalMillis) {
+            int emergencyTimeoutIntervalMillis, boolean forNormalRoutingEmergencyCall) {
         setupListeners();
         mCallback = callback;
         mInProgressListeners.clear();
@@ -102,8 +102,12 @@
             mListeners.get(i).waitForRadioOn(phone, this, forEmergencyCall, forEmergencyCall
                     && phone == phoneForEmergencyCall, timeoutCallbackInterval);
         }
-        powerOnRadio(forEmergencyCall, phoneForEmergencyCall, isTestEmergencyNumber);
-        if (SatelliteController.getInstance().isSatelliteEnabled()) {
+        powerOnRadio(forEmergencyCall, phoneForEmergencyCall, isTestEmergencyNumber,
+                forNormalRoutingEmergencyCall);
+        if (SatelliteController.getInstance().isSatelliteEnabled()
+                || SatelliteController.getInstance().isSatelliteBeingEnabled()) {
+            // TODO: phoneForEmergencyCall is actually ignored, SatelliteController#mSatelliePhone
+            //  is being used instead.
             powerOffSatellite(phoneForEmergencyCall);
         }
     }
@@ -113,17 +117,25 @@
      * get an onServiceStateChanged() callback when the radio successfully comes up.
      */
     private void powerOnRadio(boolean forEmergencyCall, Phone phoneForEmergencyCall,
-            boolean isTestEmergencyNumber) {
+            boolean isTestEmergencyNumber, boolean forNormalRoutingEmergencyCall) {
 
         // Always try to turn on the radio here independent of APM setting - if we got here in the
         // first place, the radio is off independent of APM setting.
         for (Phone phone : PhoneFactory.getPhones()) {
             Rlog.d(TAG, "powerOnRadio, enabling Radio");
             if (isTestEmergencyNumber) {
-                phone.setRadioPowerOnForTestEmergencyCall(phone == phoneForEmergencyCall);
+                phone.setRadioPowerOnForTestEmergencyCall(
+                        (phone == phoneForEmergencyCall) && !forNormalRoutingEmergencyCall);
             } else {
-                phone.setRadioPower(true, forEmergencyCall, phone == phoneForEmergencyCall,
-                        false);
+                if (forNormalRoutingEmergencyCall) {
+                    if (phone.getServiceStateTracker() != null) {
+                        // Clear all radio off reasons to ensure that the radio is turned on for
+                        // normal routing emergency call.
+                        phone.getServiceStateTracker().clearAllRadioOffReasons();
+                    }
+                }
+                phone.setRadioPower(true, forEmergencyCall && !forNormalRoutingEmergencyCall,
+                        (phone == phoneForEmergencyCall) && !forNormalRoutingEmergencyCall, false);
             }
         }
 
@@ -152,7 +164,7 @@
      */
     private void powerOffSatellite(Phone phoneForEmergencyCall) {
         SatelliteController satelliteController = SatelliteController.getInstance();
-        satelliteController.requestSatelliteEnabled(phoneForEmergencyCall.getSubId(),
+        satelliteController.requestSatelliteEnabled(
                 false /* enableSatellite */, false /* enableDemoMode */, false /* isEmergency */,
                 new IIntegerConsumer.Stub() {
                     @Override
diff --git a/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java b/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java
index fa0610a..1b1922c 100644
--- a/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java
+++ b/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java
@@ -21,7 +21,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.telephony.ServiceState;
-import android.telephony.SubscriptionManager;
 import android.telephony.satellite.ISatelliteModemStateCallback;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -148,6 +147,16 @@
         public void onSatelliteModemStateChanged(int state) {
             mHandler.obtainMessage(MSG_SATELLITE_ENABLED_CHANGED).sendToTarget();
         }
+
+        @Override
+        public void onEmergencyModeChanged(boolean isEmergency) {
+            Rlog.d(TAG, "onEmergencyModeChanged: ignored " + isEmergency);
+        }
+
+        @Override
+        public void onRegistrationFailure(int causeCode) {
+            Rlog.d(TAG, "onRegistrationFailure: causeCode " + causeCode);
+        }
     };
 
     private Callback mCallback; // The callback to notify upon completion.
@@ -393,7 +402,7 @@
                 mPhone.setRadioPower(true, mForEmergencyCall, mSelectedPhoneForEmergencyCall,
                         false);
                 if (mSatelliteController.isSatelliteEnabled()) {
-                    mSatelliteController.requestSatelliteEnabled(mPhone.getSubId(),
+                    mSatelliteController.requestSatelliteEnabled(
                             false /* enableSatellite */, false /* enableDemoMode */,
                             false /* isEmergency*/,
                             new IIntegerConsumer.Stub() {
@@ -497,16 +506,11 @@
     }
 
     private void registerForSatelliteEnabledChanged() {
-        mSatelliteController.registerForSatelliteModemStateChanged(
-                mPhone.getSubId(), mSatelliteCallback);
+        mSatelliteController.registerForSatelliteModemStateChanged(mSatelliteCallback);
     }
 
     private void unregisterForSatelliteEnabledChanged() {
-        int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-        if (mPhone != null) {
-            subId = mPhone.getSubId();
-        }
-        mSatelliteController.unregisterForModemStateChanged(subId, mSatelliteCallback);
+        mSatelliteController.unregisterForModemStateChanged(mSatelliteCallback);
         mHandler.removeMessages(MSG_SATELLITE_ENABLED_CHANGED);
     }
 
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
index 4e773f3..b758733 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
@@ -831,6 +831,7 @@
                         }
                         case CMD_DOWNLOAD_SUBSCRIPTION: {
                             DownloadRequest request = (DownloadRequest) message.obj;
+                            EuiccSession.get().startSession(EuiccSession.DOWNLOAD);
                             mEuiccService.downloadSubscription(slotId,
                                     request.mPortIndex,
                                     request.mSubscription,
@@ -845,6 +846,7 @@
                                                     .onDownloadComplete(result);
                                                 onCommandEnd(callback);
                                             });
+                                            EuiccSession.get().endSession(EuiccSession.DOWNLOAD);
                                         }
                                     });
                             break;
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index 1a5b99e..715116e 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -25,7 +25,6 @@
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
-import android.app.admin.flags.Flags;
 import android.app.compat.CompatChanges;
 import android.content.ComponentName;
 import android.content.Context;
@@ -622,24 +621,21 @@
             Bundle resolvedBundle, PendingIntent callbackIntent) {
         mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
 
-        boolean callerHasAdminPrivileges = false;
-        if (Flags.esimManagementEnabled()) {
-            callerHasAdminPrivileges = callerCanManageDevicePolicyManagedSubscriptions(
-                    callingPackage);
-            if (callerHasAdminPrivileges && (switchAfterDownload && !shouldAllowSwitchAfterDownload(
-                    callingPackage))) {
-                // Throw error if calling admin does not have privileges to enable
-                // subscription silently after download but switchAfterDownload is passed as true.
-                sendResult(callbackIntent, ERROR, null);
-                return;
-            }
-            if (mContext.getSystemService(UserManager.class).hasUserRestriction(
-                    UserManager.DISALLOW_SIM_GLOBALLY) && !callerHasAdminPrivileges) {
-                // Only admin managed subscriptions are allowed, but the caller is not authorised to
-                // download admin managed subscriptions. Abort.
-                sendResult(callbackIntent, ERROR, null);
-                return;
-            }
+        boolean callerHasAdminPrivileges =
+                callerCanManageDevicePolicyManagedSubscriptions(callingPackage);
+        if (callerHasAdminPrivileges && (switchAfterDownload && !shouldAllowSwitchAfterDownload(
+                callingPackage))) {
+            // Throw error if calling admin does not have privileges to enable
+            // subscription silently after download but switchAfterDownload is passed as true.
+            sendResult(callbackIntent, ERROR, null);
+            return;
+        }
+        if (mContext.getSystemService(UserManager.class).hasUserRestriction(
+                UserManager.DISALLOW_SIM_GLOBALLY) && !callerHasAdminPrivileges) {
+            // Only admin managed subscriptions are allowed, but the caller is not authorised to
+            // download admin managed subscriptions. Abort.
+            sendResult(callbackIntent, ERROR, null);
+            return;
         }
         // Don't try to resolve the port index for apps which are not targeting on T for backward
         // compatibility. instead always use default port 0.
@@ -837,7 +833,7 @@
                 subscription,
                 switchAfterDownload,
                 forceDeactivateSim,
-                resolvedBundle,
+                addCallingPackageToBundle(resolvedBundle, callingPackage),
                 new EuiccConnector.DownloadCommandCallback() {
                     @Override
                     public void onDownloadComplete(DownloadSubscriptionResult result) {
@@ -936,6 +932,13 @@
                 });
     }
 
+    private static Bundle addCallingPackageToBundle(
+                @Nullable Bundle resolvedBundle, String callingPackage) {
+        resolvedBundle = resolvedBundle == null ? new Bundle() : resolvedBundle;
+        resolvedBundle.putString(EuiccService.EXTRA_PACKAGE_NAME, callingPackage);
+        return resolvedBundle;
+    }
+
     /**
      * Blocking call to {@link EuiccService#onGetEuiccProfileInfoList} of the eUICC with card ID
      * {@code cardId}.
@@ -1070,9 +1073,7 @@
     public void deleteSubscription(int cardId, int subscriptionId, String callingPackage,
             PendingIntent callbackIntent) {
         boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
-        boolean callerIsAdmin =
-                Flags.esimManagementEnabled()
-                        && callerCanManageDevicePolicyManagedSubscriptions(callingPackage);
+        boolean callerIsAdmin = callerCanManageDevicePolicyManagedSubscriptions(callingPackage);
         mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
 
         long token = Binder.clearCallingIdentity();
@@ -1745,7 +1746,7 @@
 
     private void refreshSubscriptionsOwnership(boolean isCallerAdmin, String callingPackage,
             int cardId, Set<Integer> subscriptionsBefore) {
-        if (Flags.esimManagementEnabled() && isCallerAdmin) {
+        if (isCallerAdmin) {
             // Mark the newly downloaded subscriptions as being owned by an admin so
             // that actions for that subscription can be restricted,
             // and the admin is limited to effecting only these subscriptions.
@@ -1854,7 +1855,12 @@
         if (bestComponent != null) {
             intent.setPackage(bestComponent.packageName);
         }
-        mContext.sendBroadcast(intent, permission.WRITE_EMBEDDED_SUBSCRIPTIONS);
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                    permission.WRITE_EMBEDDED_SUBSCRIPTIONS);
+        } else {
+            mContext.sendBroadcast(intent, permission.WRITE_EMBEDDED_SUBSCRIPTIONS);
+        }
     }
 
     @Nullable
@@ -1871,9 +1877,6 @@
     }
 
     private Set<Integer> getCurrentEmbeddedSubscriptionIds(int cardId) {
-        if (!Flags.esimManagementEnabled()) {
-            return new ArraySet<>();
-        }
         List<SubscriptionInfo> subscriptionInfos =
                 mSubscriptionManager.getAvailableSubscriptionInfoList();
         int subCount = (subscriptionInfos != null) ? subscriptionInfos.size() : 0;
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccSession.java b/src/java/com/android/internal/telephony/euicc/EuiccSession.java
new file mode 100644
index 0000000..99b8720
--- /dev/null
+++ b/src/java/com/android/internal/telephony/euicc/EuiccSession.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.uicc.euicc.apdu.ApduSender;
+import com.android.telephony.Rlog;
+
+import java.util.Set;
+
+/**
+ * A eUICC transaction session aims to optimize multiple back-to-back EuiccPort API calls by only
+ * open and close a logical channel once.
+ *
+ * <p>This class is thread-safe.
+ */
+public class EuiccSession {
+    private static final String TAG = "EuiccSession";
+
+    // **** Well known session IDs, see #startSession() ****
+    public static final String DOWNLOAD = "DOWNLOAD";
+
+    @GuardedBy("EuiccSession.class")
+    private static EuiccSession sInstance;
+
+    public static synchronized EuiccSession get() {
+        if (sInstance == null) {
+            sInstance = new EuiccSession();
+        }
+        return sInstance;
+    }
+
+    @GuardedBy("this")
+    private final Set<String> mSessions = new ArraySet<>();
+
+    @GuardedBy("this")
+    private final Set<ApduSender> mApduSenders = new ArraySet<>();
+
+    /**
+     * Marks the start of a eUICC transaction session.
+     *
+     * <p>A session means a long-open logical channel (see {@link ApduSender}) used to
+     * send multiple APDUs for one action e.g. {@link EuiccController#downloadSubscription()}.
+     * Those APDUs can be send by one or multiple {@link EuiccCardController} methods.
+     *
+     * <p>Ideally a session should correespond to one phoneId and hence just one logical channel.
+     * But many {@link EuiccCardController} methods uses first available port and is not specific
+     * to a phoneId. So EuiccController cannot choose one phoneId to use. Hence a session has to
+     * be not specific to phoneId, i.e. for DSDS device both phoneId's will be in a session.
+     *
+     * <p>If called multiple times with different {@code sessionId}'s, the session is truly closed
+     * when the all sessions are ended. See {@link #endSession()}.
+     *
+     * @param sessionId The session ID.
+     */
+    public void startSession(String sessionId) {
+        if (!Flags.optimizationApduSender()) {
+            // Other methods in this class is no-op if no session started.
+            // Do not add flag to other methods, so if the flag gets turned off,
+            // the session can be ended properly.
+            return;
+        }
+        Rlog.i(TAG, "startSession: " + sessionId);
+        synchronized(this) {
+            mSessions.add(sessionId);
+        }
+    }
+
+    /** Returns {@code true} if there is at least one session ongoing. */
+    public boolean hasSession() {
+        boolean hasSession;
+        synchronized(this) {
+            hasSession = !mSessions.isEmpty();
+        }
+        Rlog.i(TAG, "hasSession: " + hasSession);
+        return hasSession;
+    }
+
+    /**
+     * Notes that a logical channel may be opened by the {@code apduSender}, which will
+     * be used to close the channel when session ends (see {@link #endSession()}).
+     *
+     * <p>No-op if no session ongoing (see {@link #hasSession()}).
+     *
+     * @param apduSender The ApduSender that will open the channel.
+     */
+    public void noteChannelOpen(ApduSender apduSender) {
+        Rlog.i(TAG, "noteChannelOpen: " + apduSender);
+        synchronized(this) {
+            if (hasSession()) {
+                mApduSenders.add(apduSender);
+            }
+        }
+    }
+
+    /**
+     * Marks the end of a eUICC transaction session. If this ends the last ongoing session,
+     * try to close the logical channel using the noted {@code apduSender}
+     * (see {@link #noteChannelOpen()}).
+     *
+     * @param sessionId The session ID.
+     */
+    public void endSession(String sessionId) {
+        Rlog.i(TAG, "endSession: " + sessionId);
+        ApduSender[] apduSenders = new ApduSender[0];
+        synchronized(this) {
+            boolean sessionRemoved = mSessions.remove(sessionId);
+            // sessionRemoved is false if the `sessionId` was never started or there was
+            // no session at all i.e. `sessions` is empty. Don't bother invoke `apduSender`.
+            if (sessionRemoved && mSessions.isEmpty()) {
+                // copy mApduSenders to a local variable so we don't call closeAnyOpenChannel()
+                // which can take time in synchronized block.
+                apduSenders = mApduSenders.toArray(apduSenders);
+                mApduSenders.clear();
+            }
+        }
+        for (ApduSender apduSender : apduSenders) {
+            apduSender.closeAnyOpenChannel();
+        }
+    }
+
+    @VisibleForTesting
+    public EuiccSession() {}
+}
diff --git a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
index 907f158..bf8098f 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
@@ -38,6 +38,7 @@
 import com.android.internal.telephony.SmsMessageBase;
 import com.android.internal.telephony.SmsStorageMonitor;
 import com.android.internal.telephony.VisualVoicemailSmsFilter;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.uicc.UsimServiceTable;
 
 /**
@@ -58,8 +59,8 @@
      * Create a new GSM inbound SMS handler.
      */
     private GsmInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor,
-            Phone phone, Looper looper) {
-        super("GsmInboundSmsHandler", context, storageMonitor, phone, looper);
+            Phone phone, Looper looper, FeatureFlags featureFlags) {
+        super("GsmInboundSmsHandler", context, storageMonitor, phone, looper, featureFlags);
         phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);
         mDataDownloadHandler = new UsimDataDownloadHandler(phone.mCi, phone.getPhoneId());
         mCellBroadcastServiceManager.enable();
@@ -129,9 +130,10 @@
      * Wait for state machine to enter startup state. We can't send any messages until then.
      */
     public static GsmInboundSmsHandler makeInboundSmsHandler(Context context,
-            SmsStorageMonitor storageMonitor, Phone phone, Looper looper) {
+            SmsStorageMonitor storageMonitor, Phone phone, Looper looper,
+            FeatureFlags featureFlags) {
         GsmInboundSmsHandler handler =
-                new GsmInboundSmsHandler(context, storageMonitor, phone, looper);
+                new GsmInboundSmsHandler(context, storageMonitor, phone, looper, featureFlags);
         handler.start();
         return handler;
     }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java b/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java
index 234723f..f2b5dee 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java
@@ -27,6 +27,8 @@
 import static android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
 
+import static com.android.internal.telephony.CommandsInterface.IMS_MMTEL_CAPABILITY_VOICE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -52,21 +54,19 @@
 public class ImsNrSaModeHandler extends Handler{
 
     public static final String TAG = "ImsNrSaModeHandler";
-    public static final String MMTEL_FEATURE_TAG =
-            "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"";
 
     private static final int MSG_PRECISE_CALL_STATE_CHANGED = 101;
-    private static final int MSG_REQUEST_IS_VONR_ENABLED = 102;
-    private static final int MSG_RESULT_IS_VONR_ENABLED = 103;
+    private static final int MSG_RESULT_IS_VONR_ENABLED = 102;
 
     private final @NonNull ImsPhone mPhone;
     private @Nullable CarrierConfigManager mCarrierConfigManager;
 
     private @NrSaDisablePolicy int mNrSaDisablePolicy;
     private boolean mIsNrSaDisabledForWfc;
-    private boolean mIsVowifiRegistered;
+    private boolean mIsWifiRegistered;
     private boolean mIsInImsCall;
     private boolean mIsNrSaSupported;
+    private boolean mIsVoiceCapable;
 
     private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
             (slotIndex, subId, carrierId, specificCarrierId) -> setNrSaDisablePolicy(subId);
@@ -100,37 +100,16 @@
      */
     public void onImsRegistered(
             @ImsRegistrationTech int imsRadioTech, @NonNull Set<String> featureTags) {
-        if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_NONE) {
+        if (!mIsNrSaSupported || mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_NONE) {
             return;
         }
 
         Log.d(TAG, "onImsRegistered: ImsRegistrationTech = " + imsRadioTech);
 
-        boolean isVowifiRegChanged = false;
-
-        if (isVowifiRegistered() && imsRadioTech != REGISTRATION_TECH_IWLAN) {
-            setVowifiRegStatus(false);
-            isVowifiRegChanged = true;
-        } else if (!isVowifiRegistered() && imsRadioTech == REGISTRATION_TECH_IWLAN
-                && featureTags.contains(MMTEL_FEATURE_TAG)) {
-            setVowifiRegStatus(true);
-            isVowifiRegChanged = true;
-        }
-
-        if (isVowifiRegChanged) {
-            if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED) {
-                setNrSaMode(!isVowifiRegistered());
-            } else if ((mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED
-                    || mNrSaDisablePolicy
-                    == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED)
-                    && isImsCallOngoing()) {
-                if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED) {
-                    requestIsVonrEnabled(!isVowifiRegistered());
-                    return;
-                }
-
-                setNrSaMode(!isVowifiRegistered());
-            }
+        final boolean isNewWifiRegistered = imsRadioTech == REGISTRATION_TECH_IWLAN;
+        if (isWifiRegistered() != isNewWifiRegistered) {
+            setWifiRegStatus(isNewWifiRegistered);
+            calculateAndControlNrSaIfNeeded();
         }
     }
 
@@ -141,27 +120,15 @@
      */
     public void onImsUnregistered(
             @ImsRegistrationTech int imsRadioTech) {
-        if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_NONE
-                || imsRadioTech != REGISTRATION_TECH_IWLAN || !isVowifiRegistered()) {
+        if (!mIsNrSaSupported || mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_NONE
+                || imsRadioTech != REGISTRATION_TECH_IWLAN || !isWifiRegistered()) {
             return;
         }
 
         Log.d(TAG, "onImsUnregistered : ImsRegistrationTech = " + imsRadioTech);
 
-        setVowifiRegStatus(false);
-
-        if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED) {
-            setNrSaMode(true);
-        } else if ((mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED
-                || mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED)
-                && isImsCallOngoing()) {
-            if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED) {
-                requestIsVonrEnabled(true);
-                return;
-            }
-
-            setNrSaMode(true);
-        }
+        setWifiRegStatus(false);
+        calculateAndControlNrSaIfNeeded();
     }
 
     /**
@@ -182,13 +149,23 @@
             isImsCallStatusChanged = true;
         }
 
-        if (isVowifiRegistered() && isImsCallStatusChanged) {
-            if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED) {
-                requestIsVonrEnabled(!isImsCallOngoing());
-                return;
-            }
+        if (isWifiRegistered() && isImsCallStatusChanged) {
+            calculateAndControlNrSaIfNeeded();
+        }
+    }
 
-            setNrSaMode(!isImsCallOngoing());
+    /**
+     * Updates Capability.
+     */
+    public void updateImsCapability(int capabilities) {
+        if (!mIsNrSaSupported || mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_NONE) {
+            return;
+        }
+
+        boolean isVoiceCapable = (IMS_MMTEL_CAPABILITY_VOICE & capabilities) != 0;
+        if (mIsVoiceCapable != isVoiceCapable) {
+            mIsVoiceCapable = isVoiceCapable;
+            calculateAndControlNrSaIfNeeded();
         }
     }
 
@@ -200,11 +177,6 @@
             case MSG_PRECISE_CALL_STATE_CHANGED :
                 onPreciseCallStateChanged();
                 break;
-            case MSG_REQUEST_IS_VONR_ENABLED :
-                Log.d(TAG, "request isVoNrEnabled");
-                mPhone.getDefaultPhone().mCi.isVoNrEnabled(
-                        obtainMessage(MSG_RESULT_IS_VONR_ENABLED, msg.obj), null);
-                break;
             case MSG_RESULT_IS_VONR_ENABLED :
                 ar = (AsyncResult) msg.obj;
 
@@ -212,8 +184,9 @@
                     boolean vonrEnabled = ((Boolean) ar.result).booleanValue();
 
                     Log.d(TAG, "result isVoNrEnabled = " + vonrEnabled);
-                    if (!vonrEnabled) {
-                        setNrSaMode(((Boolean) ar.userObj).booleanValue());
+                    if (isWifiCallingOngoing() && !vonrEnabled) {
+                        // If still WiFi calling is ongoing and VoNR is disabled, disable NR SA.
+                        setNrSaMode(false);
                     }
                 }
 
@@ -262,14 +235,18 @@
         if (mPhone.getSubId() == subId && mCarrierConfigManager != null) {
             PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(),
                     KEY_NR_SA_DISABLE_POLICY_INT, KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
-            mNrSaDisablePolicy = bundle.getInt(KEY_NR_SA_DISABLE_POLICY_INT);
             int[] nrAvailabilities = bundle.getIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
             mIsNrSaSupported = nrAvailabilities != null
                     && Arrays.stream(nrAvailabilities).anyMatch(
                             value -> value == CARRIER_NR_AVAILABILITY_SA);
 
-            Log.d(TAG, "setNrSaDisablePolicy : NrSaDisablePolicy = "
-                    + mNrSaDisablePolicy + ", IsNrSaSupported = "  + mIsNrSaSupported);
+            if (!mIsNrSaSupported) {
+                return;
+            }
+
+            mNrSaDisablePolicy = bundle.getInt(KEY_NR_SA_DISABLE_POLICY_INT);
+
+            Log.d(TAG, "setNrSaDisablePolicy : NrSaDisablePolicy = " + mNrSaDisablePolicy);
 
             if (mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED
                     || mNrSaDisablePolicy == NR_SA_DISABLE_POLICY_WFC_ESTABLISHED) {
@@ -280,27 +257,20 @@
         }
     }
 
-    private void requestIsVonrEnabled(boolean onOrOff) {
-        Message msg = obtainMessage(MSG_REQUEST_IS_VONR_ENABLED, onOrOff);
-        msg.sendToTarget();
-    }
-
     private void setNrSaMode(boolean onOrOff) {
-        if (mIsNrSaSupported) {
-            mPhone.getDefaultPhone().setN1ModeEnabled(onOrOff, null);
-            Log.i(TAG, "setNrSaMode : " + onOrOff);
+        mPhone.getDefaultPhone().setN1ModeEnabled(onOrOff, null);
+        Log.i(TAG, "setNrSaMode : " + onOrOff);
 
-            setNrSaDisabledForWfc(!onOrOff);
-        }
+        setNrSaDisabledForWfc(!onOrOff);
     }
 
     /**
-     * Sets VoWiFi reg status.
+     * Sets WiFi reg status.
      */
     @VisibleForTesting
-    public void setVowifiRegStatus(boolean registered) {
-        Log.d(TAG, "setVowifiRegStatus : " + registered);
-        mIsVowifiRegistered = registered;
+    public void setWifiRegStatus(boolean registered) {
+        Log.d(TAG, "setWifiRegStatus : " + registered);
+        mIsWifiRegistered = registered;
     }
 
     /**
@@ -313,8 +283,8 @@
     }
 
     @VisibleForTesting
-    public boolean isVowifiRegistered() {
-        return mIsVowifiRegistered;
+    public boolean isWifiRegistered() {
+        return mIsWifiRegistered;
     }
 
     @VisibleForTesting
@@ -322,8 +292,7 @@
         return mIsInImsCall;
     }
 
-    @VisibleForTesting
-    public boolean isNrSaDisabledForWfc() {
+    private boolean isNrSaDisabledForWfc() {
         return mIsNrSaDisabledForWfc;
     }
 
@@ -353,4 +322,56 @@
 
         return false;
     }
+
+    private void calculateAndControlNrSaIfNeeded() {
+        switch (mNrSaDisablePolicy) {
+            case NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED:
+                if (isNrSaDisabledForWfc() == isWifiRegisteredForVoice()) {
+                    // NR SA is already disabled or condition is not met for disabling NR SA.
+                    // So, no need for further action
+                    return;
+                }
+
+                // Disable NR SA if VoWiFi registered otherwise enable
+                setNrSaMode(!isWifiRegisteredForVoice());
+                return;
+            case NR_SA_DISABLE_POLICY_WFC_ESTABLISHED:
+                if (isNrSaDisabledForWfc() == isWifiCallingOngoing()) {
+                    // NR SA is already disabled or condition is not met for disabling NR SA.
+                    // So, no need for further action
+                    return;
+                }
+
+                // Disable NR SA if VoWiFi call established otherwise enable
+                setNrSaMode(!isWifiCallingOngoing());
+                return;
+            case NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED:
+                if (isNrSaDisabledForWfc() == isWifiCallingOngoing()) {
+                    // NR SA is already disabled or condition is not met for disabling NR SA.
+                    // So, no need for further action
+                    return;
+                }
+
+                if (isWifiCallingOngoing()) {
+                    // Query whether VoNR is enabled or not.
+                    mPhone.getDefaultPhone().mCi.isVoNrEnabled(
+                            obtainMessage(MSG_RESULT_IS_VONR_ENABLED), null);
+                    return;
+                }
+
+                // Enable NR SA if there are no VoWiFi calls.
+                setNrSaMode(true);
+                return;
+            default:
+                break;
+        }
+    }
+
+    private boolean isWifiRegisteredForVoice() {
+        return isWifiRegistered() && mIsVoiceCapable;
+    }
+
+    private boolean isWifiCallingOngoing() {
+        return isWifiRegistered() && mIsVoiceCapable && isImsCallOngoing();
+    }
 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 10cbe77..22b8a75 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -1016,8 +1016,8 @@
 
         // Only look at the Network portion for mmi
         String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
-        ImsPhoneMmiCode mmi =
-                ImsPhoneMmiCode.newFromDialString(networkPortion, this, wrappedCallback);
+        ImsPhoneMmiCode mmi =  ImsPhoneMmiCode.newFromDialString(networkPortion, this,
+                wrappedCallback, mFeatureFlags);
         if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'...");
 
         if (mmi == null) {
@@ -1440,12 +1440,13 @@
     }
 
     @Override
-    public void sendUssdResponse(String ussdMessge) {
+    public void sendUssdResponse(String ussdMessage) {
         logd("sendUssdResponse");
-        ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessge, this);
+        ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessage, this,
+                mFeatureFlags);
         mPendingMMIs.add(mmi);
         mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
-        mmi.sendUssd(ussdMessge);
+        mmi.sendUssd(ussdMessage);
     }
 
     public void sendUSSD(String ussdString, Message response) {
@@ -1575,14 +1576,12 @@
                 // also, discard if there is no message to present
                 ImsPhoneMmiCode mmi;
                 mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
-                        isUssdRequest,
-                        this);
+                        isUssdRequest, this, mFeatureFlags);
                 onNetworkInitiatedUssd(mmi);
         } else if (isUssdError) {
             ImsPhoneMmiCode mmi;
             mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
-                    true,
-                    this);
+                    true, this, mFeatureFlags);
             mmi.onUssdFinishedError();
         }
     }
@@ -2763,7 +2762,7 @@
     }
 
     /**
-     * Update IMS registration information to modem.
+     * Update IMS registration information to modem and other modules.
      *
      * @param capabilities indicate MMTEL capability such as VOICE, VIDEO and SMS.
      */
@@ -2784,6 +2783,8 @@
             mDefaultPhone.mCi.updateImsRegistrationInfo(mImsRegistrationState,
                     mImsRegistrationTech, 0, capabilities, null);
             mNotifiedRegisteredState = true;
+
+            mImsNrSaModeHandler.updateImsCapability(capabilities);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index e73eafd..26b6e18 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -70,6 +70,7 @@
 import android.os.RegistrantList;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.sysprop.TelephonyProperties;
@@ -1543,7 +1544,11 @@
         Intent intent = new Intent(intentAction);
         intent.putExtra(ImsManager.EXTRA_PHONE_ID, mPhone.getPhoneId());
         if (mPhone != null && mPhone.getContext() != null) {
-            mPhone.getContext().sendBroadcast(intent);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mPhone.getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+            } else {
+                mPhone.getContext().sendBroadcast(intent);
+            }
         }
     }
 
@@ -2878,6 +2883,14 @@
         ImsPhoneConnection conn = findConnection(imsCall);
         boolean rejectCall = false;
 
+        if (mFeatureFlags.preventHangupDuringCallMerge()) {
+            if (imsCall.isCallSessionMergePending()) {
+                if (DBG) log("hangup call failed during call merge");
+
+                throw new CallStateException("can not hangup during call merge");
+            }
+        }
+
         String logResult = "(undefined)";
         if (call == mRingingCall) {
             logResult = "(ringing) hangup incoming";
@@ -3260,6 +3273,12 @@
         int cause = DisconnectCause.ERROR_UNSPECIFIED;
 
         int code = maybeRemapReasonCode(reasonInfo);
+
+        if (mFeatureFlags.remapDisconnectCauseSipRequestCancelled() &&
+                code == ImsReasonInfo.CODE_SIP_REQUEST_CANCELLED) {
+            return DisconnectCause.NORMAL;
+        }
+
         switch (code) {
             case ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL:
                 return DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL;
@@ -4666,8 +4685,14 @@
                     configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
                     configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
                     if (mPhone != null && mPhone.getContext() != null) {
-                        mPhone.getContext().sendBroadcast(configChangedIntent,
-                                Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+                        if (mFeatureFlags.hsumBroadcast()) {
+                            mPhone.getContext().sendBroadcastAsUser(configChangedIntent,
+                                    UserHandle.ALL,
+                                    Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+                        } else {
+                            mPhone.getContext().sendBroadcast(configChangedIntent,
+                                    Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+                        }
                     }
                 }
 
@@ -4951,7 +4976,7 @@
             }
             case EVENT_SUPP_SERVICE_INDICATION: {
                 ar = (AsyncResult) msg.obj;
-                ImsPhoneMmiCode mmiCode = new ImsPhoneMmiCode(mPhone);
+                ImsPhoneMmiCode mmiCode = new ImsPhoneMmiCode(mPhone, mFeatureFlags);
                 try {
                     mmiCode.setIsSsInfo(true);
                     mmiCode.processImsSsData(ar);
@@ -6327,8 +6352,13 @@
         configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
         configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
         if (mPhone != null && mPhone.getContext() != null) {
-            mPhone.getContext().sendBroadcast(
-                    configChangedIntent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mPhone.getContext().sendBroadcastAsUser(configChangedIntent, UserHandle.ALL,
+                        Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            } else {
+                mPhone.getContext().sendBroadcast(
+                        configChangedIntent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            }
         }
     }
 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index 25fa8a2..d573f5d 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -63,6 +63,7 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.MmiCode;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.gsm.GsmMmiCode;
 import com.android.internal.telephony.uicc.IccRecords;
 import com.android.telephony.Rlog;
@@ -165,6 +166,9 @@
     private static final int NUM_PRESENTATION_ALLOWED     = 0;
     private static final int NUM_PRESENTATION_RESTRICTED  = 1;
 
+    // Using same value with CallForwardEditPreference#DEFAULT_NO_REPLY_TIMER_FOR_CFNRY
+    private static final int DEFAULT_NO_REPLY_TIMER_FOR_CFNRY = 20;
+
     //***** Supplementary Service Query Bundle Keys
     // Used by IMS Service layer to put supp. serv. query
     // responses into the ssInfo Bundle.
@@ -245,6 +249,8 @@
     private static final int MATCH_GROUP_DIALING_NUMBER = 12;
     static private String[] sTwoDigitNumberPattern;
 
+    private final FeatureFlags mFeatureFlags;
+
     //***** Public Class methods
 
     /**
@@ -262,12 +268,13 @@
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     @VisibleForTesting
-    public static ImsPhoneMmiCode newFromDialString(String dialString, ImsPhone phone) {
-       return newFromDialString(dialString, phone, null);
+    public static ImsPhoneMmiCode newFromDialString(String dialString, ImsPhone phone,
+            FeatureFlags featureFlags) {
+        return newFromDialString(dialString, phone, null, featureFlags);
     }
 
-    static ImsPhoneMmiCode newFromDialString(String dialString,
-                                             ImsPhone phone, ResultReceiver wrappedCallback) {
+    static ImsPhoneMmiCode newFromDialString(String dialString, ImsPhone phone,
+            ResultReceiver wrappedCallback, FeatureFlags featureFlags) {
         Matcher m;
         ImsPhoneMmiCode ret = null;
 
@@ -287,7 +294,7 @@
 
         // Is this formatted like a standard supplementary service code?
         if (m.matches()) {
-            ret = new ImsPhoneMmiCode(phone);
+            ret = new ImsPhoneMmiCode(phone, featureFlags);
             ret.mPoundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING));
             ret.mAction = makeEmptyNull(m.group(MATCH_GROUP_ACTION));
             ret.mSc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
@@ -305,7 +312,7 @@
             if (ret.mDialingNumber != null &&
                     ret.mDialingNumber.endsWith("#") &&
                     dialString.endsWith("#")){
-                ret = new ImsPhoneMmiCode(phone);
+                ret = new ImsPhoneMmiCode(phone, featureFlags);
                 ret.mPoundString = dialString;
             }
         } else if (dialString.endsWith("#")) {
@@ -313,7 +320,7 @@
             // "Entry of any characters defined in the 3GPP TS 23.038 [8] Default Alphabet
             // (up to the maximum defined in 3GPP TS 24.080 [10]), followed by #SEND".
 
-            ret = new ImsPhoneMmiCode(phone);
+            ret = new ImsPhoneMmiCode(phone, featureFlags);
             ret.mPoundString = dialString;
         } else if (GsmMmiCode.isTwoDigitShortCode(phone.getContext(), phone.getSubId(),
                 dialString)) {
@@ -321,7 +328,7 @@
             ret = null;
         } else if (isShortCode(dialString, phone)) {
             // this may be a short code, as defined in TS 22.030, 6.5.3.2
-            ret = new ImsPhoneMmiCode(phone);
+            ret = new ImsPhoneMmiCode(phone, featureFlags);
             ret.mDialingNumber = dialString;
         }
         return ret;
@@ -348,10 +355,10 @@
     }
 
     public static ImsPhoneMmiCode newNetworkInitiatedUssd(String ussdMessage,
-            boolean isUssdRequest, ImsPhone phone) {
+            boolean isUssdRequest, ImsPhone phone, FeatureFlags featureFlags) {
         ImsPhoneMmiCode ret;
 
-        ret = new ImsPhoneMmiCode(phone);
+        ret = new ImsPhoneMmiCode(phone, featureFlags);
 
         ret.mMessage = ussdMessage;
         ret.mIsUssdRequest = isUssdRequest;
@@ -368,10 +375,11 @@
         return ret;
     }
 
-    static ImsPhoneMmiCode newFromUssdUserInput(String ussdMessge, ImsPhone phone) {
-        ImsPhoneMmiCode ret = new ImsPhoneMmiCode(phone);
+    static ImsPhoneMmiCode newFromUssdUserInput(String ussdMessage, ImsPhone phone,
+            FeatureFlags featureFlags) {
+        ImsPhoneMmiCode ret = new ImsPhoneMmiCode(phone, featureFlags);
 
-        ret.mMessage = ussdMessge;
+        ret.mMessage = ussdMessage;
         ret.mState = State.PENDING;
         ret.mIsPendingUSSD = true;
 
@@ -584,13 +592,15 @@
 
     //***** Constructor
 
-    public ImsPhoneMmiCode(ImsPhone phone) {
+    public ImsPhoneMmiCode(ImsPhone phone, FeatureFlags featureFlags) {
         // The telephony unit-test cases may create ImsPhoneMmiCode's
         // in secondary threads
         super(phone.getHandler().getLooper());
         mPhone = phone;
         mContext = phone.getContext();
         mIccRecords = mPhone.mDefaultPhone.getIccRecords();
+
+        mFeatureFlags = featureFlags;
     }
 
     //***** MmiCode implementation
@@ -907,7 +917,19 @@
                 String dialingNumber = mSia;
                 int reason = scToCallForwardReason(mSc);
                 int serviceClass = siToServiceClass(mSib);
-                int time = siToTime(mSic);
+                int time;
+
+                if (mFeatureFlags.useCarrierConfigForCfnryTimeViaMmi()) {
+                    // If the code is CFNRy and time is null(empty)
+                    // use the default time value from CarrierConfig
+                    if (mSc.equals(SC_CFNRy) && isEmptyOrNull(mSic)) {
+                        time = getCfnryTime();
+                    } else {
+                        time = siToTime(mSic);
+                    }
+                } else {
+                    time = siToTime(mSic);
+                }
 
                 if (isInterrogate()) {
                     mPhone.getCallForwardingOption(reason, serviceClass,
@@ -1147,6 +1169,27 @@
         }
     }
 
+    private int getCfnryTime() {
+        CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class);
+        if (configManager == null) {
+            return DEFAULT_NO_REPLY_TIMER_FOR_CFNRY;
+        }
+
+        PersistableBundle carrierConfig = configManager.getConfigForSubId(mPhone.getSubId());
+        if (carrierConfig == null
+                || !carrierConfig.getBoolean(
+                        CarrierConfigManager.KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true)) {
+            return DEFAULT_NO_REPLY_TIMER_FOR_CFNRY;
+        }
+
+        int time = carrierConfig.getInt(
+                CarrierConfigManager.KEY_NO_REPLY_TIMER_FOR_CFNRY_SEC_INT,
+                DEFAULT_NO_REPLY_TIMER_FOR_CFNRY);
+
+        Rlog.d(LOG_TAG, "getCfnryTime: " + time);
+        return time;
+    }
+
     private boolean isUssdOverImsAllowed() {
         if (mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_allow_ussd_over_ims)) {
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index a83cd06..e43bf3c 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -1147,7 +1147,8 @@
                 session.vonrEnabled,
                 session.isNtn,
                 session.supportsBusinessCallComposer,
-                session.callComposerStatus);
+                session.callComposerStatus,
+                session.preciseCallStateOnSetup);
 
     }
 
@@ -1474,7 +1475,12 @@
                 satelliteController.countOfDatagramTypeKeepAliveFail,
                 satelliteController.countOfAllowedSatelliteAccess,
                 satelliteController.countOfDisallowedSatelliteAccess,
-                satelliteController.countOfSatelliteAccessCheckFail);
+                satelliteController.countOfSatelliteAccessCheckFail,
+                satelliteController.isProvisioned,
+                satelliteController.carrierId,
+                satelliteController.countOfSatelliteAllowedStateChangedEvents,
+                satelliteController.countOfSuccessfulLocationQueries,
+                satelliteController.countOfFailedLocationQueries);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteSession satelliteSession) {
@@ -1492,7 +1498,11 @@
                 satelliteSession.countOfIncomingDatagramSuccess,
                 satelliteSession.countOfIncomingDatagramFailed,
                 satelliteSession.isDemoMode,
-                satelliteSession.maxNtnSignalStrengthLevel);
+                satelliteSession.maxNtnSignalStrengthLevel,
+                satelliteSession.carrierId,
+                satelliteSession.countOfSatelliteNotificationDisplayed,
+                satelliteSession.countOfAutoExitDueToScreenOff,
+                satelliteSession.countOfAutoExitDueToTnNetwork);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteIncomingDatagram stats) {
@@ -1501,7 +1511,8 @@
                 stats.resultCode,
                 stats.datagramSizeBytes,
                 stats.datagramTransferTimeMillis,
-                stats.isDemoMode);
+                stats.isDemoMode,
+                stats.carrierId);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteOutgoingDatagram stats) {
@@ -1511,7 +1522,8 @@
                 stats.resultCode,
                 stats.datagramSizeBytes,
                 stats.datagramTransferTimeMillis,
-                stats.isDemoMode);
+                stats.isDemoMode,
+                stats.carrierId);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteProvision stats) {
@@ -1520,7 +1532,8 @@
                 stats.resultCode,
                 stats.provisioningTimeSec,
                 stats.isProvisionRequest,
-                stats.isCanceled);
+                stats.isCanceled,
+                stats.carrierId);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteSosMessageRecommender stats) {
@@ -1533,7 +1546,9 @@
                 stats.count,
                 stats.isMultiSim,
                 stats.recommendingHandoverType,
-                stats.isSatelliteAllowedInCurrentLocation);
+                stats.isSatelliteAllowedInCurrentLocation,
+                stats.isWifiConnected,
+                stats.carrierId);
     }
 
     private static StatsEvent buildStatsEvent(DataNetworkValidation stats) {
@@ -1578,7 +1593,9 @@
                 stats.countOfSatelliteNotificationDisplayed,
                 stats.satelliteSessionGapMinSec,
                 stats.satelliteSessionGapAvgSec,
-                stats.satelliteSessionGapMaxSec);
+                stats.satelliteSessionGapMaxSec,
+                stats.carrierId,
+                stats.isDeviceEntitled);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteEntitlement stats) {
@@ -1610,7 +1627,9 @@
                 stats.isEmergency,
                 stats.resultCode,
                 stats.countryCodes,
-                stats.configDataSource);
+                stats.configDataSource,
+                stats.carrierId,
+                stats.triggeringEvent);
     }
 
     /** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index 12dab7a..cf44aaf 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -771,6 +771,14 @@
         atom.countOfDisallowedSatelliteAccess += stats.countOfDisallowedSatelliteAccess;
         atom.countOfSatelliteAccessCheckFail += stats.countOfSatelliteAccessCheckFail;
 
+        atom.isProvisioned = stats.isProvisioned;
+        atom.carrierId = stats.carrierId;
+
+        atom.countOfSatelliteAllowedStateChangedEvents
+                += stats.countOfSatelliteAllowedStateChangedEvents;
+        atom.countOfSuccessfulLocationQueries += stats.countOfSuccessfulLocationQueries;
+        atom.countOfFailedLocationQueries += stats.countOfFailedLocationQueries;
+
         mAtoms.satelliteController = atomArray;
         saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
     }
@@ -869,6 +877,8 @@
         atom.satelliteSessionGapMinSec = stats.satelliteSessionGapMinSec;
         atom.satelliteSessionGapAvgSec = stats.satelliteSessionGapAvgSec;
         atom.satelliteSessionGapMaxSec = stats.satelliteSessionGapMaxSec;
+        atom.carrierId = stats.carrierId;
+        atom.isDeviceEntitled = stats.isDeviceEntitled;
 
         mAtoms.carrierRoamingSatelliteControllerStats = atomArray;
         saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
@@ -2331,7 +2341,12 @@
                     && stats.countOfIncomingDatagramSuccess == key.countOfIncomingDatagramSuccess
                     && stats.countOfIncomingDatagramFailed == key.countOfIncomingDatagramFailed
                     && stats.isDemoMode == key.isDemoMode
-                    && stats.maxNtnSignalStrengthLevel == key.maxNtnSignalStrengthLevel) {
+                    && stats.maxNtnSignalStrengthLevel == key.maxNtnSignalStrengthLevel
+                    && stats.carrierId == key.carrierId
+                    && stats.countOfSatelliteNotificationDisplayed
+                    == key.countOfSatelliteNotificationDisplayed
+                    && stats.countOfAutoExitDueToScreenOff == key.countOfAutoExitDueToScreenOff
+                    && stats.countOfAutoExitDueToTnNetwork == key.countOfAutoExitDueToTnNetwork) {
                 return stats;
             }
         }
@@ -2350,7 +2365,9 @@
                     && stats.isImsRegistered == key.isImsRegistered
                     && stats.cellularServiceState == key.cellularServiceState
                     && stats.isMultiSim == key.isMultiSim
-                    && stats.recommendingHandoverType == key.recommendingHandoverType) {
+                    && stats.recommendingHandoverType == key.recommendingHandoverType
+                    && stats.isWifiConnected == key.isWifiConnected
+                    && stats.carrierId == key.carrierId) {
                 return stats;
             }
         }
diff --git a/src/java/com/android/internal/telephony/metrics/SatelliteStats.java b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
index c2b2753..71c1bf3 100644
--- a/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
+++ b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
@@ -17,6 +17,9 @@
 package com.android.internal.telephony.metrics;
 
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
+
+import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_UNKNOWN;
 
 import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteManager;
@@ -37,6 +40,7 @@
 import com.android.telephony.Rlog;
 
 import java.util.Arrays;
+import java.util.Optional;
 
 /** Tracks Satellite metrics for each phone */
 public class SatelliteStats {
@@ -91,6 +95,11 @@
         private final int mCountOfAllowedSatelliteAccess;
         private final int mCountOfDisallowedSatelliteAccess;
         private final int mCountOfSatelliteAccessCheckFail;
+        private static boolean sIsProvisioned;
+        private static int sCarrierId = UNKNOWN_CARRIER_ID;
+        private final int mCountOfSatelliteAllowedStateChangedEvents;
+        private final int mCountOfSuccessfulLocationQueries;
+        private final int mCountOfFailedLocationQueries;
 
         private SatelliteControllerParams(Builder builder) {
             this.mCountOfSatelliteServiceEnablementsSuccess =
@@ -136,6 +145,23 @@
                     builder.mCountOfDisallowedSatelliteAccess;
             this.mCountOfSatelliteAccessCheckFail =
                     builder.mCountOfSatelliteAccessCheckFail;
+
+            // isProvisioned value should be updated only when it is meaningful.
+            if (builder.mIsProvisioned.isPresent()) {
+                this.sIsProvisioned = builder.mIsProvisioned.get();
+            }
+
+            // Carrier ID value should be updated only when it is meaningful.
+            if (builder.mCarrierId.isPresent()) {
+                this.sCarrierId = builder.mCarrierId.get();
+            }
+
+            this.mCountOfSatelliteAllowedStateChangedEvents =
+                    builder.mCountOfSatelliteAllowedStateChangedEvents;
+            this.mCountOfSuccessfulLocationQueries =
+                    builder.mCountOfSuccessfulLocationQueries;
+            this.mCountOfFailedLocationQueries =
+                    builder.mCountOfFailedLocationQueries;
         }
 
         public int getCountOfSatelliteServiceEnablementsSuccess() {
@@ -250,6 +276,26 @@
             return mCountOfSatelliteAccessCheckFail;
         }
 
+        public boolean isProvisioned() {
+            return sIsProvisioned;
+        }
+
+        public int getCarrierId() {
+            return sCarrierId;
+        }
+
+        public int getCountOfSatelliteAllowedStateChangedEvents() {
+            return mCountOfSatelliteAllowedStateChangedEvents;
+        }
+
+        public int getCountOfSuccessfulLocationQueries() {
+            return mCountOfSuccessfulLocationQueries;
+        }
+
+        public int getCountOfFailedLocationQueries() {
+            return mCountOfFailedLocationQueries;
+        }
+
         /**
          * A builder class to create {@link SatelliteControllerParams} data structure class
          */
@@ -282,6 +328,11 @@
             private int mCountOfAllowedSatelliteAccess = 0;
             private int mCountOfDisallowedSatelliteAccess = 0;
             private int mCountOfSatelliteAccessCheckFail = 0;
+            private Optional<Boolean> mIsProvisioned = Optional.empty();
+            private Optional<Integer> mCarrierId = Optional.empty();
+            private int mCountOfSatelliteAllowedStateChangedEvents = 0;
+            private int mCountOfSuccessfulLocationQueries = 0;
+            private int mCountOfFailedLocationQueries = 0;
 
             /**
              * Sets countOfSatelliteServiceEnablementsSuccess value of {@link SatelliteController}
@@ -561,6 +612,55 @@
             }
 
             /**
+             * Sets isProvisioned value of {@link SatelliteController} atom
+             * then returns Builder class
+             */
+            public Builder setIsProvisioned(boolean isProvisioned) {
+                this.mIsProvisioned = Optional.of(isProvisioned);
+                return this;
+            }
+
+            /**
+             * Sets Carrier ID value of {@link SatelliteController} atom
+             * then returns Builder class
+             */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = Optional.of(carrierId);
+                return this;
+            }
+
+            /**
+             * Sets countOfSatelliteAllowedStateChangedEvents value of {@link SatelliteController}
+             * atom
+             * then returns Builder class
+             */
+            public Builder setCountOfSatelliteAllowedStateChangedEvents(
+                    int countOfSatelliteAllowedStateChangedEvents) {
+                this.mCountOfSatelliteAllowedStateChangedEvents =
+                        countOfSatelliteAllowedStateChangedEvents;
+                return this;
+            }
+
+            /**
+             * Sets countOfSuccessfulLocationQueries value of {@link SatelliteController} atom
+             * then returns Builder class
+             */
+            public Builder setCountOfSuccessfulLocationQueries(
+                    int countOfSuccessfulLocationQueries) {
+                this.mCountOfSuccessfulLocationQueries = countOfSuccessfulLocationQueries;
+                return this;
+            }
+
+            /**
+             * Sets countOfFailedLocationQueries value of {@link SatelliteController} atom
+             * then returns Builder class
+             */
+            public Builder setCountOfFailedLocationQueries(int countOfFailedLocationQueries) {
+                this.mCountOfFailedLocationQueries = countOfFailedLocationQueries;
+                return this;
+            }
+
+            /**
              * Returns ControllerParams, which contains whole component of
              * {@link SatelliteController} atom
              */
@@ -609,6 +709,12 @@
                     + ", countOfAllowedSatelliteAccess=" + mCountOfAllowedSatelliteAccess
                     + ", countOfDisallowedSatelliteAccess=" + mCountOfDisallowedSatelliteAccess
                     + ", countOfSatelliteAccessCheckFail=" + mCountOfSatelliteAccessCheckFail
+                    + ", isProvisioned=" + sIsProvisioned
+                    + ", carrierId=" + sCarrierId
+                    + ", countOfSatelliteAllowedStateChangedEvents="
+                    + mCountOfSatelliteAllowedStateChangedEvents
+                    + ", countOfSuccessfulLocationQueries=" + mCountOfSuccessfulLocationQueries
+                    + ", countOfFailedLocationQueries=" + mCountOfFailedLocationQueries
                     + ")";
         }
     }
@@ -630,6 +736,11 @@
         private final int mCountOfIncomingDatagramFailed;
         private final boolean mIsDemoMode;
         private final @NtnSignalStrength.NtnSignalStrengthLevel int mMaxNtnSignalStrengthLevel;
+        private final int mCarrierId;
+        private final int mCountOfSatelliteNotificationDisplayed;
+        private final int mCountOfAutoExitDueToScreenOff;
+        private final int mCountOfAutoExitDueToTnNetwork;
+
 
         private SatelliteSessionParams(Builder builder) {
             this.mSatelliteServiceInitializationResult =
@@ -646,6 +757,11 @@
             this.mCountOfIncomingDatagramFailed = builder.mCountOfIncomingDatagramFailed;
             this.mIsDemoMode = builder.mIsDemoMode;
             this.mMaxNtnSignalStrengthLevel = builder.mMaxNtnSignalStrengthLevel;
+            this.mCarrierId = builder.mCarrierId;
+            this.mCountOfSatelliteNotificationDisplayed =
+                    builder.mCountOfSatelliteNotificationDisplayed;
+            this.mCountOfAutoExitDueToScreenOff = builder.mCountOfAutoExitDueToScreenOff;
+            this.mCountOfAutoExitDueToTnNetwork = builder.mCountOfAutoExitDueToTnNetwork;
         }
 
         public int getSatelliteServiceInitializationResult() {
@@ -696,6 +812,22 @@
             return mMaxNtnSignalStrengthLevel;
         }
 
+        public int getCarrierId() {
+            return mCarrierId;
+        }
+
+        public int getCountOfSatelliteNotificationDisplayed() {
+            return mCountOfSatelliteNotificationDisplayed;
+        }
+
+        public int getCountOfAutoExitDueToScreenOff() {
+            return mCountOfAutoExitDueToScreenOff;
+        }
+
+        public int getCountOfAutoExitDueToTnNetwork() {
+            return mCountOfAutoExitDueToTnNetwork;
+        }
+
         /**
          * A builder class to create {@link SatelliteSessionParams} data structure class
          */
@@ -713,6 +845,10 @@
             private boolean mIsDemoMode = false;
             private @NtnSignalStrength.NtnSignalStrengthLevel int mMaxNtnSignalStrengthLevel =
                     NTN_SIGNAL_STRENGTH_NONE;
+            private int mCarrierId = UNKNOWN_CARRIER_ID;
+            private int mCountOfSatelliteNotificationDisplayed = -1;
+            private int mCountOfAutoExitDueToScreenOff = -1;
+            private int mCountOfAutoExitDueToTnNetwork = -1;
 
             /**
              * Sets satelliteServiceInitializationResult value of {@link SatelliteSession}
@@ -788,13 +924,49 @@
                 return this;
             }
 
-            /** Sets the max ntn signal strength for the satellite session */
+            /** Sets the max ntn signal strength for the satellite session. */
             public Builder setMaxNtnSignalStrengthLevel(
                     @NtnSignalStrength.NtnSignalStrengthLevel int maxNtnSignalStrengthLevel) {
                 this.mMaxNtnSignalStrengthLevel = maxNtnSignalStrengthLevel;
                 return this;
             }
 
+            /** Sets the currently active NB-IoT NTN carrier ID. */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = carrierId;
+                return this;
+            }
+
+            /**
+             * Sets Total number of times the user is notified that the device is eligible for
+             * satellite service for this session.
+             */
+            public Builder setCountOfSatelliteNotificationDisplayed(
+                    int countOfSatelliteNotificationDisplayed) {
+                this.mCountOfSatelliteNotificationDisplayed = countOfSatelliteNotificationDisplayed;
+                return this;
+            }
+
+            /**
+             * Sets Total number of times exit P2P message service automatically due to screen is
+             * off and timer is expired.
+             */
+            public Builder setCountOfAutoExitDueToScreenOff(
+                    int countOfAutoExitDueToScreenOff) {
+                this.mCountOfAutoExitDueToScreenOff = countOfAutoExitDueToScreenOff;
+                return this;
+            }
+
+            /**
+             * Sets Total number of times times exit P2P message service automatically due to
+             * scan TN network.
+             */
+            public Builder setCountOfAutoExitDueToTnNetwork(
+                    int countOfAutoExitDueToTnNetwork) {
+                this.mCountOfAutoExitDueToTnNetwork = countOfAutoExitDueToTnNetwork;
+                return this;
+            }
+
             /**
              * Returns SessionParams, which contains whole component of
              * {@link SatelliteSession} atom
@@ -820,6 +992,11 @@
                     + ", CountOfIncomingDatagramFailed=" + mCountOfIncomingDatagramFailed
                     + ", IsDemoMode=" + mIsDemoMode
                     + ", MaxNtnSignalStrengthLevel=" + mMaxNtnSignalStrengthLevel
+                    + ", CarrierId=" + mCarrierId
+                    + ", CountOfSatelliteNotificationDisplayed"
+                    + mCountOfSatelliteNotificationDisplayed
+                    + ", CountOfAutoExitDueToScreenOff" + mCountOfAutoExitDueToScreenOff
+                    + ", CountOfAutoExitDueToTnNetwork" + mCountOfAutoExitDueToTnNetwork
                     + ")";
         }
     }
@@ -833,12 +1010,14 @@
         private final int mDatagramSizeBytes;
         private final long mDatagramTransferTimeMillis;
         private final boolean mIsDemoMode;
+        private final int mCarrierId;
 
         private SatelliteIncomingDatagramParams(Builder builder) {
             this.mResultCode = builder.mResultCode;
             this.mDatagramSizeBytes = builder.mDatagramSizeBytes;
             this.mDatagramTransferTimeMillis = builder.mDatagramTransferTimeMillis;
             this.mIsDemoMode = builder.mIsDemoMode;
+            this.mCarrierId = builder.mCarrierId;
         }
 
         public int getResultCode() {
@@ -857,6 +1036,10 @@
             return mIsDemoMode;
         }
 
+        public int getCarrierId() {
+            return mCarrierId;
+        }
+
         /**
          * A builder class to create {@link SatelliteIncomingDatagramParams} data structure class
          */
@@ -865,6 +1048,7 @@
             private int mDatagramSizeBytes = -1;
             private long mDatagramTransferTimeMillis = -1;
             private boolean mIsDemoMode = false;
+            private int mCarrierId = UNKNOWN_CARRIER_ID;
 
             /**
              * Sets resultCode value of {@link SatelliteIncomingDatagram} atom
@@ -902,6 +1086,12 @@
                 return this;
             }
 
+            /** Sets the currently active NB-IoT NTN carrier ID. */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = carrierId;
+                return this;
+            }
+
             /**
              * Returns IncomingDatagramParams, which contains whole component of
              * {@link SatelliteIncomingDatagram} atom
@@ -919,6 +1109,7 @@
                     + ", datagramSizeBytes=" + mDatagramSizeBytes
                     + ", datagramTransferTimeMillis=" + mDatagramTransferTimeMillis
                     + ", isDemoMode=" + mIsDemoMode
+                    + ", CarrierId=" + mCarrierId
                     + ")";
         }
     }
@@ -933,6 +1124,7 @@
         private final int mDatagramSizeBytes;
         private final long mDatagramTransferTimeMillis;
         private final boolean mIsDemoMode;
+        private final int mCarrierId;
 
         private SatelliteOutgoingDatagramParams(Builder builder) {
             this.mDatagramType = builder.mDatagramType;
@@ -940,6 +1132,7 @@
             this.mDatagramSizeBytes = builder.mDatagramSizeBytes;
             this.mDatagramTransferTimeMillis = builder.mDatagramTransferTimeMillis;
             this.mIsDemoMode = builder.mIsDemoMode;
+            this.mCarrierId = builder.mCarrierId;
         }
 
         public int getDatagramType() {
@@ -962,6 +1155,10 @@
             return mIsDemoMode;
         }
 
+        public int getCarrierId() {
+            return mCarrierId;
+        }
+
         /**
          * A builder class to create {@link SatelliteOutgoingDatagramParams} data structure class
          */
@@ -971,6 +1168,7 @@
             private int mDatagramSizeBytes = -1;
             private long mDatagramTransferTimeMillis = -1;
             private boolean mIsDemoMode = false;
+            private int mCarrierId = UNKNOWN_CARRIER_ID;
 
             /**
              * Sets datagramType value of {@link SatelliteOutgoingDatagram} atom
@@ -1017,6 +1215,12 @@
                 return this;
             }
 
+            /** Sets the currently active NB-IoT NTN carrier ID. */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = carrierId;
+                return this;
+            }
+
             /**
              * Returns OutgoingDatagramParams, which contains whole component of
              * {@link SatelliteOutgoingDatagram} atom
@@ -1035,6 +1239,7 @@
                     + ", datagramSizeBytes=" + mDatagramSizeBytes
                     + ", datagramTransferTimeMillis=" + mDatagramTransferTimeMillis
                     + ", isDemoMode=" + mIsDemoMode
+                    + ", CarrierId=" + mCarrierId
                     + ")";
         }
     }
@@ -1048,12 +1253,14 @@
         private final int mProvisioningTimeSec;
         private final boolean mIsProvisionRequest;
         private final boolean mIsCanceled;
+        private final int mCarrierId;
 
         private SatelliteProvisionParams(Builder builder) {
             this.mResultCode = builder.mResultCode;
             this.mProvisioningTimeSec = builder.mProvisioningTimeSec;
             this.mIsProvisionRequest = builder.mIsProvisionRequest;
             this.mIsCanceled = builder.mIsCanceled;
+            this.mCarrierId = builder.mCarrierId;
         }
 
         public int getResultCode() {
@@ -1072,6 +1279,10 @@
             return mIsCanceled;
         }
 
+        public int getCarrierId() {
+            return mCarrierId;
+        }
+
         /**
          * A builder class to create {@link SatelliteProvisionParams} data structure class
          */
@@ -1080,6 +1291,7 @@
             private int mProvisioningTimeSec = -1;
             private boolean mIsProvisionRequest = false;
             private boolean mIsCanceled = false;
+            private int mCarrierId = UNKNOWN_CARRIER_ID;
 
             /**
              * Sets resultCode value of {@link SatelliteProvision} atom
@@ -1117,6 +1329,12 @@
                 return this;
             }
 
+            /** Sets the currently active NB-IoT NTN carrier ID. */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = carrierId;
+                return this;
+            }
+
             /**
              * Returns ProvisionParams, which contains whole component of
              * {@link SatelliteProvision} atom
@@ -1133,7 +1351,9 @@
                     + "resultCode=" + mResultCode
                     + ", provisioningTimeSec=" + mProvisioningTimeSec
                     + ", isProvisionRequest=" + mIsProvisionRequest
-                    + ", isCanceled" + mIsCanceled + ")";
+                    + ", isCanceled" + mIsCanceled
+                    + ", CarrierId=" + mCarrierId
+                    + ")";
         }
     }
 
@@ -1149,6 +1369,8 @@
         private final boolean mIsMultiSim;
         private final int mRecommendingHandoverType;
         private final boolean mIsSatelliteAllowedInCurrentLocation;
+        private final boolean mIsWifiConnected;
+        private final int mCarrierId;
 
         private SatelliteSosMessageRecommenderParams(Builder builder) {
             this.mIsDisplaySosMessageSent = builder.mIsDisplaySosMessageSent;
@@ -1159,6 +1381,8 @@
             this.mRecommendingHandoverType = builder.mRecommendingHandoverType;
             this.mIsSatelliteAllowedInCurrentLocation =
                     builder.mIsSatelliteAllowedInCurrentLocation;
+            this.mIsWifiConnected = builder.mIsWifiConnected;
+            this.mCarrierId = builder.mCarrierId;
         }
 
         public boolean isDisplaySosMessageSent() {
@@ -1189,6 +1413,14 @@
             return mIsSatelliteAllowedInCurrentLocation;
         }
 
+        public boolean isWifiConnected() {
+            return mIsWifiConnected;
+        }
+
+        public int getCarrierId() {
+            return mCarrierId;
+        }
+
         /**
          * A builder class to create {@link SatelliteProvisionParams} data structure class
          */
@@ -1200,6 +1432,8 @@
             private boolean mIsMultiSim = false;
             private int mRecommendingHandoverType = -1;
             private boolean mIsSatelliteAllowedInCurrentLocation = false;
+            private boolean mIsWifiConnected = false;
+            private int mCarrierId = UNKNOWN_CARRIER_ID;
 
 
             /**
@@ -1268,6 +1502,24 @@
             }
 
             /**
+             * Sets whether Wi-Fi is connected value of {@link SatelliteSosMessageRecommender} atom
+             * then returns Builder class
+             */
+            public Builder setIsWifiConnected(boolean isWifiConnected) {
+                this.mIsWifiConnected = isWifiConnected;
+                return this;
+            }
+
+            /**
+             * Sets carrier ID value of {@link SatelliteSosMessageRecommender} atom then returns
+             * Builder class.
+             */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = carrierId;
+                return this;
+            }
+
+            /**
              * Returns SosMessageRecommenderParams, which contains whole component of
              * {@link SatelliteSosMessageRecommenderParams} atom
              */
@@ -1287,7 +1539,10 @@
                     + ", isMultiSim=" + mIsMultiSim
                     + ", recommendingHandoverType=" + mRecommendingHandoverType
                     + ", isSatelliteAllowedInCurrentLocation="
-                    + mIsSatelliteAllowedInCurrentLocation + ")";
+                    + mIsSatelliteAllowedInCurrentLocation
+                    + ", isWifiConnected=" + mIsWifiConnected
+                    + ", carrierId=" + mCarrierId
+                    + ")";
         }
     }
 
@@ -1614,6 +1869,8 @@
         private final int mSatelliteSessionGapMinSec;
         private final int mSatelliteSessionGapAvgSec;
         private final int mSatelliteSessionGapMaxSec;
+        private static int sCarrierId;
+        private static boolean sIsDeviceEntitled;
 
         private CarrierRoamingSatelliteControllerStatsParams(Builder builder) {
             this.mConfigDataSource = builder.mConfigDataSource;
@@ -1626,6 +1883,16 @@
             this.mSatelliteSessionGapMinSec = builder.mSatelliteSessionGapMinSec;
             this.mSatelliteSessionGapAvgSec = builder.mSatelliteSessionGapAvgSec;
             this.mSatelliteSessionGapMaxSec = builder.mSatelliteSessionGapMaxSec;
+
+            // Carrier ID value should be updated only when it is meaningful.
+            if (builder.mCarrierId.isPresent()) {
+                this.sCarrierId = builder.mCarrierId.get();
+            }
+
+            // isDeviceEntitled value should be updated only when it is meaningful.
+            if (builder.mIsDeviceEntitled.isPresent()) {
+                this.sIsDeviceEntitled = builder.mIsDeviceEntitled.get();
+            }
         }
 
         public int getConfigDataSource() {
@@ -1657,6 +1924,14 @@
             return mSatelliteSessionGapMaxSec;
         }
 
+        public int getCarrierId() {
+            return sCarrierId;
+        }
+
+        public boolean isDeviceEntitled() {
+            return sIsDeviceEntitled;
+        }
+
         /**
          * A builder class to create {@link CarrierRoamingSatelliteControllerStatsParams}
          * data structure class
@@ -1669,6 +1944,8 @@
             private int mSatelliteSessionGapMinSec = 0;
             private int mSatelliteSessionGapAvgSec = 0;
             private int mSatelliteSessionGapMaxSec = 0;
+            private Optional<Integer> mCarrierId = Optional.empty();
+            private Optional<Boolean> mIsDeviceEntitled = Optional.empty();
 
             /**
              * Sets configDataSource value of {@link CarrierRoamingSatelliteControllerStats} atom
@@ -1736,6 +2013,18 @@
                 return this;
             }
 
+            /** Sets the currently active NB-IoT NTN carrier ID. */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = Optional.of(carrierId);
+                return this;
+            }
+
+            /** Sets whether the device is currently entitled or not. */
+            public Builder setIsDeviceEntitled(boolean isDeviceEntitled) {
+                this.mIsDeviceEntitled = Optional.of(isDeviceEntitled);
+                return this;
+            }
+
             /**
              * Returns CarrierRoamingSatelliteControllerStatsParams, which contains whole component
              * of {@link CarrierRoamingSatelliteControllerStats} atom
@@ -1759,6 +2048,8 @@
                     + ", satelliteSessionGapMinSec=" + mSatelliteSessionGapMinSec
                     + ", satelliteSessionGapAvgSec=" + mSatelliteSessionGapAvgSec
                     + ", satelliteSessionGapMaxSec=" + mSatelliteSessionGapMaxSec
+                    + ", carrierId=" + sCarrierId
+                    + ", isDeviceEntitled=" + sIsDeviceEntitled
                     + ")";
         }
     }
@@ -1990,6 +2281,8 @@
         private final @SatelliteManager.SatelliteResult int mResultCode;
         private final String[] mCountryCodes;
         private final @SatelliteConstants.ConfigDataSource int mConfigDataSource;
+        private final int mCarrierId;
+        private final int mTriggeringEvent;
 
         private SatelliteAccessControllerParams(Builder builder) {
             this.mAccessControlType = builder.mAccessControlType;
@@ -2001,6 +2294,8 @@
             this.mResultCode = builder.mResultCode;
             this.mCountryCodes = builder.mCountryCodes;
             this.mConfigDataSource = builder.mConfigDataSource;
+            this.mCarrierId = builder.mCarrierId;
+            this.mTriggeringEvent = builder.mTriggeringEvent;
         }
 
         public @SatelliteConstants.AccessControlType int getAccessControlType() {
@@ -2039,6 +2334,14 @@
             return mConfigDataSource;
         }
 
+        public int getCarrierId() {
+            return mCarrierId;
+        }
+
+        @SatelliteConstants.TriggeringEvent public int getTriggeringEvent() {
+            return mTriggeringEvent;
+        }
+
         /**
          * A builder class to create {@link SatelliteAccessControllerParams} data structure class
          */
@@ -2052,6 +2355,9 @@
             private @SatelliteManager.SatelliteResult int mResultCode;
             private String[] mCountryCodes;
             private @SatelliteConstants.ConfigDataSource int mConfigDataSource;
+            private int mCarrierId = UNKNOWN_CARRIER_ID;
+            private @SatelliteConstants.TriggeringEvent int mTriggeringEvent =
+                    TRIGGERING_EVENT_UNKNOWN;
 
             /**
              * Sets AccessControlType value of {@link #SatelliteAccessController}
@@ -2113,6 +2419,19 @@
                 return this;
             }
 
+            /** Sets the currently active NB-IoT NTN carrier ID. */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = carrierId;
+                return this;
+            }
+
+            /** Sets the triggering evenr for current satellite access controller metric. */
+            public Builder setTriggeringEvent(
+                    @SatelliteConstants.TriggeringEvent int triggeringEvent) {
+                this.mTriggeringEvent = triggeringEvent;
+                return this;
+            }
+
             /**
              * Returns AccessControllerParams, which contains whole component of
              * {@link #SatelliteAccessController} atom
@@ -2135,6 +2454,8 @@
                     + ", ResultCode=" + mResultCode
                     + ", CountryCodes=" + Arrays.toString(mCountryCodes)
                     + ", ConfigDataSource=" + mConfigDataSource
+                    + ", CarrierId=" + mCarrierId
+                    + ", TriggeringEvent=" + mTriggeringEvent
                     + ")";
         }
     }
@@ -2175,6 +2496,16 @@
         proto.countOfDemoModeIncomingDatagramFail = param.getCountOfDemoModeIncomingDatagramFail();
         proto.countOfDatagramTypeKeepAliveSuccess = param.getCountOfDatagramTypeKeepAliveSuccess();
         proto.countOfDatagramTypeKeepAliveFail = param.getCountOfDatagramTypeKeepAliveFail();
+        proto.countOfAllowedSatelliteAccess = param.getCountOfAllowedSatelliteAccess();
+        proto.countOfDisallowedSatelliteAccess = param.getCountOfDisallowedSatelliteAccess();
+        proto.countOfSatelliteAccessCheckFail = param.getCountOfSatelliteAccessCheckFail();
+        proto.isProvisioned = param.isProvisioned();
+        proto.carrierId = param.getCarrierId();
+        proto.countOfSatelliteAllowedStateChangedEvents =
+                param.getCountOfSatelliteAllowedStateChangedEvents();
+        proto.countOfSuccessfulLocationQueries = param.getCountOfSuccessfulLocationQueries();
+        proto.countOfFailedLocationQueries = param.getCountOfFailedLocationQueries();
+
         mAtomsStorage.addSatelliteControllerStats(proto);
     }
 
@@ -2195,6 +2526,11 @@
         proto.countOfIncomingDatagramFailed = param.getCountOfOutgoingDatagramFailed();
         proto.isDemoMode = param.getIsDemoMode();
         proto.maxNtnSignalStrengthLevel = param.getMaxNtnSignalStrengthLevel();
+        proto.carrierId = param.getCarrierId();
+        proto.countOfSatelliteNotificationDisplayed =
+                param.getCountOfSatelliteNotificationDisplayed();
+        proto.countOfAutoExitDueToScreenOff = param.getCountOfAutoExitDueToScreenOff();
+        proto.countOfAutoExitDueToTnNetwork = param.getCountOfAutoExitDueToTnNetwork();
         mAtomsStorage.addSatelliteSessionStats(proto);
     }
 
@@ -2206,6 +2542,7 @@
         proto.datagramSizeBytes = param.getDatagramSizeBytes();
         proto.datagramTransferTimeMillis = param.getDatagramTransferTimeMillis();
         proto.isDemoMode = param.getIsDemoMode();
+        proto.carrierId = param.getCarrierId();
         mAtomsStorage.addSatelliteIncomingDatagramStats(proto);
     }
 
@@ -2218,6 +2555,7 @@
         proto.datagramSizeBytes = param.getDatagramSizeBytes();
         proto.datagramTransferTimeMillis = param.getDatagramTransferTimeMillis();
         proto.isDemoMode = param.getIsDemoMode();
+        proto.carrierId = param.getCarrierId();
         mAtomsStorage.addSatelliteOutgoingDatagramStats(proto);
     }
 
@@ -2228,6 +2566,7 @@
         proto.provisioningTimeSec = param.getProvisioningTimeSec();
         proto.isProvisionRequest = param.getIsProvisionRequest();
         proto.isCanceled = param.getIsCanceled();
+        proto.carrierId = param.getCarrierId();
         mAtomsStorage.addSatelliteProvisionStats(proto);
     }
 
@@ -2242,6 +2581,8 @@
         proto.isMultiSim = param.isMultiSim();
         proto.recommendingHandoverType = param.getRecommendingHandoverType();
         proto.isSatelliteAllowedInCurrentLocation = param.isSatelliteAllowedInCurrentLocation();
+        proto.isWifiConnected = param.isWifiConnected();
+        proto.carrierId = param.getCarrierId();
         proto.count = 1;
         mAtomsStorage.addSatelliteSosMessageRecommenderStats(proto);
     }
@@ -2280,6 +2621,8 @@
         proto.satelliteSessionGapMinSec = param.mSatelliteSessionGapMinSec;
         proto.satelliteSessionGapAvgSec = param.mSatelliteSessionGapAvgSec;
         proto.satelliteSessionGapMaxSec = param.mSatelliteSessionGapMaxSec;
+        proto.carrierId = param.getCarrierId();
+        proto.isDeviceEntitled = param.isDeviceEntitled();
         mAtomsStorage.addCarrierRoamingSatelliteControllerStats(proto);
     }
 
@@ -2317,6 +2660,8 @@
         proto.resultCode = param.getResultCode();
         proto.countryCodes = param.getCountryCodes();
         proto.configDataSource = param.getConfigDataSource();
+        proto.carrierId = param.getCarrierId();
+        proto.triggeringEvent = param.getTriggeringEvent();
         mAtomsStorage.addSatelliteAccessControllerStats(proto);
     }
 }
diff --git a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
index 911424e..b6a26c6 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
@@ -26,6 +26,16 @@
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_THIRTY_MINUTES;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_MORE_THAN_ONE_HOUR;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_DIALING;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_DISCONNECTED;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_DISCONNECTING;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_HOLDING;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_IDLE;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_INCOMING;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_WAITING;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_UNKNOWN;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_FULLBAND;
@@ -540,6 +550,7 @@
 
         // Compute time it took to fail setup (except for MT calls that have never been picked up)
         if (proto.setupFailed && proto.setupBeginMillis != 0L && proto.setupDurationMillis == 0) {
+            proto.preciseCallStateOnSetup = convertCallStateEnumToInt(Call.State.DISCONNECTED);
             proto.setupDurationMillis = (int) (getTimeMillis() - proto.setupBeginMillis);
         }
 
@@ -632,6 +643,7 @@
 
     private void checkCallSetup(Connection conn, VoiceCallSession proto) {
         if (proto.setupBeginMillis != 0L && isSetupFinished(conn.getCall())) {
+            proto.preciseCallStateOnSetup = convertCallStateEnumToInt(conn.getState());
             proto.setupDurationMillis = (int) (getTimeMillis() - proto.setupBeginMillis);
             proto.setupBeginMillis = 0L;
         }
@@ -1092,4 +1104,29 @@
                 proto.handoverInProgress = false;
         }
     }
+
+    private int convertCallStateEnumToInt(Call.State state) {
+        switch (state) {
+            case IDLE:
+                return VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_IDLE;
+            case ACTIVE:
+                return VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
+            case HOLDING:
+                return VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_HOLDING;
+            case DIALING:
+                return VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_DIALING;
+            case ALERTING:
+                return VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
+            case INCOMING:
+                return VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_INCOMING;
+            case WAITING:
+                return VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_WAITING;
+            case DISCONNECTED:
+                return VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_DISCONNECTED;
+            case DISCONNECTING:
+                return VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_DISCONNECTING;
+            default:
+                return VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_UNKNOWN;
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramController.java b/src/java/com/android/internal/telephony/satellite/DatagramController.java
index ff2ee9f..a816906 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramController.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramController.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.telephony.satellite;
 
-import static android.telephony.SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
@@ -253,6 +252,7 @@
             @NonNull Consumer<Integer> callback) {
         mDatagramDispatcher.sendSatelliteDatagram(subId, datagramType, datagram,
                 needFullScreenPointingUI, callback);
+        mPointingAppController.onSendDatagramRequested(subId, datagramType);
     }
 
     /**
@@ -344,6 +344,12 @@
         return mReceivePendingCount;
     }
 
+
+    /** @return {@code true} if already sent an emergency datagram during a session. */
+    public boolean isEmergencyCommunicationEstablished() {
+        return mDatagramDispatcher.isEmergencyCommunicationEstablished();
+    }
+
     /**
      * This function is used by {@link SatelliteController} to notify {@link DatagramController}
      * that satellite modem state has changed.
@@ -609,7 +615,9 @@
                         }
                     }
                 };
-                pollPendingSatelliteDatagrams(DEFAULT_SUBSCRIPTION_ID, internalCallback);
+                pollPendingSatelliteDatagrams(
+                        SatelliteController.getInstance().getHighestPrioritySubscrption(),
+                        internalCallback);
             }
         }
     }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
index 2c9463f..682123f 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -16,13 +16,16 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SMS;
 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NETWORK_ERROR;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import static com.android.internal.telephony.satellite.DatagramController.ROUNDING_UNIT;
+import static com.android.internal.telephony.SmsDispatchersController.PendingRequest;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -35,14 +38,15 @@
 import android.telephony.DropBoxManagerLoggerBackend;
 import android.telephony.PersistentLogger;
 import android.telephony.Rlog;
-import android.telephony.SubscriptionManager;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.SmsDispatchersController;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.SatelliteStats;
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
@@ -69,6 +73,8 @@
     private static final int EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT = 5;
     private static final int EVENT_ABORT_SENDING_SATELLITE_DATAGRAMS_DONE = 6;
     private static final int EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT = 7;
+    private static final int CMD_SEND_SMS = 8;
+    private static final int EVENT_SEND_SMS_DONE = 9;
     private static final Long TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE = TimeUnit.SECONDS.toMillis(10);
     @NonNull private static DatagramDispatcher sInstance;
     @NonNull private final Context mContext;
@@ -88,8 +94,12 @@
     private final Object mLock = new Object();
     private long mDemoTimeoutDuration = TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE;
 
+    /** {@code true} if already sent an emergency datagram during a session */
     @GuardedBy("mLock")
-    private boolean mSendingDatagramInProgress;
+    private boolean mIsEmergencyCommunicationEstablished = false;
+
+    @GuardedBy("mLock")
+    private boolean mSendingInProgress;
 
     /**
      * Map key: datagramId, value: SendSatelliteDatagramArgument to retry sending emergency
@@ -107,6 +117,13 @@
     private final LinkedHashMap<Long, SendSatelliteDatagramArgument>
             mPendingNonEmergencyDatagramsMap = new LinkedHashMap<>();
 
+    /**
+     * Map key: messageId, value: {@link PendingRequest} which contains all the information to send
+     * carrier roaming nb iot ntn SMS.
+     */
+    @GuardedBy("mLock")
+    private final LinkedHashMap<Long, PendingRequest> mPendingSmsMap = new LinkedHashMap<>();
+
     private long mWaitTimeForDatagramSendingResponse;
     private long mWaitTimeForDatagramSendingForLastMessageResponse;
     @SatelliteManager.DatagramType
@@ -164,7 +181,7 @@
         }
 
         synchronized (mLock) {
-            mSendingDatagramInProgress = false;
+            mSendingInProgress = false;
         }
         mWaitTimeForDatagramSendingResponse = getWaitForDatagramSendingResponseTimeoutMillis();
         mWaitTimeForDatagramSendingForLastMessageResponse =
@@ -289,13 +306,16 @@
                     }
 
                     stopWaitForDatagramSendingResponseTimer();
-                    mSendingDatagramInProgress = false;
+                    mSendingInProgress = false;
 
                     // Log metrics about the outgoing datagram
                     reportSendDatagramCompleted(argument, error);
                     // Remove current datagram from pending map.
                     if (SatelliteServiceUtils.isSosMessage(argument.datagramType)) {
                         mPendingEmergencyDatagramsMap.remove(argument.datagramId);
+                        if (error == SATELLITE_RESULT_SUCCESS) {
+                            mIsEmergencyCommunicationEstablished = true;
+                        }
                     } else {
                         mPendingNonEmergencyDatagramsMap.remove(argument.datagramId);
                     }
@@ -304,20 +324,20 @@
                         // Update send status for current datagram
                         mDatagramController.updateSendStatus(argument.subId, argument.datagramType,
                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
-                                getPendingDatagramCount(), error);
+                                getPendingMessagesCount(), error);
                         startWaitForSimulatedPollDatagramsDelayTimer(request);
                     } else {
                         // Update send status
                         mDatagramController.updateSendStatus(argument.subId, argument.datagramType,
                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
-                                getPendingDatagramCount(), error);
+                                getPendingMessagesCount(), error);
                     }
 
-                    if (getPendingDatagramCount() > 0) {
+                    if (getPendingMessagesCount() > 0) {
                         // Send response for current datagram
                         argument.callback.accept(error);
                         // Send pending datagrams
-                        sendPendingDatagrams();
+                        sendPendingMessages();
                     } else {
                         mDatagramController.updateSendStatus(argument.subId,
                                 argument.datagramType,
@@ -341,8 +361,7 @@
             }
 
             case EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT:
-                handleEventDatagramWaitForConnectedStateTimedOut(
-                        (SendSatelliteDatagramArgument) msg.obj);
+                handleEventDatagramWaitForConnectedStateTimedOut((int) msg.obj);
                 break;
 
             case EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT:
@@ -351,6 +370,38 @@
                         (SendSatelliteDatagramArgument) request.argument);
                 break;
 
+            case CMD_SEND_SMS: {
+                PendingRequest pendingRequest = (PendingRequest) msg.obj;
+                Phone satellitePhone = SatelliteController.getInstance().getSatellitePhone();
+                if (satellitePhone == null) {
+                    ploge("CMD_SEND_SMS: satellitePhone is null.");
+                    return;
+                }
+
+                SmsDispatchersController smsDispatchersController =
+                        satellitePhone.getSmsDispatchersController();
+                if (smsDispatchersController == null) {
+                    ploge("CMD_SEND_SMS: smsDispatchersController is null.");
+                    return;
+                }
+
+                smsDispatchersController.sendCarrierRoamingNbIotNtnText(pendingRequest);
+                break;
+            }
+
+            case EVENT_SEND_SMS_DONE: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                int subId = (int) args.arg1;
+                long messageId = (long) args.arg2;
+                boolean success = (boolean) args.arg3;
+                try {
+                    handleEventSendSmsDone(subId, messageId, success);
+                } finally {
+                    args.recycle();
+                }
+                break;
+            }
+
             default:
                 plogw("DatagramDispatcherHandler: unexpected message code: " + msg.what);
                 break;
@@ -397,28 +448,27 @@
                 plogd("sendDatagram: wait for satellite connected");
                 mDatagramController.updateSendStatus(subId, datagramType,
                         SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
-                        getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
-                startDatagramWaitForConnectedStateTimer(datagramArgs);
-            } else if (!mSendingDatagramInProgress && mDatagramController.isPollingInIdleState()) {
+                        getPendingMessagesCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
+                startDatagramWaitForConnectedStateTimer(datagramArgs.datagramType);
+            } else if (!mSendingInProgress && mDatagramController.isPollingInIdleState()) {
                 // Modem can be busy receiving datagrams, so send datagram only when modem is
                 // not busy.
-                mSendingDatagramInProgress = true;
+                mSendingInProgress = true;
                 datagramArgs.setDatagramStartTime();
                 mDatagramController.updateSendStatus(subId, datagramType,
                         SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
-                        getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
+                        getPendingMessagesCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
                 sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArgs, phone);
             } else {
-                plogd("sendDatagram: mSendingDatagramInProgress="
-                        + mSendingDatagramInProgress + ", isPollingInIdleState="
-                        + mDatagramController.isPollingInIdleState());
+                plogd("sendDatagram: mSendingInProgress=" + mSendingInProgress
+                        + ", isPollingInIdleState=" + mDatagramController.isPollingInIdleState());
             }
         }
     }
 
     public void retrySendingDatagrams() {
         synchronized (mLock) {
-            sendPendingDatagrams();
+            sendPendingMessages();
         }
     }
 
@@ -500,6 +550,23 @@
         removeMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT);
     }
 
+    @GuardedBy("mLock")
+    private void sendPendingMessages() {
+        plogd("sendPendingMessages");
+
+        // Pending datagrams are prioritized over pending SMS.
+        if (getPendingDatagramCount() > 0) {
+            sendPendingDatagrams();
+            return;
+        }
+
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+            if (getPendingSmsCount() > 0) {
+                sendPendingSms();
+            }
+        }
+    }
+
     /**
      * Send pending satellite datagrams. Emergency datagrams are given priority over
      * non-emergency datagrams.
@@ -520,9 +587,9 @@
 
         Phone phone = SatelliteServiceUtils.getPhone();
         Set<Entry<Long, SendSatelliteDatagramArgument>> pendingDatagram = null;
-        if (!mSendingDatagramInProgress && !mPendingEmergencyDatagramsMap.isEmpty()) {
+        if (!mSendingInProgress && !mPendingEmergencyDatagramsMap.isEmpty()) {
             pendingDatagram = mPendingEmergencyDatagramsMap.entrySet();
-        } else if (!mSendingDatagramInProgress && !mPendingNonEmergencyDatagramsMap.isEmpty()) {
+        } else if (!mSendingInProgress && !mPendingNonEmergencyDatagramsMap.isEmpty()) {
             pendingDatagram = mPendingNonEmergencyDatagramsMap.entrySet();
         }
 
@@ -534,12 +601,12 @@
                 return;
             }
 
-            mSendingDatagramInProgress = true;
+            mSendingInProgress = true;
             // Sets the trigger time for getting pending datagrams
             datagramArg.setDatagramStartTime();
             mDatagramController.updateSendStatus(datagramArg.subId, datagramArg.datagramType,
                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
-                    getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
+                    getPendingMessagesCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
             sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArg, phone);
         }
     }
@@ -583,6 +650,21 @@
         plogd("abortSendingPendingDatagrams()");
         sendErrorCodeAndCleanupPendingDatagrams(mPendingEmergencyDatagramsMap, errorCode);
         sendErrorCodeAndCleanupPendingDatagrams(mPendingNonEmergencyDatagramsMap, errorCode);
+        sendErrorCodeAndCleanupPendingSms(mPendingSmsMap, errorCode);
+    }
+
+    /**
+     * Return pending datagram and SMS count
+     * @return pending messages count
+     */
+    public int getPendingMessagesCount() {
+        synchronized (mLock) {
+            if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+                return getPendingDatagramCount() + getPendingSmsCount();
+            } else {
+                return getPendingDatagramCount();
+            }
+        }
     }
 
     /**
@@ -595,6 +677,16 @@
         }
     }
 
+    /**
+     * Return pending SMS count
+     * @return pending SMS count
+     */
+    public int getPendingSmsCount() {
+        synchronized (mLock) {
+            return mPendingSmsMap.size();
+        }
+    }
+
     /** Return pending user messages count */
     public int getPendingUserMessagesCount() {
         synchronized (mLock) {
@@ -637,6 +729,7 @@
                         .setDatagramTransferTimeMillis(argument.datagramStartTime > 0
                                 ? (System.currentTimeMillis() - argument.datagramStartTime) : 0)
                         .setIsDemoMode(mIsDemoMode)
+                        .setCarrierId(SatelliteController.getInstance().getSatelliteCarrierId())
                         .build());
         if (resultCode == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
             mControllerMetricsStats.reportOutgoingDatagramSuccessCount(argument.datagramType,
@@ -671,13 +764,13 @@
                 plogd("onSatelliteModemStateChanged: cleaning up resources");
                 cleanUpResources();
             } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_IDLE) {
-                sendPendingDatagrams();
+                sendPendingMessages();
             }
 
             if (state == SATELLITE_MODEM_STATE_CONNECTED
                     && isDatagramWaitForConnectedStateTimerStarted()) {
                 stopDatagramWaitForConnectedStateTimer();
-                sendPendingDatagrams();
+                sendPendingMessages();
             }
         }
     }
@@ -685,18 +778,21 @@
     @GuardedBy("mLock")
     private void cleanUpResources() {
         plogd("cleanUpResources");
-        mSendingDatagramInProgress = false;
-        if (getPendingDatagramCount() > 0) {
-            mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+        mSendingInProgress = false;
+        mIsEmergencyCommunicationEstablished = false;
+
+        int subId = SatelliteController.getInstance().getHighestPrioritySubscrption();
+        if (getPendingMessagesCount() > 0) {
+            mDatagramController.updateSendStatus(subId,
                     mLastSendRequestDatagramType,
                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
-                    getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
+                    getPendingMessagesCount(), SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
         }
-        mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+        mDatagramController.updateSendStatus(subId,
                 mLastSendRequestDatagramType,
                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
                 0, SatelliteManager.SATELLITE_RESULT_SUCCESS);
-        abortSendingPendingDatagrams(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+        abortSendingPendingDatagrams(subId,
                 SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
 
         stopSatelliteAlignedTimer();
@@ -709,16 +805,23 @@
         mLastSendRequestDatagramType = DATAGRAM_TYPE_UNKNOWN;
     }
 
+    /** @return {@code true} if already sent an emergency datagram during a session. */
+    public boolean isEmergencyCommunicationEstablished() {
+        synchronized (mLock) {
+            return mIsEmergencyCommunicationEstablished;
+        }
+    }
+
     private void startDatagramWaitForConnectedStateTimer(
-            @NonNull SendSatelliteDatagramArgument datagramArgs) {
+            @SatelliteManager.DatagramType int datagramType) {
         if (isDatagramWaitForConnectedStateTimerStarted()) {
             plogd("DatagramWaitForConnectedStateTimer is already started");
             return;
         }
         sendMessageDelayed(obtainMessage(
-                        EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT, datagramArgs),
+                        EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT, datagramType),
                 mDatagramController.getDatagramWaitTimeForConnectedState(
-                        SatelliteServiceUtils.isLastSosMessage(datagramArgs.datagramType)));
+                        SatelliteServiceUtils.isLastSosMessage(datagramType)));
     }
 
     private void stopDatagramWaitForConnectedStateTimer() {
@@ -761,20 +864,21 @@
     }
 
     private void handleEventDatagramWaitForConnectedStateTimedOut(
-            @NonNull SendSatelliteDatagramArgument argument) {
+            @SatelliteManager.DatagramType int datagramType) {
         plogw("Timed out to wait for satellite connected before sending datagrams");
         synchronized (mLock) {
+            int subId = SatelliteController.getInstance().getHighestPrioritySubscrption();
             // Update send status
-            mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                    argument.datagramType,
+            mDatagramController.updateSendStatus(subId,
+                    datagramType,
                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
-                    getPendingDatagramCount(),
+                    getPendingMessagesCount(),
                     SATELLITE_RESULT_NOT_REACHABLE);
-            mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                    argument.datagramType,
+            mDatagramController.updateSendStatus(subId,
+                    datagramType,
                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
                     0, SatelliteManager.SATELLITE_RESULT_SUCCESS);
-            abortSendingPendingDatagrams(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+            abortSendingPendingDatagrams(subId,
                     SATELLITE_RESULT_NOT_REACHABLE);
         }
     }
@@ -827,12 +931,12 @@
             // Ask vendor service to abort all datagram-sending requests
             SatelliteModemInterface.getInstance().abortSendingSatelliteDatagrams(
                     obtainMessage(EVENT_ABORT_SENDING_SATELLITE_DATAGRAMS_DONE, argument));
-            mSendingDatagramInProgress = false;
+            mSendingInProgress = false;
 
             // Update send status
             mDatagramController.updateSendStatus(argument.subId, argument.datagramType,
                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
-                    getPendingDatagramCount(), SATELLITE_RESULT_MODEM_TIMEOUT);
+                    getPendingMessagesCount(), SATELLITE_RESULT_MODEM_TIMEOUT);
             mDatagramController.updateSendStatus(argument.subId, argument.datagramType,
                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
                     0, SatelliteManager.SATELLITE_RESULT_SUCCESS);
@@ -934,6 +1038,176 @@
         plogd("setTimeoutDatagramDelayInDemoMode " + mDemoTimeoutDuration + " reset=" + reset);
     }
 
+    /**
+     * Send carrier roaming nb iot ntn sms.
+     *
+     * Store SMS in a pending list until following conditions are met:
+     * - If messages can be sent only when satellite is connected, then wait until modem state
+     * becomes {@link SatelliteManager#SATELLITE_MODEM_STATE_CONNECTED}
+     * - If modem is already sending datagrms/SMS or receiving datagrams, then wait until modem
+     * becomes IDLE to send current SMS.
+     *
+     * @param pendingSms {@link PendingRequest} that contains all the information required to send
+     *                    carrier roaming nb iot ntn SMS.
+     */
+    public void sendSms(@NonNull PendingRequest pendingSms) {
+        Phone satellitePhone = SatelliteController.getInstance().getSatellitePhone();
+        if (satellitePhone == null) {
+            ploge("sendSms: satellitePhone is null.");
+            return;
+        }
+
+        SatelliteController.getInstance().startPointingUI();
+
+        int subId = satellitePhone.getSubId();
+        long messageId = pendingSms.messageId;
+        plogd("sendSms: subId=" + subId + " messageId:" + messageId);
+
+        synchronized (mLock) {
+            // Add SMS to pending list
+            mPendingSmsMap.put(messageId, pendingSms);
+            int datagramType = SatelliteManager.DATAGRAM_TYPE_SMS;
+            mLastSendRequestDatagramType = datagramType;
+
+            if (mDatagramController.needsWaitingForSatelliteConnected(datagramType)) {
+                plogd("sendSms: wait for satellite connected");
+                mDatagramController.updateSendStatus(subId, datagramType,
+                        SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
+                        getPendingMessagesCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
+                startDatagramWaitForConnectedStateTimer(datagramType);
+            } else if (!mSendingInProgress && mDatagramController.isPollingInIdleState()) {
+                mSendingInProgress = true;
+                mDatagramController.updateSendStatus(subId, datagramType,
+                        SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+                        getPendingMessagesCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
+
+                sendMessage(obtainMessage(CMD_SEND_SMS, pendingSms));
+            } else {
+                plogd("sendSms: mSendingInProgress=" + mSendingInProgress
+                        + ", isPollingInIdleState=" + mDatagramController.isPollingInIdleState());
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void sendPendingSms() {
+        plogd("sendPendingSms");
+        if (!mDatagramController.isPollingInIdleState()) {
+            // Datagram or SMS should be sent to satellite modem when modem is free.
+            plogd("sendPendingSms: modem is receiving datagrams");
+            return;
+        }
+
+        Phone satellitePhone = SatelliteController.getInstance().getSatellitePhone();
+        if (satellitePhone == null) {
+            ploge("sendPendingSms: satellitePhone is null.");
+            return;
+        }
+        int subId = satellitePhone.getSubId();
+
+        Set<Entry<Long, PendingRequest>> pendingSms = null;
+        if (!mSendingInProgress) {
+            pendingSms = mPendingSmsMap.entrySet();
+        }
+
+        if (pendingSms != null && pendingSms.iterator().hasNext()) {
+            if (mDatagramController.needsWaitingForSatelliteConnected(DATAGRAM_TYPE_SMS)) {
+                plogd("sendPendingSms: wait for satellite connected");
+                return;
+            }
+
+            mSendingInProgress = true;
+            PendingRequest pendingRequest = pendingSms.iterator().next().getValue();
+            mDatagramController.updateSendStatus(subId, DATAGRAM_TYPE_SMS,
+                    SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+                    getPendingMessagesCount(), SATELLITE_RESULT_SUCCESS);
+            sendMessage(obtainMessage(CMD_SEND_SMS, pendingRequest));
+        } else {
+            plogd("sendPendingSms: mSendingInProgress=" + mSendingInProgress
+                    + " pendingSmsCount=" + getPendingSmsCount());
+        }
+    }
+
+    /**
+     * Sending MO SMS is completed.
+     * @param subId subscription ID
+     * @param messageId message ID of MO SMS
+     * @param success boolean specifying whether MO SMS is successfully sent or not.
+     */
+    public void onSendSmsDone(int subId, long messageId, boolean success) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = subId;
+        args.arg2 = messageId;
+        args.arg3 = success;
+        sendMessage(obtainMessage(EVENT_SEND_SMS_DONE, args));
+    }
+
+    @GuardedBy("mLock")
+    private void sendErrorCodeAndCleanupPendingSms(
+            LinkedHashMap<Long, PendingRequest> pendingSmsMap,
+            @SatelliteManager.SatelliteResult int errorCode) {
+        if (pendingSmsMap.size() == 0) {
+            plogd("sendErrorCodeAndCleanupPendingSms: pendingSmsMap is empty.");
+            return;
+        }
+        ploge("sendErrorCodeAndCleanupPendingSms: cleaning up resources. "
+                + "pendingSmsMap size=" + getPendingSmsCount());
+
+        Phone satellitePhone = SatelliteController.getInstance().getSatellitePhone();
+        if (satellitePhone == null) {
+            ploge("sendErrorCodeAndCleanupPendingSms: satellitePhone is null.");
+            pendingSmsMap.clear();
+            return;
+        }
+
+        SmsDispatchersController smsDispatchersController =
+                satellitePhone.getSmsDispatchersController();
+        if (smsDispatchersController == null) {
+            ploge("sendErrorCodeAndCleanupPendingSms: smsDispatchersController is null.");
+            pendingSmsMap.clear();
+            return;
+        }
+
+        // Send error code to all the pending text
+        for (Entry<Long, PendingRequest> entry : pendingSmsMap.entrySet()) {
+            PendingRequest pendingRequest = entry.getValue();
+            smsDispatchersController.onSendCarrierRoamingNbIotNtnTextError(
+                    pendingRequest, errorCode);
+        }
+
+        // Clear pending text map
+        pendingSmsMap.clear();
+    }
+
+    private void handleEventSendSmsDone(int subId, long messageId, boolean success) {
+        synchronized (mLock) {
+            mSendingInProgress = false;
+            mPendingSmsMap.remove(messageId);
+            int datagramType = DATAGRAM_TYPE_SMS;
+
+            plogd("handleEventSendSmsDone subId=" + subId + " messageId=" + messageId
+                    + " success=" + success);
+            if (success) {
+                // Update send status for current datagram
+                mDatagramController.updateSendStatus(subId, datagramType,
+                        SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
+                        getPendingMessagesCount(), SATELLITE_RESULT_SUCCESS);
+            } else {
+                mDatagramController.updateSendStatus(subId, datagramType,
+                        SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
+                        getPendingMessagesCount(), SATELLITE_RESULT_NETWORK_ERROR);
+            }
+
+            if (getPendingMessagesCount() > 0) {
+                sendPendingMessages();
+            } else {
+                mDatagramController.updateSendStatus(subId, datagramType,
+                        SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 0,
+                        SatelliteManager.SATELLITE_RESULT_SUCCESS);
+            }
+        }
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
index ea75f03..5a89c40 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
@@ -455,8 +455,7 @@
 
                 if (mIsDemoMode && error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                     SatelliteDatagram datagram = mDatagramController.popDemoModeDatagram();
-                    final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(
-                            request.subId, mContext);
+                    final int validSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
                     SatelliteDatagramListenerHandler listenerHandler =
                             mSatelliteDatagramListenerHandlers.get(validSubId);
                     if (listenerHandler != null) {
@@ -518,7 +517,7 @@
             return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
 
-        final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+        final int validSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         SatelliteDatagramListenerHandler satelliteDatagramListenerHandler =
                 mSatelliteDatagramListenerHandlers.get(validSubId);
         if (satelliteDatagramListenerHandler == null) {
@@ -544,7 +543,7 @@
      */
     public void unregisterForSatelliteDatagram(int subId,
             @NonNull ISatelliteDatagramCallback callback) {
-        final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+        final int validSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         SatelliteDatagramListenerHandler handler =
                 mSatelliteDatagramListenerHandlers.get(validSubId);
         if (handler != null) {
@@ -576,7 +575,8 @@
             callback.accept(SatelliteManager.SATELLITE_RESULT_MODEM_BUSY);
             return;
         }
-        pollPendingSatelliteDatagramsInternal(subId, callback);
+        pollPendingSatelliteDatagramsInternal(
+                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, callback);
     }
 
     private void handleSatelliteConnectedEvent() {
@@ -695,13 +695,15 @@
             }
             stopDatagramWaitForConnectedStateTimer();
         }
+
+        int subId = SatelliteController.getInstance().getHighestPrioritySubscrption();
         if (mDatagramController.isReceivingDatagrams()) {
-            mDatagramController.updateReceiveStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+            mDatagramController.updateReceiveStatus(subId,
                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED,
                     mDatagramController.getReceivePendingCount(),
                     SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
         }
-        mDatagramController.updateReceiveStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+        mDatagramController.updateReceiveStatus(subId,
                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 0,
                 SatelliteManager.SATELLITE_RESULT_SUCCESS);
         cleanupDemoModeResources();
@@ -746,6 +748,7 @@
                         .setDatagramSizeBytes(datagramSizeRoundedBytes)
                         .setDatagramTransferTimeMillis(datagramTransferTime)
                         .setIsDemoMode(mIsDemoMode)
+                        .setCarrierId(SatelliteController.getInstance().getSatelliteCarrierId())
                         .build());
 
         mControllerMetricsStats.reportIncomingDatagramCount(resultCode, mIsDemoMode);
diff --git a/src/java/com/android/internal/telephony/satellite/DemoSimulator.java b/src/java/com/android/internal/telephony/satellite/DemoSimulator.java
index 3c31ae8..7b64c61 100644
--- a/src/java/com/android/internal/telephony/satellite/DemoSimulator.java
+++ b/src/java/com/android/internal/telephony/satellite/DemoSimulator.java
@@ -131,7 +131,7 @@
                 NtnSignalStrength ntnSignalStrength = new NtnSignalStrength();
                 ntnSignalStrength.signalStrengthLevel = 0;
                 mISatelliteListener.onSatelliteModemStateChanged(
-                        SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+                        SatelliteModemState.SATELLITE_MODEM_STATE_OUT_OF_SERVICE);
                 mISatelliteListener.onNtnSignalStrengthChanged(ntnSignalStrength);
 
                 synchronized (mLock) {
@@ -191,7 +191,7 @@
                 NtnSignalStrength ntnSignalStrength = new NtnSignalStrength();
                 ntnSignalStrength.signalStrengthLevel = 2;
                 mISatelliteListener.onSatelliteModemStateChanged(
-                        SatelliteModemState.SATELLITE_MODEM_STATE_CONNECTED);
+                        SatelliteModemState.SATELLITE_MODEM_STATE_IN_SERVICE);
                 mISatelliteListener.onNtnSignalStrengthChanged(ntnSignalStrength);
 
                 synchronized (mLock) {
diff --git a/src/java/com/android/internal/telephony/satellite/PointingAppController.java b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
index 06281c7..9606150 100644
--- a/src/java/com/android/internal/telephony/satellite/PointingAppController.java
+++ b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
@@ -34,9 +34,11 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.telephony.DropBoxManagerLoggerBackend;
 import android.telephony.PersistentLogger;
 import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
 import android.telephony.satellite.PointingInfo;
 import android.telephony.satellite.SatelliteManager;
@@ -189,6 +191,7 @@
         public static final int EVENT_SEND_DATAGRAM_STATE_CHANGED = 2;
         public static final int EVENT_RECEIVE_DATAGRAM_STATE_CHANGED = 3;
         public static final int EVENT_DATAGRAM_TRANSFER_STATE_CHANGED = 4;
+        public static final int EVENT_SEND_DATAGRAM_REQUESTED = 5;
 
         private final ConcurrentHashMap<IBinder, ISatelliteTransmissionUpdateCallback> mListeners;
 
@@ -277,6 +280,24 @@
                     break;
                 }
 
+                case EVENT_SEND_DATAGRAM_REQUESTED: {
+                    logd("Received EVENT_SEND_DATAGRAM_REQUESTED");
+                    int datagramType = (int) msg.obj;
+                    List<IBinder> toBeRemoved = new ArrayList<>();
+                    mListeners.values().forEach(listener -> {
+                        try {
+                            listener.onSendDatagramRequested(datagramType);
+                        } catch (RemoteException e) {
+                            logd("EVENT_SEND_DATAGRAM_REQUESTED RemoteException: " + e);
+                            toBeRemoved.add(listener.asBinder());
+                        }
+                    });
+                    toBeRemoved.forEach(listener -> {
+                        mListeners.remove(listener);
+                    });
+                    break;
+                }
+
                 default:
                     loge("SatelliteTransmissionUpdateHandler unknown event: " + msg.what);
             }
@@ -290,6 +311,7 @@
      */
     public void registerForSatelliteTransmissionUpdates(int subId,
             ISatelliteTransmissionUpdateCallback callback) {
+        subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         SatelliteTransmissionUpdateHandler handler =
                 mSatelliteTransmissionUpdateHandlers.get(subId);
         if (handler != null) {
@@ -318,6 +340,7 @@
      */
     public void unregisterForSatelliteTransmissionUpdates(int subId, Consumer<Integer> result,
             ISatelliteTransmissionUpdateCallback callback) {
+        subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         SatelliteTransmissionUpdateHandler handler =
                 mSatelliteTransmissionUpdateHandlers.get(subId);
         if (handler != null) {
@@ -404,7 +427,7 @@
             mLastNeedFullScreenPointingUI = needFullScreenPointingUI;
             mLastIsDemoMode = isDemoMode;
             mLastIsEmergency = isEmergency;
-            mContext.startActivity(launchIntent);
+            mContext.startActivityAsUser(launchIntent, UserHandle.CURRENT);
         } catch (ActivityNotFoundException ex) {
             ploge("startPointingUI: Pointing UI app activity is not found, ex=" + ex);
         }
@@ -426,6 +449,7 @@
             int sendPendingCount, int errorCode) {
         DatagramTransferStateHandlerRequest request = new DatagramTransferStateHandlerRequest(
                 datagramType, datagramTransferState, sendPendingCount, errorCode);
+        subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         SatelliteTransmissionUpdateHandler handler =
                 mSatelliteTransmissionUpdateHandlers.get(subId);
 
@@ -439,12 +463,32 @@
         }
     }
 
+    /**
+     * This API is used to notify PointingAppController that a send datagram has just been
+     * requested.
+     */
+    public void onSendDatagramRequested(
+            int subId, @SatelliteManager.DatagramType int datagramType) {
+        subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+        SatelliteTransmissionUpdateHandler handler =
+                mSatelliteTransmissionUpdateHandlers.get(subId);
+        if (handler != null) {
+            Message msg = handler.obtainMessage(
+                    SatelliteTransmissionUpdateHandler.EVENT_SEND_DATAGRAM_REQUESTED,
+                    datagramType);
+            msg.sendToTarget();
+        } else {
+            ploge("SatelliteTransmissionUpdateHandler not found for subId: " + subId);
+        }
+    }
+
     public void updateReceiveDatagramTransferState(int subId,
             @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState,
             int receivePendingCount, int errorCode) {
         DatagramTransferStateHandlerRequest request = new DatagramTransferStateHandlerRequest(
                 SatelliteManager.DATAGRAM_TYPE_UNKNOWN, datagramTransferState, receivePendingCount,
                 errorCode);
+        subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         SatelliteTransmissionUpdateHandler handler =
                 mSatelliteTransmissionUpdateHandlers.get(subId);
 
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteConstants.java b/src/java/com/android/internal/telephony/satellite/SatelliteConstants.java
index 384dfa5..a5afe4a 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteConstants.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteConstants.java
@@ -104,4 +104,22 @@
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AccessControlType {}
+
+    //// Unknown reason.
+    public static final int TRIGGERING_EVENT_UNKNOWN = 0;
+    // Satellite Access Controller has been triggered by an external event.
+    public static final int TRIGGERING_EVENT_EXTERNAL_REQUEST = 1;
+    // Satellite Access Controller has been triggered by an MCC change event.
+    public static final int TRIGGERING_EVENT_MCC_CHANGED = 2;
+    //Satellite Access Controller has been triggered due to the location setting being enabled.
+    public static final int TRIGGERING_EVENT_LOCATION_SETTINGS_ENABLED = 3;
+
+    @IntDef(prefix = {"TRIGGERING_EVENT_"}, value = {
+            TRIGGERING_EVENT_UNKNOWN,
+            TRIGGERING_EVENT_EXTERNAL_REQUEST,
+            TRIGGERING_EVENT_MCC_CHANGED,
+            TRIGGERING_EVENT_LOCATION_SETTINGS_ENABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TriggeringEvent {}
 }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 14e7b76..695887e 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -17,23 +17,39 @@
 package com.android.internal.telephony.satellite;
 
 import static android.provider.Settings.ACTION_SATELLITE_SETTING;
+import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL;
+import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_TYPE;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT;
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE;
 import static android.telephony.CarrierConfigManager.KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT;
 import static android.telephony.CarrierConfigManager.KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ESOS_SUPPORTED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_NIDD_APN_NAME_STRING;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL;
 import static android.telephony.SubscriptionManager.SATELLITE_ATTACH_ENABLED_FOR_CARRIER;
 import static android.telephony.SubscriptionManager.SATELLITE_ENTITLEMENT_STATUS;
 import static android.telephony.SubscriptionManager.isValidSubscriptionId;
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
 import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS;
 import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
 import static android.telephony.satellite.SatelliteManager.KEY_NTN_SIGNAL_STRENGTH;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_ERROR;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
@@ -48,6 +64,7 @@
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -71,6 +88,7 @@
 import android.os.ICancellationSignal;
 import android.os.Looper;
 import android.os.Message;
+import android.os.OutcomeReceiver;
 import android.os.PersistableBundle;
 import android.os.Registrant;
 import android.os.RegistrantList;
@@ -82,6 +100,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Telephony;
+import android.telecom.TelecomManager;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
 import android.telephony.DropBoxManagerLoggerBackend;
@@ -89,6 +108,7 @@
 import android.telephony.PersistentLogger;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.satellite.INtnSignalStrengthCallback;
@@ -102,6 +122,10 @@
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.SatelliteModemEnableRequestAttributes;
+import android.telephony.satellite.SatelliteSubscriberInfo;
+import android.telephony.satellite.SatelliteSubscriberProvisionStatus;
+import android.telephony.satellite.SatelliteSubscriptionInfo;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -116,6 +140,7 @@
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyCountryDetector;
 import com.android.internal.telephony.configupdate.ConfigParser;
 import com.android.internal.telephony.configupdate.ConfigProviderAdaptor;
 import com.android.internal.telephony.configupdate.TelephonyConfigUpdateInstallReceiver;
@@ -125,6 +150,7 @@
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
 import com.android.internal.telephony.satellite.metrics.ProvisionMetricsStats;
 import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.internal.util.FunctionalUtils;
@@ -180,6 +206,10 @@
     public static final long DEFAULT_CARRIER_EMERGENCY_CALL_WAIT_FOR_CONNECTION_TIMEOUT_MILLIS =
             TimeUnit.SECONDS.toMillis(30);
 
+    /** Sets report entitled metrics cool down to 23 hours to help enforcing privacy requirement.*/
+    private static final long WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS =
+            TimeUnit.HOURS.toMillis(23);
+
     /** Message codes used in handleMessage() */
     //TODO: Move the Commands and events related to position updates to PointingAppController
     private static final int CMD_START_SATELLITE_TRANSMISSION_UPDATES = 1;
@@ -203,7 +233,6 @@
     private static final int EVENT_RADIO_STATE_CHANGED = 23;
     private static final int CMD_IS_SATELLITE_PROVISIONED = 24;
     private static final int EVENT_IS_SATELLITE_PROVISIONED_DONE = 25;
-    private static final int EVENT_SATELLITE_PROVISION_STATE_CHANGED = 26;
     private static final int EVENT_PENDING_DATAGRAMS = 27;
     private static final int EVENT_SATELLITE_MODEM_STATE_CHANGED = 28;
     private static final int EVENT_SET_SATELLITE_PLMN_INFO_DONE = 29;
@@ -216,10 +245,23 @@
     private static final int EVENT_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING_DONE = 36;
     private static final int EVENT_SERVICE_STATE_CHANGED = 37;
     private static final int EVENT_SATELLITE_CAPABILITIES_CHANGED = 38;
-    private static final int EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT = 39;
+    protected static final int EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT = 39;
     private static final int EVENT_SATELLITE_CONFIG_DATA_UPDATED = 40;
     private static final int EVENT_SATELLITE_SUPPORTED_STATE_CHANGED = 41;
     private static final int EVENT_NOTIFY_NTN_HYSTERESIS_TIMED_OUT = 42;
+    private static final int CMD_EVALUATE_ESOS_PROFILES_PRIORITIZATION = 43;
+    private static final int CMD_UPDATE_PROVISION_SATELLITE_TOKEN = 44;
+    private static final int EVENT_UPDATE_PROVISION_SATELLITE_TOKEN_DONE = 45;
+    private static final int EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT = 46;
+    private static final int EVENT_WIFI_CONNECTIVITY_STATE_CHANGED = 47;
+    private static final int EVENT_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT = 48;
+    protected static final int EVENT_WAIT_FOR_CELLULAR_MODEM_OFF_TIMED_OUT = 49;
+    private static final int CMD_UPDATE_SATELLITE_ENABLE_ATTRIBUTES = 50;
+    private static final int EVENT_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_DONE = 51;
+    protected static final int
+            EVENT_WAIT_FOR_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_RESPONSE_TIMED_OUT = 52;
+    private static final int EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT = 53;
+    protected static final int EVENT_SATELLITE_REGISTRATION_FAILURE = 54;
 
     @NonNull private static SatelliteController sInstance;
     @NonNull private final Context mContext;
@@ -233,9 +275,16 @@
     @NonNull private SessionMetricsStats mSessionMetricsStats;
     @NonNull private CarrierRoamingSatelliteControllerStats mCarrierRoamingSatelliteControllerStats;
     @NonNull private final SubscriptionManagerService mSubscriptionManagerService;
+    @NonNull private final TelephonyCountryDetector mCountryDetector;
+    @NonNull private final TelecomManager mTelecomManager;
     private final CommandsInterface mCi;
     private ContentResolver mContentResolver;
     private final DeviceStateMonitor mDSM;
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected final Object mSatellitePhoneLock = new Object();
+    @GuardedBy("mSatellitePhoneLock")
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected Phone mSatellitePhone = null;
 
     private final Object mRadioStateLock = new Object();
 
@@ -256,19 +305,30 @@
     private boolean mDisableWifiOnSatelliteEnabled = false;
 
     private final Object mSatelliteEnabledRequestLock = new Object();
+    /* This variable is used to store the first enable request that framework has received in the
+     * current session.
+     */
     @GuardedBy("mSatelliteEnabledRequestLock")
     private RequestSatelliteEnabledArgument mSatelliteEnabledRequest = null;
+    /* This variable is used to store a disable request that framework has received.
+     */
+    @GuardedBy("mSatelliteEnabledRequestLock")
+    private RequestSatelliteEnabledArgument mSatelliteDisabledRequest = null;
+    /* This variable is used to store an enable request that updates the enable attributes of an
+     * existing satellite session.
+     */
+    @GuardedBy("mSatelliteEnabledRequestLock")
+    private RequestSatelliteEnabledArgument mSatelliteEnableAttributesUpdateRequest = null;
     /** Flag to indicate that satellite is enabled successfully
      * and waiting for all the radios to be disabled so that success can be sent to callback
      */
     @GuardedBy("mSatelliteEnabledRequestLock")
     private boolean mWaitingForRadioDisabled = false;
-
+    @GuardedBy("mSatelliteEnabledRequestLock")
     private boolean mWaitingForDisableSatelliteModemResponse = false;
+    @GuardedBy("mSatelliteEnabledRequestLock")
     private boolean mWaitingForSatelliteModemOff = false;
 
-    private final AtomicBoolean mRegisteredForProvisionStateChangedWithSatelliteService =
-            new AtomicBoolean(false);
     private final AtomicBoolean mRegisteredForPendingDatagramCountWithSatelliteService =
             new AtomicBoolean(false);
     private final AtomicBoolean mRegisteredForSatelliteModemStateChangedWithSatelliteService =
@@ -282,6 +342,8 @@
             new AtomicBoolean(false);
     private final AtomicBoolean mRegisteredForSatelliteSupportedStateChanged =
             new AtomicBoolean(false);
+    private final AtomicBoolean mRegisteredForSatelliteRegistrationFailure =
+            new AtomicBoolean(false);
     /**
      * Map key: subId, value: callback to get error code of the provision request.
      */
@@ -310,6 +372,12 @@
      */
     private final ConcurrentHashMap<IBinder, ISatelliteSupportedStateCallback>
             mSatelliteSupportedStateChangedListeners = new ConcurrentHashMap<>();
+
+    /**
+     * Map key: binder of the callback, value: callback to satellite registration failure
+     */
+    private final ConcurrentHashMap<IBinder, ISatelliteModemStateCallback>
+            mSatelliteRegistrationFailureListeners = new ConcurrentHashMap<>();
     private final Object mIsSatelliteSupportedLock = new Object();
     @GuardedBy("mIsSatelliteSupportedLock")
     private Boolean mIsSatelliteSupported = null;
@@ -318,12 +386,18 @@
     private final Object mIsSatelliteEnabledLock = new Object();
     @GuardedBy("mIsSatelliteEnabledLock")
     private Boolean mIsSatelliteEnabled = null;
-    private final Object mIsRadioOnLock = new Object();
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected final Object mIsRadioOnLock = new Object();
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected boolean mIsRadioOn;
     @GuardedBy("mIsRadioOnLock")
-    private boolean mIsRadioOn = false;
-    private final Object mSatelliteViaOemProvisionLock = new Object();
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected boolean mRadioOffRequested = false;
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected final Object mSatelliteViaOemProvisionLock = new Object();
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     @GuardedBy("mSatelliteViaOemProvisionLock")
-    private Boolean mIsSatelliteViaOemProvisioned = null;
+    protected Boolean mIsSatelliteViaOemProvisioned = null;
     @GuardedBy("mSatelliteViaOemProvisionLock")
     private Boolean mOverriddenIsSatelliteViaOemProvisioned = null;
     private final Object mSatelliteCapabilitiesLock = new Object();
@@ -349,6 +423,8 @@
             mCarrierConfigChangeListener;
     @NonNull private final ConfigProviderAdaptor.Callback mConfigDataUpdatedCallback;
     @NonNull private final Object mCarrierConfigArrayLock = new Object();
+    @NonNull
+    private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener;
     @GuardedBy("mCarrierConfigArrayLock")
     @NonNull private final SparseArray<PersistableBundle> mCarrierConfigArray = new SparseArray<>();
     @GuardedBy("mIsSatelliteEnabledLock")
@@ -380,6 +456,17 @@
     @GuardedBy("mSatelliteConnectedLock")
     @NonNull private final SparseBooleanArray mInitialized = new SparseBooleanArray();
 
+    /**
+     * Boolean set to {@code true} when device is eligible to connect to carrier roaming
+     * non-terrestrial network else set to {@code false}.
+     */
+    @GuardedBy("mSatellitePhoneLock")
+    private Boolean mLastNotifiedNtnEligibility = null;
+    @GuardedBy("mSatellitePhoneLock")
+    private boolean mNtnEligibilityHysteresisTimedOut = false;
+    @GuardedBy("mSatellitePhoneLock")
+    private boolean mCheckingAccessRestrictionInProgress = false;
+
     @GuardedBy("mSatelliteConnectedLock")
     @NonNull private final Map<Integer, CarrierRoamingSatelliteSessionStats>
             mCarrierRoamingSatelliteSessionStatsMap = new HashMap<>();
@@ -422,6 +509,22 @@
     @GuardedBy("mSupportedSatelliteServicesLock")
     private final SparseArray<List<String>> mMergedPlmnListPerCarrier = new SparseArray<>();
     private static AtomicLong sNextSatelliteEnableRequestId = new AtomicLong(0);
+    // key : subscriberId, value : provisioned or not.
+    @GuardedBy("mSatelliteTokenProvisionedLock")
+    private Map<String, Boolean> mProvisionedSubscriberId = new HashMap<>();
+    // key : subscriberId, value : subId
+    @GuardedBy("mSatelliteTokenProvisionedLock")
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected Map<String, Integer> mSubscriberIdPerSub = new HashMap<>();
+    // key : priority, low value is high, value : List<SubscriptionInfo>
+    @GuardedBy("mSatelliteTokenProvisionedLock")
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected Map<Integer, List<SubscriptionInfo>> mSubsInfoListPerPriority = new HashMap<>();
+    // The last ICC ID that framework configured to modem.
+    @GuardedBy("mSatelliteTokenProvisionedLock")
+    private String mLastConfiguredIccId;
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    @NonNull protected final Object mSatelliteTokenProvisionedLock = new Object();
     private long mWaitTimeForSatelliteEnablingResponse;
     private long mDemoPointingAlignedDurationMillis;
     private long mDemoPointingNotAlignedDurationMillis;
@@ -450,6 +553,30 @@
 
     // Variable for backup and restore device's screen rotation settings.
     private String mDeviceRotationLockToBackupAndRestore = null;
+    // This is used for testing only. Context#getSystemService is a final API and cannot be
+    // mocked. Using this to inject a mock SubscriptionManager to work around this limitation.
+    private SubscriptionManager mInjectSubscriptionManager = null;
+
+    private final Object mIsWifiConnectedLock = new Object();
+    @GuardedBy("mIsWifiConnectedLock")
+    private boolean mIsWifiConnected = false;
+    private boolean mHasSentBroadcast = false;
+    // For satellite CTS test which to configure intent component with the necessary values.
+    private boolean mChangeIntentComponent = false;
+    private String mConfigSatelliteGatewayServicePackage = "";
+    private String mConfigSatelliteCarrierRoamingEsosProvisionedClass = "";
+
+    private BroadcastReceiver
+            mDefaultSmsSubscriptionChangedBroadcastReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (intent.getAction().equals(
+                            SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED)) {
+                        plogd("Default SMS subscription changed");
+                        sendRequestAsync(CMD_EVALUATE_ESOS_PROFILES_PRIORITIZATION, null, null);
+                    }
+                }
+            };
 
     /**
      * @return The singleton instance of SatelliteController.
@@ -495,12 +622,19 @@
         mContext = context;
         mFeatureFlags = featureFlags;
         Phone phone = SatelliteServiceUtils.getPhone();
+        synchronized (mSatellitePhoneLock) {
+            mSatellitePhone = phone;
+        }
         mCi = phone.mCi;
         mDSM = phone.getDeviceStateMonitor();
         // Create the SatelliteModemInterface singleton, which is used to manage connections
         // to the satellite service and HAL interface.
         mSatelliteModemInterface = SatelliteModemInterface.make(
                 mContext, this, mFeatureFlags);
+        mCountryDetector = TelephonyCountryDetector.getInstance(context, mFeatureFlags);
+        mCountryDetector.registerForWifiConnectivityStateChanged(this,
+                EVENT_WIFI_CONNECTIVITY_STATE_CHANGED, null);
+        mTelecomManager = mContext.getSystemService(TelecomManager.class);
 
         // Create the PointingUIController singleton,
         // which is used to manage interactions with PointingUI app.
@@ -525,7 +659,6 @@
             mIsRadioOn = phone.isRadioOn();
         }
 
-        registerForSatelliteProvisionStateChanged();
         registerForPendingDatagramCount();
         registerForSatelliteModemStateChanged();
         registerForServiceStateChanged();
@@ -553,8 +686,10 @@
         mCarrierConfigChangeListener =
                 (slotIndex, subId, carrierId, specificCarrierId) ->
                         handleCarrierConfigChanged(slotIndex, subId, carrierId, specificCarrierId);
-        mCarrierConfigManager.registerCarrierConfigChangeListener(
-                        new HandlerExecutor(new Handler(looper)), mCarrierConfigChangeListener);
+        if (mCarrierConfigManager != null) {
+            mCarrierConfigManager.registerCarrierConfigChangeListener(
+                    new HandlerExecutor(new Handler(looper)), mCarrierConfigChangeListener);
+        }
 
         mConfigDataUpdatedCallback = new ConfigProviderAdaptor.Callback() {
             @Override
@@ -577,6 +712,30 @@
                 getDemoPointingNotAlignedDurationMillisFromResources();
         mSatelliteEmergencyModeDurationMillis =
                 getSatelliteEmergencyModeDurationFromOverlayConfig(context);
+        sendMessageDelayed(obtainMessage(CMD_EVALUATE_ESOS_PROFILES_PRIORITIZATION),
+                /* delayMillis= */ TimeUnit.MINUTES.toMillis(1));
+
+        SubscriptionManager subscriptionManager = mContext.getSystemService(
+                SubscriptionManager.class);
+        mSubscriptionsChangedListener = new SatelliteSubscriptionsChangedListener();
+        if (subscriptionManager != null) {
+            subscriptionManager.addOnSubscriptionsChangedListener(
+                    new HandlerExecutor(new Handler(looper)), mSubscriptionsChangedListener);
+        }
+        registerDefaultSmsSubscriptionChangedBroadcastReceiver();
+        updateSatelliteProvisionedStatePerSubscriberId();
+    }
+
+    class SatelliteSubscriptionsChangedListener
+            extends SubscriptionManager.OnSubscriptionsChangedListener {
+
+        /**
+         * Callback invoked when there is any change to any SubscriptionInfo.
+         */
+        @Override
+        public void onSubscriptionsChanged() {
+            handleSubscriptionsChanged();
+        }
     }
 
     /**
@@ -939,11 +1098,12 @@
                     break;
                 }
                 mSatelliteProvisionCallbacks.put(argument.subId, argument.callback);
-                onCompleted = obtainMessage(EVENT_PROVISION_SATELLITE_SERVICE_DONE, request);
                 // Log the current time for provision triggered
                 mProvisionMetricsStats.setProvisioningStartTime();
-                mSatelliteModemInterface.provisionSatelliteService(argument.token,
-                        argument.provisionData, onCompleted);
+                Message provisionSatelliteServiceDoneEvent = this.obtainMessage(
+                        EVENT_PROVISION_SATELLITE_SERVICE_DONE,
+                        new AsyncResult(request, SATELLITE_RESULT_SUCCESS, null));
+                provisionSatelliteServiceDoneEvent.sendToTarget();
                 break;
             }
 
@@ -962,11 +1122,13 @@
                 request = (SatelliteControllerHandlerRequest) msg.obj;
                 ProvisionSatelliteServiceArgument argument =
                         (ProvisionSatelliteServiceArgument) request.argument;
-                onCompleted = obtainMessage(EVENT_DEPROVISION_SATELLITE_SERVICE_DONE, request);
                 if (argument.callback != null) {
                     mProvisionMetricsStats.setProvisioningStartTime();
                 }
-                mSatelliteModemInterface.deprovisionSatelliteService(argument.token, onCompleted);
+                Message deprovisionSatelliteServiceDoneEvent = this.obtainMessage(
+                        EVENT_DEPROVISION_SATELLITE_SERVICE_DONE,
+                        new AsyncResult(request, SATELLITE_RESULT_SUCCESS, null));
+                deprovisionSatelliteServiceDoneEvent.sendToTarget();
                 break;
             }
 
@@ -1003,64 +1165,50 @@
                             + argument.enableSatellite + " was already processed");
                     return;
                 }
-                stopWaitForSatelliteEnablingResponseTimer(argument);
+                if (shouldStopWaitForEnableResponseTimer(argument)) {
+                    stopWaitForSatelliteEnablingResponseTimer(argument);
+                } else {
+                    plogd("Still waiting for the OFF state from modem");
+                }
 
                 if (error == SATELLITE_RESULT_SUCCESS) {
                     if (argument.enableSatellite) {
                         synchronized (mSatelliteEnabledRequestLock) {
                             mWaitingForRadioDisabled = true;
+                            setDemoModeEnabled(argument.enableDemoMode);
                         }
+                        // TODO (b/361139260): Start a timer to wait for other radios off
                         setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_TRUE);
                         setSettingsKeyToAllowDeviceRotation(SATELLITE_MODE_ENABLED_TRUE);
                         evaluateToSendSatelliteEnabledSuccess();
                     } else {
-                        /**
-                         * Unregister Importance Listener for Pointing UI
-                         * when Satellite is disabled
-                         */
+                        // Unregister importance listener for PointingUI when satellite is disabled
                         if (mNeedsSatellitePointing) {
                             mPointingAppController.removeListenerForPointingUI();
                         }
                         synchronized (mSatelliteEnabledRequestLock) {
-                            if (mSatelliteEnabledRequest != null &&
-                                    mSatelliteEnabledRequest.enableSatellite == true &&
-                                    argument.enableSatellite == false && mWaitingForRadioDisabled) {
-                                // Previous mSatelliteEnabledRequest is successful but waiting for
-                                // all radios to be turned off.
-                                mSatelliteEnabledRequest.callback.accept(
-                                        SATELLITE_RESULT_SUCCESS);
-                            }
-                        }
-
-                        synchronized (mIsSatelliteEnabledLock) {
                             if (!mWaitingForSatelliteModemOff) {
                                 moveSatelliteToOffStateAndCleanUpResources(
-                                        SATELLITE_RESULT_SUCCESS,
-                                        argument.callback);
-                            } else {
-                                plogd("Wait for satellite modem off before updating satellite"
-                                        + " modem state");
+                                        SATELLITE_RESULT_SUCCESS);
                             }
                             mWaitingForDisableSatelliteModemResponse = false;
                         }
                     }
-                    // Request Ntn signal strength report when satellite enabled or disabled done.
+                    // Request NTN signal strength report when satellite enabled or disabled done.
                     mLatestRequestedStateForNtnSignalStrengthReport.set(argument.enableSatellite);
                     updateNtnSignalStrengthReporting(argument.enableSatellite);
                 } else {
-                    synchronized (mSatelliteEnabledRequestLock) {
-                        if (mSatelliteEnabledRequest != null &&
-                                mSatelliteEnabledRequest.enableSatellite == true &&
-                                argument.enableSatellite == false && mWaitingForRadioDisabled) {
-                            // Previous mSatelliteEnabledRequest is successful but waiting for
-                            // all radios to be turned off.
-                            mSatelliteEnabledRequest.callback.accept(
-                                    SATELLITE_RESULT_SUCCESS);
-                        }
+                    if (argument.enableSatellite) {
+                        /* Framework need to abort the enable attributes update request if any since
+                         * modem failed to enable satellite.
+                         */
+                        abortSatelliteEnableAttributesUpdateRequest(
+                                SATELLITE_RESULT_REQUEST_ABORTED);
+                        resetSatelliteEnabledRequest();
+                    } else {
+                        resetSatelliteDisabledRequest();
                     }
-                    notifyEnablementFailedToSatelliteSessionController();
-                    resetSatelliteEnabledRequest();
-
+                    notifyEnablementFailedToSatelliteSessionController(argument.enableSatellite);
                     // If Satellite enable/disable request returned Error, no need to wait for radio
                     argument.callback.accept(error);
                 }
@@ -1070,7 +1218,8 @@
                             .setSatelliteTechnology(getSupportedNtnRadioTechnology())
                             .setInitializationProcessingTime(
                                     System.currentTimeMillis() - mSessionProcessingTimeStamp)
-                            .setIsDemoMode(mIsDemoModeEnabled);
+                            .setIsDemoMode(mIsDemoModeEnabled)
+                            .setCarrierId(getSatelliteCarrierId());
                     mSessionProcessingTimeStamp = 0;
 
                     if (error == SATELLITE_RESULT_SUCCESS) {
@@ -1089,22 +1238,98 @@
                             .reportSessionMetrics();
                     mSessionStartTimeStamp = 0;
                     mSessionProcessingTimeStamp = 0;
-
                     mControllerMetricsStats.onSatelliteDisabled();
-
                     handlePersistentLoggingOnSessionEnd(mIsEmergency);
-
-                    synchronized (mIsSatelliteEnabledLock) {
+                    synchronized (mSatelliteEnabledRequestLock) {
                         mWaitingForDisableSatelliteModemResponse = false;
                     }
                 }
                 break;
             }
 
-            case EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT:
+            case EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT: {
                 handleEventWaitForSatelliteEnablingResponseTimedOut(
                         (RequestSatelliteEnabledArgument) msg.obj);
                 break;
+            }
+
+            case CMD_UPDATE_SATELLITE_ENABLE_ATTRIBUTES: {
+                request = (SatelliteControllerHandlerRequest) msg.obj;
+                RequestSatelliteEnabledArgument argument =
+                        (RequestSatelliteEnabledArgument) request.argument;
+
+                if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+                    plogd("UpdateEnableAttributes: carrierRoamingNbIotNtn flag is disabled");
+                    sendErrorAndReportSessionMetrics(
+                            SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS, argument.callback);
+                    synchronized (mSatelliteEnabledRequestLock) {
+                        mSatelliteEnableAttributesUpdateRequest = null;
+                    }
+                    break;
+                }
+
+                synchronized (mSatelliteEnabledRequestLock) {
+                    if (mSatelliteEnabledRequest != null) {
+                        plogd("UpdateEnableAttributes: Satellite is being enabled. Need to "
+                                + "wait until enable complete before updating attributes");
+                        break;
+                    }
+                    if (isSatelliteBeingDisabled()) {
+                        plogd("UpdateEnableAttributes: Satellite is being disabled. Aborting the "
+                                + "enable attributes update request");
+                        mSatelliteEnableAttributesUpdateRequest = null;
+                        argument.callback.accept(SATELLITE_RESULT_REQUEST_ABORTED);
+                        break;
+                    }
+                }
+                onCompleted = obtainMessage(EVENT_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_DONE, request);
+                mSatelliteModemInterface.requestSatelliteEnabled(
+                        createModemEnableRequest(argument), onCompleted);
+                startWaitForUpdateSatelliteEnableAttributesResponseTimer(argument);
+                break;
+            }
+
+            case EVENT_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_DONE: {
+                ar = (AsyncResult) msg.obj;
+                request = (SatelliteControllerHandlerRequest) ar.userObj;
+                RequestSatelliteEnabledArgument argument =
+                        (RequestSatelliteEnabledArgument) request.argument;
+                int error =  SatelliteServiceUtils.getSatelliteError(
+                        ar, "updateSatelliteEnableAttributes");
+                plogd("EVENT_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_DONE = " + error);
+
+                /*
+                 * The timer to wait for EVENT_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_DONE might have
+                 * expired and thus the request resources might have been cleaned up.
+                 */
+                if (!shouldProcessEventUpdateSatelliteEnableAttributesDone(argument)) {
+                    plogw("The update request ID=" + argument.requestId + " was already processed");
+                    return;
+                }
+                stopWaitForUpdateSatelliteEnableAttributesResponseTimer(argument);
+
+                if (error == SATELLITE_RESULT_SUCCESS) {
+                    setDemoModeEnabled(argument.enableDemoMode);
+                    setEmergencyMode(argument.isEmergency);
+                }
+                synchronized (mSatelliteEnabledRequestLock) {
+                    mSatelliteEnableAttributesUpdateRequest = null;
+                }
+                argument.callback.accept(error);
+                break;
+            }
+
+            case EVENT_WAIT_FOR_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_RESPONSE_TIMED_OUT: {
+                RequestSatelliteEnabledArgument argument =
+                        (RequestSatelliteEnabledArgument) msg.obj;
+                plogw("Timed out to wait for the response from the modem for the request to "
+                        + "update satellite enable attributes, request ID = " + argument.requestId);
+                synchronized (mSatelliteEnabledRequestLock) {
+                    mSatelliteEnableAttributesUpdateRequest = null;
+                }
+                argument.callback.accept(SATELLITE_RESULT_MODEM_TIMEOUT);
+                break;
+            }
 
             case CMD_IS_SATELLITE_ENABLED: {
                 request = (SatelliteControllerHandlerRequest) msg.obj;
@@ -1130,7 +1355,7 @@
                         updateSatelliteEnabledState(enabled, "EVENT_IS_SATELLITE_ENABLED_DONE");
                     }
                 } else if (error == SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED) {
-                    updateSatelliteSupportedStateWhenSatelliteServiceConnected(false);
+                    updateSatelliteSupportedState(false);
                 }
                 ((ResultReceiver) request.argument).send(error, bundle);
                 break;
@@ -1156,7 +1381,7 @@
                         boolean supported = (boolean) ar.result;
                         plogd("isSatelliteSupported: " + supported);
                         bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, supported);
-                        updateSatelliteSupportedStateWhenSatelliteServiceConnected(supported);
+                        updateSatelliteSupportedState(supported);
                     }
                 }
                 ((ResultReceiver) request.argument).send(error, bundle);
@@ -1236,6 +1461,14 @@
                         mIsRadioOn = true;
                     } else if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF) {
                         resetCarrierRoamingSatelliteModeParams();
+                        synchronized (mIsRadioOnLock) {
+                            if (mRadioOffRequested) {
+                                logd("EVENT_RADIO_STATE_CHANGED: set mIsRadioOn to false");
+                                stopWaitForCellularModemOffTimer();
+                                mIsRadioOn = false;
+                                mRadioOffRequested = false;
+                            }
+                        }
                     }
                 }
 
@@ -1262,8 +1495,10 @@
 
             case CMD_IS_SATELLITE_PROVISIONED: {
                 request = (SatelliteControllerHandlerRequest) msg.obj;
-                onCompleted = obtainMessage(EVENT_IS_SATELLITE_PROVISIONED_DONE, request);
-                mSatelliteModemInterface.requestIsSatelliteProvisioned(onCompleted);
+                Message isProvisionedDoneEvent = this.obtainMessage(
+                        EVENT_IS_SATELLITE_PROVISIONED_DONE,
+                        new AsyncResult(request, SATELLITE_RESULT_SUCCESS, null));
+                isProvisionedDoneEvent.sendToTarget();
                 break;
             }
 
@@ -1272,15 +1507,6 @@
                 break;
             }
 
-            case EVENT_SATELLITE_PROVISION_STATE_CHANGED:
-                ar = (AsyncResult) msg.obj;
-                if (ar.result == null) {
-                    ploge("EVENT_SATELLITE_PROVISION_STATE_CHANGED: result is null");
-                } else {
-                    handleEventSatelliteProvisionStateChanged((boolean) ar.result);
-                }
-                break;
-
             case EVENT_PENDING_DATAGRAMS:
                 plogd("Received EVENT_PENDING_DATAGRAMS");
                 IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
@@ -1289,8 +1515,7 @@
                         plogd("pollPendingSatelliteDatagram result: " + result);
                     }
                 };
-                pollPendingDatagrams(
-                        SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, internalCallback);
+                pollPendingDatagrams(internalCallback);
                 break;
 
             case EVENT_SATELLITE_MODEM_STATE_CHANGED:
@@ -1462,6 +1687,127 @@
                 break;
             }
 
+            case EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT: {
+                synchronized (mSatellitePhoneLock) {
+                    mNtnEligibilityHysteresisTimedOut = true;
+                    boolean eligible = isCarrierRoamingNtnEligible(mSatellitePhone);
+                    plogd("EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT:"
+                            + " isCarrierRoamingNtnEligible=" + eligible);
+                    if (eligible) {
+                        requestIsSatelliteAllowedForCurrentLocation();
+                    }
+                }
+                break;
+            }
+
+            case CMD_EVALUATE_ESOS_PROFILES_PRIORITIZATION: {
+                evaluateESOSProfilesPrioritization();
+                break;
+            }
+
+            case CMD_UPDATE_PROVISION_SATELLITE_TOKEN: {
+                request = (SatelliteControllerHandlerRequest) msg.obj;
+                RequestProvisionSatelliteArgument argument =
+                        (RequestProvisionSatelliteArgument) request.argument;
+                onCompleted = obtainMessage(EVENT_UPDATE_PROVISION_SATELLITE_TOKEN_DONE, request);
+                // only pass to index 0.
+                // TODO: Select the subscription with highest priority and set it to mSatelliteSubId
+                int subId = -1;
+                synchronized (mSatelliteTokenProvisionedLock) {
+                    subId = mSubscriberIdPerSub.getOrDefault(
+                            argument.mSatelliteSubscriberInfoList.get(0).getSubscriberId(), -1);
+                }
+                setSatellitePhone(subId);
+                String iccId = mSubscriptionManagerService.getSubscriptionInfo(subId).getIccId();
+                argument.setIccId(iccId);
+                boolean sendResponse = false;
+                synchronized (mSatelliteTokenProvisionedLock) {
+                    if (!iccId.equals(mLastConfiguredIccId)) {
+                        logd("updateSatelliteSubscription subId=" + subId + ", iccId=" + iccId
+                                + " to modem");
+                        mSatelliteModemInterface.updateSatelliteSubscription(iccId, onCompleted);
+                    } else {
+                        sendResponse = true;
+                    }
+                }
+                handleEventSatelliteSubscriptionProvisionStateChanged(
+                        argument.mSatelliteSubscriberInfoList, true);
+                if (sendResponse) {
+                    // The response is sent immediately because the ICCID has already been
+                    // delivered to the modem.
+                    Bundle bundle = new Bundle();
+                    bundle.putBoolean(SatelliteManager.KEY_PROVISION_SATELLITE_TOKENS, true);
+                    argument.mResult.send(SATELLITE_RESULT_SUCCESS, bundle);
+                }
+                break;
+            }
+
+            case EVENT_UPDATE_PROVISION_SATELLITE_TOKEN_DONE: {
+                ar = (AsyncResult) msg.obj;
+                request = (SatelliteControllerHandlerRequest) ar.userObj;
+                RequestProvisionSatelliteArgument argument =
+                        (RequestProvisionSatelliteArgument) request.argument;
+                int error = SatelliteServiceUtils.getSatelliteError(ar,
+                        "updateSatelliteSubscription");
+                if (error == SATELLITE_RESULT_SUCCESS) {
+                    synchronized (mSatelliteTokenProvisionedLock) {
+                        mLastConfiguredIccId = argument.getIccId();
+                    }
+                }
+                logd("updateSatelliteSubscription result=" + error);
+                Bundle bundle = new Bundle();
+                bundle.putBoolean(SatelliteManager.KEY_PROVISION_SATELLITE_TOKENS,
+                        error == SATELLITE_RESULT_SUCCESS);
+                argument.mResult.send(error, bundle);
+                break;
+            }
+
+            case EVENT_WIFI_CONNECTIVITY_STATE_CHANGED: {
+                synchronized (mIsWifiConnectedLock) {
+                    ar = (AsyncResult) msg.obj;
+                    mIsWifiConnected = (boolean) ar.result;
+                    plogd("EVENT_WIFI_CONNECTIVITY_STATE_CHANGED: mIsWifiConnected="
+                            + mIsWifiConnected);
+                    handleStateChangedForCarrierRoamingNtnEligibility();
+                }
+                break;
+            }
+            case EVENT_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT: {
+                handleSatelliteAccessRestrictionCheckingResult((boolean) msg.obj);
+                break;
+            }
+
+            case EVENT_WAIT_FOR_CELLULAR_MODEM_OFF_TIMED_OUT: {
+                plogw("Timed out to wait for cellular modem OFF state");
+                synchronized (mIsRadioOnLock) {
+                    mRadioOffRequested = false;
+                }
+                break;
+            }
+
+            case EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT: {
+                // TODO: b/366329504 report carrier roaming metrics for multiple subscription IDs.
+                synchronized (mSupportedSatelliteServicesLock) {
+                    int defaultSubId = mSubscriptionManagerService.getDefaultSubId();
+                    boolean isEntitled = mSatelliteEntitlementStatusPerCarrier.get(defaultSubId,
+                            false);
+                    mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(isEntitled);
+                }
+                sendMessageDelayed(obtainMessage(
+                                EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT),
+                        WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS);
+                break;
+            }
+
+            case EVENT_SATELLITE_REGISTRATION_FAILURE:
+                ar = (AsyncResult) msg.obj;
+                if (ar.result == null) {
+                    loge("EVENT_SATELLITE_REGISTRATION_FAILURE: result is null");
+                } else {
+                    handleEventSatelliteRegistrationFailure((int) ar.result);
+                }
+                break;
+
             default:
                 Log.w(TAG, "SatelliteControllerHandler: unexpected message code: " +
                         msg.what);
@@ -1469,6 +1815,30 @@
         }
     }
 
+    private static final class RequestProvisionSatelliteArgument {
+        public List<SatelliteSubscriberInfo> mSatelliteSubscriberInfoList;
+        @NonNull
+        public ResultReceiver mResult;
+        public long mRequestId;
+        public String mIccId;
+
+        RequestProvisionSatelliteArgument(List<SatelliteSubscriberInfo> satelliteSubscriberInfoList,
+                ResultReceiver result) {
+            this.mSatelliteSubscriberInfoList = satelliteSubscriberInfoList;
+            this.mResult = result;
+            this.mRequestId = sNextSatelliteEnableRequestId.getAndUpdate(
+                    n -> ((n + 1) % Long.MAX_VALUE));
+        }
+
+        public void setIccId(String iccId) {
+            mIccId = iccId;
+        }
+
+        public String getIccId() {
+            return mIccId;
+        }
+    }
+
     private void handleEventConfigDataUpdated() {
         updateSupportedSatelliteServicesForActiveSubscriptions();
         int[] activeSubIds = mSubscriptionManagerService.getActiveSubIdList(true);
@@ -1493,16 +1863,15 @@
      * enabled, this will also disable the cellular modem, and if the satellite modem is disabled,
      * this will also re-enable the cellular modem.
      *
-     * @param subId The subId of the subscription to set satellite enabled for.
      * @param enableSatellite {@code true} to enable the satellite modem and
      *                        {@code false} to disable.
      * @param enableDemoMode {@code true} to enable demo mode and {@code false} to disable.
      * @param isEmergency {@code true} to enable emergency mode, {@code false} otherwise.
      * @param callback The callback to get the error code of the request.
      */
-    public void requestSatelliteEnabled(int subId, boolean enableSatellite, boolean enableDemoMode,
+    public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
             boolean isEmergency, @NonNull IIntegerConsumer callback) {
-        plogd("requestSatelliteEnabled subId: " + subId + " enableSatellite: " + enableSatellite
+        plogd("requestSatelliteEnabled enableSatellite: " + enableSatellite
                 + " enableDemoMode: " + enableDemoMode + " isEmergency: " + isEmergency);
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
         int error = evaluateOemSatelliteRequestAllowed(true);
@@ -1519,32 +1888,25 @@
                             SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE, result);
                     return;
                 }
+                if (mRadioOffRequested) {
+                    ploge("Radio is being powering off, can not enable satellite");
+                    sendErrorAndReportSessionMetrics(
+                            SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE, result);
+                    return;
+                }
+            }
+
+            if (mTelecomManager.isInEmergencyCall()) {
+                plogd("requestSatelliteEnabled: reject as emergency call is ongoing.");
+                sendErrorAndReportSessionMetrics(
+                        SatelliteManager.SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS, result);
+                return;
             }
         } else {
             /* if disable satellite, always assume demo is also disabled */
             enableDemoMode = false;
         }
 
-        synchronized (mIsSatelliteEnabledLock) {
-            if (mIsSatelliteEnabled != null) {
-                if (mIsSatelliteEnabled == enableSatellite) {
-                    if (enableDemoMode != mIsDemoModeEnabled) {
-                        ploge("Received invalid demo mode while satellite session is enabled"
-                                + " enableDemoMode = " + enableDemoMode);
-                        sendErrorAndReportSessionMetrics(
-                                SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS, result);
-                        return;
-                    } else {
-                        plogd("Enable request matches with current state"
-                                + " enableSatellite = " + enableSatellite);
-                        sendErrorAndReportSessionMetrics(
-                                SatelliteManager.SATELLITE_RESULT_SUCCESS, result);
-                        return;
-                    }
-                }
-            }
-        }
-
         RequestSatelliteEnabledArgument request =
                 new RequestSatelliteEnabledArgument(enableSatellite, enableDemoMode, isEmergency,
                         result);
@@ -1559,35 +1921,147 @@
          *      4. ongoing request = enable, current request = disable: send request to modem
          */
         synchronized (mSatelliteEnabledRequestLock) {
-            if (mSatelliteEnabledRequest == null) {
-                mSatelliteEnabledRequest = request;
-            } else if (mSatelliteEnabledRequest.enableSatellite == request.enableSatellite) {
-                plogd("requestSatelliteEnabled enableSatellite: " + enableSatellite
-                        + " is already in progress.");
-                sendErrorAndReportSessionMetrics(
-                        SatelliteManager.SATELLITE_RESULT_REQUEST_IN_PROGRESS, result);
+            if (!isSatelliteEnabledRequestInProgress()) {
+                synchronized (mIsSatelliteEnabledLock) {
+                    if (mIsSatelliteEnabled != null && mIsSatelliteEnabled == enableSatellite) {
+                        evaluateToUpdateSatelliteEnabledAttributes(result,
+                                SatelliteManager.SATELLITE_RESULT_SUCCESS, request,
+                                mIsDemoModeEnabled, mIsEmergency);
+                        return;
+                    }
+                }
+                if (enableSatellite) {
+                    mSatelliteEnabledRequest = request;
+                } else {
+                    mSatelliteDisabledRequest = request;
+                }
+            } else if (isSatelliteBeingDisabled()) {
+                int resultCode = SatelliteManager.SATELLITE_RESULT_REQUEST_IN_PROGRESS;
+                if (enableSatellite) {
+                    plogw("requestSatelliteEnabled: The enable request cannot be "
+                            + "processed since disable satellite is in progress.");
+                    resultCode = SatelliteManager.SATELLITE_RESULT_DISABLE_IN_PROGRESS;
+                } else {
+                    plogd("requestSatelliteEnabled: Disable is already in progress.");
+                }
+                sendErrorAndReportSessionMetrics(resultCode, result);
                 return;
-            } else if (mSatelliteEnabledRequest.enableSatellite == false
-                    && request.enableSatellite == true) {
-                plogd("requestSatelliteEnabled enableSatellite: " + enableSatellite + " cannot be "
-                        + "processed. Disable satellite is already in progress.");
-                sendErrorAndReportSessionMetrics(
-                        SatelliteManager.SATELLITE_RESULT_ERROR, result);
+            } else {
+                // Satellite is being enabled or satellite enable attributes are being updated
+                if (enableSatellite) {
+                    if (mSatelliteEnableAttributesUpdateRequest == null) {
+                        /* Satellite is being enabled and framework receive a new enable request to
+                         * update the enable attributes.
+                         */
+                        evaluateToUpdateSatelliteEnabledAttributes(result,
+                                SatelliteManager.SATELLITE_RESULT_REQUEST_IN_PROGRESS,
+                                request, mSatelliteEnabledRequest.enableDemoMode,
+                                mSatelliteEnabledRequest.isEmergency);
+                    } else {
+                        /* The enable attributes update request is already being processed.
+                         * Framework can't handle one more request to update enable attributes.
+                         */
+                        plogd("requestSatelliteEnabled: enable attributes update request is already"
+                                + " in progress.");
+                        sendErrorAndReportSessionMetrics(
+                                SatelliteManager.SATELLITE_RESULT_REQUEST_IN_PROGRESS, result);
+                    }
+                    return;
+                } else {
+                    /* Users might want to end the satellite session while it is being enabled, or
+                     * the satellite session need to be disabled for an emergency call. Note: some
+                     * carriers want to disable satellite for prioritizing emergency calls. Thus,
+                     * we need to push the disable request to modem while enable is in progress.
+                     */
+                    if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+                        plogd("requestSatelliteEnabled: carrierRoamingNbIotNtn flag is disabled");
+                        sendErrorAndReportSessionMetrics(
+                                SatelliteManager.SATELLITE_RESULT_ENABLE_IN_PROGRESS, result);
+                        return;
+                    }
+                    mSatelliteDisabledRequest = request;
+                }
+            }
+        }
+        sendRequestAsync(CMD_SET_SATELLITE_ENABLED, request, null);
+    }
+
+    /**
+     * Validate the newly-received enable attributes against the current ones. If the new attributes
+     * are valid and different from the current ones, framework will send a request to update the
+     * enable attributes to modem. Otherwise, framework will return
+     * {@code SATELLITE_RESULT_INVALID_ARGUMENTS} to the requesting clients.
+     *
+     * @param result The callback that returns the result to the requesting client.
+     * @param resultCode The result code to send back to the requesting client when framework does
+     *                   not need to reconfigure modem.
+     * @param enableRequest The new enable request to update satellite enable attributes.
+     * @param currentDemoMode The current demo mode at framework.
+     * @param currentEmergencyMode The current emergency mode at framework.
+     */
+    private void evaluateToUpdateSatelliteEnabledAttributes(@NonNull Consumer<Integer> result,
+            @SatelliteManager.SatelliteResult int resultCode,
+            @NonNull RequestSatelliteEnabledArgument enableRequest, boolean currentDemoMode,
+            boolean currentEmergencyMode) {
+        boolean needToReconfigureModem = false;
+        if (enableRequest.enableDemoMode != currentDemoMode) {
+            if (enableRequest.enableDemoMode) {
+                ploge("Moving from real mode to demo mode is rejected");
+                sendErrorAndReportSessionMetrics(SATELLITE_RESULT_INVALID_ARGUMENTS, result);
                 return;
+            } else {
+                plogd("Moving from demo mode to real mode. Need to reconfigure"
+                        + " modem with real mode");
+                needToReconfigureModem = true;
+            }
+        } else if (enableRequest.isEmergency != currentEmergencyMode) {
+            if (enableRequest.isEmergency) {
+                plogd("Moving from non-emergency to emergency mode. Need to "
+                        + "reconfigure modem");
+                needToReconfigureModem = true;
+            } else {
+                plogd("Non-emergency requests can be served during an emergency"
+                        + " satellite session. No need to reconfigure modem.");
             }
         }
 
-        sendRequestAsync(CMD_SET_SATELLITE_ENABLED, request, null);
+        if (needToReconfigureModem) {
+            synchronized (mSatelliteEnabledRequestLock) {
+                mSatelliteEnableAttributesUpdateRequest = enableRequest;
+            }
+            sendRequestAsync(
+                    CMD_UPDATE_SATELLITE_ENABLE_ATTRIBUTES, enableRequest, null);
+        } else {
+            if (resultCode != SatelliteManager.SATELLITE_RESULT_SUCCESS) {
+                plogd("requestSatelliteEnabled enable satellite is already in progress.");
+            }
+            sendErrorAndReportSessionMetrics(resultCode, result);
+        }
+        return;
+    }
+
+    /**
+     * @return {@code true} when either enable request, disable request, or enable attributes update
+     * request is in progress, {@code false} otherwise.
+     */
+    private boolean isSatelliteEnabledRequestInProgress() {
+        synchronized (mSatelliteEnabledRequestLock) {
+            plogd("mSatelliteEnabledRequest: " + (mSatelliteEnabledRequest != null)
+                    + ", mSatelliteDisabledRequest: " + (mSatelliteDisabledRequest != null)
+                    + ", mSatelliteEnableAttributesUpdateRequest: "
+                    + (mSatelliteEnableAttributesUpdateRequest != null));
+            return (mSatelliteEnabledRequest != null || mSatelliteDisabledRequest != null
+                    || mSatelliteEnableAttributesUpdateRequest != null);
+        }
     }
 
     /**
      * Request to get whether the satellite modem is enabled.
      *
-     * @param subId The subId of the subscription to check whether satellite is enabled for.
      * @param result The result receiver that returns whether the satellite modem is enabled
      *               if the request is successful or an error code if the request failed.
      */
-    public void requestIsSatelliteEnabled(int subId, @NonNull ResultReceiver result) {
+    public void requestIsSatelliteEnabled(@NonNull ResultReceiver result) {
         int error = evaluateOemSatelliteRequestAllowed(false);
         if (error != SATELLITE_RESULT_SUCCESS) {
             result.send(error, null);
@@ -1640,14 +2114,24 @@
     }
 
     /**
+     * Get whether satellite modem is being disabled.
+     *
+     * @return {@code true} if the satellite modem is being disabled and {@code false} otherwise.
+     */
+    public boolean isSatelliteBeingDisabled() {
+        if (mSatelliteSessionController != null) {
+            return mSatelliteSessionController.isInDisablingState();
+        }
+        return false;
+    }
+
+    /**
      * Request to get whether the satellite service demo mode is enabled.
      *
-     * @param subId The subId of the subscription to check whether the satellite demo mode
-     *              is enabled for.
      * @param result The result receiver that returns whether the satellite demo mode is enabled
      *               if the request is successful or an error code if the request failed.
      */
-    public void requestIsDemoModeEnabled(int subId, @NonNull ResultReceiver result) {
+    public void requestIsDemoModeEnabled(@NonNull ResultReceiver result) {
         int error = evaluateOemSatelliteRequestAllowed(true);
         if (error != SATELLITE_RESULT_SUCCESS) {
             result.send(error, null);
@@ -1673,13 +2157,33 @@
     }
 
     /**
+     * Request to get whether the satellite enabled request is for emergency or not.
+     *
+     * @param result The result receiver that returns whether the request is for emergency
+     *               if the request is successful or an error code if the request failed.
+     */
+    public void requestIsEmergencyModeEnabled(@NonNull ResultReceiver result) {
+        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            plogd("requestIsEmergencyModeEnabled: oemEnabledSatelliteFlag is disabled");
+            result.send(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED, null);
+            return;
+        }
+
+        synchronized (mSatelliteEnabledRequestLock) {
+            Bundle bundle = new Bundle();
+            bundle.putBoolean(SatelliteManager.KEY_EMERGENCY_MODE_ENABLED,
+                    getRequestIsEmergency());
+            result.send(SATELLITE_RESULT_SUCCESS, bundle);
+        }
+    }
+
+    /**
      * Request to get whether the satellite service is supported on the device.
      *
-     * @param subId The subId of the subscription to check satellite service support for.
      * @param result The result receiver that returns whether the satellite service is supported on
      *               the device if the request is successful or an error code if the request failed.
      */
-    public void requestIsSatelliteSupported(int subId, @NonNull ResultReceiver result) {
+    public void requestIsSatelliteSupported(@NonNull ResultReceiver result) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             plogd("requestIsSatelliteSupported: oemEnabledSatelliteFlag is disabled");
             result.send(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED, null);
@@ -1690,7 +2194,7 @@
                 /* We have already successfully queried the satellite modem. */
                 Bundle bundle = new Bundle();
                 bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, mIsSatelliteSupported);
-                bundle.putInt(SATELLITE_SUBSCRIPTION_ID, subId);
+                bundle.putInt(SATELLITE_SUBSCRIPTION_ID, getHighestPrioritySubscrption());
                 result.send(SATELLITE_RESULT_SUCCESS, bundle);
                 return;
             }
@@ -1702,11 +2206,10 @@
     /**
      * Request to get the {@link SatelliteCapabilities} of the satellite service.
      *
-     * @param subId The subId of the subscription to get the satellite capabilities for.
      * @param result The result receiver that returns the {@link SatelliteCapabilities}
      *               if the request is successful or an error code if the request failed.
      */
-    public void requestSatelliteCapabilities(int subId, @NonNull ResultReceiver result) {
+    public void requestSatelliteCapabilities(@NonNull ResultReceiver result) {
         int error = evaluateOemSatelliteRequestAllowed(false);
         if (error != SATELLITE_RESULT_SUCCESS) {
             result.send(error, null);
@@ -1731,11 +2234,10 @@
      * 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 subId The subId of the subscription to start satellite transmission updates for.
      * @param errorCallback The callback to get the error code of the request.
      * @param callback The callback to notify of satellite transmission updates.
      */
-    public void startSatelliteTransmissionUpdates(int subId,
+    public void startSatelliteTransmissionUpdates(
             @NonNull IIntegerConsumer errorCallback,
             @NonNull ISatelliteTransmissionUpdateCallback callback) {
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(errorCallback::accept);
@@ -1745,7 +2247,7 @@
             return;
         }
 
-        final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+        final int validSubId = getHighestPrioritySubscrption();
         mPointingAppController.registerForSatelliteTransmissionUpdates(validSubId, callback);
         sendRequestAsync(CMD_START_SATELLITE_TRANSMISSION_UPDATES,
                 new SatelliteTransmissionUpdateArgument(result, callback, validSubId), null);
@@ -1755,23 +2257,15 @@
      * Stop receiving satellite transmission updates.
      * This can be called by the pointing UI when the user stops pointing to the satellite.
      *
-     * @param subId The subId of the subscription to stop satellite transmission updates for.
      * @param errorCallback The callback to get the error code of the request.
      * @param callback The callback that was passed to {@link #startSatelliteTransmissionUpdates(
      *                 int, IIntegerConsumer, ISatelliteTransmissionUpdateCallback)}.
      */
-    public void stopSatelliteTransmissionUpdates(int subId, @NonNull IIntegerConsumer errorCallback,
+    public void stopSatelliteTransmissionUpdates(@NonNull IIntegerConsumer errorCallback,
             @NonNull ISatelliteTransmissionUpdateCallback callback) {
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(errorCallback::accept);
-        int error = evaluateOemSatelliteRequestAllowed(true);
-        if (error != SATELLITE_RESULT_SUCCESS) {
-            result.accept(error);
-            return;
-        }
-
-        final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
         mPointingAppController.unregisterForSatelliteTransmissionUpdates(
-                validSubId, result, callback);
+                getHighestPrioritySubscrption(), result, callback);
 
         // Even if handler is null - which means there are no listeners, the modem command to stop
         // satellite transmission updates might have failed. The callers might want to retry
@@ -1783,7 +2277,6 @@
      * Register the subscription with a satellite provider.
      * This is needed to register the subscription if the provider allows dynamic registration.
      *
-     * @param subId The subId of the subscription to be provisioned.
      * @param token The token to be used as a unique identifier for provisioning with satellite
      *              gateway.
      * @param provisionData Data from the provisioning app that can be used by provisioning server
@@ -1792,7 +2285,7 @@
      * @return The signal transport used by the caller to cancel the provision request,
      *         or {@code null} if the request failed.
      */
-    @Nullable public ICancellationSignal provisionSatelliteService(int subId,
+    @Nullable public ICancellationSignal provisionSatelliteService(
             @NonNull String token, @NonNull byte[] provisionData,
             @NonNull IIntegerConsumer callback) {
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
@@ -1802,7 +2295,7 @@
             return null;
         }
 
-        final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+        final int validSubId = getHighestPrioritySubscrption();
         if (mSatelliteProvisionCallbacks.containsKey(validSubId)) {
             result.accept(SatelliteManager.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS);
             return null;
@@ -1835,11 +2328,10 @@
      * #onSatelliteProvisionStateChanged(boolean)}
      * should report as deprovisioned.
      *
-     * @param subId The subId of the subscription to be deprovisioned.
      * @param token The token of the device/subscription to be deprovisioned.
      * @param callback The callback to get the error code of the request.
      */
-    public void deprovisionSatelliteService(int subId,
+    public void deprovisionSatelliteService(
             @NonNull String token, @NonNull IIntegerConsumer callback) {
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
         int error = evaluateOemSatelliteRequestAllowed(false);
@@ -1848,38 +2340,44 @@
             return;
         }
 
-        Boolean satelliteProvisioned = isSatelliteViaOemProvisioned();
-        if (satelliteProvisioned == null) {
-            result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
-            return;
-        }
-        if (!satelliteProvisioned) {
+        if (Boolean.FALSE.equals(isSatelliteViaOemProvisioned())) {
             result.accept(SATELLITE_RESULT_SUCCESS);
             return;
         }
 
-        final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
         sendRequestAsync(CMD_DEPROVISION_SATELLITE_SERVICE,
-                new ProvisionSatelliteServiceArgument(token, null, result, validSubId),
+                new ProvisionSatelliteServiceArgument(token, null,
+                        result, getHighestPrioritySubscrption()),
                 null);
     }
 
     /**
      * Registers for the satellite provision state changed.
      *
-     * @param subId The subId of the subscription to register for provision state changed.
      * @param callback The callback to handle the satellite provision state changed event.
      *
      * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
      */
     @SatelliteManager.SatelliteResult public int registerForSatelliteProvisionStateChanged(
-            int subId, @NonNull ISatelliteProvisionStateCallback callback) {
+            @NonNull ISatelliteProvisionStateCallback callback) {
         int error = evaluateOemSatelliteRequestAllowed(false);
         if (error != SATELLITE_RESULT_SUCCESS) {
             return error;
         }
 
         mSatelliteProvisionStateChangedListeners.put(callback.asBinder(), callback);
+
+        boolean isProvisioned = Boolean.TRUE.equals(isSatelliteViaOemProvisioned());
+        try {
+            callback.onSatelliteProvisionStateChanged(isProvisioned);
+        } catch (RemoteException ex) {
+            loge("registerForSatelliteProvisionStateChanged: " + ex);
+        }
+        synchronized (mSatelliteViaOemProvisionLock) {
+            plogd("registerForSatelliteProvisionStateChanged: report current provisioned "
+                    + "state, state=" + isProvisioned);
+        }
+
         return SATELLITE_RESULT_SUCCESS;
     }
 
@@ -1887,12 +2385,11 @@
      * Unregisters for the satellite provision state changed.
      * If callback was not registered before, the request will be ignored.
      *
-     * @param subId The subId of the subscription to unregister for provision state changed.
      * @param callback The callback that was passed to
      * {@link #registerForSatelliteProvisionStateChanged(int, ISatelliteProvisionStateCallback)}.
      */
     public void unregisterForSatelliteProvisionStateChanged(
-            int subId, @NonNull ISatelliteProvisionStateCallback callback) {
+            @NonNull ISatelliteProvisionStateCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             plogd("unregisterForSatelliteProvisionStateChanged: "
                     + "oemEnabledSatelliteFlag is disabled");
@@ -1904,12 +2401,11 @@
     /**
      * Request to get whether the device is provisioned with a satellite provider.
      *
-     * @param subId The subId of the subscription to get whether the device is provisioned for.
      * @param result The result receiver that returns whether the device is provisioned with a
      *               satellite provider if the request is successful or an error code if the
      *               request failed.
      */
-    public void requestIsSatelliteProvisioned(int subId, @NonNull ResultReceiver result) {
+    public void requestIsSatelliteProvisioned(@NonNull ResultReceiver result) {
         int error = evaluateOemSatelliteRequestAllowed(false);
         if (error != SATELLITE_RESULT_SUCCESS) {
             result.send(error, null);
@@ -1932,17 +2428,20 @@
     /**
      * Registers for modem state changed from satellite modem.
      *
-     * @param subId The subId of the subscription to register for satellite modem state changed.
      * @param callback The callback to handle the satellite modem state changed event.
      *
      * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
      */
-    @SatelliteManager.SatelliteResult public int registerForSatelliteModemStateChanged(int subId,
+    @SatelliteManager.SatelliteResult public int registerForSatelliteModemStateChanged(
             @NonNull ISatelliteModemStateCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             plogd("registerForSatelliteModemStateChanged: oemEnabledSatelliteFlag is disabled");
             return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("registerForSatelliteModemStateChanged: add RegistrationFailure Listeners");
+            mSatelliteRegistrationFailureListeners.put(callback.asBinder(), callback);
+        }
         if (mSatelliteSessionController != null) {
             mSatelliteSessionController.registerForSatelliteModemStateChanged(callback);
         } else {
@@ -1957,11 +2456,10 @@
      * Unregisters for modem state changed from satellite modem.
      * If callback was not registered before, the request will be ignored.
      *
-     * @param subId The subId of the subscription to unregister for satellite modem state changed.
      * @param callback The callback that was passed to
      * {@link #registerForSatelliteModemStateChanged(int, ISatelliteModemStateCallback)}.
      */
-    public void unregisterForModemStateChanged(int subId,
+    public void unregisterForModemStateChanged(
             @NonNull ISatelliteModemStateCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             plogd("unregisterForModemStateChanged: oemEnabledSatelliteFlag is disabled");
@@ -1973,17 +2471,20 @@
             ploge("unregisterForModemStateChanged: mSatelliteSessionController"
                     + " is not initialized yet");
         }
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("unregisterForModemStateChanged: remove RegistrationFailure Listeners");
+            mSatelliteRegistrationFailureListeners.remove(callback.asBinder());
+        }
     }
 
     /**
      * Register to receive incoming datagrams over satellite.
      *
-     * @param subId The subId of the subscription to register for incoming satellite datagrams.
      * @param callback The callback to handle incoming datagrams over satellite.
      *
      * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
      */
-    @SatelliteManager.SatelliteResult public int registerForIncomingDatagram(int subId,
+    @SatelliteManager.SatelliteResult public int registerForIncomingDatagram(
             @NonNull ISatelliteDatagramCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             plogd("registerForIncomingDatagram: oemEnabledSatelliteFlag is disabled");
@@ -1993,18 +2494,18 @@
             return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
         plogd("registerForIncomingDatagram: callback=" + callback);
-        return mDatagramController.registerForSatelliteDatagram(subId, callback);
+        return mDatagramController.registerForSatelliteDatagram(
+                getHighestPrioritySubscrption(), callback);
     }
 
     /**
      * Unregister to stop receiving incoming datagrams over satellite.
      * If callback was not registered before, the request will be ignored.
      *
-     * @param subId The subId of the subscription to unregister for incoming satellite datagrams.
      * @param callback The callback that was passed to
      *                 {@link #registerForIncomingDatagram(int, ISatelliteDatagramCallback)}.
      */
-    public void unregisterForIncomingDatagram(int subId,
+    public void unregisterForIncomingDatagram(
             @NonNull ISatelliteDatagramCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             plogd("unregisterForIncomingDatagram: oemEnabledSatelliteFlag is disabled");
@@ -2014,7 +2515,8 @@
             return;
         }
         plogd("unregisterForIncomingDatagram: callback=" + callback);
-        mDatagramController.unregisterForSatelliteDatagram(subId, callback);
+        mDatagramController.unregisterForSatelliteDatagram(
+                getHighestPrioritySubscrption(), callback);
     }
 
     /**
@@ -2025,10 +2527,9 @@
      * {@link android.telephony.satellite.SatelliteDatagramCallback#onSatelliteDatagramReceived(
      * long, SatelliteDatagram, int, Consumer)}
      *
-     * @param subId The subId of the subscription used for receiving datagrams.
      * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request.
      */
-    public void pollPendingDatagrams(int subId, @NonNull IIntegerConsumer callback) {
+    public void pollPendingDatagrams(@NonNull IIntegerConsumer callback) {
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
         int error = evaluateOemSatelliteRequestAllowed(true);
         if (error != SATELLITE_RESULT_SUCCESS) {
@@ -2036,8 +2537,8 @@
             return;
         }
 
-        final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
-        mDatagramController.pollPendingSatelliteDatagrams(validSubId, result);
+        mDatagramController.pollPendingSatelliteDatagrams(
+                getHighestPrioritySubscrption(), result);
     }
 
     /**
@@ -2047,7 +2548,6 @@
      * input to this method. Datagram received here will be passed down to modem without any
      * encoding or encryption.
      *
-     * @param subId The subId of the subscription to send satellite datagrams for.
      * @param datagramType datagram type indicating whether the datagram is of type
      *                     SOS_SMS or LOCATION_SHARING.
      * @param datagram encoded gateway datagram which is encrypted by the caller.
@@ -2056,10 +2556,10 @@
      *                                 full screen mode.
      * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request.
      */
-    public void sendDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
+    public void sendDatagram(@SatelliteManager.DatagramType int datagramType,
             SatelliteDatagram datagram, boolean needFullScreenPointingUI,
             @NonNull IIntegerConsumer callback) {
-        plogd("sendSatelliteDatagram: subId: " + subId + " datagramType: " + datagramType
+        plogd("sendSatelliteDatagram: datagramType: " + datagramType
                 + " needFullScreenPointingUI: " + needFullScreenPointingUI);
 
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
@@ -2078,19 +2578,17 @@
                     mIsEmergency);
         }
 
-        final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
-        mDatagramController.sendSatelliteDatagram(validSubId, datagramType, datagram,
-                needFullScreenPointingUI, result);
+        mDatagramController.sendSatelliteDatagram(getHighestPrioritySubscrption(), datagramType,
+                datagram, needFullScreenPointingUI, result);
     }
 
     /**
      * Request to get the time after which the satellite will be visible.
      *
-     * @param subId The subId to get the time after which the satellite will be visible for.
      * @param result The result receiver that returns the time after which the satellite will
      *               be visible if the request is successful or an error code if the request failed.
      */
-    public void requestTimeForNextSatelliteVisibility(int subId, @NonNull ResultReceiver result) {
+    public void requestTimeForNextSatelliteVisibility(@NonNull ResultReceiver result) {
         int error = evaluateOemSatelliteRequestAllowed(true);
         if (error != SATELLITE_RESULT_SUCCESS) {
             result.send(error, null);
@@ -2103,10 +2601,9 @@
     /**
      * Inform whether the device is aligned with satellite for demo mode.
      *
-     * @param subId The subId of the subscription.
      * @param isAligned {@true} means device is aligned with the satellite, otherwise {@false}.
      */
-    public void setDeviceAlignedWithSatellite(@NonNull int subId, @NonNull boolean isAligned) {
+    public void setDeviceAlignedWithSatellite(@NonNull boolean isAligned) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             plogd("setDeviceAlignedWithSatellite: oemEnabledSatelliteFlag is disabled");
             return;
@@ -2114,6 +2611,12 @@
 
         DemoSimulator.getInstance().setDeviceAlignedWithSatellite(isAligned);
         mDatagramController.setDeviceAlignedWithSatellite(isAligned);
+        if (mSatelliteSessionController != null) {
+            mSatelliteSessionController.setDeviceAlignedWithSatellite(isAligned);
+        } else {
+            ploge("setDeviceAlignedWithSatellite: mSatelliteSessionController"
+                    + " is not initialized yet");
+        }
     }
 
     /**
@@ -2220,11 +2723,10 @@
     /**
      * Request to get the signal strength of the satellite connection.
      *
-     * @param subId The subId of the subscription to request for.
      * @param result Result receiver to get the error code of the request and the current signal
      * strength of the satellite connection.
      */
-    public void requestNtnSignalStrength(int subId, @NonNull ResultReceiver result) {
+    public void requestNtnSignalStrength(@NonNull ResultReceiver result) {
         if (DBG) plogd("requestNtnSignalStrength()");
 
         int error = evaluateOemSatelliteRequestAllowed(true);
@@ -2253,7 +2755,6 @@
      * is not successful, a {@link ServiceSpecificException} that contains
      * {@link SatelliteManager.SatelliteResult} will be thrown.
      *
-     * @param subId The id of the subscription to request for.
      * @param callback The callback to handle the NTN signal strength changed event. If the
      * operation is successful, {@link INtnSignalStrengthCallback#onNtnSignalStrengthChanged(
      * NtnSignalStrength)} will return an instance of {@link NtnSignalStrength} with a value of
@@ -2262,11 +2763,11 @@
      *
      * @throws ServiceSpecificException If the callback registration operation fails.
      */
-    public void registerForNtnSignalStrengthChanged(int subId,
+    public void registerForNtnSignalStrengthChanged(
             @NonNull INtnSignalStrengthCallback callback) throws RemoteException {
         if (DBG) plogd("registerForNtnSignalStrengthChanged()");
 
-        int error = evaluateOemSatelliteRequestAllowed(true);
+        int error = evaluateOemSatelliteRequestAllowed(false);
         if (error == SATELLITE_RESULT_SUCCESS) {
             mNtnSignalStrengthChangedListeners.put(callback.asBinder(), callback);
             synchronized (mNtnSignalsStrengthLock) {
@@ -2287,34 +2788,28 @@
      * Unregisters for NTN signal strength changed from satellite modem.
      * If callback was not registered before, the request will be ignored.
      *
-     * @param subId The id of the subscription to unregister for listening NTN signal strength
      * changed event.
      * @param callback The callback that was passed to
      * {@link #registerForNtnSignalStrengthChanged(int, INtnSignalStrengthCallback)}
      */
     public void unregisterForNtnSignalStrengthChanged(
-            int subId, @NonNull INtnSignalStrengthCallback callback) {
+            @NonNull INtnSignalStrengthCallback callback) {
         if (DBG) plogd("unregisterForNtnSignalStrengthChanged()");
-
-        int error = evaluateOemSatelliteRequestAllowed(true);
-        if (error == SATELLITE_RESULT_SUCCESS) {
-            mNtnSignalStrengthChangedListeners.remove(callback.asBinder());
-        }
+        mNtnSignalStrengthChangedListeners.remove(callback.asBinder());
     }
 
     /**
      * Registers for satellite capabilities change event from the satellite service.
      *
-     * @param subId The id of the subscription to request for.
      * @param callback The callback to handle the satellite capabilities changed event.
      *
      * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
      */
     @SatelliteManager.SatelliteResult public int registerForCapabilitiesChanged(
-            int subId, @NonNull ISatelliteCapabilitiesCallback callback) {
+            @NonNull ISatelliteCapabilitiesCallback callback) {
         if (DBG) plogd("registerForCapabilitiesChanged()");
 
-        int error = evaluateOemSatelliteRequestAllowed(true);
+        int error = evaluateOemSatelliteRequestAllowed(false);
         if (error != SATELLITE_RESULT_SUCCESS) return error;
 
         mSatelliteCapabilitiesChangedListeners.put(callback.asBinder(), callback);
@@ -2325,31 +2820,25 @@
      * Unregisters for satellite capabilities change event from the satellite service.
      * If callback was not registered before, the request will be ignored.
      *
-     * @param subId The id of the subscription to unregister for listening satellite capabilities
      * changed event.
      * @param callback The callback that was passed to
      * {@link #registerForCapabilitiesChanged(int, ISatelliteCapabilitiesCallback)}
      */
     public void unregisterForCapabilitiesChanged(
-            int subId, @NonNull ISatelliteCapabilitiesCallback callback) {
+            @NonNull ISatelliteCapabilitiesCallback callback) {
         if (DBG) plogd("unregisterForCapabilitiesChanged()");
-
-        int error = evaluateOemSatelliteRequestAllowed(true);
-        if (error == SATELLITE_RESULT_SUCCESS) {
-            mSatelliteCapabilitiesChangedListeners.remove(callback.asBinder());
-        }
+        mSatelliteCapabilitiesChangedListeners.remove(callback.asBinder());
     }
 
     /**
      * Registers for the satellite supported state changed.
      *
-     * @param subId The subId of the subscription to register for supported state changed.
      * @param callback The callback to handle the satellite supported state changed event.
      *
      * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
      */
     @SatelliteManager.SatelliteResult public int registerForSatelliteSupportedStateChanged(
-            int subId, @NonNull ISatelliteSupportedStateCallback callback) {
+            @NonNull ISatelliteSupportedStateCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             plogd("registerForSatelliteSupportedStateChanged: oemEnabledSatelliteFlag is disabled");
             return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
@@ -2363,12 +2852,11 @@
      * Unregisters for the satellite supported state changed.
      * If callback was not registered before, the request will be ignored.
      *
-     * @param subId The subId of the subscription to unregister for supported state changed.
      * @param callback The callback that was passed to
      * {@link #registerForSatelliteSupportedStateChanged(int, ISatelliteSupportedStateCallback)}.
      */
     public void unregisterForSatelliteSupportedStateChanged(
-            int subId, @NonNull ISatelliteSupportedStateCallback callback) {
+            @NonNull ISatelliteSupportedStateCallback callback) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             plogd("unregisterForSatelliteSupportedStateChanged: "
                     + "oemEnabledSatelliteFlag is disabled");
@@ -2381,26 +2869,28 @@
      * This API can be used by only CTS to update satellite vendor service package name.
      *
      * @param servicePackageName The package name of the satellite vendor service.
+     * @param provisioned          Whether satellite should be provisioned or not.
      * @return {@code true} if the satellite vendor service is set successfully,
      * {@code false} otherwise.
      */
-    public boolean setSatelliteServicePackageName(@Nullable String servicePackageName) {
-        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            plogd("setSatelliteServicePackageName: oemEnabledSatelliteFlag is disabled");
-            return false;
-        }
+    public boolean setSatelliteServicePackageName(@Nullable String servicePackageName,
+            String provisioned) {
         if (!isMockModemAllowed()) {
             plogd("setSatelliteServicePackageName: mock modem not allowed");
             return false;
         }
 
         // Cached states need to be cleared whenever switching satellite vendor services.
-        plogd("setSatelliteServicePackageName: Resetting cached states");
+        plogd("setSatelliteServicePackageName: Resetting cached states, provisioned="
+                + provisioned);
         synchronized (mIsSatelliteSupportedLock) {
             mIsSatelliteSupported = null;
         }
         synchronized (mSatelliteViaOemProvisionLock) {
-            mIsSatelliteViaOemProvisioned = null;
+            mIsSatelliteViaOemProvisioned = Optional.ofNullable(provisioned)
+                    .filter(s -> s.equalsIgnoreCase("true") || s.equalsIgnoreCase("false"))
+                    .map(s -> s.equalsIgnoreCase("true"))
+                    .orElse(null);
         }
         synchronized (mIsSatelliteEnabledLock) {
             mIsSatelliteEnabled = null;
@@ -2642,21 +3132,20 @@
             plogd("onSatelliteServiceConnected: oemEnabledSatelliteFlag is disabled");
             return;
         }
+
         if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
-            synchronized (mIsSatelliteSupportedLock) {
-                if (mIsSatelliteSupported == null) {
-                    ResultReceiver receiver = new ResultReceiver(this) {
-                        @Override
-                        protected void onReceiveResult(
-                                int resultCode, Bundle resultData) {
-                            plogd("onSatelliteServiceConnected.requestIsSatelliteSupported:"
-                                    + " resultCode=" + resultCode);
-                        }
-                    };
-                    requestIsSatelliteSupported(
-                            SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, receiver);
+            plogd("onSatelliteServiceConnected");
+            // Vendor service might have just come back from a crash
+            moveSatelliteToOffStateAndCleanUpResources(SATELLITE_RESULT_MODEM_ERROR);
+            ResultReceiver receiver = new ResultReceiver(this) {
+                @Override
+                protected void onReceiveResult(
+                        int resultCode, Bundle resultData) {
+                    plogd("onSatelliteServiceConnected.requestIsSatelliteSupported:"
+                            + " resultCode=" + resultCode);
                 }
-            }
+            };
+            requestIsSatelliteSupported(receiver);
         } else {
             plogd("onSatelliteServiceConnected: Satellite vendor service is not supported."
                     + " Ignored the event");
@@ -2665,27 +3154,50 @@
 
     /**
      * This function is used by {@link com.android.internal.telephony.ServiceStateTracker} to notify
-     * {@link SatelliteController} that it has received a request to power off the cellular radio
-     * modem. {@link SatelliteController} will then power off the satellite modem.
+     * {@link SatelliteController} that it has received a request to power on or off the cellular
+     * radio modem.
+     *
+     * @param powerOn {@code true} means cellular radio is about to be powered on, {@code false}
+     *                 means cellular modem is about to be powered off.
      */
-    public void onCellularRadioPowerOffRequested() {
-        logd("onCellularRadioPowerOffRequested()");
+    public void onSetCellularRadioPowerStateRequested(boolean powerOn) {
+        logd("onSetCellularRadioPowerStateRequested: powerOn=" + powerOn);
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            plogd("onCellularRadioPowerOffRequested: oemEnabledSatelliteFlag is disabled");
+            plogd("onSetCellularRadioPowerStateRequested: oemEnabledSatelliteFlag is disabled");
             return;
         }
 
         synchronized (mIsRadioOnLock) {
-            mIsRadioOn = false;
+            mRadioOffRequested = !powerOn;
         }
-        requestSatelliteEnabled(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                false /* enableSatellite */, false /* enableDemoMode */, false /* isEmergency */,
-                new IIntegerConsumer.Stub() {
-                    @Override
-                    public void accept(int result) {
-                        plogd("onRadioPowerOffRequested: requestSatelliteEnabled result=" + result);
-                    }
-                });
+        if (powerOn) {
+            stopWaitForCellularModemOffTimer();
+        } else {
+            requestSatelliteEnabled(
+                    false /* enableSatellite */, false /* enableDemoMode */,
+                    false /* isEmergency */,
+                    new IIntegerConsumer.Stub() {
+                        @Override
+                        public void accept(int result) {
+                            plogd("onSetCellularRadioPowerStateRequested: requestSatelliteEnabled"
+                                    + " result=" + result);
+                        }
+                    });
+            startWaitForCellularModemOffTimer();
+        }
+    }
+
+    /**
+     * This function is used by {@link com.android.internal.telephony.ServiceStateTracker} to notify
+     * {@link SatelliteController} that the request to power off the cellular radio modem has
+     * failed.
+     */
+    public void onPowerOffCellularRadioFailed() {
+        logd("onPowerOffCellularRadioFailed");
+        synchronized (mIsRadioOnLock) {
+            mRadioOffRequested = false;
+            stopWaitForCellularModemOffTimer();
+        }
     }
 
     /**
@@ -2839,7 +3351,7 @@
      * @return {@code Pair<true, subscription ID>} if any subscription on the device is connected to
      * satellite, {@code Pair<false, null>} otherwise.
      */
-    private Pair<Boolean, Integer> isUsingNonTerrestrialNetworkViaCarrier() {
+    Pair<Boolean, Integer> isUsingNonTerrestrialNetworkViaCarrier() {
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
             logd("isUsingNonTerrestrialNetwork: carrierEnabledSatelliteFlag is disabled");
             return new Pair<>(false, null);
@@ -2910,8 +3422,9 @@
             return true;
         }
 
-        if (getWwanIsInService(serviceState)) {
-            // Device is connected to terrestrial network which has coverage
+        if (getWwanIsInService(serviceState)
+                || serviceState.getState() == ServiceState.STATE_POWER_OFF) {
+            // Device is connected to terrestrial network which has coverage or radio is turned off
             resetCarrierRoamingSatelliteModeParams(subId);
             return false;
         }
@@ -2934,6 +3447,54 @@
     }
 
     /**
+     * @return {@code true} if should exit satellite mode unless already sent a datagram in this
+     * esos session.
+     */
+    public boolean shouldTurnOffCarrierSatelliteForEmergencyCall() {
+        synchronized (mSatellitePhoneLock) {
+            if (mSatellitePhone == null) return false;
+            return !mDatagramController.isEmergencyCommunicationEstablished()
+                    && getConfigForSubId(mSatellitePhone.getSubId()).getBoolean(
+                    KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL);
+        }
+    }
+
+    /**
+     * Return whether the satellite request is for an emergency or not.
+     *
+     * @return {@code true} if the satellite request is for an emergency and
+     *                      {@code false} otherwise.
+     */
+    public boolean getRequestIsEmergency() {
+        return mIsEmergency;
+    }
+
+    /**
+     * @return {@code true} if device is in carrier roaming nb iot ntn mode,
+     * else {@return false}
+     */
+    public boolean isInCarrierRoamingNbIotNtn() {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("isInCarrierRoamingNbIotNtn: carrier roaming nb iot ntn "
+                    + "feature flag is disabled");
+            return false;
+        }
+
+        if (!isSatelliteEnabled()) {
+            plogd("iisInCarrierRoamingNbIotNtn: satellite is disabled");
+            return false;
+        }
+
+        Phone satellitePhone = getSatellitePhone();
+        if (!isCarrierRoamingNtnEligible(satellitePhone)) {
+            plogd("isInCarrierRoamingNbIotNtn: not carrier roaming ntn eligible.");
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
      * Return capabilities of carrier roaming satellite network.
      *
      * @param phone phone object
@@ -3073,6 +3634,13 @@
             if (mSatelliteEntitlementStatusPerCarrier.get(subId, false) != entitlementEnabled) {
                 logd("update the carrier satellite enabled to " + entitlementEnabled);
                 mSatelliteEntitlementStatusPerCarrier.put(subId, entitlementEnabled);
+                mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(entitlementEnabled);
+                if (hasMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT)) {
+                    removeMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT);
+                    sendMessageDelayed(obtainMessage(
+                                    EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT),
+                            WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS);
+                }
                 try {
                     mSubscriptionManagerService.setSubscriptionProperty(subId,
                             SATELLITE_ENTITLEMENT_STATUS, entitlementEnabled ? "1" : "0");
@@ -3132,7 +3700,7 @@
          * We have not successfully checked whether the modem supports satellite service.
          * Thus, we need to retry it now.
          */
-        requestIsSatelliteSupported(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+        requestIsSatelliteSupported(
                 new ResultReceiver(this) {
                     @Override
                     protected void onReceiveResult(int resultCode, Bundle resultData) {
@@ -3156,6 +3724,7 @@
             mProvisionMetricsStats
                     .setResultCode(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE)
                     .setIsProvisionRequest(true)
+                    .setCarrierId(getSatelliteCarrierId())
                     .reportProvisionMetrics();
             mControllerMetricsStats.reportProvisionCount(
                     SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
@@ -3174,6 +3743,7 @@
         }
         mProvisionMetricsStats.setResultCode(result)
                 .setIsProvisionRequest(true)
+                .setCarrierId(getSatelliteCarrierId())
                 .reportProvisionMetrics();
         mControllerMetricsStats.reportProvisionCount(result);
     }
@@ -3203,6 +3773,7 @@
         }
         mProvisionMetricsStats.setResultCode(result)
                 .setIsProvisionRequest(false)
+                .setCarrierId(getSatelliteCarrierId())
                 .reportProvisionMetrics();
         mControllerMetricsStats.reportDeprovisionCount(result);
     }
@@ -3241,38 +3812,6 @@
     }
 
     /**
-     * Posts the specified command to be executed on the main thread. As this is a synchronous
-     * request, it waits until the request is complete and then return the result.
-     *
-     * @param command command to be executed on the main thread
-     * @param argument additional parameters required to perform of the operation
-     * @param phone phone object used to perform the operation.
-     * @return result of the operation
-     */
-    private @Nullable Object sendRequest(int command, @NonNull Object argument,
-            @Nullable Phone phone) {
-        if (Looper.myLooper() == this.getLooper()) {
-            throw new RuntimeException("This method will deadlock if called from the main thread");
-        }
-
-        SatelliteControllerHandlerRequest request = new SatelliteControllerHandlerRequest(
-                argument, phone);
-        Message msg = this.obtainMessage(command, request);
-        msg.sendToTarget();
-
-        synchronized (request) {
-            while(request.result == null) {
-                try {
-                    request.wait();
-                } catch (InterruptedException e) {
-                    // Do nothing, go back and wait until the request is complete.
-                }
-            }
-        }
-        return request.result;
-    }
-
-    /**
      * Check if satellite is provisioned for a subscription on the device.
      * @return true if satellite is provisioned on the given subscription else return false.
      */
@@ -3284,19 +3823,11 @@
                 return mOverriddenIsSatelliteViaOemProvisioned;
             }
 
-            if (mIsSatelliteViaOemProvisioned != null) {
-                return mIsSatelliteViaOemProvisioned;
+            if (mIsSatelliteViaOemProvisioned == null) {
+                mIsSatelliteViaOemProvisioned = getPersistedOemEnabledSatelliteProvisionStatus();
             }
+            return mIsSatelliteViaOemProvisioned;
         }
-
-        requestIsSatelliteProvisioned(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                new ResultReceiver(this) {
-                    @Override
-                    protected void onReceiveResult(int resultCode, Bundle resultData) {
-                        plogd("isSatelliteViaOemProvisioned: resultCode=" + resultCode);
-                    }
-                });
-        return null;
     }
 
     private void handleSatelliteEnabled(SatelliteControllerHandlerRequest request) {
@@ -3309,16 +3840,21 @@
             ploge("handleSatelliteEnabled: mSatelliteSessionController is not initialized yet");
         }
 
+        /* Framework will send back the disable result to the requesting client only after receiving
+         * both confirmation for the disable request from modem, and OFF state from modem if the
+         * modem is not in OFF state.
+         */
         if (!argument.enableSatellite && mSatelliteModemInterface.isSatelliteServiceSupported()) {
-            synchronized (mIsSatelliteEnabledLock) {
+            synchronized (mSatelliteEnabledRequestLock) {
                 mWaitingForDisableSatelliteModemResponse = true;
-                mWaitingForSatelliteModemOff = true;
+                if (!isSatelliteDisabled()) mWaitingForSatelliteModemOff = true;
             }
         }
 
         Message onCompleted = obtainMessage(EVENT_SET_SATELLITE_ENABLED_DONE, request);
-        mSatelliteModemInterface.requestSatelliteEnabled(argument.enableSatellite,
-                argument.enableDemoMode, argument.isEmergency, onCompleted);
+        mSatelliteModemInterface.requestSatelliteEnabled(
+                createModemEnableRequest(argument),
+                onCompleted);
         startWaitForSatelliteEnablingResponseTimer(argument);
         // Logs satellite session timestamps for session metrics
         if (argument.enableSatellite) {
@@ -3327,6 +3863,18 @@
         mSessionProcessingTimeStamp = System.currentTimeMillis();
     }
 
+    /** Get the request attributes that modem needs to enable/disable satellite */
+    private SatelliteModemEnableRequestAttributes createModemEnableRequest(
+            @NonNull RequestSatelliteEnabledArgument arg) {
+        int subId = getHighestPrioritySubscrption();
+        SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(subId);
+        String iccid = subInfo != null ? subInfo.getIccId() : "";
+        String apn = getConfigForSubId(subId).getString(KEY_SATELLITE_NIDD_APN_NAME_STRING, "");
+        return new SatelliteModemEnableRequestAttributes(
+                arg.enableSatellite, arg.enableDemoMode, arg.isEmergency,
+                new SatelliteSubscriptionInfo(iccid, apn));
+    }
+
     private void handleRequestSatelliteAttachRestrictionForCarrierCmd(
             SatelliteControllerHandlerRequest request) {
         RequestHandleSatelliteAttachRestrictionForCarrierArgument argument =
@@ -3342,44 +3890,44 @@
         evaluateEnablingSatelliteForCarrier(argument.subId, argument.reason, argument.callback);
     }
 
-    private void updateSatelliteSupportedStateWhenSatelliteServiceConnected(boolean supported) {
+    private void updateSatelliteSupportedState(boolean supported) {
         synchronized (mIsSatelliteSupportedLock) {
             mIsSatelliteSupported = supported;
         }
         mSatelliteSessionController = SatelliteSessionController.make(
                 mContext, getLooper(), mFeatureFlags, supported);
-        plogd("create a new SatelliteSessionController due to isSatelliteSupported state has "
-                + "changed to " + supported);
+        plogd("updateSatelliteSupportedState: create a new SatelliteSessionController because "
+                + "satellite supported state has changed to " + supported);
 
         if (supported) {
-            registerForSatelliteProvisionStateChanged();
             registerForPendingDatagramCount();
             registerForSatelliteModemStateChanged();
             registerForNtnSignalStrengthChanged();
             registerForCapabilitiesChanged();
+            registerForSatelliteRegistrationFailure();
 
-            requestIsSatelliteProvisioned(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+            requestIsSatelliteProvisioned(
                     new ResultReceiver(this) {
                         @Override
                         protected void onReceiveResult(int resultCode, Bundle resultData) {
-                            plogd("requestIsSatelliteProvisioned: resultCode=" + resultCode
-                                    + ", resultData=" + resultData);
-                            requestSatelliteEnabled(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                                    false, false, false,
+                            plogd("updateSatelliteSupportedState.requestIsSatelliteProvisioned: "
+                                    + "resultCode=" + resultCode + ", resultData=" + resultData);
+                            requestSatelliteEnabled(false, false, false,
                                     new IIntegerConsumer.Stub() {
                                         @Override
                                         public void accept(int result) {
-                                            plogd("requestSatelliteEnabled: result=" + result);
+                                            plogd("updateSatelliteSupportedState."
+                                                    + "requestSatelliteEnabled: result=" + result);
                                         }
                                     });
                         }
                     });
-            requestSatelliteCapabilities(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+            requestSatelliteCapabilities(
                     new ResultReceiver(this) {
                         @Override
                         protected void onReceiveResult(int resultCode, Bundle resultData) {
-                            plogd("requestSatelliteCapabilities: resultCode=" + resultCode
-                                    + ", resultData=" + resultData);
+                            plogd("updateSatelliteSupportedState.requestSatelliteCapabilities: "
+                                    + "resultCode=" + resultCode + ", resultData=" + resultData);
                         }
                     });
         }
@@ -3401,16 +3949,6 @@
         }
     }
 
-    private void registerForSatelliteProvisionStateChanged() {
-        if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
-            if (!mRegisteredForProvisionStateChangedWithSatelliteService.get()) {
-                mSatelliteModemInterface.registerForSatelliteProvisionStateChanged(
-                        this, EVENT_SATELLITE_PROVISION_STATE_CHANGED, null);
-                mRegisteredForProvisionStateChangedWithSatelliteService.set(true);
-            }
-        }
-    }
-
     private void registerForPendingDatagramCount() {
         if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
             if (!mRegisteredForPendingDatagramCountWithSatelliteService.get()) {
@@ -3471,6 +4009,16 @@
         }
     }
 
+    private void registerForSatelliteRegistrationFailure() {
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+            if (!mRegisteredForSatelliteRegistrationFailure.get()) {
+                mSatelliteModemInterface.registerForSatelliteRegistrationFailure(this,
+                        EVENT_SATELLITE_REGISTRATION_FAILURE, null);
+                mRegisteredForSatelliteRegistrationFailure.set(true);
+            }
+        }
+    }
+
     private void handleEventSatelliteProvisionStateChanged(boolean provisioned) {
         plogd("handleSatelliteProvisionStateChangedEvent: provisioned=" + provisioned);
 
@@ -3493,42 +4041,110 @@
         });
     }
 
+    private void handleEventSatelliteSubscriptionProvisionStateChanged(
+            List<SatelliteSubscriberInfo> newList, boolean provisioned) {
+        logd("handleEventSatelliteSubscriptionProvisionStateChanged: newList=" + newList
+                + " , provisioned=" + provisioned);
+        boolean provisionChanged = false;
+        synchronized (mSatelliteTokenProvisionedLock) {
+            for (SatelliteSubscriberInfo subscriberInfo : newList) {
+                if (mProvisionedSubscriberId.getOrDefault(subscriberInfo.getSubscriberId(), false)
+                        == provisioned) {
+                    continue;
+                }
+                provisionChanged = true;
+                mProvisionedSubscriberId.put(subscriberInfo.getSubscriberId(), provisioned);
+                int subId = mSubscriberIdPerSub.getOrDefault(subscriberInfo.getSubscriberId(),
+                        SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+                try {
+                    mSubscriptionManagerService.setIsSatelliteProvisionedForNonIpDatagram(subId,
+                            provisioned);
+                    plogd("handleEventSatelliteSubscriptionProvisionStateChanged: set Provision "
+                            + "state to db subId=" + subId);
+                } catch (IllegalArgumentException | SecurityException ex) {
+                    ploge("setIsSatelliteProvisionedForNonIpDatagram: subId=" + subId + ", ex="
+                            + ex);
+                }
+            }
+        }
+        if (!provisionChanged) {
+            logd("handleEventSatelliteSubscriptionProvisionStateChanged: provision state nothing "
+                    + "changed.");
+            return;
+        }
+        List<SatelliteSubscriberProvisionStatus> informList =
+                getPrioritizedSatelliteSubscriberProvisionStatusList();
+        plogd("handleEventSatelliteSubscriptionProvisionStateChanged: " + informList);
+        notifySatelliteSubscriptionProvisionStateChanged(informList);
+        // Report updated provisioned status
+        synchronized (mSatelliteTokenProvisionedLock) {
+            boolean isProvisioned = !mProvisionedSubscriberId.isEmpty()
+                    && mProvisionedSubscriberId.containsValue(Boolean.TRUE);
+            mControllerMetricsStats.setIsProvisioned(isProvisioned);
+        }
+        handleStateChangedForCarrierRoamingNtnEligibility();
+    }
+
+    private void notifySatelliteSubscriptionProvisionStateChanged(
+            @NonNull List<SatelliteSubscriberProvisionStatus> list) {
+        List<ISatelliteProvisionStateCallback> deadCallersList = new ArrayList<>();
+        mSatelliteProvisionStateChangedListeners.values().forEach(listener -> {
+            try {
+                listener.onSatelliteSubscriptionProvisionStateChanged(list);
+            } catch (RemoteException e) {
+                plogd("notifySatelliteSubscriptionProvisionStateChanged: " + e);
+                deadCallersList.add(listener);
+            }
+        });
+        deadCallersList.forEach(listener -> {
+            mSatelliteProvisionStateChangedListeners.remove(listener.asBinder());
+        });
+    }
+
     private void handleEventSatelliteModemStateChanged(
             @SatelliteManager.SatelliteModemState int state) {
         plogd("handleEventSatelliteModemStateChanged: state=" + state);
         if (state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
                 || state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
-            synchronized (mIsSatelliteEnabledLock) {
-                if ((state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE)
-                        || ((mIsSatelliteEnabled == null || isSatelliteEnabled())
-                        && !mWaitingForDisableSatelliteModemResponse)) {
-                    int error = (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF)
-                            ? SATELLITE_RESULT_SUCCESS
-                            : SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE;
-                    Consumer<Integer> callback = null;
-                    synchronized (mSatelliteEnabledRequestLock) {
-                        if (mSatelliteEnabledRequest != null) {
-                            callback = mSatelliteEnabledRequest.callback;
-                        }
-                    }
-                    moveSatelliteToOffStateAndCleanUpResources(error, callback);
+            synchronized (mSatelliteEnabledRequestLock) {
+                if (!mWaitingForDisableSatelliteModemResponse) {
+                    moveSatelliteToOffStateAndCleanUpResources(
+                            SATELLITE_RESULT_SUCCESS);
                 } else {
-                    plogd("Either waiting for the response of disabling satellite modem or the"
-                            + " event should be ignored because isSatelliteEnabled="
-                            + isSatelliteEnabled()
-                            + ", mIsSatelliteEnabled=" + mIsSatelliteEnabled);
+                    notifyModemStateChangedToSessionController(
+                            SatelliteManager.SATELLITE_MODEM_STATE_OFF);
                 }
                 mWaitingForSatelliteModemOff = false;
             }
         } else {
-            if (mSatelliteSessionController != null) {
-                mSatelliteSessionController.onSatelliteModemStateChanged(state);
+            if (isSatelliteEnabled() || isSatelliteBeingEnabled() || isSatelliteBeingDisabled()) {
+                notifyModemStateChangedToSessionController(state);
             } else {
-                ploge("handleEventSatelliteModemStateChanged: mSatelliteSessionController is null");
+                // Telephony framework and modem are out of sync. We need to disable modem
+                synchronized (mSatelliteEnabledRequestLock) {
+                    plogw("Satellite modem is in a bad state. Disabling satellite modem now ...");
+                    Consumer<Integer> result = integer -> plogd(
+                            "handleEventSatelliteModemStateChanged: disabling satellite result="
+                            + integer);
+                    mSatelliteDisabledRequest = new RequestSatelliteEnabledArgument(
+                            false /* enableSatellite */, false /* enableDemoMode */,
+                            false /* isEmergency */, result);
+                    sendRequestAsync(CMD_SET_SATELLITE_ENABLED, mSatelliteDisabledRequest, null);
+                }
             }
         }
     }
 
+    private void notifyModemStateChangedToSessionController(
+            @SatelliteManager.SatelliteModemState int state) {
+        if (mSatelliteSessionController != null) {
+            mSatelliteSessionController.onSatelliteModemStateChanged(state);
+        } else {
+            ploge("notifyModemStateChangedToSessionController: mSatelliteSessionController is "
+                    + "null");
+        }
+    }
+
     private void handleEventNtnSignalStrengthChanged(NtnSignalStrength ntnSignalStrength) {
         logd("handleEventNtnSignalStrengthChanged: ntnSignalStrength=" + ntnSignalStrength);
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
@@ -3592,7 +4208,7 @@
                 return;
             }
 
-            updateSatelliteSupportedStateWhenSatelliteServiceConnected(supported);
+            updateSatelliteSupportedState(supported);
 
             /* In case satellite has been reported as not support from modem, but satellite is
                enabled, request disable satellite. */
@@ -3600,15 +4216,13 @@
                 if (!supported && mIsSatelliteEnabled != null && mIsSatelliteEnabled) {
                     plogd("Invoke requestSatelliteEnabled(), supported=false, "
                             + "mIsSatelliteEnabled=true");
-                    requestSatelliteEnabled(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                            false /* enableSatellite */, false /* enableDemoMode */,
+                    requestSatelliteEnabled(false /* enableSatellite */, false /* enableDemoMode */,
                             false /* isEmergency */,
                             new IIntegerConsumer.Stub() {
                                 @Override
                                 public void accept(int result) {
                                     plogd("handleSatelliteSupportedStateChangedEvent: request "
-                                            + "satellite disable, result="
-                                            + result);
+                                            + "satellite disable, result=" + result);
                                 }
                             });
 
@@ -3762,8 +4376,6 @@
             if (areAllRadiosDisabled() && (mSatelliteEnabledRequest != null)
                     && mWaitingForRadioDisabled) {
                 plogd("Sending success to callback that sent enable satellite request");
-                setDemoModeEnabled(mSatelliteEnabledRequest.enableDemoMode);
-                mIsEmergency = mSatelliteEnabledRequest.isEmergency;
                 synchronized (mIsSatelliteEnabledLock) {
                     mIsSatelliteEnabled = mSatelliteEnabledRequest.enableSatellite;
                 }
@@ -3771,8 +4383,21 @@
                 updateSatelliteEnabledState(
                         mSatelliteEnabledRequest.enableSatellite,
                         "EVENT_SET_SATELLITE_ENABLED_DONE");
+                setEmergencyMode(mSatelliteEnabledRequest.isEmergency);
+                if (mSatelliteEnabledRequest.enableSatellite
+                        && !mSatelliteEnabledRequest.isEmergency) {
+                    plogd("Starting pointingUI needFullscreenPointingUI=" + true
+                            + "mIsDemoModeEnabled=" + mIsDemoModeEnabled + ", isEmergency="
+                            + mSatelliteEnabledRequest.isEmergency);
+                    mPointingAppController.startPointingUI(true, mIsDemoModeEnabled, false);
+                }
                 mSatelliteEnabledRequest = null;
                 mWaitingForRadioDisabled = false;
+
+                if (mSatelliteEnableAttributesUpdateRequest != null) {
+                    sendRequestAsync(CMD_UPDATE_SATELLITE_ENABLE_ATTRIBUTES,
+                            mSatelliteEnableAttributesUpdateRequest, null);
+                }
             }
         }
     }
@@ -3785,18 +4410,37 @@
         }
     }
 
-    private void moveSatelliteToOffStateAndCleanUpResources(
-            @SatelliteManager.SatelliteResult int error, @Nullable Consumer<Integer> callback) {
+    private void resetSatelliteDisabledRequest() {
+        plogd("resetSatelliteDisabledRequest");
+        synchronized (mSatelliteEnabledRequestLock) {
+            mSatelliteDisabledRequest = null;
+            mWaitingForDisableSatelliteModemResponse = false;
+            mWaitingForSatelliteModemOff = false;
+        }
+    }
+
+    /**
+     * Move to OFF state and clean up resources.
+     *
+     * @param resultCode The result code will be returned to requesting clients.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void moveSatelliteToOffStateAndCleanUpResources(
+            @SatelliteManager.SatelliteResult int resultCode) {
         plogd("moveSatelliteToOffStateAndCleanUpResources");
         synchronized (mIsSatelliteEnabledLock) {
-            resetSatelliteEnabledRequest();
             setDemoModeEnabled(false);
             handlePersistentLoggingOnSessionEnd(mIsEmergency);
-            mIsEmergency = false;
+            setEmergencyMode(false);
             mIsSatelliteEnabled = false;
             setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_FALSE);
             setSettingsKeyToAllowDeviceRotation(SATELLITE_MODE_ENABLED_FALSE);
-            if (callback != null) callback.accept(error);
+            abortSatelliteDisableRequest(resultCode);
+            abortSatelliteEnableRequest(resultCode);
+            abortSatelliteEnableAttributesUpdateRequest(resultCode);
+            resetSatelliteEnabledRequest();
+            resetSatelliteDisabledRequest();
+            // TODO (b/361139260): Stop timer to wait for other radios off
             updateSatelliteEnabledState(
                     false, "moveSatelliteToOffStateAndCleanUpResources");
         }
@@ -3808,6 +4452,18 @@
         plogd("setDemoModeEnabled: mIsDemoModeEnabled=" + mIsDemoModeEnabled);
     }
 
+    private void setEmergencyMode(boolean isEmergency) {
+        plogd("setEmergencyMode: mIsEmergency=" + mIsEmergency + ", isEmergency=" + isEmergency);
+        if (mIsEmergency != isEmergency) {
+            mIsEmergency = isEmergency;
+            if (mSatelliteSessionController != null) {
+                mSatelliteSessionController.onEmergencyModeChanged(mIsEmergency);
+            } else {
+                plogw("setEmergencyMode: mSatelliteSessionController is null");
+            }
+        }
+    }
+
     private boolean isMockModemAllowed() {
         return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false));
     }
@@ -3963,14 +4619,28 @@
     }
 
     @NonNull private PersistableBundle getConfigForSubId(int subId) {
-        PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId,
-                KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
-                KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
-                KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT,
-                KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL,
-                KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY,
-                KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL,
-                KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT);
+        PersistableBundle config = null;
+        if (mCarrierConfigManager != null) {
+            config = mCarrierConfigManager.getConfigForSubId(subId,
+                    KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
+                    KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                    KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL,
+                    KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT,
+                    KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL,
+                    KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY,
+                    KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL,
+                    KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT,
+                    KEY_SATELLITE_ESOS_SUPPORTED_BOOL,
+                    KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL,
+                    KEY_SATELLITE_NIDD_APN_NAME_STRING,
+                    KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT,
+                    KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT,
+                    KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT,
+                    KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT,
+                    KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
+                    KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT
+            );
+        }
         if (config == null || config.isEmpty()) {
             config = CarrierConfigManager.getDefaultConfig();
         }
@@ -3987,10 +4657,21 @@
         }
 
         updateCarrierConfig(subId);
+        updateSatelliteESOSSupported(subId);
+        updateSatelliteProvisionedStatePerSubscriberId();
         updateEntitlementPlmnListPerCarrier(subId);
         updateSupportedSatelliteServicesForActiveSubscriptions();
         processNewCarrierConfigData(subId);
         resetCarrierRoamingSatelliteModeParams(subId);
+        handleStateChangedForCarrierRoamingNtnEligibility();
+        sendMessageDelayed(obtainMessage(CMD_EVALUATE_ESOS_PROFILES_PRIORITIZATION),
+                TimeUnit.MINUTES.toMillis(1));
+    }
+
+    // imsi, msisdn, default sms subId change
+    private void handleSubscriptionsChanged() {
+        sendMessageDelayed(obtainMessage(CMD_EVALUATE_ESOS_PROFILES_PRIORITIZATION),
+                TimeUnit.MINUTES.toMillis(1));
     }
 
     private void processNewCarrierConfigData(int subId) {
@@ -4050,6 +4731,54 @@
         }
     }
 
+    /**
+     * Update the value of SimInfo.COLUMN_SATELLITE_ESOS_SUPPORTED stored in the database based
+     * on the value in the carrier config.
+     */
+    private void updateSatelliteESOSSupported(int subId) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            return;
+        }
+
+        boolean isSatelliteESosSupportedFromDB =
+                mSubscriptionManagerService.getSatelliteESOSSupported(subId);
+        boolean isSatelliteESosSupportedFromCarrierConfig = getConfigForSubId(subId).getBoolean(
+                KEY_SATELLITE_ESOS_SUPPORTED_BOOL, false);
+        if (isSatelliteESosSupportedFromDB != isSatelliteESosSupportedFromCarrierConfig) {
+            mSubscriptionManagerService.setSatelliteESOSSupported(subId,
+                    isSatelliteESosSupportedFromCarrierConfig);
+            logd("updateSatelliteESOSSupported: " + isSatelliteESosSupportedFromCarrierConfig);
+        }
+    }
+
+    /** If the provision state per subscriberId for the cached is not exist, check the database for
+     * the corresponding value and use it. */
+    private void updateSatelliteProvisionedStatePerSubscriberId() {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            return;
+        }
+
+        List<SubscriptionInfo> allSubInfos = mSubscriptionManagerService.getAllSubInfoList(
+                mContext.getOpPackageName(), mContext.getAttributionTag());
+        for (SubscriptionInfo info : allSubInfos) {
+            int subId = info.getSubscriptionId();
+            Pair<String, Integer> subscriberIdPair = getSubscriberIdAndType(
+                    mSubscriptionManagerService.getSubscriptionInfo(subId));
+            String subscriberId = subscriberIdPair.first;
+            synchronized (mSatelliteTokenProvisionedLock) {
+                if (mProvisionedSubscriberId.get(subscriberId) == null) {
+                    boolean Provisioned = mSubscriptionManagerService
+                            .isSatelliteProvisionedForNonIpDatagram(subId);
+                    if (Provisioned) {
+                        mProvisionedSubscriberId.put(subscriberId, true);
+                        logd("updateSatelliteProvisionStatePerSubscriberId: " + subscriberId
+                                + " set true");
+                    }
+                }
+            }
+        }
+    }
+
     @NonNull
     private String[] readStringArrayFromOverlayConfig(@ArrayRes int id) {
         String[] strArray = null;
@@ -4070,6 +4799,35 @@
     }
 
     /**
+     * Return whether the device support P2P SMS mode from carrier config.
+     *
+     * @param subId Associated subscription ID
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean isSatelliteRoamingP2pSmSSupported(int subId) {
+        return getConfigForSubId(subId).getBoolean(KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL);
+    }
+
+    /**
+     * Return whether the device support ESOS mode from carrier config.
+     *
+     * @param subId Associated subscription ID
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean isSatelliteEsosSupported(int subId) {
+        return getConfigForSubId(subId).getBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL);
+    }
+
+    private int getCarrierRoamingNtnConnectType(int subId) {
+        return getConfigForSubId(subId).getInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT);
+    }
+
+    protected int getCarrierRoamingNtnEmergencyCallToSatelliteHandoverType(int subId) {
+        return getConfigForSubId(subId).getInt(
+                KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT);
+    }
+
+    /**
      * Check if satellite attach is enabled by user for the carrier associated with the
      * {@code subId}.
      *
@@ -4176,6 +4934,13 @@
                 }
                 boolean result = entitlementStatus.equals("1");
                 mSatelliteEntitlementStatusPerCarrier.put(subId, result);
+                mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(result);
+                if (hasMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT)) {
+                    removeMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT);
+                    sendMessageDelayed(obtainMessage(
+                                    EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT),
+                            WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS);
+                }
             }
 
             if (!mSatelliteEntitlementStatusPerCarrier.get(subId, false)) {
@@ -4320,6 +5085,7 @@
                 return SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
             }
             if (!satelliteProvisioned) {
+                plogd("evaluateOemSatelliteRequestAllowed: satellite service is not provisioned");
                 return SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED;
             }
         }
@@ -4348,6 +5114,7 @@
         mSessionMetricsStats.setInitializationResult(error)
                 .setSatelliteTechnology(getSupportedNtnRadioTechnology())
                 .setIsDemoMode(mIsDemoModeEnabled)
+                .setCarrierId(getSatelliteCarrierId())
                 .reportSessionMetrics();
         mSessionStartTimeStamp = 0;
         mSessionProcessingTimeStamp = 0;
@@ -4363,8 +5130,8 @@
     }
 
     private void handleEventServiceStateChanged() {
+        handleStateChangedForCarrierRoamingNtnEligibility();
         handleServiceStateForSatelliteConnectionViaCarrier();
-        determineSystemNotification();
     }
 
     private void handleServiceStateForSatelliteConnectionViaCarrier() {
@@ -4400,7 +5167,8 @@
                     }
                 } else {
                     Boolean connected = mWasSatelliteConnectedViaCarrier.get(subId);
-                    if (getWwanIsInService(serviceState)) {
+                    if (getWwanIsInService(serviceState)
+                            || serviceState.getState() == ServiceState.STATE_POWER_OFF) {
                         resetCarrierRoamingSatelliteModeParams(subId);
                     } else if (connected != null && connected) {
                         // The device just got disconnected from a satellite network
@@ -4424,6 +5192,7 @@
                 updateLastNotifiedNtnModeAndNotify(phone);
             }
         }
+        determineAutoConnectSystemNotification();
     }
 
     private void updateLastNotifiedNtnModeAndNotify(@Nullable Phone phone) {
@@ -4467,37 +5236,164 @@
         }
     }
 
+    private void handleStateChangedForCarrierRoamingNtnEligibility() {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("handleStateChangedForCarrierRoamingNtnEligibility: "
+                    + "carrierRoamingNbIotNtn flag is disabled");
+            return;
+        }
+
+        synchronized (mSatellitePhoneLock) {
+            boolean eligible = isCarrierRoamingNtnEligible(mSatellitePhone);
+            plogd("handleStateChangedForCarrierRoamingNtnEligibility: "
+                    + "isCarrierRoamingNtnEligible=" + eligible);
+
+            if (eligible) {
+                if (shouldStartNtnEligibilityHysteresisTimer(eligible)) {
+                    startNtnEligibilityHysteresisTimer();
+                }
+            } else {
+                mNtnEligibilityHysteresisTimedOut = false;
+                stopNtnEligibilityHysteresisTimer();
+                updateLastNotifiedNtnEligibilityAndNotify(false);
+            }
+        }
+    }
+
+    private boolean shouldStartNtnEligibilityHysteresisTimer(boolean eligible) {
+        if (!eligible) {
+            return false;
+        }
+
+        if (hasMessages(EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT)) {
+            plogd("shouldStartNtnEligibilityHysteresisTimer: Timer is already running.");
+            return false;
+        }
+
+        synchronized (mSatellitePhoneLock) {
+            if (mLastNotifiedNtnEligibility != null && mLastNotifiedNtnEligibility) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private void startNtnEligibilityHysteresisTimer() {
+        synchronized (mSatellitePhoneLock) {
+            if (mSatellitePhone == null) {
+                ploge("startNtnEligibilityHysteresisTimer: mSatellitePhone is null.");
+                return;
+            }
+
+            int subId = getHighestPrioritySubscrption();
+            long timeout = getCarrierSupportedSatelliteNotificationHysteresisTimeMillis(subId);
+            mNtnEligibilityHysteresisTimedOut = false;
+            plogd("startNtnEligibilityHysteresisTimer: sendMessageDelayed subId=" + subId
+                    + ", phoneId=" + mSatellitePhone.getPhoneId() + ", timeout=" + timeout);
+            sendMessageDelayed(obtainMessage(EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT),
+                    timeout);
+        }
+    }
+
+    private void stopNtnEligibilityHysteresisTimer() {
+        if (hasMessages(EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT)) {
+            removeMessages(EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT);
+        }
+    }
+
+    private void updateLastNotifiedNtnEligibilityAndNotify(boolean currentNtnEligibility) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("notifyNtnEligibility: carrierRoamingNbIotNtn flag is disabled");
+            return;
+        }
+
+        synchronized (mSatellitePhoneLock) {
+            if (mSatellitePhone == null) {
+                ploge("notifyNtnEligibility: mSatellitePhone is null");
+                return;
+            }
+
+            plogd("notifyNtnEligibility: phoneId=" + mSatellitePhone.getPhoneId()
+                    + " currentNtnEligibility=" + currentNtnEligibility);
+            if (mLastNotifiedNtnEligibility == null
+                    || mLastNotifiedNtnEligibility != currentNtnEligibility) {
+                mLastNotifiedNtnEligibility = currentNtnEligibility;
+                mSatellitePhone.notifyCarrierRoamingNtnEligibleStateChanged(currentNtnEligibility);
+                updateSatelliteSystemNotification(getHighestPrioritySubscrption(),
+                        CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL,
+                        currentNtnEligibility);
+            }
+        }
+    }
+
     private long getSatelliteConnectionHysteresisTimeMillis(int subId) {
         PersistableBundle config = getPersistableBundle(subId);
         return (config.getInt(
                 KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT) * 1000L);
     }
 
+    private long getCarrierSupportedSatelliteNotificationHysteresisTimeMillis(int subId) {
+        PersistableBundle config = getPersistableBundle(subId);
+        return (config.getInt(
+                KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT) * 1000L);
+    }
+
     private void persistOemEnabledSatelliteProvisionStatus(boolean isProvisioned) {
         synchronized (mSatelliteViaOemProvisionLock) {
             plogd("persistOemEnabledSatelliteProvisionStatus: isProvisioned=" + isProvisioned);
-
-            if (!loadSatelliteSharedPreferences()) return;
-
-            if (mSharedPreferences == null) {
-                ploge("persistOemEnabledSatelliteProvisionStatus: mSharedPreferences is null");
+            if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+                int subId = SatelliteServiceUtils.getNtnOnlySubscriptionId(mContext);
+                if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                    try {
+                        mSubscriptionManagerService.setIsSatelliteProvisionedForNonIpDatagram(subId,
+                                isProvisioned);
+                        plogd("persistOemEnabledSatelliteProvisionStatus: subId=" + subId);
+                    } catch (IllegalArgumentException | SecurityException ex) {
+                        ploge("setIsSatelliteProvisionedForNonIpDatagram: subId=" + subId + ", ex="
+                                + ex);
+                    }
+                } else {
+                    plogd("persistOemEnabledSatelliteProvisionStatus: INVALID_SUBSCRIPTION_ID");
+                }
             } else {
-                mSharedPreferences.edit().putBoolean(
-                        OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY, isProvisioned).apply();
+                if (!loadSatelliteSharedPreferences()) return;
+
+                if (mSharedPreferences == null) {
+                    ploge("persistOemEnabledSatelliteProvisionStatus: mSharedPreferences is null");
+                } else {
+                    mSharedPreferences.edit().putBoolean(
+                            OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY, isProvisioned).apply();
+                }
             }
         }
     }
 
-    private boolean getPersistedOemEnabledSatelliteProvisionStatus() {
+    @Nullable
+    private Boolean getPersistedOemEnabledSatelliteProvisionStatus() {
+        plogd("getPersistedOemEnabledSatelliteProvisionStatus:");
         synchronized (mSatelliteViaOemProvisionLock) {
-            if (!loadSatelliteSharedPreferences()) return false;
-
-            if (mSharedPreferences == null) {
-                ploge("getPersistedOemEnabledSatelliteProvisionStatus: mSharedPreferences is null");
-                return false;
+            if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+                int subId = SatelliteServiceUtils.getNtnOnlySubscriptionId(mContext);
+                if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                    return mSubscriptionManagerService.isSatelliteProvisionedForNonIpDatagram(
+                            subId);
+                } else {
+                    plogd("getPersistedOemEnabledSatelliteProvisionStatus: "
+                            + "subId=INVALID_SUBSCRIPTION_ID, return null");
+                    return null;
+                }
             } else {
-                return mSharedPreferences.getBoolean(
-                        OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY, false);
+                if (!loadSatelliteSharedPreferences()) return false;
+
+                if (mSharedPreferences == null) {
+                    ploge("getPersistedOemEnabledSatelliteProvisionStatus: mSharedPreferences is "
+                            + "null");
+                    return false;
+                } else {
+                    return mSharedPreferences.getBoolean(
+                            OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY, false);
+                }
             }
         }
     }
@@ -4518,31 +5414,13 @@
     }
 
     private void handleIsSatelliteProvisionedDoneEvent(@NonNull AsyncResult ar) {
+        logd("handleIsSatelliteProvisionedDoneEvent:");
         SatelliteControllerHandlerRequest request = (SatelliteControllerHandlerRequest) ar.userObj;
-        int error = SatelliteServiceUtils.getSatelliteError(
-                ar, "handleIsSatelliteProvisionedDoneEvent");
-        boolean isSatelliteProvisionedInModem = false;
-        if (error == SATELLITE_RESULT_SUCCESS) {
-            if (ar.result == null) {
-                ploge("handleIsSatelliteProvisionedDoneEvent: result is null");
-                error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
-            } else {
-                isSatelliteProvisionedInModem = ((int[]) ar.result)[0] == 1;
-            }
-        } else if (error == SATELLITE_RESULT_REQUEST_NOT_SUPPORTED) {
-            plogd("handleIsSatelliteProvisionedDoneEvent: Modem does not support this request");
-            isSatelliteProvisionedInModem = true;
-        }
-        boolean isSatelliteViaOemProvisioned =
-                isSatelliteProvisionedInModem && getPersistedOemEnabledSatelliteProvisionStatus();
-        plogd("isSatelliteProvisionedInModem=" + isSatelliteProvisionedInModem
-                + ", isSatelliteViaOemProvisioned=" + isSatelliteViaOemProvisioned);
+
         Bundle bundle = new Bundle();
-        bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED, isSatelliteViaOemProvisioned);
-        synchronized (mSatelliteViaOemProvisionLock) {
-            mIsSatelliteViaOemProvisioned = isSatelliteViaOemProvisioned;
-        }
-        ((ResultReceiver) request.argument).send(error, bundle);
+        bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED,
+                Boolean.TRUE.equals(isSatelliteViaOemProvisioned()));
+        ((ResultReceiver) request.argument).send(SATELLITE_RESULT_SUCCESS, bundle);
     }
 
     private long getWaitForSatelliteEnablingResponseTimeoutMillis() {
@@ -4550,6 +5428,32 @@
                 R.integer.config_wait_for_satellite_enabling_response_timeout_millis);
     }
 
+    private long getWaitForCellularModemOffTimeoutMillis() {
+        return mContext.getResources().getInteger(
+                R.integer.config_satellite_wait_for_cellular_modem_off_timeout_millis);
+    }
+
+    private void startWaitForCellularModemOffTimer() {
+        synchronized (mIsRadioOnLock) {
+            if (hasMessages(EVENT_WAIT_FOR_CELLULAR_MODEM_OFF_TIMED_OUT)) {
+                plogd("startWaitForCellularModemOffTimer: the timer was already started");
+                return;
+            }
+            long timeoutMillis = getWaitForCellularModemOffTimeoutMillis();
+            plogd("Start timer to wait for cellular modem OFF state, timeoutMillis="
+                    + timeoutMillis);
+            sendMessageDelayed(obtainMessage(EVENT_WAIT_FOR_CELLULAR_MODEM_OFF_TIMED_OUT),
+                    timeoutMillis);
+        }
+    }
+
+    private void stopWaitForCellularModemOffTimer() {
+        synchronized (mSatelliteEnabledRequestLock) {
+            plogd("Stop timer to wait for cellular modem OFF state");
+            removeMessages(EVENT_WAIT_FOR_CELLULAR_MODEM_OFF_TIMED_OUT);
+        }
+    }
+
     private void startWaitForSatelliteEnablingResponseTimer(
             @NonNull RequestSatelliteEnabledArgument argument) {
         synchronized (mSatelliteEnabledRequestLock) {
@@ -4586,78 +5490,96 @@
         }
     }
 
+    private void startWaitForUpdateSatelliteEnableAttributesResponseTimer(
+            @NonNull RequestSatelliteEnabledArgument argument) {
+        synchronized (mSatelliteEnabledRequestLock) {
+            if (hasMessages(EVENT_WAIT_FOR_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_RESPONSE_TIMED_OUT,
+                    argument)) {
+                plogd("WaitForUpdateSatelliteEnableAttributesResponseTimer of request ID "
+                        + argument.requestId + " was already started");
+                return;
+            }
+            plogd("Start timer to wait for response of the update satellite enable attributes"
+                    + " request ID=" + argument.requestId
+                    + ", enableSatellite=" + argument.enableSatellite
+                    + ", mWaitTimeForSatelliteEnablingResponse="
+                    + mWaitTimeForSatelliteEnablingResponse);
+            sendMessageDelayed(obtainMessage(
+                    EVENT_WAIT_FOR_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_RESPONSE_TIMED_OUT,
+                    argument), mWaitTimeForSatelliteEnablingResponse);
+        }
+    }
+
+    private void stopWaitForUpdateSatelliteEnableAttributesResponseTimer(
+            @NonNull RequestSatelliteEnabledArgument argument) {
+        synchronized (mSatelliteEnabledRequestLock) {
+            plogd("Stop timer to wait for response of the enable attributes update request ID="
+                    + argument.requestId + ", enableSatellite=" + argument.enableSatellite);
+            removeMessages(
+                    EVENT_WAIT_FOR_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_RESPONSE_TIMED_OUT, argument);
+        }
+    }
+
+    private boolean shouldProcessEventUpdateSatelliteEnableAttributesDone(
+            @NonNull RequestSatelliteEnabledArgument argument) {
+        synchronized (mSatelliteEnabledRequestLock) {
+            if (hasMessages(EVENT_WAIT_FOR_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_RESPONSE_TIMED_OUT,
+                    argument)) {
+                return true;
+            }
+            return false;
+        }
+    }
+
     private void handleEventWaitForSatelliteEnablingResponseTimedOut(
             @NonNull RequestSatelliteEnabledArgument argument) {
         plogw("Timed out to wait for response of the satellite enabling request ID="
                 + argument.requestId + ", enableSatellite=" + argument.enableSatellite);
 
-        synchronized (mSatelliteEnabledRequestLock) {
-            if (mSatelliteEnabledRequest != null) {
-                if (mSatelliteEnabledRequest.enableSatellite && !argument.enableSatellite
-                        && mWaitingForRadioDisabled) {
-                    // Previous mSatelliteEnabledRequest is successful but waiting for
-                    // all radios to be turned off.
-                    mSatelliteEnabledRequest.callback.accept(SATELLITE_RESULT_SUCCESS);
-                    resetSatelliteEnabledRequest();
-                } else if (mSatelliteEnabledRequest.requestId == argument.requestId) {
-                    resetSatelliteEnabledRequest();
-                }
-            }
-        }
         argument.callback.accept(SATELLITE_RESULT_MODEM_TIMEOUT);
-
         synchronized (mIsSatelliteEnabledLock) {
             if (argument.enableSatellite) {
-                if (!mWaitingForDisableSatelliteModemResponse && !mWaitingForSatelliteModemOff) {
-                    resetSatelliteEnabledRequest();
-                    IIntegerConsumer callback = new IIntegerConsumer.Stub() {
-                        @Override
-                        public void accept(int result) {
-                            plogd("handleEventWaitForSatelliteEnablingResponseTimedOut: "
-                                    + "disable satellite result=" + result);
-                        }
-                    };
-                    Consumer<Integer> result =
-                            FunctionalUtils.ignoreRemoteException(callback::accept);
-                    RequestSatelliteEnabledArgument request = new RequestSatelliteEnabledArgument(
-                            false, false, false, result);
-                    synchronized (mSatelliteEnabledRequestLock) {
-                        mSatelliteEnabledRequest = request;
+                resetSatelliteEnabledRequest();
+                abortSatelliteEnableAttributesUpdateRequest(SATELLITE_RESULT_REQUEST_ABORTED);
+                synchronized (mSatelliteEnabledRequestLock) {
+                    if (mSatelliteDisabledRequest == null) {
+                        IIntegerConsumer callback = new IIntegerConsumer.Stub() {
+                            @Override
+                            public void accept(int result) {
+                                plogd("handleEventWaitForSatelliteEnablingResponseTimedOut: "
+                                        + "disable satellite result=" + result);
+                            }
+                        };
+                        Consumer<Integer> result =
+                                FunctionalUtils.ignoreRemoteException(callback::accept);
+                        mSatelliteDisabledRequest = new RequestSatelliteEnabledArgument(
+                                false, false, false, result);
+                        sendRequestAsync(CMD_SET_SATELLITE_ENABLED, mSatelliteDisabledRequest,
+                                null);
                     }
-                    sendRequestAsync(CMD_SET_SATELLITE_ENABLED, request, null);
                 }
-                notifyEnablementFailedToSatelliteSessionController();
+
                 mControllerMetricsStats.reportServiceEnablementFailCount();
                 mSessionMetricsStats.setInitializationResult(SATELLITE_RESULT_MODEM_TIMEOUT)
                         .setSatelliteTechnology(getSupportedNtnRadioTechnology())
                         .setInitializationProcessingTime(
                                 System.currentTimeMillis() - mSessionProcessingTimeStamp)
                         .setIsDemoMode(mIsDemoModeEnabled)
+                        .setCarrierId(getSatelliteCarrierId())
                         .reportSessionMetrics();
-                mSessionStartTimeStamp = 0;
-                mSessionProcessingTimeStamp = 0;
             } else {
-                /*
-                 * Unregister Importance Listener for Pointing UI when Satellite is disabled
-                 */
-                synchronized (mNeedsSatellitePointingLock) {
-                    if (mNeedsSatellitePointing) {
-                        mPointingAppController.removeListenerForPointingUI();
-                    }
-                }
-                moveSatelliteToOffStateAndCleanUpResources(SATELLITE_RESULT_MODEM_TIMEOUT, null);
+                resetSatelliteDisabledRequest();
                 mControllerMetricsStats.onSatelliteDisabled();
-                mWaitingForDisableSatelliteModemResponse = false;
-                mWaitingForSatelliteModemOff = false;
                 mSessionMetricsStats.setTerminationResult(SATELLITE_RESULT_MODEM_TIMEOUT)
                         .setSatelliteTechnology(getSupportedNtnRadioTechnology())
                         .setTerminationProcessingTime(
                                 System.currentTimeMillis() - mSessionProcessingTimeStamp)
                         .setSessionDurationSec(calculateSessionDurationTimeSec())
                         .reportSessionMetrics();
-                mSessionStartTimeStamp = 0;
-                mSessionProcessingTimeStamp = 0;
             }
+            notifyEnablementFailedToSatelliteSessionController(argument.enableSatellite);
+            mSessionStartTimeStamp = 0;
+            mSessionProcessingTimeStamp = 0;
         }
     }
 
@@ -4730,7 +5652,7 @@
         return true;
     }
 
-    private void determineSystemNotification() {
+    private void determineAutoConnectSystemNotification() {
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
             logd("determineSystemNotification: carrierEnabledSatelliteFlag is disabled");
             return;
@@ -4751,15 +5673,32 @@
                 return;
             }
             if (!mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false)) {
-                showSatelliteSystemNotification(isNtn.second);
+                updateSatelliteSystemNotification(isNtn.second,
+                        CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC,
+                        /*visible*/ true);
                 mSharedPreferences.edit().putBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY,
                         true).apply();
             }
         }
     }
 
-    private void showSatelliteSystemNotification(int subId) {
-        plogd("showSatelliteSystemNotification");
+    /**
+     * Update the system notification to reflect the current satellite status, that's either already
+     * connected OR needs to be manually enabled. The device should only display one notification
+     * at a time to prevent confusing the user, so the same NOTIFICATION_CHANNEL and NOTIFICATION_ID
+     * are used.
+     *
+     * @param subId The subId that provides the satellite connection.
+     * @param carrierRoamingNtnConnectType {@link CarrierConfigManager
+     * .CARRIER_ROAMING_NTN_CONNECT_TYPE}
+     * @param visible {@code true} to show the notification, {@code false} to cancel it.
+     */
+    private void updateSatelliteSystemNotification(int subId,
+            @CARRIER_ROAMING_NTN_CONNECT_TYPE int carrierRoamingNtnConnectType, boolean visible) {
+        plogd("updateSatelliteSystemNotification subId=" + subId
+                + ", carrierRoamingNtnConnectType=" + SatelliteServiceUtils
+                .carrierRoamingNtnConnectTypeToString(carrierRoamingNtnConnectType)
+                + ", visible=" + visible);
         final NotificationChannel notificationChannel = new NotificationChannel(
                 NOTIFICATION_CHANNEL_ID,
                 NOTIFICATION_CHANNEL,
@@ -4768,15 +5707,30 @@
         notificationChannel.setSound(null, null);
         NotificationManager notificationManager = mContext.getSystemService(
                 NotificationManager.class);
+        if (notificationManager == null) {
+            ploge("updateSatelliteSystemNotification: notificationManager is null");
+            return;
+        }
+        if (!visible) { // Cancel if any.
+            notificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID, UserHandle.ALL);
+            return;
+        }
         notificationManager.createNotificationChannel(notificationChannel);
 
-        Notification.Builder notificationBuilder = new Notification.Builder(mContext)
-                .setContentTitle(mContext.getResources().getString(
-                        R.string.satellite_notification_title))
-                .setContentText(mContext.getResources().getString(
-                        R.string.satellite_notification_summary))
+        // if carrierRoamingNtnConnectType is CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC
+        int title = R.string.satellite_notification_title;
+        int summary = R.string.satellite_notification_summary;
+        if (carrierRoamingNtnConnectType
+                == CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL) {
+            title = R.string.satellite_notification_manual_title;
+            summary = R.string.satellite_notification_manual_summary;
+        }
+
+        Notification.Builder notificationBuilder = new Notification.Builder(mContext,
+                NOTIFICATION_CHANNEL_ID)
+                .setContentTitle(mContext.getResources().getString(title))
+                .setContentText(mContext.getResources().getString(summary))
                 .setSmallIcon(R.drawable.ic_android_satellite_24px)
-                .setChannelId(NOTIFICATION_CHANNEL_ID)
                 .setAutoCancel(true)
                 .setColor(mContext.getColor(
                         com.android.internal.R.color.system_notification_accent_color))
@@ -4826,6 +5780,8 @@
                 notificationBuilder.build(), UserHandle.ALL);
 
         mCarrierRoamingSatelliteControllerStats.reportCountOfSatelliteNotificationDisplayed();
+        mCarrierRoamingSatelliteControllerStats.reportCarrierId(getSatelliteCarrierId());
+        mSessionMetricsStats.addCountOfSatelliteNotificationDisplayed();
     }
 
     private void resetCarrierRoamingSatelliteModeParams() {
@@ -4846,8 +5802,15 @@
         }
     }
 
+    /**
+     * Read carrier config items for satellite
+     *
+     * @param subId Associated subscription ID
+     * @return PersistableBundle including carrier config values
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     @NonNull
-    private PersistableBundle getPersistableBundle(int subId) {
+    public PersistableBundle getPersistableBundle(int subId) {
         synchronized (mCarrierConfigArrayLock) {
             PersistableBundle config = mCarrierConfigArray.get(subId);
             if (config == null) {
@@ -4866,15 +5829,61 @@
                 - mSessionMetricsStats.getSessionTerminationProcessingTimeMillis()) / 1000);
     }
 
-    private void notifyEnablementFailedToSatelliteSessionController() {
+    private void notifyEnablementFailedToSatelliteSessionController(boolean enabled) {
         if (mSatelliteSessionController != null) {
-            mSatelliteSessionController.onSatelliteEnablementFailed();
+            mSatelliteSessionController.onSatelliteEnablementFailed(enabled);
         } else {
             ploge("notifyEnablementFailedToSatelliteSessionController: mSatelliteSessionController"
                     + " is not initialized yet");
         }
     }
 
+    private void abortSatelliteEnableRequest(@SatelliteManager.SatelliteResult int resultCode) {
+        synchronized (mSatelliteEnabledRequestLock) {
+            if (mSatelliteEnabledRequest != null) {
+                plogw("abortSatelliteEnableRequest");
+                if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                    resultCode = SATELLITE_RESULT_REQUEST_ABORTED;
+                }
+                mSatelliteEnabledRequest.callback.accept(resultCode);
+                stopWaitForSatelliteEnablingResponseTimer(mSatelliteEnabledRequest);
+                mSatelliteEnabledRequest = null;
+            }
+        }
+    }
+
+    private void abortSatelliteDisableRequest(@SatelliteManager.SatelliteResult int resultCode) {
+        synchronized (mSatelliteEnabledRequestLock) {
+            if (mSatelliteDisabledRequest != null) {
+                plogd("abortSatelliteDisableRequest");
+                mSatelliteDisabledRequest.callback.accept(resultCode);
+                stopWaitForSatelliteEnablingResponseTimer(mSatelliteDisabledRequest);
+                mSatelliteDisabledRequest = null;
+            }
+        }
+    }
+
+    private void abortSatelliteEnableAttributesUpdateRequest(
+            @SatelliteManager.SatelliteResult int resultCode) {
+        synchronized (mSatelliteEnabledRequestLock) {
+            if (mSatelliteEnableAttributesUpdateRequest != null) {
+                plogd("abortSatelliteEnableAttributesUpdateRequest");
+                if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                    resultCode = SATELLITE_RESULT_REQUEST_ABORTED;
+                }
+                mSatelliteEnableAttributesUpdateRequest.callback.accept(resultCode);
+                stopWaitForUpdateSatelliteEnableAttributesResponseTimer(
+                        mSatelliteEnableAttributesUpdateRequest);
+                mSatelliteEnableAttributesUpdateRequest = null;
+            }
+        }
+    }
+
+    private void stopWaitForEnableResponseTimers() {
+        plogd("stopWaitForEnableResponseTimers");
+        removeMessages(EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT);
+    }
+
     private long getDemoPointingAlignedDurationMillisFromResources() {
         long durationMillis = 15000L;
         try {
@@ -4909,12 +5918,15 @@
         return mDemoPointingNotAlignedDurationMillis;
     }
 
-    private boolean getWwanIsInService(ServiceState serviceState) {
+    /** Returns {@code true} if WWAN is in service, else {@code false}.*/
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean getWwanIsInService(@NonNull ServiceState serviceState) {
         List<NetworkRegistrationInfo> nriList = serviceState
                 .getNetworkRegistrationInfoListForTransportType(
                         AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
         for (NetworkRegistrationInfo nri : nriList) {
-            if (nri.isInService()) {
+            if (nri.isInService() || nri.isEmergencyEnabled()) {
                 logv("getWwanIsInService: return true");
                 return true;
             }
@@ -5033,4 +6045,589 @@
         }
         return TimeUnit.SECONDS.toMillis(duration);
     }
+
+    /**
+     * Calculate priority
+     * 1. Active eSOS profiles are higher priority than inactive eSOS profiles.
+     * 2. Carrier Enabled eSOS profile is higher priority than OEM enabled eSOS profile.
+     * 3. Among active carrier eSOS profiles user selected(default SMS SIM) eSOS profile will be
+     * the highest priority.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    protected void evaluateESOSProfilesPrioritization() {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("evaluateESOSProfilesPrioritization: Flag CarrierRoamingNbIotNtn is disabled");
+            return;
+        }
+        boolean isChanged = false;
+        List<SubscriptionInfo> allSubInfos = mSubscriptionManagerService.getAllSubInfoList(
+                mContext.getOpPackageName(), mContext.getAttributionTag());
+        // Key : priority - lower value has higher priority; Value : List<SubscriptionInfo>
+        Map<Integer, List<SubscriptionInfo>> newSubsInfoListPerPriority = new HashMap<>();
+        synchronized (mSatelliteTokenProvisionedLock) {
+            for (SubscriptionInfo info : allSubInfos) {
+                int subId = info.getSubscriptionId();
+                boolean isActive = info.isActive();
+                boolean isDefaultSmsSubId =
+                        mSubscriptionManagerService.getDefaultSmsSubId() == subId;
+                boolean isNtnOnly = info.isOnlyNonTerrestrialNetwork();
+                boolean isESOSSupported = info.isSatelliteESOSSupported();
+                if (!isNtnOnly && !isESOSSupported) {
+                    continue;
+                }
+
+                int keyPriority = (isESOSSupported && isActive && isDefaultSmsSubId) ? 0
+                        : (isESOSSupported && isActive) ? 1
+                                : (isNtnOnly) ? 2 : (isESOSSupported) ? 3 : -1;
+                if (keyPriority != -1) {
+                    newSubsInfoListPerPriority.computeIfAbsent(keyPriority,
+                            k -> new ArrayList<>()).add(info);
+                } else {
+                    plogw("evaluateESOSProfilesPrioritization: Got -1 keyPriority for subId="
+                            + info.getSubscriptionId());
+                }
+
+                Pair<String, Integer> subscriberIdPair = getSubscriberIdAndType(info);
+                String newSubscriberId = subscriberIdPair.first;
+                Optional<String> oldSubscriberId = mSubscriberIdPerSub.entrySet().stream()
+                        .filter(entry -> entry.getValue().equals(subId))
+                        .map(Map.Entry::getKey).findFirst();
+
+                if (oldSubscriberId.isPresent()
+                        && !newSubscriberId.equals(oldSubscriberId.get())) {
+                    mSubscriberIdPerSub.remove(oldSubscriberId.get());
+                    mProvisionedSubscriberId.remove(oldSubscriberId.get());
+                    logd("Old phone number is removed: id = " + subId);
+                    isChanged = true;
+                }
+            }
+        }
+
+        if (!mHasSentBroadcast && newSubsInfoListPerPriority.size() == 0) {
+            logd("evaluateESOSProfilesPrioritization: no satellite subscription available");
+            return;
+        }
+
+        // If priority has changed, send broadcast for provisioned ESOS subs IDs
+        synchronized (mSatelliteTokenProvisionedLock) {
+            if (isPriorityChanged(mSubsInfoListPerPriority, newSubsInfoListPerPriority)
+                    || isChanged) {
+                mSubsInfoListPerPriority = newSubsInfoListPerPriority;
+                sendBroadCastForProvisionedESOSSubs();
+                mHasSentBroadcast = true;
+            }
+        }
+    }
+
+    // The subscriberId for ntnOnly SIMs is the Iccid, whereas for ESOS supported SIMs, the
+    // subscriberId is the Imsi prefix 6 digit + phone number.
+    private Pair<String, Integer> getSubscriberIdAndType(SubscriptionInfo info) {
+        String subscriberId = "";
+        @SatelliteSubscriberInfo.SubscriberIdType int subscriberIdType =
+                SatelliteSubscriberInfo.ICCID;
+        if (info.isSatelliteESOSSupported()) {
+            subscriberId = getPhoneNumberBasedCarrier(info.getSubscriptionId());
+            subscriberIdType = SatelliteSubscriberInfo.IMSI_MSISDN;
+        }
+        if (info.isOnlyNonTerrestrialNetwork()) {
+            subscriberId = info.getIccId();
+        }
+        logd("getSubscriberIdAndType: subscriberId=" + subscriberId + ", subscriberIdType="
+                + subscriberIdType);
+        return new Pair<>(subscriberId, subscriberIdType);
+    }
+
+    private String getPhoneNumberBasedCarrier(int subId) {
+        SubscriptionInfoInternal internal = mSubscriptionManagerService.getSubscriptionInfoInternal(
+                subId);
+        SubscriptionManager subscriptionManager = mContext.getSystemService(
+                SubscriptionManager.class);
+        if (mInjectSubscriptionManager != null) {
+            logd("getPhoneNumberBasedCarrier: InjectSubscriptionManager");
+            subscriptionManager = mInjectSubscriptionManager;
+        }
+        String phoneNumber = subscriptionManager.getPhoneNumber(subId);
+        if (phoneNumber == null) {
+            logd("getPhoneNumberBasedCarrier: phoneNumber null");
+            return "";
+        }
+        return internal.getImsi() == null ? "" : internal.getImsi().substring(0, 6)
+                + phoneNumber.replaceFirst("^\\+", "");
+    }
+
+    private boolean isPriorityChanged(Map<Integer, List<SubscriptionInfo>> currentMap,
+            Map<Integer, List<SubscriptionInfo>> newMap) {
+        if (currentMap.size() == 0 || currentMap.size() != newMap.size()) {
+            return true;
+        }
+
+        for (Map.Entry<Integer, List<SubscriptionInfo>> entry : currentMap.entrySet()) {
+            List<SubscriptionInfo> currentList = entry.getValue();
+            List<SubscriptionInfo> newList = newMap.get(entry.getKey());
+            if (newList == null || currentList == null || currentList.size() != newList.size()) {
+                return true;
+            }
+            for (int i = 0; i < currentList.size(); i++) {
+                if (currentList.get(i).getSubscriptionId() != newList.get(i).getSubscriptionId()) {
+                    logd("isPriorityChanged: cur=" + currentList.get(i) + " , new=" + newList.get(
+                            i));
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void sendBroadCastForProvisionedESOSSubs() {
+        String packageName = getConfigSatelliteGatewayServicePackage();
+        String className = getConfigSatelliteCarrierRoamingEsosProvisionedClass();
+        if (packageName == null || className == null || packageName.isEmpty()
+                || className.isEmpty()) {
+            logd("sendBroadCastForProvisionedESOSSubs: packageName or className is null or empty.");
+            return;
+        }
+        String action = SatelliteManager.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED;
+
+        Intent intent = new Intent(action);
+        intent.setComponent(new ComponentName(packageName, className));
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(intent);
+        }
+        logd("sendBroadCastForProvisionedESOSSubs" + intent);
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    protected String getStringFromOverlayConfig(int resourceId) {
+        String name;
+        try {
+            name = mContext.getResources().getString(resourceId);
+        } catch (Resources.NotFoundException ex) {
+            loge("getStringFromOverlayConfig: ex=" + ex);
+            name = null;
+        }
+        return name;
+    }
+
+    /**
+     * Request to get list of prioritized satellite tokens to be used for provision.
+     *
+     * @param result The result receiver, which returns the list of prioritized satellite tokens
+     * to be used for provision if the request is successful or an error code if the request failed.
+     */
+    public void requestSatelliteSubscriberProvisionStatus(@NonNull ResultReceiver result) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
+            return;
+        }
+        List<SatelliteSubscriberProvisionStatus> list =
+                getPrioritizedSatelliteSubscriberProvisionStatusList();
+        logd("requestSatelliteSubscriberProvisionStatus: " + list);
+        final Bundle bundle = new Bundle();
+        bundle.putParcelableList(SatelliteManager.KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN, list);
+        result.send(SATELLITE_RESULT_SUCCESS, bundle);
+    }
+
+    private List<SatelliteSubscriberProvisionStatus>
+            getPrioritizedSatelliteSubscriberProvisionStatusList() {
+        List<SatelliteSubscriberProvisionStatus> list = new ArrayList<>();
+        synchronized (mSatelliteTokenProvisionedLock) {
+            for (int priority : mSubsInfoListPerPriority.keySet()) {
+                List<SubscriptionInfo> infoList = mSubsInfoListPerPriority.get(priority);
+                if (infoList == null) {
+                    logd("getPrioritySatelliteSubscriberProvisionStatusList: no exist this "
+                            + "priority " + priority);
+                    continue;
+                }
+                for (SubscriptionInfo info : infoList) {
+                    Pair<String, Integer> subscriberIdPair = getSubscriberIdAndType(info);
+                    String subscriberId = subscriberIdPair.first;
+                    int carrierId = info.getCarrierId();
+                    String apn = getConfigForSubId(info.getSubscriptionId())
+                            .getString(KEY_SATELLITE_NIDD_APN_NAME_STRING, "");
+                    logd("getPrioritySatelliteSubscriberProvisionStatusList: subscriberId:"
+                            + subscriberId + " , carrierId=" + carrierId + " , apn=" + apn);
+                    if (subscriberId.isEmpty()) {
+                        logd("getPrioritySatelliteSubscriberProvisionStatusList: getSubscriberId "
+                                + "failed skip this subscriberId.");
+                        continue;
+                    }
+                    SatelliteSubscriberInfo satelliteSubscriberInfo =
+                            new SatelliteSubscriberInfo.Builder().setSubscriberId(subscriberId)
+                                    .setCarrierId(carrierId).setNiddApn(apn)
+                                    .setSubId(info.getSubscriptionId())
+                                    .setSubscriberIdType(subscriberIdPair.second)
+                                    .build();
+                    boolean provisioned = mProvisionedSubscriberId.getOrDefault(subscriberId,
+                            false);
+                    logd("getPrioritySatelliteSubscriberProvisionStatusList: "
+                            + "satelliteSubscriberInfo=" + satelliteSubscriberInfo
+                            + ", provisioned=" + provisioned);
+                    list.add(new SatelliteSubscriberProvisionStatus.Builder()
+                            .setSatelliteSubscriberInfo(satelliteSubscriberInfo)
+                            .setProvisionStatus(provisioned).build());
+                    mSubscriberIdPerSub.put(subscriberId, info.getSubscriptionId());
+                }
+            }
+        }
+        return list;
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected boolean isSubscriptionProvisioned(int subId) {
+        plogd("isSubscriptionProvisioned: subId=" + subId);
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("isSubscriptionProvisioned: carrierRoamingNbIotNtn flag is disabled");
+            return false;
+        }
+
+        String subscriberId = getSubscriberIdAndType(
+                mSubscriptionManagerService.getSubscriptionInfo(subId)).first;
+        if (subscriberId.isEmpty()) {
+            plogd("isSubscriptionProvisioned: subId=" + subId + " subscriberId is empty.");
+            return false;
+        }
+
+        synchronized (mSatelliteTokenProvisionedLock) {
+            return mProvisionedSubscriberId.getOrDefault(subscriberId, false);
+        }
+    }
+
+    /**
+     * Deliver the list of provisioned satellite subscriber ids.
+     *
+     * @param list List of provisioned satellite subscriber ids.
+     * @param result The result receiver that returns whether deliver success or fail.
+     */
+    public void provisionSatellite(@NonNull List<SatelliteSubscriberInfo> list,
+            @NonNull ResultReceiver result) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
+            return;
+        }
+        if (list.size() == 0) {
+            result.send(SATELLITE_RESULT_INVALID_ARGUMENTS, null);
+            return;
+        }
+
+        logd("provisionSatellite:" + list);
+        RequestProvisionSatelliteArgument request = new RequestProvisionSatelliteArgument(list,
+                result);
+        sendRequestAsync(CMD_UPDATE_PROVISION_SATELLITE_TOKEN, request, null);
+    }
+
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected void setSatellitePhone(int subId) {
+        synchronized (mSatellitePhoneLock) {
+            mSatellitePhone = SatelliteServiceUtils.getPhone(subId);
+            if (mSatellitePhone == null) {
+                mSatellitePhone = SatelliteServiceUtils.getPhone();
+            }
+            plogd("mSatellitePhone:" + (mSatellitePhone != null) + ", subId=" + subId);
+            int carrierId = mSatellitePhone.getCarrierId();
+            if (carrierId != UNKNOWN_CARRIER_ID) {
+                mControllerMetricsStats.setCarrierId(carrierId);
+            } else {
+                logd("setSatellitePhone: Carrier ID is UNKNOWN_CARRIER_ID");
+            }
+        }
+    }
+
+    /** Return the carrier ID of the binding satellite subscription. */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public int getSatelliteCarrierId() {
+        synchronized (mSatellitePhoneLock) {
+            if (mSatellitePhone != null) {
+                return mSatellitePhone.getCarrierId();
+            } else {
+                logd("getSatelliteCarrierId: returns UNKNOWN_CARRIER_ID");
+                return UNKNOWN_CARRIER_ID;
+            }
+        }
+    }
+
+    /**
+     * Get whether phone is eligible to connect to carrier roaming non-terrestrial network.
+     *
+     * @param phone phone object
+     * return {@code true} when the subscription is eligible for satellite
+     * communication if all the following conditions are met:
+     * <ul>
+     * <li>Subscription supports P2P satellite messaging which is defined by
+     * {@link CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} </li>
+     * <li>{@link CarrierConfigManager#KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT} set to
+     * {@link CarrierConfigManager#CARRIER_ROAMING_NTN_CONNECT_MANUAL} </li>
+     * <li>The device is in {@link ServiceState#STATE_OUT_OF_SERVICE}, not connected to Wi-Fi. </li>
+     * </ul>
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public boolean isCarrierRoamingNtnEligible(@Nullable Phone phone) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("isCarrierRoamingNtnEligible: carrierRoamingNbIotNtn flag is disabled");
+            return false;
+        }
+
+        if (phone == null) {
+            plogd("isCarrierRoamingNtnEligible: phone is null");
+            return false;
+        }
+
+        int subId = phone.getSubId();
+        if (!isSatelliteSupportedViaCarrier(subId)) {
+            plogd("isCarrierRoamingNtnEligible[phoneId=" + phone.getPhoneId()
+                    + "]: satellite is not supported via carrier");
+            return false;
+        }
+
+        if (!isSubscriptionProvisioned(subId)) {
+            plogd("isCarrierRoamingNtnEligible[phoneId=" + phone.getPhoneId()
+                    + "]: subscription is not provisioned to use satellite.");
+            return false;
+        }
+
+        if (!isSatelliteServiceSupportedByCarrier(subId,
+                NetworkRegistrationInfo.SERVICE_TYPE_SMS)) {
+            plogd("isCarrierRoamingNtnEligible[phoneId=" + phone.getPhoneId()
+                    + "]: SMS is not supported by carrier");
+            return false;
+        }
+
+        int carrierRoamingNtnConnectType = getCarrierRoamingNtnConnectType(subId);
+        if (carrierRoamingNtnConnectType != CARRIER_ROAMING_NTN_CONNECT_MANUAL) {
+            plogd("isCarrierRoamingNtnEligible[phoneId=" + phone.getPhoneId() + "]: not manual "
+                    + "connect. carrierRoamingNtnConnectType = " + carrierRoamingNtnConnectType);
+            return false;
+        }
+
+        if (SatelliteServiceUtils.isCellularAvailable()) {
+            plogd("isCarrierRoamingNtnEligible[phoneId=" + phone.getPhoneId()
+                    + "]: cellular is available");
+            return false;
+        }
+
+        synchronized (mIsWifiConnectedLock) {
+            if (mIsWifiConnected) {
+                plogd("isCarrierRoamingNtnEligible[phoneId=" + phone.getPhoneId()
+                        + "]: Wi-Fi is connected");
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private boolean isSatelliteServiceSupportedByCarrier(int subId,
+            @NetworkRegistrationInfo.ServiceType int serviceType) {
+        List<String> satellitePlmnList = getSatellitePlmnsForCarrier(subId);
+        for (String satellitePlmn : satellitePlmnList) {
+            if (getSupportedSatelliteServices(subId, satellitePlmn).contains(serviceType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** return satellite phone */
+    @Nullable
+    public Phone getSatellitePhone() {
+        synchronized (mSatellitePhoneLock) {
+            return mSatellitePhone;
+        }
+    }
+
+    /**
+     * Return the highest priority satellite subscirption ID.
+     */
+    public int getHighestPrioritySubscrption() {
+        synchronized (mSatellitePhoneLock) {
+            return mSatellitePhone.getSubId();
+        }
+    }
+
+    /** Start PointingUI if it is required. */
+    public void startPointingUI() {
+        synchronized (mNeedsSatellitePointingLock) {
+            plogd("startPointingUI: mNeedsSatellitePointing=" + mNeedsSatellitePointing
+                    + ", mIsDemoModeEnabled=" + mIsDemoModeEnabled
+                    + ", mIsEmergency=" + mIsEmergency);
+            if (mNeedsSatellitePointing) {
+                mPointingAppController.startPointingUI(false /*needFullScreenPointingUI*/,
+                        mIsDemoModeEnabled, mIsEmergency);
+            }
+        }
+    }
+
+    private void requestIsSatelliteAllowedForCurrentLocation() {
+        plogd("requestIsSatelliteAllowedForCurrentLocation()");
+        synchronized (mSatellitePhoneLock) {
+            if (mCheckingAccessRestrictionInProgress) {
+                plogd("requestIsSatelliteCommunicationAllowedForCurrentLocation was already sent");
+                return;
+            }
+            mCheckingAccessRestrictionInProgress = true;
+        }
+
+        OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> callback =
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(Boolean result) {
+                        plogd("requestIsSatelliteAllowedForCurrentLocation: result=" + result);
+                        sendMessage(obtainMessage(
+                                EVENT_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT, result));
+                    }
+
+                    @Override
+                    public void onError(SatelliteManager.SatelliteException ex) {
+                        plogd("requestIsSatelliteAllowedForCurrentLocation: onError, ex=" + ex);
+                        sendMessage(obtainMessage(
+                                EVENT_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT, false));
+                    }
+                };
+        requestIsSatelliteCommunicationAllowedForCurrentLocation(callback);
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected void requestIsSatelliteCommunicationAllowedForCurrentLocation(
+            @NonNull OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> callback) {
+        SatelliteManager satelliteManager = mContext.getSystemService(SatelliteManager.class);
+        if (satelliteManager == null) {
+            ploge("requestIsSatelliteCommunicationAllowedForCurrentLocation: "
+                    + "SatelliteManager is null");
+            return;
+        }
+
+        satelliteManager.requestIsCommunicationAllowedForCurrentLocation(
+                this::post, callback);
+    }
+
+    private void handleSatelliteAccessRestrictionCheckingResult(boolean satelliteAllowed) {
+        synchronized (mSatellitePhoneLock) {
+            mCheckingAccessRestrictionInProgress = false;
+            boolean eligible = isCarrierRoamingNtnEligible(mSatellitePhone);
+            plogd("handleSatelliteAccessRestrictionCheckingResult:"
+                    + " satelliteAllowed=" + satelliteAllowed
+                    + ", isCarrierRoamingNtnEligible=" + eligible
+                    + ", mNtnEligibilityHysteresisTimedOut=" + mNtnEligibilityHysteresisTimedOut);
+            if (satelliteAllowed && eligible && mNtnEligibilityHysteresisTimedOut) {
+                updateLastNotifiedNtnEligibilityAndNotify(true);
+                mNtnEligibilityHysteresisTimedOut = false;
+            }
+        }
+    }
+
+    private void handleEventSatelliteRegistrationFailure(int causeCode) {
+        plogd("handleEventSatelliteRegistrationFailure: " + causeCode);
+
+        List<ISatelliteModemStateCallback> deadCallersList = new ArrayList<>();
+        mSatelliteRegistrationFailureListeners.values().forEach(listener -> {
+            try {
+                listener.onRegistrationFailure(causeCode);
+            } catch (RemoteException e) {
+                logd("handleEventSatelliteRegistrationFailure RemoteException: " + e);
+                deadCallersList.add(listener);
+            }
+        });
+        deadCallersList.forEach(listener -> {
+            mSatelliteRegistrationFailureListeners.remove(listener.asBinder());
+        });
+    }
+
+    /**
+     * This API can be used by only CTS to override the cached value for the device overlay config
+     * value :
+     * config_satellite_gateway_service_package and
+     * config_satellite_carrier_roaming_esos_provisioned_class.
+     * These values are set before sending an intent to broadcast there are any change to list of
+     * subscriber informations.
+     *
+     * @param name the name is one of the following that constitute an intent.
+     *             component package name, or component class name.
+     * @return {@code true} if the setting is successful, {@code false} otherwise.
+     */
+    public boolean setSatelliteSubscriberIdListChangedIntentComponent(String name) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            logd("setSatelliteSubscriberIdListChangedIntentComponent: carrierRoamingNbIotNtn is "
+                    + "disabled");
+            return false;
+        }
+        if (!isMockModemAllowed()) {
+            logd("setSatelliteSubscriberIdListChangedIntentComponent: mock modem is not allowed");
+            return false;
+        }
+        logd("setSatelliteSubscriberIdListChangedIntentComponent:" + name);
+
+        if (name.contains("/")) {
+            mChangeIntentComponent = true;
+        } else {
+            mChangeIntentComponent = false;
+            return true;
+        }
+        boolean result = true;
+        String[] cmdPart = name.split("/");
+        switch (cmdPart[0]) {
+            case "-p": {
+                mConfigSatelliteGatewayServicePackage = cmdPart[1];
+                break;
+            }
+            case "-c": {
+                mConfigSatelliteCarrierRoamingEsosProvisionedClass = cmdPart[1];
+                break;
+            }
+            default:
+                logd("setSatelliteSubscriberIdListChangedIntentComponent: invalid name " + name);
+                result = false;
+                break;
+        }
+        return result;
+    }
+
+    private String getConfigSatelliteGatewayServicePackage() {
+        if (!mChangeIntentComponent) {
+            return getStringFromOverlayConfig(
+                    R.string.config_satellite_gateway_service_package);
+        }
+        logd("getConfigSatelliteGatewayServicePackage: " + mConfigSatelliteGatewayServicePackage);
+        return mConfigSatelliteGatewayServicePackage;
+    }
+
+    private String getConfigSatelliteCarrierRoamingEsosProvisionedClass() {
+        if (!mChangeIntentComponent) {
+            return getStringFromOverlayConfig(
+                    R.string.config_satellite_carrier_roaming_esos_provisioned_class);
+        }
+        logd("getConfigSatelliteCarrierRoamingEsosProvisionedClass: "
+                + mConfigSatelliteCarrierRoamingEsosProvisionedClass);
+        return mConfigSatelliteCarrierRoamingEsosProvisionedClass;
+    }
+
+    private void registerDefaultSmsSubscriptionChangedBroadcastReceiver() {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("registerDefaultSmsSubscriptionChangedBroadcastReceiver: Flag "
+                    + "CarrierRoamingNbIotNtn is disabled");
+            return;
+        }
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED);
+        mContext.registerReceiver(mDefaultSmsSubscriptionChangedBroadcastReceiver, intentFilter);
+    }
+
+    FeatureFlags getFeatureFlags() {
+        return mFeatureFlags;
+    }
+
+    private boolean isSatelliteDisabled() {
+        synchronized (mIsSatelliteEnabledLock) {
+            return ((mIsSatelliteEnabled != null) && !mIsSatelliteEnabled);
+        }
+    }
+
+    private boolean shouldStopWaitForEnableResponseTimer(
+            @NonNull RequestSatelliteEnabledArgument argument) {
+        if (argument.enableSatellite) return true;
+        synchronized (mSatelliteEnabledRequestLock) {
+            return !mWaitingForSatelliteModemOff;
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
index da4c69b..a19f802 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -40,6 +40,7 @@
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.SatelliteManager.SatelliteException;
+import android.telephony.satellite.SatelliteModemEnableRequestAttributes;
 import android.telephony.satellite.stub.INtnSignalStrengthConsumer;
 import android.telephony.satellite.stub.ISatellite;
 import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer;
@@ -86,8 +87,6 @@
     private boolean mIsBinding;
     @Nullable private PersistentLogger mPersistentLogger = null;
 
-    @NonNull private final RegistrantList mSatelliteProvisionStateChangedRegistrants =
-            new RegistrantList();
     @NonNull private final RegistrantList mSatellitePositionInfoChangedRegistrants =
             new RegistrantList();
     @NonNull private final RegistrantList mDatagramTransferStateChangedRegistrants =
@@ -103,6 +102,8 @@
             new RegistrantList();
     @NonNull private final RegistrantList mSatelliteSupportedStateChangedRegistrants =
             new RegistrantList();
+    @NonNull private final RegistrantList mSatelliteRegistrationFailureRegistrants =
+            new RegistrantList();
 
     private class SatelliteListener extends ISatelliteListener.Stub {
 
@@ -113,11 +114,6 @@
         }
 
         @Override
-        public void onSatelliteProvisionStateChanged(boolean provisioned) {
-            mSatelliteProvisionStateChangedRegistrants.notifyResult(provisioned);
-        }
-
-        @Override
         public void onSatelliteDatagramReceived(
                 android.telephony.satellite.stub.SatelliteDatagram datagram, int pendingCount) {
             if (notifyResultIfExpectedListener()) {
@@ -193,7 +189,7 @@
 
         @Override
         public void onRegistrationFailure(int causeCode) {
-            // TO-DO notify registrants
+            mSatelliteRegistrationFailureRegistrants.notifyResult(causeCode);
         }
 
         private boolean notifyResultIfExpectedListener() {
@@ -403,27 +399,6 @@
     }
 
     /**
-     * Registers for the satellite provision state changed.
-     *
-     * @param h Handler for notification message.
-     * @param what User-defined message code.
-     * @param obj User object.
-     */
-    public void registerForSatelliteProvisionStateChanged(
-            @NonNull Handler h, int what, @Nullable Object obj) {
-        mSatelliteProvisionStateChangedRegistrants.add(h, what, obj);
-    }
-
-    /**
-     * Unregisters for the satellite provision state changed.
-     *
-     * @param h Handler to be removed from the registrant list.
-     */
-    public void unregisterForSatelliteProvisionStateChanged(@NonNull Handler h) {
-        mSatelliteProvisionStateChangedRegistrants.remove(h);
-    }
-
-    /**
      * Registers for satellite position info changed from satellite modem.
      *
      * @param h Handler for notification message.
@@ -591,6 +566,27 @@
     }
 
     /**
+     * Registers for the satellite registration failed.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForSatelliteRegistrationFailure(
+            @NonNull Handler h, int what, @Nullable Object obj) {
+        mSatelliteRegistrationFailureRegistrants.add(h, what, obj);
+    }
+
+    /**
+     * Unregisters for the satellite registration failed.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    public void unregisterForSatelliteRegistrationFailure(@NonNull Handler h) {
+        mSatelliteRegistrationFailureRegistrants.remove(h);
+    }
+
+    /**
      * Request to enable or disable the satellite service listening mode.
      * Listening mode allows the satellite service to listen for incoming pages.
      *
@@ -682,17 +678,16 @@
      * is enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
      * this may also re-enable the cellular modem.
      *
-     * @param enableSatellite True to enable the satellite modem and false to disable.
-     * @param enableDemoMode True to enable demo mode and false to disable.
-     * @param isEmergency {@code true} to enable emergency mode, {@code false} otherwise.
+     * @param enableAttributes info needed to allow carrier to roam to satellite.
      * @param message The Message to send to result of the operation to.
      */
-    public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
-            boolean isEmergency, @NonNull Message message) {
+    public void requestSatelliteEnabled(SatelliteModemEnableRequestAttributes enableAttributes,
+            @NonNull Message message) {
         if (mSatelliteService != null) {
             try {
-                mSatelliteService.requestSatelliteEnabled(enableSatellite, enableDemoMode,
-                        isEmergency, new IIntegerConsumer.Stub() {
+                mSatelliteService.requestSatelliteEnabled(SatelliteServiceUtils
+                        .toSatelliteModemEnableRequestAttributes(enableAttributes),
+                        new IIntegerConsumer.Stub() {
                             @Override
                             public void accept(int result) {
                                 int error = SatelliteServiceUtils.fromSatelliteError(result);
@@ -889,113 +884,6 @@
     }
 
     /**
-     * Provision the device with a satellite provider.
-     * This is needed if the provider allows dynamic registration.
-     * Once provisioned, ISatelliteListener#onSatelliteProvisionStateChanged should report true.
-     *
-     * @param token The token to be used as a unique identifier for provisioning with satellite
-     *              gateway.
-     * @param provisionData Data from the provisioning app that can be used by provisioning server
-     * @param message The Message to send to result of the operation to.
-     */
-    public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
-            @NonNull Message message) {
-        if (mSatelliteService != null) {
-            try {
-                mSatelliteService.provisionSatelliteService(token, provisionData,
-                        new IIntegerConsumer.Stub() {
-                            @Override
-                            public void accept(int result) {
-                                int error = SatelliteServiceUtils.fromSatelliteError(result);
-                                plogd("provisionSatelliteService: " + error);
-                                Binder.withCleanCallingIdentity(() ->
-                                        sendMessageWithResult(message, null, error));
-                            }
-                        });
-            } catch (RemoteException e) {
-                ploge("provisionSatelliteService: RemoteException " + e);
-                sendMessageWithResult(message, null,
-                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
-            }
-        } else {
-            ploge("provisionSatelliteService: Satellite service is unavailable.");
-            sendMessageWithResult(message, null,
-                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
-        }
-    }
-
-    /**
-     * Deprovision the device with the satellite provider.
-     * This is needed if the provider allows dynamic registration.
-     * Once deprovisioned, ISatelliteListener#onSatelliteProvisionStateChanged should report false.
-     *
-     * @param token The token of the device/subscription to be deprovisioned.
-     * @param message The Message to send to result of the operation to.
-     */
-    public void deprovisionSatelliteService(@NonNull String token, @NonNull Message message) {
-        if (mSatelliteService != null) {
-            try {
-                mSatelliteService.deprovisionSatelliteService(token, new IIntegerConsumer.Stub() {
-                    @Override
-                    public void accept(int result) {
-                        int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        plogd("deprovisionSatelliteService: " + error);
-                        Binder.withCleanCallingIdentity(() ->
-                                sendMessageWithResult(message, null, error));
-                    }
-                });
-            } catch (RemoteException e) {
-                ploge("deprovisionSatelliteService: RemoteException " + e);
-                sendMessageWithResult(message, null,
-                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
-            }
-        } else {
-            ploge("deprovisionSatelliteService: Satellite service is unavailable.");
-            sendMessageWithResult(message, null,
-                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
-        }
-    }
-
-    /**
-     * Request to get whether this device is provisioned with a satellite provider.
-     *
-     * @param message The Message to send to result of the operation to.
-     */
-    public void requestIsSatelliteProvisioned(@NonNull Message message) {
-        if (mSatelliteService != null) {
-            try {
-                mSatelliteService.requestIsSatelliteProvisioned(new IIntegerConsumer.Stub() {
-                    @Override
-                    public void accept(int result) {
-                        int error = SatelliteServiceUtils.fromSatelliteError(result);
-                        plogd("requestIsSatelliteProvisioned: " + error);
-                        Binder.withCleanCallingIdentity(() ->
-                                sendMessageWithResult(message, null, error));
-                    }
-                }, new IBooleanConsumer.Stub() {
-                    @Override
-                    public void accept(boolean result) {
-                        // Convert for compatibility with SatelliteResponse
-                        // TODO: This should just report result instead.
-                        int[] provisioned = new int[] {result ? 1 : 0};
-                        plogd("requestIsSatelliteProvisioned: " + Arrays.toString(provisioned));
-                        Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
-                                message, provisioned, SatelliteManager.SATELLITE_RESULT_SUCCESS));
-                    }
-                });
-            } catch (RemoteException e) {
-                ploge("requestIsSatelliteProvisioned: RemoteException " + e);
-                sendMessageWithResult(message, null,
-                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
-            }
-        } else {
-            ploge("requestIsSatelliteProvisioned: Satellite service is unavailable.");
-            sendMessageWithResult(message, null,
-                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
-        }
-    }
-
-    /**
      * Poll the pending datagrams to be received over satellite.
      * The satellite service should check if there are any pending datagrams to be received over
      * satellite and report them via ISatelliteListener#onSatelliteDatagramsReceived.
@@ -1410,6 +1298,34 @@
     }
 
     /**
+     * Provision UUID with a satellite provider.
+     */
+    public void updateSatelliteSubscription(@NonNull String iccId, @NonNull Message message) {
+        if (mSatelliteService != null) {
+            try {
+                mSatelliteService.updateSatelliteSubscription(iccId,
+                        new IIntegerConsumer.Stub() {
+                            @Override
+                            public void accept(int result) {
+                                int error = SatelliteServiceUtils.fromSatelliteError(result);
+                                plogd("updateSatelliteSubscription: " + error);
+                                Binder.withCleanCallingIdentity(() ->
+                                        sendMessageWithResult(message, null, error));
+                            }
+                        });
+            } catch (RemoteException e) {
+                ploge("updateSatelliteSubscription: RemoteException " + e);
+                sendMessageWithResult(message, null,
+                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
+            }
+        } else {
+            ploge("updateSatelliteSubscription: Satellite service is unavailable.");
+            sendMessageWithResult(message, null,
+                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
+        }
+    }
+
+    /**
      * This API can be used by only CTS to update satellite vendor service package name.
      *
      * @param servicePackageName The package name of the satellite vendor service.
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
index 0e938c1..0bf8184 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
@@ -45,19 +45,17 @@
 import android.os.SystemProperties;
 import android.provider.DeviceConfig;
 import android.telecom.Connection;
-import android.telephony.AccessNetworkConstants;
 import android.telephony.DropBoxManagerLoggerBackend;
-import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PersistentLogger;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
-import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ImsRegistrationAttributes;
 import android.telephony.ims.RegistrationManager;
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
 import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.SatelliteSubscriberProvisionStatus;
 import android.text.TextUtils;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -70,6 +68,9 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.SmsApplication;
+import com.android.internal.telephony.TelephonyCountryDetector;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.metrics.SatelliteStats;
 
 import java.util.List;
@@ -97,7 +98,11 @@
     @NonNull private final Context mContext;
     @NonNull
     private final SatelliteController mSatelliteController;
+    @NonNull
+    private final TelephonyCountryDetector mCountryDetector;
     private ImsManager mImsManager;
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
 
     private Connection mEmergencyConnection = null;
     private final ISatelliteProvisionStateCallback mISatelliteProvisionStateCallback;
@@ -151,6 +156,8 @@
         }
         mContext = context;
         mSatelliteController = satelliteController;
+        mFeatureFlags = mSatelliteController.getFeatureFlags();
+        mCountryDetector = TelephonyCountryDetector.getInstance(context, mFeatureFlags);
         mImsManager = imsManager;
         mOemEnabledTimeoutMillis =
                 getOemEnabledEmergencyCallWaitForConnectionTimeoutMillis(context);
@@ -160,6 +167,13 @@
                 plogd("onSatelliteProvisionStateChanged: provisioned=" + provisioned);
                 sendMessage(obtainMessage(EVENT_SATELLITE_PROVISIONED_STATE_CHANGED, provisioned));
             }
+
+            @Override
+            public void onSatelliteSubscriptionProvisionStateChanged(
+                    List<SatelliteSubscriberProvisionStatus> satelliteSubscriberProvisionStatus) {
+                plogd("onSatelliteSubscriptionProvisionStateChanged: "
+                        + satelliteSubscriberProvisionStatus);
+            }
         };
     }
 
@@ -245,6 +259,7 @@
     }
 
     private void handleEmergencyCallStartedEvent(@NonNull Connection connection) {
+        plogd("handleEmergencyCallStartedEvent: connection=" + connection);
         mSatelliteController.setLastEmergencyCallTime();
 
         if (sendEventDisplayEmergencyMessageForcefully(connection)) {
@@ -299,9 +314,11 @@
             updateSatelliteViaCarrierAvailability();
 
             boolean isDialerNotified = false;
-            if (!isCellularAvailable()
+            boolean isCellularAvailable = SatelliteServiceUtils.isCellularAvailable();
+            if (!isCellularAvailable
                     && isSatelliteAllowed()
-                    && (isSatelliteViaOemAvailable() || isSatelliteViaCarrierAvailable())
+                    && (isSatelliteViaOemAvailable()
+                    || isSatelliteConnectedViaCarrierWithinHysteresisTime())
                     && shouldTrackCall(mEmergencyConnection.getState())) {
                 plogd("handleTimeoutEvent: Sent EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer");
                 Bundle extras = createExtraBundleForEventDisplayEmergencyMessage();
@@ -311,17 +328,17 @@
 
             }
             plogd("handleTimeoutEvent: isImsRegistered=" + isImsRegistered()
-                    + ", isCellularAvailable=" + isCellularAvailable()
+                    + ", isCellularAvailable=" + isCellularAvailable
                     + ", isSatelliteAllowed=" + isSatelliteAllowed()
                     + ", shouldTrackCall=" + shouldTrackCall(mEmergencyConnection.getState()));
-            reportEsosRecommenderDecision(isDialerNotified);
+            reportESosRecommenderDecision(isDialerNotified);
             cleanUpResources();
         }
     }
 
     private boolean isSatelliteAllowed() {
         synchronized (mLock) {
-            if (isSatelliteViaCarrierAvailable()) return true;
+            if (isSatelliteConnectedViaCarrierWithinHysteresisTime()) return true;
             return mIsSatelliteAllowedForCurrentLocation;
         }
     }
@@ -343,7 +360,7 @@
         return satelliteProvisioned != null ? satelliteProvisioned : false;
     }
 
-    private boolean isSatelliteViaCarrierAvailable() {
+    private boolean isSatelliteConnectedViaCarrierWithinHysteresisTime() {
         return mIsSatelliteConnectedViaCarrierWithinHysteresisTime.get();
     }
 
@@ -371,7 +388,7 @@
         }
 
         if (!shouldTrackCall(state)) {
-            reportEsosRecommenderDecision(false);
+            reportESosRecommenderDecision(false);
             cleanUpResources();
         } else {
             // Location service will enter emergency mode only when connection state changes to
@@ -383,7 +400,7 @@
         }
     }
 
-    private void reportEsosRecommenderDecision(boolean isDialerNotified) {
+    private void reportESosRecommenderDecision(boolean isDialerNotified) {
         SatelliteStats.getInstance().onSatelliteSosMessageRecommender(
                 new SatelliteStats.SatelliteSosMessageRecommenderParams.Builder()
                         .setDisplaySosMessageSent(isDialerNotified)
@@ -393,10 +410,12 @@
                         .setIsMultiSim(isMultiSim())
                         .setRecommendingHandoverType(getEmergencyCallToSatelliteHandoverType())
                         .setIsSatelliteAllowedInCurrentLocation(isSatelliteAllowed())
-                        .build());
+                        .setIsWifiConnected(mCountryDetector.isWifiNetworkConnected())
+                        .setCarrierId(getAvailableNtnCarrierID()).build());
     }
 
     private void cleanUpResources() {
+        plogd("cleanUpResources");
         synchronized (mLock) {
             stopTimer();
             if (mEmergencyConnection != null) {
@@ -412,7 +431,7 @@
 
     private void registerForInterestedStateChangedEvents() {
         mSatelliteController.registerForSatelliteProvisionStateChanged(
-                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, mISatelliteProvisionStateCallback);
+                mISatelliteProvisionStateCallback);
         for (Phone phone : PhoneFactory.getPhones()) {
             phone.registerForServiceStateChanged(
                     this, EVENT_SERVICE_STATE_CHANGED, null);
@@ -432,7 +451,7 @@
 
     private void unregisterForInterestedStateChangedEvents() {
         mSatelliteController.unregisterForSatelliteProvisionStateChanged(
-                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, mISatelliteProvisionStateCallback);
+                mISatelliteProvisionStateCallback);
         for (Phone phone : PhoneFactory.getPhones()) {
             phone.unregisterForServiceStateChanged(this);
         }
@@ -450,51 +469,6 @@
         }
     }
 
-    private boolean isCellularAvailable() {
-        for (Phone phone : PhoneFactory.getPhones()) {
-            ServiceState serviceState = phone.getServiceState();
-            if (serviceState != null) {
-                int state = serviceState.getState();
-                if ((state == STATE_IN_SERVICE || state == STATE_EMERGENCY_ONLY
-                        || serviceState.isEmergencyOnly())
-                        && !isSatellitePlmn(phone.getSubId(), serviceState)) {
-                    logv("isCellularAvailable true");
-                    return true;
-                }
-            }
-        }
-        logv("isCellularAvailable false");
-        return false;
-    }
-
-    private boolean isSatellitePlmn(int subId, @NonNull ServiceState serviceState) {
-        List<String> satellitePlmnList =
-                mSatelliteController.getSatellitePlmnsForCarrier(subId);
-        if (satellitePlmnList.isEmpty()) {
-            logv("isSatellitePlmn: satellitePlmnList is empty");
-            return false;
-        }
-
-        for (NetworkRegistrationInfo nri :
-                serviceState.getNetworkRegistrationInfoListForTransportType(
-                        AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) {
-            String registeredPlmn = nri.getRegisteredPlmn();
-            String mccmnc = nri.getCellIdentity().getMccString()
-                    + nri.getCellIdentity().getMncString();
-            for (String satellitePlmn : satellitePlmnList) {
-                if (TextUtils.equals(satellitePlmn, registeredPlmn)
-                        || TextUtils.equals(satellitePlmn, mccmnc)) {
-                    logv("isSatellitePlmn: return true, satellitePlmn:" + satellitePlmn
-                            + " registeredPlmn:" + registeredPlmn + " mccmnc:" + mccmnc);
-                    return true;
-                }
-            }
-        }
-
-        logv("isSatellitePlmn: return false");
-        return false;
-    }
-
     /**
      * @return {@link ServiceState#STATE_IN_SERVICE} if any subscription is in this state; else
      * {@link ServiceState#STATE_EMERGENCY_ONLY} if any subscription is in this state; else
@@ -526,10 +500,11 @@
     }
 
     private synchronized void handleStateChangedEventForHysteresisTimer() {
-        if (!isCellularAvailable() && mEmergencyConnection != null) {
+        if (!SatelliteServiceUtils.isCellularAvailable() && mEmergencyConnection != null) {
             startTimer();
         } else {
-            logv("handleStateChangedEventForHysteresisTimer stopTimer");
+            plogd("handleStateChangedEventForHysteresisTimer stopTimer, mEmergencyConnection="
+                    + mEmergencyConnection);
             stopTimer();
         }
     }
@@ -542,7 +517,7 @@
             sendMessageDelayed(obtainMessage(EVENT_TIME_OUT), mTimeoutMillis);
             mCountOfTimerStarted++;
             mIsTimerTimedOut = false;
-            logd("startTimer mCountOfTimerStarted=" + mCountOfTimerStarted);
+            plogd("startTimer mCountOfTimerStarted=" + mCountOfTimerStarted);
         }
     }
 
@@ -561,7 +536,7 @@
     }
 
     private void selectEmergencyCallWaitForConnectionTimeoutDuration() {
-        if (mSatelliteController.isSatelliteEmergencyMessagingSupportedViaCarrier()) {
+        if (isSatelliteConnectedViaCarrierWithinHysteresisTime()) {
             mTimeoutMillis =
                     mSatelliteController.getCarrierEmergencyCallWaitForConnectionTimeoutMillis();
         } else {
@@ -668,7 +643,7 @@
         String className = oemSatelliteMessagingApp.second;
         String action = getSatelliteEmergencyHandoverIntentActionFromOverlayConfig(mContext);
 
-        if (isSatelliteViaCarrierAvailable()
+        if (isSatelliteConnectedViaCarrierWithinHysteresisTime()
                 || isEmergencyCallToSatelliteHandoverTypeT911Enforced()) {
             ComponentName defaultSmsAppComponent = getDefaultSmsApp();
             handoverType = EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
@@ -749,8 +724,19 @@
         return telephonyManager.isMultiSimEnabled();
     }
 
-    private int getEmergencyCallToSatelliteHandoverType() {
-        if (isSatelliteViaCarrierAvailable()) {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public int getEmergencyCallToSatelliteHandoverType() {
+        if (Flags.carrierRoamingNbIotNtn() && isSatelliteViaOemAvailable()
+                && isSatelliteConnectedViaCarrierWithinHysteresisTime()) {
+            Phone satellitePhone = mSatelliteController.getSatellitePhone();
+            if (satellitePhone == null) {
+                ploge("getEmergencyCallToSatelliteHandoverType: satellitePhone is null");
+                return EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
+            }
+            int satelliteSubId = satellitePhone.getSubId();
+            return mSatelliteController.getCarrierRoamingNtnEmergencyCallToSatelliteHandoverType(
+                    satelliteSubId);
+        } else if (isSatelliteConnectedViaCarrierWithinHysteresisTime()) {
             return EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
         } else {
             return EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS;
@@ -836,6 +822,23 @@
         }
     }
 
+    /** Returns the carrier ID of NTN subscription */
+    private int getAvailableNtnCarrierID() {
+        Pair<Boolean, Integer> ntnSubInfo =
+                mSatelliteController.isUsingNonTerrestrialNetworkViaCarrier();
+        if (ntnSubInfo.first) {
+            TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+            return tm.createForSubscriptionId(ntnSubInfo.second).getSimCarrierId();
+        }
+
+        Phone satellitePhone = mSatelliteController.getSatellitePhone();
+        if (satellitePhone != null) {
+            return satellitePhone.getCarrierId();
+        }
+
+        return TelephonyManager.UNKNOWN_CARRIER_ID;
+    }
+
     private void plogd(@NonNull String log) {
         Rlog.d(TAG, log);
         if (mPersistentLogger != null) {
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
index 4dc425d..3936a7e 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.ServiceState.STATE_EMERGENCY_ONLY;
+import static android.telephony.ServiceState.STATE_IN_SERVICE;
+
 import static java.util.stream.Collectors.joining;
 
 import android.annotation.NonNull;
@@ -24,8 +27,13 @@
 import android.os.AsyncResult;
 import android.os.Binder;
 import android.os.PersistableBundle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentity;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.satellite.AntennaPosition;
 import android.telephony.satellite.NtnSignalStrength;
@@ -33,9 +41,12 @@
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.SatelliteModemEnableRequestAttributes;
+import android.telephony.satellite.SatelliteSubscriptionInfo;
 import android.telephony.satellite.stub.NTRadioTechnology;
 import android.telephony.satellite.stub.SatelliteModemState;
 import android.telephony.satellite.stub.SatelliteResult;
+import android.text.TextUtils;
 
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
@@ -57,6 +68,21 @@
     private static final String TAG = "SatelliteServiceUtils";
 
     /**
+     * Converts a carrier roaming NTN (Non-Terrestrial Network) connect type constant
+     * from {@link CarrierConfigManager} to string.
+     * @param type The carrier roaming NTN connect type constant.
+     * @return A string representation of the connect type, or "Unknown(type)" if not recognized.
+     */
+    public static String carrierRoamingNtnConnectTypeToString(
+            @CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_TYPE int type) {
+        return switch (type) {
+            case CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC -> "AUTOMATIC";
+            case CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL -> "MANUAL";
+            default -> "Unknown(" + type + ")";
+        };
+    }
+
+    /**
      * Convert radio technology from service definition to framework definition.
      * @param radioTechnology The NTRadioTechnology from the satellite service.
      * @return The converted NTRadioTechnology for the framework.
@@ -148,9 +174,9 @@
                 return SatelliteManager.SATELLITE_MODEM_STATE_OFF;
             case SatelliteModemState.SATELLITE_MODEM_STATE_UNAVAILABLE:
                 return SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE;
-            case SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED:
+            case SatelliteModemState.SATELLITE_MODEM_STATE_OUT_OF_SERVICE:
                 return SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED;
-            case SatelliteModemState.SATELLITE_MODEM_STATE_CONNECTED:
+            case SatelliteModemState.SATELLITE_MODEM_STATE_IN_SERVICE:
                 return SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
             default:
                 loge("Received invalid modem state: " + modemState);
@@ -234,6 +260,41 @@
     }
 
     /**
+     * Convert SatelliteSubscriptionInfo from framework definition to service definition.
+     * @param info The SatelliteSubscriptionInfo from the framework.
+     * @return The converted SatelliteSubscriptionInfo for the satellite service.
+     */
+    @NonNull public static android.telephony.satellite.stub
+            .SatelliteSubscriptionInfo toSatelliteSubscriptionInfo(
+            @NonNull SatelliteSubscriptionInfo info
+    ) {
+        android.telephony.satellite.stub.SatelliteSubscriptionInfo converted =
+                new android.telephony.satellite.stub.SatelliteSubscriptionInfo();
+        converted.iccId = info.getIccId();
+        converted.niddApn = info.getNiddApn();
+        return converted;
+    }
+
+    /**
+     * Convert SatelliteModemEnableRequestAttributes from framework definition to service definition
+     * @param attributes The SatelliteModemEnableRequestAttributes from the framework.
+     * @return The converted SatelliteModemEnableRequestAttributes for the satellite service.
+     */
+    @NonNull public static android.telephony.satellite.stub
+            .SatelliteModemEnableRequestAttributes toSatelliteModemEnableRequestAttributes(
+            @NonNull SatelliteModemEnableRequestAttributes attributes
+    ) {
+        android.telephony.satellite.stub.SatelliteModemEnableRequestAttributes converted =
+                new android.telephony.satellite.stub.SatelliteModemEnableRequestAttributes();
+        converted.isEnabled = attributes.isEnabled();
+        converted.isDemoMode = attributes.isDemoMode();
+        converted.isEmergencyMode = attributes.isEmergencyMode();
+        converted.satelliteSubscriptionInfo = toSatelliteSubscriptionInfo(
+                attributes.getSatelliteSubscriptionInfo());
+        return converted;
+    }
+
+    /**
      * Get the {@link SatelliteManager.SatelliteResult} from the provided result.
      *
      * @param ar AsyncResult used to determine the error code.
@@ -282,6 +343,26 @@
     }
 
     /**
+     * Get the subscription ID which supports OEM based NTN satellite service.
+     *
+     * @return ID of the subscription that supports OEM-based satellite if any,
+     * return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} otherwise.
+     */
+    public static int getNtnOnlySubscriptionId(@NonNull Context context) {
+        List<SubscriptionInfo> infoList =
+                SubscriptionManagerService.getInstance().getAllSubInfoList(
+                        context.getOpPackageName(), null);
+
+        int subId = infoList.stream()
+                .filter(info -> info.isOnlyNonTerrestrialNetwork())
+                .mapToInt(SubscriptionInfo::getSubscriptionId)
+                .findFirst()
+                .orElse(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        logd("getNtnOnlySubscriptionId: subId=" + subId);
+        return subId;
+    }
+
+    /**
      * Expected format of the input dictionary bundle is:
      * <ul>
      *     <li>Key: PLMN string.</li>
@@ -391,6 +472,76 @@
         return PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId));
     }
 
+    /** Return {@code true} if device has cellular coverage, else return {@code false}. */
+    public static boolean isCellularAvailable() {
+        for (Phone phone : PhoneFactory.getPhones()) {
+            ServiceState serviceState = phone.getServiceState();
+            if (serviceState != null) {
+                int state = serviceState.getState();
+                if ((state == STATE_IN_SERVICE || state == STATE_EMERGENCY_ONLY
+                        || serviceState.isEmergencyOnly())
+                        && !isSatellitePlmn(phone.getSubId(), serviceState)) {
+                    logd("isCellularAvailable true");
+                    return true;
+                }
+            }
+        }
+        logd("isCellularAvailable false");
+        return false;
+    }
+
+    /** Check whether device is connected to satellite PLMN */
+    public static boolean isSatellitePlmn(int subId, @NonNull ServiceState serviceState) {
+        List<String> satellitePlmnList =
+                SatelliteController.getInstance().getSatellitePlmnsForCarrier(subId);
+        if (satellitePlmnList.isEmpty()) {
+            logd("isSatellitePlmn: satellitePlmnList is empty");
+            return false;
+        }
+
+        for (NetworkRegistrationInfo nri :
+                serviceState.getNetworkRegistrationInfoListForTransportType(
+                        AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) {
+            String registeredPlmn = nri.getRegisteredPlmn();
+            if (TextUtils.isEmpty(registeredPlmn)) {
+                logd("isSatellitePlmn: registeredPlmn is empty");
+                continue;
+            }
+
+            String mccmnc = getMccMnc(nri);
+            for (String satellitePlmn : satellitePlmnList) {
+                if (TextUtils.equals(satellitePlmn, registeredPlmn)
+                        || TextUtils.equals(satellitePlmn, mccmnc)) {
+                    logd("isSatellitePlmn: return true, satellitePlmn:" + satellitePlmn
+                            + " registeredPlmn:" + registeredPlmn + " mccmnc:" + mccmnc);
+                    return true;
+                }
+            }
+        }
+
+        logd("isSatellitePlmn: return false");
+        return false;
+    }
+
+    /** Get mccmnc string from NetworkRegistrationInfo. */
+    @Nullable
+    public static String getMccMnc(@NonNull NetworkRegistrationInfo nri) {
+        CellIdentity cellIdentity = nri.getCellIdentity();
+        if (cellIdentity == null) {
+            logd("getMccMnc: cellIdentity is null");
+            return null;
+        }
+
+        String mcc = cellIdentity.getMccString();
+        String mnc = cellIdentity.getMncString();
+        if (mcc == null || mnc == null) {
+            logd("getMccMnc: mcc or mnc is null. mcc=" + mcc + " mnc=" + mnc);
+            return null;
+        }
+
+        return mcc + mnc;
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
@@ -398,4 +549,8 @@
     private static void loge(@NonNull String log) {
         Rlog.e(TAG, log);
     }
+
+    private static void logv(@NonNull String log) {
+        Rlog.v(TAG, log);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index dde10a0..f4208f7 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE;
@@ -25,6 +28,13 @@
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_ENABLING_SATELLITE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_LISTENING;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -37,11 +47,11 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.telephony.DropBoxManagerLoggerBackend;
 import android.telephony.PersistentLogger;
-import android.telephony.Rlog;
 import android.telephony.satellite.ISatelliteModemStateCallback;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.stub.ISatelliteGateway;
@@ -52,10 +62,15 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.DeviceStateMonitor;
 import com.android.internal.telephony.ExponentialBackoff;
+import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.android.telephony.Rlog;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -106,10 +121,17 @@
     protected static final int EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT = 6;
     private static final int EVENT_SATELLITE_ENABLEMENT_STARTED = 7;
     private static final int EVENT_SATELLITE_ENABLEMENT_FAILED = 8;
+    private static final int EVENT_SCREEN_STATE_CHANGED = 9;
+    protected static final int EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT = 10;
+    protected static final int EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT = 11;
 
     private static final long REBIND_INITIAL_DELAY = 2 * 1000; // 2 seconds
     private static final long REBIND_MAXIMUM_DELAY = 64 * 1000; // 1 minute
     private static final int REBIND_MULTIPLIER = 2;
+    private static final int DEFAULT_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC = 30;
+    private static final int DEFAULT_P2P_SMS_INACTIVITY_TIMEOUT_SEC = 180;
+    private static final int DEFAULT_ESOS_INACTIVITY_TIMEOUT_SEC = 600;
+
     @NonNull private final ExponentialBackoff mExponentialBackoff;
     @NonNull private final Object mLock = new Object();
     @Nullable
@@ -118,6 +140,7 @@
     @Nullable private SatelliteGatewayServiceConnection mSatelliteGatewayServiceConnection;
     private boolean mIsBound;
     private boolean mIsBinding;
+    private boolean mIsRegisteredScreenStateChanged = false;
 
     @NonNull private static SatelliteSessionController sInstance;
 
@@ -139,14 +162,22 @@
     private long mSatelliteNbIotInactivityTimeoutMillis;
     private final ConcurrentHashMap<IBinder, ISatelliteModemStateCallback> mListeners;
     @SatelliteManager.SatelliteModemState private int mCurrentState;
+    @SatelliteManager.SatelliteModemState private int mPreviousState;
     final boolean mIsSatelliteSupported;
     private boolean mIsDemoMode = false;
+    // Interested in screen off, so use default value true
+    boolean mIsScreenOn = true;
+    private boolean mIsDeviceAlignedWithSatellite = false;
+
     @GuardedBy("mLock")
     @NonNull private boolean mIsDisableCellularModemInProgress = false;
     @NonNull private final SatelliteController mSatelliteController;
     @NonNull private final DatagramController mDatagramController;
     @Nullable private PersistentLogger mPersistentLogger = null;
+    @Nullable private DeviceStateMonitor mDeviceStateMonitor;
+    @NonNull private SessionMetricsStats mSessionMetricsStats;
 
+    @NonNull private FeatureFlags mFeatureFlags;
 
     /**
      * @return The singleton instance of SatelliteSessionController.
@@ -173,12 +204,22 @@
             @NonNull FeatureFlags featureFlags,
             boolean isSatelliteSupported) {
         if (sInstance == null || isSatelliteSupported != sInstance.mIsSatelliteSupported) {
+            ConcurrentHashMap<IBinder, ISatelliteModemStateCallback> existingListeners = null;
+            if (sInstance != null) {
+                existingListeners = sInstance.mListeners;
+                sInstance.cleanUpResource();
+            }
+
             sInstance = new SatelliteSessionController(
                     context,
                     looper,
                     featureFlags,
                     isSatelliteSupported,
                     SatelliteModemInterface.getInstance());
+            if (existingListeners != null) {
+                Log.d(TAG, "make() existingListeners: " + existingListeners.size());
+                sInstance.mListeners.putAll(existingListeners);
+            }
         }
         return sInstance;
     }
@@ -205,6 +246,7 @@
         }
 
         mContext = context;
+        mFeatureFlags = featureFlags;
         mSatelliteModemInterface = satelliteModemInterface;
         mSatelliteController = SatelliteController.getInstance();
         mDatagramController = DatagramController.getInstance();
@@ -215,7 +257,8 @@
                 getSatelliteNbIotInactivityTimeoutMillis();
         mListeners = new ConcurrentHashMap<>();
         mIsSendingTriggeredDuringTransferringState = new AtomicBoolean(false);
-        mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
+        mPreviousState = SATELLITE_MODEM_STATE_UNKNOWN;
+        mCurrentState = SATELLITE_MODEM_STATE_UNKNOWN;
         mIsSatelliteSupported = isSatelliteSupported;
         mExponentialBackoff = new ExponentialBackoff(REBIND_INITIAL_DELAY, REBIND_MAXIMUM_DELAY,
                 REBIND_MULTIPLIER, looper, () -> {
@@ -234,6 +277,13 @@
             bindService();
         });
 
+        Phone phone = mSatelliteController.getSatellitePhone();
+        if (phone == null) {
+            phone = SatelliteServiceUtils.getPhone();
+        }
+        mDeviceStateMonitor = phone.getDeviceStateMonitor();
+        mSessionMetricsStats = SessionMetricsStats.getInstance();
+
         addState(mUnavailableState);
         addState(mPowerOffState);
         addState(mEnablingState);
@@ -290,10 +340,12 @@
     /**
      * {@link SatelliteController} uses this function to notify {@link SatelliteSessionController}
      * that the satellite enablement has just failed.
+     *
+     * @param enabled {@code true} if satellite is enabled, {@code false} satellite is disabled.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public void onSatelliteEnablementFailed() {
-        sendMessage(EVENT_SATELLITE_ENABLEMENT_FAILED);
+    public void onSatelliteEnablementFailed(boolean enabled) {
+        sendMessage(EVENT_SATELLITE_ENABLEMENT_FAILED, enabled);
     }
 
     /**
@@ -308,6 +360,35 @@
     }
 
     /**
+     * {@link SatelliteController} uses this function to notify {@link SatelliteSessionController}
+     * that the satellite emergency mode has changed.
+     *
+     * @param isEmergencyMode The satellite emergency mode.
+     */
+    public void onEmergencyModeChanged(boolean isEmergencyMode) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("onEmergencyModeChanged: carrierRoamingNbIotNtn is disabled");
+            return;
+        }
+
+        plogd("onEmergencyModeChanged " + isEmergencyMode);
+
+        List<ISatelliteModemStateCallback> toBeRemoved = new ArrayList<>();
+        mListeners.values().forEach(listener -> {
+            try {
+                listener.onEmergencyModeChanged(isEmergencyMode);
+            } catch (RemoteException e) {
+                plogd("onEmergencyModeChanged RemoteException: " + e);
+                toBeRemoved.add(listener);
+            }
+        });
+
+        toBeRemoved.forEach(listener -> {
+            mListeners.remove(listener.asBinder());
+        });
+    }
+
+    /**
      * Registers for modem state changed from satellite modem.
      *
      * @param callback The callback to handle the satellite modem state changed event.
@@ -316,6 +397,9 @@
             @NonNull ISatelliteModemStateCallback callback) {
         try {
             callback.onSatelliteModemStateChanged(mCurrentState);
+            if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+                callback.onEmergencyModeChanged(mSatelliteController.getRequestIsEmergency());
+            }
             mListeners.put(callback.asBinder(), callback);
         } catch (RemoteException ex) {
             ploge("registerForSatelliteModemStateChanged: Got RemoteException ex=" + ex);
@@ -413,6 +497,29 @@
     }
 
     /**
+     * Notify whether the device is aligned with the satellite
+     *
+     * @param isAligned {@code true} Device is aligned with the satellite,
+     *                  {@code false} otherwise.
+     */
+    public void setDeviceAlignedWithSatellite(boolean isAligned) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("setDeviceAlignedWithSatellite: carrierRoamingNbIotNtn is disabled");
+            return;
+        }
+
+        mIsDeviceAlignedWithSatellite = isAligned;
+
+        if (mIsDeviceAlignedWithSatellite) {
+            stopCarrierRoamingNbIotInactivityTimer();
+        } else {
+            if (mCurrentState == SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED) {
+                evaluateStartingCarrierRoamingNbIotInactivityTimer();
+            }
+        }
+    }
+
+    /**
      * Get whether state machine is in enabling state.
      *
      * @return {@code true} if state machine is in enabling state and {@code false} otherwise.
@@ -422,6 +529,27 @@
         return getCurrentState() == mEnablingState;
     }
 
+    /**
+     * Get whether state machine is in disabling state.
+     *
+     * @return {@code true} if state machine is in disabling state and {@code false} otherwise.
+     */
+    public boolean isInDisablingState() {
+        if (DBG) plogd("isInDisablingState: getCurrentState=" + getCurrentState());
+        return getCurrentState() == mDisablingState;
+    }
+
+    /**
+     * Release all resource.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public void cleanUpResource() {
+        plogd("cleanUpResource");
+        mIsDeviceAlignedWithSatellite = false;
+        unregisterForScreenStateChanged();
+        quitNow();
+    }
+
     private boolean isDemoMode() {
         return mIsDemoMode;
     }
@@ -441,6 +569,7 @@
         @Override
         public void enter() {
             if (DBG) plogd("Entering UnavailableState");
+            mPreviousState = mCurrentState;
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE;
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE);
         }
@@ -457,6 +586,9 @@
         public void enter() {
             if (DBG) plogd("Entering PowerOffState");
 
+            mSatelliteController.moveSatelliteToOffStateAndCleanUpResources(
+                    SATELLITE_RESULT_REQUEST_ABORTED);
+            mPreviousState = mCurrentState;
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_OFF;
             mIsSendingTriggeredDuringTransferringState.set(false);
             synchronized (mLock) {
@@ -466,6 +598,8 @@
             stopNbIotInactivityTimer();
             DemoSimulator.getInstance().onSatelliteModeOff();
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+
+            unregisterForScreenStateChanged();
         }
 
         @Override
@@ -501,8 +635,9 @@
         public void enter() {
             if (DBG) plogd("Entering EnablingState");
 
-            mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_ENABLING_SATELLITE;
-            notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_ENABLING_SATELLITE);
+            mPreviousState = mCurrentState;
+            mCurrentState = SATELLITE_MODEM_STATE_ENABLING_SATELLITE;
+            notifyStateChangedEvent(SATELLITE_MODEM_STATE_ENABLING_SATELLITE);
         }
 
         @Override
@@ -518,10 +653,17 @@
                     handleSatelliteEnabledStateChanged((boolean) msg.obj);
                     break;
                 case EVENT_SATELLITE_ENABLEMENT_FAILED:
-                    transitionTo(mPowerOffState);
+                    if ((boolean) msg.obj) {
+                        transitionTo(mPowerOffState);
+                    } else {
+                        ploge("Unexpected failed disable event in EnablingState");
+                    }
                     break;
                 case EVENT_SATELLITE_MODEM_STATE_CHANGED:
-                    deferMessage(msg);
+                    handleSatelliteModemStateChanged(msg);
+                    break;
+                case EVENT_SATELLITE_ENABLEMENT_STARTED:
+                    handleSatelliteEnablementStarted((boolean) msg.obj);
                     break;
             }
             // Ignore all unexpected events.
@@ -536,6 +678,8 @@
                     transitionTo(mIdleState);
                 }
                 DemoSimulator.getInstance().onSatelliteModeOn();
+
+                registerForScreenStateChanged();
             } else {
                 /*
                  * During the state transition from ENABLING to NOT_CONNECTED, modem might be
@@ -547,6 +691,15 @@
                 transitionTo(mPowerOffState);
             }
         }
+
+        private void handleSatelliteModemStateChanged(@NonNull Message msg) {
+            int state = msg.arg1;
+            if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
+                transitionTo(mPowerOffState);
+            } else {
+                deferMessage(msg);
+            }
+        }
     }
 
     private class DisablingState extends State {
@@ -554,8 +707,11 @@
         public void enter() {
             if (DBG) plogd("Entering DisablingState");
 
+            mPreviousState = mCurrentState;
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_DISABLING_SATELLITE;
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_DISABLING_SATELLITE);
+
+            unregisterForScreenStateChanged();
         }
 
         @Override
@@ -570,6 +726,32 @@
                 case EVENT_SATELLITE_ENABLED_STATE_CHANGED:
                     handleSatelliteEnabledStateChanged((boolean) msg.obj);
                     break;
+                case EVENT_SATELLITE_ENABLEMENT_FAILED:
+                    boolean enablingSatellite = (boolean) msg.obj;
+                    if (enablingSatellite) {
+                        /* Framework received a disable request when the enable request was in
+                         * progress. We need to set the previous state to OFF since the enable has
+                         * failed so that when disable fail, we can move to OFF state properly.
+                         */
+                        mPreviousState = SatelliteManager.SATELLITE_MODEM_STATE_OFF;
+                        plogd("Enable request has failed. Set mPreviousState to OFF");
+                        break;
+                    }
+                    if (mPreviousState == SATELLITE_MODEM_STATE_CONNECTED
+                            || mPreviousState == SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING
+                            || mPreviousState == SATELLITE_MODEM_STATE_LISTENING) {
+                        transitionTo(mConnectedState);
+                    } else if (mPreviousState == SATELLITE_MODEM_STATE_ENABLING_SATELLITE) {
+                        transitionTo(mEnablingState);
+                    } else if (mPreviousState == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
+                        transitionTo(mPowerOffState);
+                    } else {
+                        transitionTo(mNotConnectedState);
+                    }
+                    break;
+                case EVENT_SATELLITE_MODEM_STATE_CHANGED:
+                    handleEventSatelliteModemStateChanged(msg);
+                    break;
             }
             // Ignore all unexpected events.
             return HANDLED;
@@ -577,17 +759,34 @@
 
         private void handleSatelliteEnabledStateChanged(boolean on) {
             if (on) {
-                plogw("Unexpected power on event while disabling satellite");
+                /* Framework received a disable request when the enable request was in progress.
+                 * We need to set the previous state to NOT_CONNECTED since the enable has
+                 * succeeded so that when disable fail, we can move to NOT_CONNECTED state properly.
+                 */
+                mPreviousState = SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED;
+                plogd("Enable request has succeeded. Set mPreviousState to NOT_CONNECTED");
             } else {
                 transitionTo(mPowerOffState);
             }
         }
+
+        private void handleEventSatelliteModemStateChanged(@NonNull Message msg) {
+            int state = msg.arg1;
+            if (state == SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED) {
+                mPreviousState = state;
+            } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
+                plogd("Modem OFF state will be processed after getting the confirmation of the"
+                        + " disable request");
+                deferMessage(msg);
+            }
+        }
     }
 
     private class IdleState extends State {
         @Override
         public void enter() {
             if (DBG) plogd("Entering IdleState");
+            mPreviousState = mCurrentState;
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_IDLE;
             mIsSendingTriggeredDuringTransferringState.set(false);
             stopNbIotInactivityTimer();
@@ -613,6 +812,15 @@
                 case EVENT_SATELLITE_ENABLEMENT_STARTED:
                     handleSatelliteEnablementStarted((boolean) msg.obj);
                     break;
+                case EVENT_SCREEN_STATE_CHANGED:
+                    handleEventScreenStateChanged((AsyncResult) msg.obj);
+                    break;
+                case EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT:
+                    handleEventScreenOffInactivityTimerTimedOut();
+                    break;
+                case EVENT_SATELLITE_MODEM_STATE_CHANGED:
+                    handleSatelliteModemStateChanged(msg);
+                    break;
             }
             // Ignore all unexpected events.
             return HANDLED;
@@ -656,6 +864,13 @@
             }
         }
 
+        private void handleSatelliteModemStateChanged(@NonNull Message msg) {
+            int state = msg.arg1;
+            if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
+                transitionTo(mPowerOffState);
+            }
+        }
+
         private void disableCellularModemWhileSatelliteModeIsOn() {
             synchronized (mLock) {
                 if (mIsDisableCellularModemInProgress) {
@@ -686,8 +901,9 @@
         public void enter() {
             if (DBG) plogd("Entering TransferringState");
             stopNbIotInactivityTimer();
-            mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING;
-            notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+            mPreviousState = mCurrentState;
+            mCurrentState = SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING;
+            notifyStateChangedEvent(SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
         }
 
         @Override
@@ -706,6 +922,12 @@
                 case EVENT_SATELLITE_ENABLEMENT_STARTED:
                     handleSatelliteEnablementStarted((boolean) msg.obj);
                     break;
+                case EVENT_SCREEN_STATE_CHANGED:
+                    handleEventScreenStateChanged((AsyncResult) msg.obj);
+                    break;
+                case EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT:
+                    handleEventScreenOffInactivityTimerTimedOut();
+                    break;
             }
             // Ignore all unexpected events.
             return HANDLED;
@@ -736,6 +958,8 @@
                 @SatelliteManager.SatelliteModemState int state) {
             if (state == SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED) {
                 transitionTo(mNotConnectedState);
+            } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
+                transitionTo(mPowerOffState);
             }
         }
     }
@@ -745,11 +969,12 @@
         public void enter() {
             if (DBG) plogd("Entering ListeningState");
 
-            mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_LISTENING;
+            mPreviousState = mCurrentState;
+            mCurrentState = SATELLITE_MODEM_STATE_LISTENING;
             long timeoutMillis = updateListeningMode(true);
             sendMessageDelayed(EVENT_LISTENING_TIMER_TIMEOUT, timeoutMillis);
             mIsSendingTriggeredDuringTransferringState.set(false);
-            notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_LISTENING);
+            notifyStateChangedEvent(SATELLITE_MODEM_STATE_LISTENING);
         }
 
         @Override
@@ -776,6 +1001,15 @@
                 case EVENT_SATELLITE_ENABLEMENT_STARTED:
                     handleSatelliteEnablementStarted((boolean) msg.obj);
                     break;
+                case EVENT_SCREEN_STATE_CHANGED:
+                    handleEventScreenStateChanged((AsyncResult) msg.obj);
+                    break;
+                case EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT:
+                    handleEventScreenOffInactivityTimerTimedOut();
+                    break;
+                case EVENT_SATELLITE_MODEM_STATE_CHANGED:
+                    handleEventSatelliteModemStateChange(msg);
+                    break;
             }
             // Ignore all unexpected events.
             return HANDLED;
@@ -801,6 +1035,13 @@
                 transitionTo(mTransferringState);
             }
         }
+
+        private void handleEventSatelliteModemStateChange(@NonNull Message msg) {
+            int state = msg.arg1;
+            if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
+                transitionTo(mPowerOffState);
+            }
+        }
     }
 
     private class NotConnectedState extends State {
@@ -808,14 +1049,18 @@
         public void enter() {
             if (DBG) plogd("Entering NotConnectedState");
 
+            mPreviousState = mCurrentState;
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED;
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
             startNbIotInactivityTimer();
+            evaluateStartingCarrierRoamingNbIotInactivityTimer();
         }
 
         @Override
         public void exit() {
             if (DBG) plogd("Exiting NotConnectedState");
+
+            stopCarrierRoamingNbIotInactivityTimer();
         }
 
         @Override
@@ -829,6 +1074,8 @@
                 case EVENT_SATELLITE_MODEM_STATE_CHANGED:
                     handleEventSatelliteModemStateChanged(msg.arg1);
                     break;
+                case EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT:
+                    // fall through
                 case EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT:
                     transitionTo(mIdleState);
                     break;
@@ -838,6 +1085,12 @@
                 case EVENT_SATELLITE_ENABLEMENT_STARTED:
                     handleSatelliteEnablementStarted((boolean) msg.obj);
                     break;
+                case EVENT_SCREEN_STATE_CHANGED:
+                    handleEventScreenStateChanged((AsyncResult) msg.obj);
+                    break;
+                case EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT:
+                    handleEventScreenOffInactivityTimerTimedOut();
+                    break;
             }
             // Ignore all unexpected events.
             return HANDLED;
@@ -845,8 +1098,10 @@
 
         private void handleEventSatelliteModemStateChanged(
                 @SatelliteManager.SatelliteModemState int state) {
-            if (state == SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED) {
+            if (state == SATELLITE_MODEM_STATE_CONNECTED) {
                 transitionTo(mConnectedState);
+            } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
+                transitionTo(mPowerOffState);
             }
         }
 
@@ -857,13 +1112,17 @@
                     || datagramTransferState.receiveState
                     == SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT) {
                 stopNbIotInactivityTimer();
+                stopCarrierRoamingNbIotInactivityTimer();
             } else if (datagramTransferState.sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE
                     && datagramTransferState.receiveState
                     == SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE) {
                 startNbIotInactivityTimer();
+                evaluateStartingCarrierRoamingNbIotInactivityTimer();
             } else if (isSending(datagramTransferState.sendState)
                     || isReceiving(datagramTransferState.receiveState)) {
                 restartNbIotInactivityTimer();
+                stopCarrierRoamingNbIotInactivityTimer();
+                evaluateStartingCarrierRoamingNbIotInactivityTimer();
             }
         }
     }
@@ -873,8 +1132,9 @@
         public void enter() {
             if (DBG) plogd("Entering ConnectedState");
 
-            mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
-            notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+            mPreviousState = mCurrentState;
+            mCurrentState = SATELLITE_MODEM_STATE_CONNECTED;
+            notifyStateChangedEvent(SATELLITE_MODEM_STATE_CONNECTED);
             startNbIotInactivityTimer();
         }
 
@@ -903,6 +1163,12 @@
                 case EVENT_SATELLITE_ENABLEMENT_STARTED:
                     handleSatelliteEnablementStarted((boolean) msg.obj);
                     break;
+                case EVENT_SCREEN_STATE_CHANGED:
+                    handleEventScreenStateChanged((AsyncResult) msg.obj);
+                    break;
+                case EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT:
+                    handleEventScreenOffInactivityTimerTimedOut();
+                    break;
             }
             // Ignore all unexpected events.
             return HANDLED;
@@ -912,6 +1178,8 @@
                 @SatelliteManager.SatelliteModemState int state) {
             if (state == SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED) {
                 transitionTo(mNotConnectedState);
+            } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
+                transitionTo(mPowerOffState);
             }
         }
 
@@ -955,6 +1223,15 @@
             case EVENT_SATELLITE_ENABLEMENT_FAILED:
                 whatString = "EVENT_SATELLITE_ENABLEMENT_FAILED";
                 break;
+            case EVENT_SCREEN_STATE_CHANGED:
+                whatString = "EVENT_SCREEN_STATE_CHANGED";
+                break;
+            case EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT:
+                whatString = "EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT";
+                break;
+            case EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT:
+                whatString = "EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT";
+                break;
             default:
                 whatString = "UNKNOWN EVENT " + what;
         }
@@ -969,6 +1246,15 @@
         }
     }
 
+    private int getSubId() {
+        Phone phone = mSatelliteController.getSatellitePhone();
+        if (phone == null) {
+            return SatelliteServiceUtils.getPhone().getSubId();
+        }
+
+        return phone.getSubId();
+    }
+
     private void notifyStateChangedEvent(@SatelliteManager.SatelliteModemState int state) {
         mDatagramController.onSatelliteModemStateChanged(state);
 
@@ -1074,6 +1360,7 @@
             mSatelliteGatewayServiceConnection = null;
         }
     }
+
     private class SatelliteGatewayServiceConnection implements ServiceConnection {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -1114,6 +1401,172 @@
         }
     }
 
+    private void registerForScreenStateChanged() {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            Rlog.d(TAG, "registerForScreenStateChanged: carrierRoamingNbIotNtn is disabled");
+            return;
+        }
+
+        if (mSatelliteController.getRequestIsEmergency()) {
+            if (DBG) logd("registerScreenOnOffChanged: Emergency mode");
+            // screen on/off timer is available in not emergency mode
+            return;
+        }
+
+        if (!mIsRegisteredScreenStateChanged && mDeviceStateMonitor != null) {
+            mDeviceStateMonitor.registerForScreenStateChanged(
+                    getHandler(), EVENT_SCREEN_STATE_CHANGED, null);
+
+            mIsRegisteredScreenStateChanged = true;
+            plogd("registerForScreenStateChanged: registered");
+        } else {
+            plogw("registerForScreenStateChanged: skip register, mIsRegisteredScreenStateChanged="
+                    + mIsRegisteredScreenStateChanged + ","
+                    + " mDeviceStateMonitor=" + (mDeviceStateMonitor != null));
+        }
+    }
+
+    private void unregisterForScreenStateChanged() {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            Rlog.d(TAG, "unregisterForScreenStateChanged: carrierRoamingNbIotNtn is disabled");
+            return;
+        }
+
+        if (mIsRegisteredScreenStateChanged && mDeviceStateMonitor != null) {
+            mDeviceStateMonitor.unregisterForScreenStateChanged(getHandler());
+            removeMessages(EVENT_SCREEN_STATE_CHANGED);
+            removeMessages(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT);
+
+            removeDeferredMessages(EVENT_SCREEN_STATE_CHANGED);
+            removeDeferredMessages(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT);
+
+            mIsRegisteredScreenStateChanged = false;
+            plogd("unregisterForScreenStateChanged: unregistered");
+        }
+    }
+
+    private void handleEventScreenStateChanged(AsyncResult asyncResult) {
+        if (asyncResult == null) {
+            ploge("handleEventScreenStateChanged: asyncResult is null");
+            return;
+        }
+
+        boolean screenOn = (boolean) asyncResult.result;
+        if (mIsScreenOn == screenOn) {
+            if (DBG) plogd("handleEventScreenStateChanged: screen state is not changed");
+            return;
+        }
+        mIsScreenOn = screenOn;
+
+        if (!screenOn) {
+            // Screen off, start timer
+            int timeoutMillis = getScreenOffInactivityTimeoutDurationSec() * 1000;
+            sendMessageDelayed(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT, timeoutMillis);
+
+            plogd("handleEventScreenStateChanged: start timer " + timeoutMillis);
+        } else {
+            // Screen on, stop timer
+            removeMessages(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT);
+
+            plogd("handleEventScreenStateChanged: stop timer");
+        }
+    }
+
+    private int getScreenOffInactivityTimeoutDurationSec() {
+        PersistableBundle config = mSatelliteController.getPersistableBundle(getSubId());
+
+        return config.getInt(KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT,
+                DEFAULT_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC);
+    }
+
+    private int getP2pSmsInactivityTimeoutDurationSec() {
+        PersistableBundle config = mSatelliteController.getPersistableBundle(getSubId());
+
+        return config.getInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
+                DEFAULT_P2P_SMS_INACTIVITY_TIMEOUT_SEC);
+    }
+
+    private int getEsosInactivityTimeoutDurationSec() {
+        PersistableBundle config = mSatelliteController.getPersistableBundle(getSubId());
+
+        return config.getInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT,
+                DEFAULT_ESOS_INACTIVITY_TIMEOUT_SEC);
+    }
+
+    private void evaluateStartingCarrierRoamingNbIotInactivityTimer() {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: "
+                    + "carrierRoamingNbIotNtn is disabled");
+            return;
+        }
+
+        int subId = getSubId();
+        if (!mSatelliteController.isSatelliteRoamingP2pSmSSupported(subId)
+                && !mSatelliteController.isSatelliteEsosSupported(subId)) {
+            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: "
+                    + "device does not support P2P SMS and ESOS are disabled");
+            return;
+        }
+
+        if (mIsDeviceAlignedWithSatellite) {
+            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: "
+                    + "can't start inactivity timer due to device aligned satellite");
+            return;
+        }
+
+        if (hasMessages(EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT)) {
+            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: already started");
+            return;
+        }
+
+        int timeOutMillis;
+        if (mSatelliteController.getRequestIsEmergency()) {
+            timeOutMillis = getEsosInactivityTimeoutDurationSec() * 1000;
+        } else if (mSatelliteController.isInCarrierRoamingNbIotNtn()) {
+            timeOutMillis = getP2pSmsInactivityTimeoutDurationSec() * 1000;
+        } else {
+            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: "
+                    + "can't start inactivity timer device is in not P2P SMS and ESOS mode");
+            return;
+        }
+
+        DatagramController datagramController = DatagramController.getInstance();
+        if (datagramController.isSendingInIdleState()
+                && datagramController.isPollingInIdleState()) {
+            sendMessageDelayed(EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT,
+                    timeOutMillis);
+            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: start inactivity timer "
+                    + timeOutMillis);
+        } else {
+            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: "
+                    + "can't start inactivity timer");
+        }
+    }
+
+    private void stopCarrierRoamingNbIotInactivityTimer() {
+        removeMessages(EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT);
+        plogd("stopCarrierRoamingNbIotInactivityTimer:");
+    }
+
+    private void handleEventScreenOffInactivityTimerTimedOut() {
+        plogd("handleEventScreenOffInactivityTimerTimedOut: request disable satellite");
+
+        mSatelliteController.requestSatelliteEnabled(
+                false /*enableSatellite*/,
+                false /*enableDemoMode*/,
+                mSatelliteController.getRequestIsEmergency() /*isEmergency*/,
+                new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        plogd("requestSatelliteEnabled result=" + result);
+                        if (result == SATELLITE_RESULT_SUCCESS) {
+                            mSessionMetricsStats.addCountOfAutoExitDueToScreenOff();
+                        }
+                        // TODO(b/364738085): Add CountOfAutoExitDueToTnNetwork
+                    }
+                });
+    }
+
     private boolean isMockModemAllowed() {
         return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false));
     }
@@ -1152,6 +1605,12 @@
     }
 
     private void startNbIotInactivityTimer() {
+        if (!isSatelliteEnabledForNtnOnlySubscription()) {
+            plogd("startNbIotInactivityTimer: Can't start timer "
+                    + "because satellite was not enabled for OEM based NB IOT");
+            return;
+        }
+
         if (isNbIotInactivityTimerStarted()) {
             plogd("NB IOT inactivity timer is already started");
             return;
@@ -1174,6 +1633,18 @@
         return hasMessages(EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT);
     }
 
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected boolean isSatelliteEnabledForNtnOnlySubscription() {
+        if (SatelliteServiceUtils.getNtnOnlySubscriptionId(mContext)
+                != getSubId()) {
+            plogd("isSatelliteEnabledForOemBasedNbIot: highest priority satellite subscription "
+                    + "is not NTN-only subscription");
+            return false;
+        }
+
+        return true;
+    }
+
     private boolean isSatellitePersistentLoggingEnabled(
             @NonNull Context context, @NonNull FeatureFlags featureFlags) {
         if (featureFlags.satellitePersistentLogging()) {
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/AccessControllerMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/AccessControllerMetricsStats.java
index 13ba709..4333253 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/AccessControllerMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/AccessControllerMetricsStats.java
@@ -15,9 +15,12 @@
  */
 package com.android.internal.telephony.satellite.metrics;
 
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
+import static com.android.internal.telephony.satellite.SatelliteConstants.ACCESS_CONTROL_TYPE_UNKNOWN;
 import static com.android.internal.telephony.satellite.SatelliteConstants.CONFIG_DATA_SOURCE_UNKNOWN;
+import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_UNKNOWN;
 
 import android.annotation.NonNull;
 import android.telephony.satellite.SatelliteManager;
@@ -42,6 +45,8 @@
     private @SatelliteManager.SatelliteResult int mResultCode;
     private String[] mCountryCodes;
     private @SatelliteConstants.ConfigDataSource int mConfigDataSource;
+    private int mCarrierId;
+    private @SatelliteConstants.TriggeringEvent int mTriggeringEvent;
     private AccessControllerMetricsStats() {
         initializeAccessControllerMetricsParam();
     }
@@ -61,7 +66,7 @@
         return sInstance;
     }
     private void initializeAccessControllerMetricsParam() {
-        mAccessControlType = SatelliteConstants.ACCESS_CONTROL_TYPE_UNKNOWN;
+        mAccessControlType = ACCESS_CONTROL_TYPE_UNKNOWN;
         mLocationQueryTimeMillis = 0;
         mOnDeviceLookupTimeMillis = 0;
         mTotalCheckingTimeMillis = 0;
@@ -70,6 +75,8 @@
         mResultCode = SATELLITE_RESULT_SUCCESS;
         mCountryCodes = new String[0];
         mConfigDataSource = CONFIG_DATA_SOURCE_UNKNOWN;
+        mCarrierId = UNKNOWN_CARRIER_ID;
+        mTriggeringEvent = TRIGGERING_EVENT_UNKNOWN;
     }
     /**
      * Sets the Access Control Type for current satellite enablement.
@@ -161,6 +168,26 @@
         logd("setConfigDataSource: config data source = " + mConfigDataSource);
         return this;
     }
+    /**
+     * Sets the carrier id for NTN satellite service.
+     * @param carrierId Carrier ID of currently available NTN Satellite Network.
+     */
+    public AccessControllerMetricsStats setCarrierId(int carrierId) {
+        mCarrierId = carrierId;
+        logd("setCarrierId: Carrier ID = " + mCarrierId);
+        return this;
+    }
+    /**
+     * Sets the triggering event for satellite access controller operation.
+     * @param triggeringEvent triggering event.
+     */
+    public AccessControllerMetricsStats setTriggeringEvent(
+            @SatelliteConstants.TriggeringEvent int triggeringEvent) {
+        mTriggeringEvent = triggeringEvent;
+        logd("setTriggeringEvent: triggering event = " + mTriggeringEvent);
+        return this;
+    }
+
     /** Report the access controller metrics atoms to PersistAtomsStorage in telephony. */
     public void reportAccessControllerMetrics() {
         SatelliteStats.SatelliteAccessControllerParams accessControllerParams =
@@ -174,6 +201,8 @@
                         .setResult(mResultCode)
                         .setCountryCodes(mCountryCodes)
                         .setConfigDatasource(mConfigDataSource)
+                        .setCarrierId(mCarrierId)
+                        .setTriggeringEvent(mTriggeringEvent)
                         .build();
         logd("reportAccessControllerMetrics: " + accessControllerParams.toString());
         SatelliteStats.getInstance().onSatelliteAccessControllerMetrics(accessControllerParams);
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteControllerStats.java b/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteControllerStats.java
index 9524b75..e97d234 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteControllerStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteControllerStats.java
@@ -80,6 +80,22 @@
                         .build());
     }
 
+    /** Capture the NB-IoT NTN carrier ID */
+    public void reportCarrierId(int carrierId) {
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(
+                new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                        .setCarrierId(carrierId)
+                        .build());
+    }
+
+    /** Capture whether the device is satellite entitled or not */
+    public void reportIsDeviceEntitled(boolean isDeviceEntitled) {
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(
+                new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                        .setIsDeviceEntitled(isDeviceEntitled)
+                        .build());
+    }
+
     private static void logd(@NonNull String log) {
         Log.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
index 7dff2e6..ec135da 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
@@ -371,6 +371,54 @@
         }
     }
 
+    /** Capture the latest provisioned state for satellite service */
+    @VisibleForTesting
+    public void setIsProvisioned(boolean isProvisioned) {
+        logd("setIsProvisioned:" + isProvisioned);
+        mSatelliteStats.onSatelliteControllerMetrics(
+                new SatelliteStats.SatelliteControllerParams.Builder()
+                        .setIsProvisioned(isProvisioned)
+                        .build());
+    }
+
+    /** Capture the NB-IoT NTN carrier ID */
+    @VisibleForTesting
+    public void setCarrierId(int carrierId) {
+        logd("setCarrierId:" + carrierId);
+        mSatelliteStats.onSatelliteControllerMetrics(
+                new SatelliteStats.SatelliteControllerParams.Builder()
+                        .setCarrierId(carrierId)
+                        .build());
+    }
+
+    /**
+     * Report a counter when allowed state has changed.
+     */
+    public void reportAllowedStateChanged() {
+        logd("reportAllowedStateChanged:");
+        mSatelliteStats.onSatelliteControllerMetrics(
+                new SatelliteStats.SatelliteControllerParams.Builder()
+                        .setCountOfSatelliteAllowedStateChangedEvents(ADD_COUNT)
+                        .build());
+    }
+
+    /**
+     * Report a counter when location query was successful or failed.
+     */
+    public void reportLocationQuerySuccessful(boolean result) {
+        SatelliteStats.SatelliteControllerParams.Builder builder;
+        if (result) {
+            builder = new SatelliteStats.SatelliteControllerParams.Builder()
+                    .setCountOfSuccessfulLocationQueries(ADD_COUNT);
+        } else {
+            builder = new SatelliteStats.SatelliteControllerParams.Builder()
+                    .setCountOfFailedLocationQueries(ADD_COUNT);
+        }
+        SatelliteStats.SatelliteControllerParams controllerParam = builder.build();
+        logd("reportLocationQuerySuccessful:" + controllerParam);
+        mSatelliteStats.onSatelliteControllerMetrics(controllerParam);
+    }
+
     /** Receives the battery status whether it is in charging or not, update interval is 60 sec. */
     private final BroadcastReceiver mBatteryStatusReceiver = new BroadcastReceiver() {
         private long mLastUpdatedTime = 0;
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java
index 0647231..73be042 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.satellite.metrics;
 
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
+
 import android.annotation.NonNull;
 import android.telephony.satellite.SatelliteManager;
 import android.util.Log;
@@ -37,6 +39,7 @@
     private int mProvisioningStartTimeSec;
     private boolean mIsProvisionRequest;
     private boolean mIsCanceled;
+    private int mCarrierId;
 
     private ProvisionMetricsStats() {
         initializeProvisionParams();
@@ -80,6 +83,12 @@
         return this;
     }
 
+    /** Sets the Carrier of NTN satellite */
+    public ProvisionMetricsStats setCarrierId(int carrierId) {
+        mCarrierId = carrierId;
+        return this;
+    }
+
     /** Report the provision metrics atoms to PersistAtomsStorage in telephony */
     public void reportProvisionMetrics() {
         SatelliteStats.SatelliteProvisionParams provisionParams =
@@ -89,9 +98,10 @@
                                 (System.currentTimeMillis() / 1000) - mProvisioningStartTimeSec)
                         .setIsProvisionRequest(mIsProvisionRequest)
                         .setIsCanceled(mIsCanceled)
+                        .setCarrierId(mCarrierId)
                         .build();
         SatelliteStats.getInstance().onSatelliteProvisionMetrics(provisionParams);
-        logd("reportProvisionMetrics: " + provisionParams.toString());
+        logd("reportProvisionMetrics: " + provisionParams);
         initializeProvisionParams();
     }
 
@@ -100,6 +110,7 @@
         mProvisioningStartTimeSec = INVALID_TIME;
         mIsProvisionRequest = false;
         mIsCanceled = false;
+        mCarrierId = UNKNOWN_CARRIER_ID;
     }
 
     private static void logd(@NonNull String log) {
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
index 65181c0..a234378 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.satellite.metrics;
 
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
@@ -52,6 +53,10 @@
     private int mCountOfIncomingDatagramFailed;
     private boolean mIsDemoMode;
     private @NtnSignalStrength.NtnSignalStrengthLevel int mMaxNtnSignalStrengthLevel;
+    private int mCarrierId;
+    private int mCountOfSatelliteNotificationDisplayed;
+    private int mCountOfAutoExitDueToScreenOff;
+    private int mCountOfAutoExitDueToTnNetwork;
 
     private SessionMetricsStats() {
         initializeSessionMetricsParam();
@@ -213,6 +218,35 @@
         return this;
     }
 
+    /** Sets the Carrier ID of this NTN session. */
+    public SessionMetricsStats setCarrierId(int carrierId) {
+        mCarrierId = carrierId;
+        logd("setCarrierId(" + carrierId + ")");
+        return this;
+    }
+
+    /** Increase the count of Satellite Notification Display. */
+    public SessionMetricsStats addCountOfSatelliteNotificationDisplayed() {
+        mCountOfSatelliteNotificationDisplayed++;
+        logd("addCountOfSatelliteNotificationDisplayed: current count="
+                + mCountOfSatelliteNotificationDisplayed);
+        return this;
+    }
+
+    /** Increase the count of auto exit from P2P satellite messaging due to screen off. */
+    public SessionMetricsStats addCountOfAutoExitDueToScreenOff() {
+        mCountOfAutoExitDueToScreenOff++;
+        logd("addCountOfAutoExitDueToScreenOff: current count=" + mCountOfAutoExitDueToScreenOff);
+        return this;
+    }
+
+    /** Increase the count of auto exit from P2P satellite messaging due to scan TN network. */
+    public SessionMetricsStats addCountOfAutoExitDueToTnNetwork() {
+        mCountOfAutoExitDueToTnNetwork++;
+        logd("addCountOfAutoExitDueToTnNetwork: current count=" + mCountOfAutoExitDueToTnNetwork);
+        return this;
+    }
+
     /** Report the session metrics atoms to PersistAtomsStorage in telephony. */
     public void reportSessionMetrics() {
         SatelliteStats.SatelliteSessionParams sessionParams =
@@ -229,6 +263,11 @@
                         .setCountOfIncomingDatagramFailed(mCountOfIncomingDatagramFailed)
                         .setIsDemoMode(mIsDemoMode)
                         .setMaxNtnSignalStrengthLevel(mMaxNtnSignalStrengthLevel)
+                        .setCarrierId(mCarrierId)
+                        .setCountOfSatelliteNotificationDisplayed(
+                                mCountOfSatelliteNotificationDisplayed)
+                        .setCountOfAutoExitDueToScreenOff(mCountOfAutoExitDueToScreenOff)
+                        .setCountOfAutoExitDueToTnNetwork(mCountOfAutoExitDueToTnNetwork)
                         .build();
         logd("reportSessionMetrics: " + sessionParams.toString());
         SatelliteStats.getInstance().onSatelliteSessionMetrics(sessionParams);
@@ -277,6 +316,10 @@
         mCountOfIncomingDatagramFailed = 0;
         mIsDemoMode = false;
         mMaxNtnSignalStrengthLevel = NTN_SIGNAL_STRENGTH_NONE;
+        mCarrierId = UNKNOWN_CARRIER_ID;
+        mCountOfSatelliteNotificationDisplayed = 0;
+        mCountOfAutoExitDueToScreenOff = 0;
+        mCountOfAutoExitDueToTnNetwork = 0;
     }
 
     private static void logd(@NonNull String log) {
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
index 7596754..d835f2d 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
@@ -280,7 +280,7 @@
                     SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
                     SubscriptionInfoInternal::getSatelliteAttachEnabledForCarrier),
             new AbstractMap.SimpleImmutableEntry<>(
-                    SimInfo.COLUMN_IS_NTN,
+                    SimInfo.COLUMN_IS_ONLY_NTN,
                     SubscriptionInfoInternal::getOnlyNonTerrestrialNetwork),
             new AbstractMap.SimpleImmutableEntry<>(
                     SimInfo.COLUMN_SERVICE_CAPABILITIES,
@@ -293,7 +293,13 @@
                     SubscriptionInfoInternal::getSatelliteEntitlementStatus),
             new AbstractMap.SimpleImmutableEntry<>(
                     SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS,
-                    SubscriptionInfoInternal::getSatelliteEntitlementPlmns)
+                    SubscriptionInfoInternal::getSatelliteEntitlementPlmns),
+            new AbstractMap.SimpleImmutableEntry<>(
+                    SimInfo.COLUMN_SATELLITE_ESOS_SUPPORTED,
+                    SubscriptionInfoInternal::getSatelliteESOSSupported),
+            new AbstractMap.SimpleImmutableEntry<>(
+                    SimInfo.COLUMN_IS_SATELLITE_PROVISIONED_FOR_NON_IP_DATAGRAM,
+                    SubscriptionInfoInternal::getIsSatelliteProvisionedForNonIpDatagram)
     );
 
     /**
@@ -423,7 +429,7 @@
                     SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
                     SubscriptionDatabaseManager::setSatelliteAttachEnabledForCarrier),
             new AbstractMap.SimpleImmutableEntry<>(
-                    SimInfo.COLUMN_IS_NTN,
+                    SimInfo.COLUMN_IS_ONLY_NTN,
                     SubscriptionDatabaseManager::setNtn),
             new AbstractMap.SimpleImmutableEntry<>(
                     SimInfo.COLUMN_SERVICE_CAPABILITIES,
@@ -433,7 +439,13 @@
                     SubscriptionDatabaseManager::setTransferStatus),
             new AbstractMap.SimpleImmutableEntry<>(
                     SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS,
-                    SubscriptionDatabaseManager::setSatelliteEntitlementStatus)
+                    SubscriptionDatabaseManager::setSatelliteEntitlementStatus),
+            new AbstractMap.SimpleImmutableEntry<>(
+                    SimInfo.COLUMN_SATELLITE_ESOS_SUPPORTED,
+                    SubscriptionDatabaseManager::setSatelliteESOSSupported),
+            new AbstractMap.SimpleImmutableEntry<>(
+                    SimInfo.COLUMN_IS_SATELLITE_PROVISIONED_FOR_NON_IP_DATAGRAM,
+                    SubscriptionDatabaseManager::setIsSatelliteProvisionedForNonIpDatagram)
     );
 
     /**
@@ -2063,7 +2075,7 @@
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             return;
         }
-        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IS_NTN, isNtn,
+        writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_IS_ONLY_NTN, isNtn,
                 SubscriptionInfoInternal.Builder::setOnlyNonTerrestrialNetwork);
     }
 
@@ -2162,6 +2174,41 @@
     }
 
     /**
+     * Set whether the carrier roaming to satellite is using ESOS for emergency messaging.
+     *
+     * @param subId Subscription id.
+     * @param isSatelliteESOSSupported whether the carrier roaming to satellite is using ESOS for
+     * emergency messaging.
+     * @throws IllegalArgumentException if the subscription does not exist.
+     */
+    public void setSatelliteESOSSupported(int subId, int isSatelliteESOSSupported) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            return;
+        }
+        writeDatabaseAndCacheHelper(subId,
+                SimInfo.COLUMN_SATELLITE_ESOS_SUPPORTED, isSatelliteESOSSupported,
+                SubscriptionInfoInternal.Builder::setSatelliteESOSSupported);
+    }
+
+    /**
+     * Set whether the subscription is provisioned for OEM-enabled or carrier roaming NB-IOT
+     * satellite service.
+     *
+     * @param subId Subscription ID.
+     * @param isSatelliteProvisionedForNonIpDatagram {@code 1} if it is provisioned for OEM-enabled
+     * or carrier roaming NB-IOT satellite service. {@code 0} otherwise.
+     *
+     * @throws IllegalArgumentException if the subscription does not exist.
+     */
+    public void setIsSatelliteProvisionedForNonIpDatagram(int subId,
+            int isSatelliteProvisionedForNonIpDatagram) {
+        writeDatabaseAndCacheHelper(subId,
+                SimInfo.COLUMN_IS_SATELLITE_PROVISIONED_FOR_NON_IP_DATAGRAM,
+                isSatelliteProvisionedForNonIpDatagram,
+                SubscriptionInfoInternal.Builder::setIsSatelliteProvisionedForNonIpDatagram);
+    }
+
+    /**
      * Reload the database from content provider to the cache. This must be a synchronous operation
      * to prevent cache/database out-of-sync. Callers should be cautious to call this method because
      * it might take longer time to complete.
@@ -2399,15 +2446,22 @@
                                 SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS)))
                 .setSatelliteEntitlementPlmns(cursor.getString(
                         cursor.getColumnIndexOrThrow(
-                                SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS)));
+                                SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS)))
+                .setIsSatelliteProvisionedForNonIpDatagram(cursor.getInt(
+                        cursor.getColumnIndexOrThrow(
+                                SimInfo.COLUMN_IS_SATELLITE_PROVISIONED_FOR_NON_IP_DATAGRAM)));
         if (mFeatureFlags.oemEnabledSatelliteFlag()) {
             builder.setOnlyNonTerrestrialNetwork(cursor.getInt(cursor.getColumnIndexOrThrow(
-                    SimInfo.COLUMN_IS_NTN)));
+                    SimInfo.COLUMN_IS_ONLY_NTN)));
         }
         if (mFeatureFlags.supportPsimToEsimConversion()) {
             builder.setTransferStatus(cursor.getInt(cursor.getColumnIndexOrThrow(
                     SimInfo.COLUMN_TRANSFER_STATUS)));
         }
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+            builder.setSatelliteESOSSupported(cursor.getInt(
+                    cursor.getColumnIndexOrThrow(SimInfo.COLUMN_SATELLITE_ESOS_SUPPORTED)));
+        }
         return builder.build();
     }
 
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
index c6dee7c..92e112d 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
@@ -492,6 +492,19 @@
     @NonNull private final String mSatelliteEntitlementPlmns;
 
     /**
+     * Whether the carrier roaming to satellite is using ESOS for emergency messaging.
+     * By default, its disabled. It is intended to use integer to fit the database format.
+     */
+    private final int mIsSatelliteESOSSupported;
+
+    /**
+     * Whether this subscription is provisioned for OEM-enabled or carrier roaming NB-IOT satellite
+     * service or not.
+     * By default, its disabled. It is intended to use integer to fit the database format.
+     */
+    private final int mIsSatelliteProvisionedForNonIpDatagram;
+
+    /**
      * Constructor from builder.
      *
      * @param builder Builder of {@link SubscriptionInfoInternal}.
@@ -570,6 +583,9 @@
         this.mTransferStatus = builder.mTransferStatus;
         this.mIsSatelliteEntitlementStatus = builder.mIsSatelliteEntitlementStatus;
         this.mSatelliteEntitlementPlmns = builder.mSatelliteEntitlementPlmns;
+        this.mIsSatelliteESOSSupported = builder.mIsSatelliteESOSSupported;
+        this.mIsSatelliteProvisionedForNonIpDatagram =
+                builder.mIsSatelliteProvisionedForNonIpDatagram;
     }
 
     /**
@@ -1266,6 +1282,23 @@
         return mSatelliteEntitlementPlmns;
     }
 
+    /**
+     * @return {@code 1} if the carrier roaming to satellite is using ESOS for emergency messaging.
+     */
+    public int getSatelliteESOSSupported() {
+        return mIsSatelliteESOSSupported;
+    }
+
+    /**
+     * Return whether the subscription is provisioned for oem satellite service or not.
+     *
+     * @return {@code 1} if the subscription is provisioned for oem stellite service. {@code 0}
+     * otherwise.
+     */
+    public int getIsSatelliteProvisionedForNonIpDatagram() {
+        return mIsSatelliteProvisionedForNonIpDatagram;
+    }
+
     /** @return converted {@link SubscriptionInfo}. */
     @NonNull
     public SubscriptionInfo toSubscriptionInfo() {
@@ -1305,6 +1338,7 @@
                 .setServiceCapabilities(
                         SubscriptionManager.getServiceCapabilitiesSet(mServiceCapabilities))
                 .setTransferStatus(mTransferStatus)
+                .setSatelliteESOSSupported(mIsSatelliteESOSSupported == 1)
                 .build();
     }
 
@@ -1368,6 +1402,9 @@
                 + " transferStatus=" + mTransferStatus
                 + " satelliteEntitlementStatus=" + mIsSatelliteEntitlementStatus
                 + " satelliteEntitlementPlmns=" + mSatelliteEntitlementPlmns
+                + " isSatelliteESOSSupported=" + mIsSatelliteESOSSupported
+                + " isSatelliteProvisionedForNonIpDatagram="
+                + mIsSatelliteProvisionedForNonIpDatagram
                 + "]";
     }
 
@@ -1430,7 +1467,10 @@
                 && mServiceCapabilities == that.mServiceCapabilities
                 && mTransferStatus == that.mTransferStatus
                 && mIsSatelliteEntitlementStatus == that.mIsSatelliteEntitlementStatus
-                && mSatelliteEntitlementPlmns == that.mSatelliteEntitlementPlmns;
+                && mSatelliteEntitlementPlmns.equals(that.mSatelliteEntitlementPlmns)
+                && mIsSatelliteESOSSupported == that.mIsSatelliteESOSSupported
+                && mIsSatelliteProvisionedForNonIpDatagram
+                == that.mIsSatelliteProvisionedForNonIpDatagram;
     }
 
     @Override
@@ -1463,7 +1503,8 @@
                 mIsSatelliteEnabled, mCardId, mIsGroupDisabled,
                 mIsSatelliteAttachEnabledForCarrier, mIsOnlyNonTerrestrialNetwork,
                 mServiceCapabilities, mTransferStatus, mIsSatelliteEntitlementStatus,
-                mSatelliteEntitlementPlmns);
+                mSatelliteEntitlementPlmns, mIsSatelliteESOSSupported,
+                mIsSatelliteProvisionedForNonIpDatagram);
         result = 31 * result + Arrays.hashCode(mNativeAccessRules);
         result = 31 * result + Arrays.hashCode(mCarrierConfigAccessRules);
         result = 31 * result + Arrays.hashCode(mRcsConfig);
@@ -1872,6 +1913,16 @@
         private String mSatelliteEntitlementPlmns = "";
 
         /**
+         * Whether the carrier roaming to satellite is using ESOS for emergency messaging.
+         */
+        private int mIsSatelliteESOSSupported = 0;
+
+        /**
+         * Whether this subscription is provisioned for oem satellite service or not.
+         */
+        private int mIsSatelliteProvisionedForNonIpDatagram = 0;
+
+        /**
          * Default constructor.
          */
         public Builder() {
@@ -1953,6 +2004,8 @@
             mTransferStatus = info.mTransferStatus;
             mIsSatelliteEntitlementStatus = info.mIsSatelliteEntitlementStatus;
             mSatelliteEntitlementPlmns = info.mSatelliteEntitlementPlmns;
+            mIsSatelliteESOSSupported = info.mIsSatelliteESOSSupported;
+            mIsSatelliteProvisionedForNonIpDatagram = info.mIsSatelliteProvisionedForNonIpDatagram;
         }
 
         /**
@@ -2923,6 +2976,33 @@
         }
 
         /**
+         * Set whether the carrier roaming to satellite is using ESOS for emergency messaging.
+         *
+         * @param isSatelliteESOSSupported {@code 1} if the carrier roaming to satellite is using
+         * ESOS for emergency messaging.
+         * @return The builder
+         */
+        @NonNull
+        public Builder setSatelliteESOSSupported(int isSatelliteESOSSupported) {
+            mIsSatelliteESOSSupported = isSatelliteESOSSupported;
+            return this;
+        }
+
+        /**
+         * Set whether the subscription is provisioned for oem satellite service or not.
+         *
+         * @param isSatelliteProvisionedForNonIpDatagram {@code 1} if the subscription is for NTN,
+         * {@code 0} otherwise.
+         * @return The builder.
+         */
+        @NonNull
+        public Builder setIsSatelliteProvisionedForNonIpDatagram(
+                int isSatelliteProvisionedForNonIpDatagram) {
+            mIsSatelliteProvisionedForNonIpDatagram = isSatelliteProvisionedForNonIpDatagram;
+            return this;
+        }
+
+        /**
          * Build the {@link SubscriptionInfoInternal}.
          *
          * @return The {@link SubscriptionInfoInternal} instance.
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
index 2125f45..353493b 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -193,9 +193,11 @@
             SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
             SimInfo.COLUMN_SATELLITE_ENABLED,
             SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
-            SimInfo.COLUMN_IS_NTN,
+            SimInfo.COLUMN_IS_ONLY_NTN,
             SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS,
-            SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS
+            SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS,
+            SimInfo.COLUMN_SATELLITE_ESOS_SUPPORTED,
+            SimInfo.COLUMN_IS_SATELLITE_PROVISIONED_FOR_NON_IP_DATAGRAM
     );
 
     /**
@@ -456,6 +458,13 @@
          * @param subId The subscription id.
          */
         public void onUiccApplicationsEnabledChanged(int subId) {}
+
+        /**
+         * Called when {@link #getDefaultDataSubId()} changed.
+         *
+         * @param subId The subscription id.
+         */
+        public void onDefaultDataSubscriptionChanged(int subId) {}
     }
 
     /**
@@ -591,6 +600,13 @@
         // Broadcast sub Id on service initialized.
         broadcastSubId(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED,
                 getDefaultDataSubId());
+        if (mFeatureFlags.ddsCallback()) {
+            mSubscriptionManagerServiceCallbacks.forEach(
+                    callback -> callback.invokeFromExecutor(
+                            () -> callback.onDefaultDataSubscriptionChanged(
+                                    getDefaultDataSubId())));
+        }
+
         broadcastSubId(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED,
                 getDefaultVoiceSubId());
         broadcastSubId(SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED,
@@ -911,6 +927,8 @@
      * @param subId The subscription id.
      */
     public void setCountryIso(int subId, @NonNull String iso) {
+        logl("setCountryIso: subId=" + subId + ", iso=" + iso);
+
         // This can throw IllegalArgumentException if the subscription does not exist.
         try {
             mSubscriptionDatabaseManager.setCountryIso(subId, iso);
@@ -927,6 +945,8 @@
      * @param carrierName The carrier name.
      */
     public void setCarrierName(int subId, @NonNull String carrierName) {
+        logl("setCarrierName: subId=" + subId + ", carrierName=" + carrierName);
+
         // This can throw IllegalArgumentException if the subscription does not exist.
         try {
             mSubscriptionDatabaseManager.setCarrierName(subId, carrierName);
@@ -998,6 +1018,9 @@
      * @param numberFromIms The phone number retrieved from IMS.
      */
     public void setNumberFromIms(int subId, @NonNull String numberFromIms) {
+        logl("setNumberFromIms: subId=" + subId + ", number="
+                + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, numberFromIms));
+
         // This can throw IllegalArgumentException if the subscription does not exist.
         try {
             mSubscriptionDatabaseManager.setNumberFromIms(subId, numberFromIms);
@@ -1540,6 +1563,9 @@
                             MccTable.updateMccMncConfiguration(mContext, mccMnc);
                         }
                         setMccMnc(subId, mccMnc);
+                        if (isSatelliteSpn(subInfo.getDisplayName()) || isSatellitePlmn(mccMnc)) {
+                            setNtn(subId, true);
+                        }
                     } else {
                         loge("updateSubscription: mcc/mnc is empty");
                     }
@@ -1889,7 +1915,10 @@
                     + "carrier privilege");
         }
 
-        enforceTelephonyFeatureWithException(callingPackage, "getAllSubInfoList");
+        if (!mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_force_phone_globals_creation)) {
+            enforceTelephonyFeatureWithException(callingPackage, "getAllSubInfoList");
+        }
 
         return getSubscriptionInfoStreamAsUser(BINDER_WRAPPER.getCallingUserHandle())
                 // callers have READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE can get a full
@@ -1933,7 +1962,10 @@
                     + "carrier privilege");
         }
 
-        enforceTelephonyFeatureWithException(callingPackage, "getActiveSubscriptionInfo");
+        if (!mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_force_phone_globals_creation)) {
+            enforceTelephonyFeatureWithException(callingPackage, "getActiveSubscriptionInfo");
+        }
 
         SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
                 .getSubscriptionInfoInternal(subId);
@@ -2062,7 +2094,10 @@
             return Collections.emptyList();
         }
 
-        enforceTelephonyFeatureWithException(callingPackage, "getActiveSubscriptionInfoList");
+        if (!mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_force_phone_globals_creation)) {
+            enforceTelephonyFeatureWithException(callingPackage, "getActiveSubscriptionInfoList");
+        }
 
         if (isForAllProfiles) {
             enforcePermissionAccessAllUserProfiles();
@@ -2154,7 +2189,11 @@
         enforcePermissions("getAvailableSubscriptionInfoList",
                 Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
 
-        enforceTelephonyFeatureWithException(callingPackage, "getAvailableSubscriptionInfoList");
+        if (!mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_force_phone_globals_creation)) {
+            enforceTelephonyFeatureWithException(callingPackage,
+                    "getAvailableSubscriptionInfoList");
+        }
 
         return getAvailableSubscriptionsInternalStream()
                 .sorted(Comparator.comparing(SubscriptionInfoInternal::getSimSlotIndex)
@@ -2738,7 +2777,10 @@
             return Collections.emptyList();
         }
 
-        enforceTelephonyFeatureWithException(callingPackage, "getOpportunisticSubscriptions");
+        if (!mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_force_phone_globals_creation)) {
+            enforceTelephonyFeatureWithException(callingPackage, "getOpportunisticSubscriptions");
+        }
 
         return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
                 // callers have READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE can get a full
@@ -3149,6 +3191,11 @@
 
                 broadcastSubId(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED,
                         subId);
+                if (mFeatureFlags.ddsCallback()) {
+                    mSubscriptionManagerServiceCallbacks.forEach(
+                            callback -> callback.invokeFromExecutor(
+                                    () -> callback.onDefaultDataSubscriptionChanged(subId)));
+                }
 
                 updateDefaultSubId();
             }
@@ -3311,7 +3358,10 @@
     public int[] getActiveSubIdList(boolean visibleOnly) {
         enforcePermissions("getActiveSubIdList", Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
 
-        enforceTelephonyFeatureWithException(getCurrentPackageName(), "getActiveSubIdList");
+        if (!mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_force_phone_globals_creation)) {
+            enforceTelephonyFeatureWithException(getCurrentPackageName(), "getActiveSubIdList");
+        }
 
         // UserHandle.ALL because this API is exposed as system API.
         return getActiveSubIdListAsUser(visibleOnly, UserHandle.ALL);
@@ -3875,10 +3925,20 @@
         switch(source) {
             case SubscriptionManager.PHONE_NUMBER_SOURCE_UICC:
                 final Phone phone = PhoneFactory.getPhone(getSlotIndex(subId));
-                if (phone != null) {
-                    return TextUtils.emptyIfNull(phone.getLine1Number());
-                } else {
+                if (mFeatureFlags.uiccPhoneNumberFix()) {
+                    if (phone != null) {
+                        String number = phone.getLine1Number();
+                        if (!TextUtils.isEmpty(number)) {
+                            return number;
+                        }
+                    }
                     return subInfo.getNumber();
+                } else {
+                    if (phone != null) {
+                        return TextUtils.emptyIfNull(phone.getLine1Number());
+                    } else {
+                        return subInfo.getNumber();
+                    }
                 }
             case SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER:
                 return subInfo.getNumberFromCarrier();
@@ -3986,6 +4046,9 @@
     @RequiresPermission("carrier privileges")
     public void setPhoneNumber(int subId, @PhoneNumberSource int source, @NonNull String number,
             @NonNull String callingPackage, @Nullable String callingFeatureId) {
+        logl("setPhoneNumber: subId=" + subId + ", number="
+                + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, number)
+                + ", calling package=" + callingPackage);
         if (!TelephonyPermissions.checkCarrierPrivilegeForSubId(mContext, subId)) {
             throw new SecurityException("setPhoneNumber for CARRIER needs carrier privilege.");
         }
@@ -4494,10 +4557,14 @@
      */
     @NonNull
     private String getCallingPackage() {
-        if (Binder.getCallingUid() == Process.PHONE_UID) {
+        if (UserHandle.isSameApp(Binder.getCallingUid(), Process.PHONE_UID)) {
             // Too many packages running with phone uid. Just return one here.
             return "com.android.phone";
         }
+        if (mFeatureFlags.hsumPackageManager()) {
+            return Arrays.toString(mContext.createContextAsUser(Binder.getCallingUserHandle(), 0)
+                    .getPackageManager().getPackagesForUid(Binder.getCallingUid()));
+        }
         return Arrays.toString(mContext.getPackageManager().getPackagesForUid(
                 Binder.getCallingUid()));
     }
@@ -4591,6 +4658,75 @@
     }
 
     /**
+     * Set the satellite ESOS supported value in the subscription database.
+     *
+     * @param subId subscription id.
+     * @param isSatelliteESOSSupported {@code true} satellite ESOS supported true.
+     */
+    public void setSatelliteESOSSupported(int subId, @NonNull boolean isSatelliteESOSSupported) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            return;
+        }
+        try {
+            mSubscriptionDatabaseManager.setSatelliteESOSSupported(subId,
+                    isSatelliteESOSSupported ? 1 : 0);
+        } catch (IllegalArgumentException e) {
+            loge("setSatelliteESOSSupported: invalid subId=" + subId);
+        }
+    }
+
+    /**
+     * Set whether the subscription is provisioned for OEM-enabled or carrier roaming NB-IOT
+     * satellite service or not.
+     *
+     * @param subId subscription id.
+     * @param isSatelliteProvisionedForNonIpDatagram {@code true} if subId is provisioned.
+     * {@code false} otherwise.
+     */
+    public void setIsSatelliteProvisionedForNonIpDatagram(int subId,
+            boolean isSatelliteProvisionedForNonIpDatagram) {
+        try {
+            mSubscriptionDatabaseManager.setIsSatelliteProvisionedForNonIpDatagram(
+                    subId, (isSatelliteProvisionedForNonIpDatagram ? 1 : 0));
+        } catch (IllegalArgumentException e) {
+            loge("setIsSatelliteProvisionedForNonIpDatagram: invalid subId=" + subId);
+        }
+    }
+
+    /**
+     * Get the satellite ESOS supported value in the subscription database.
+     *
+     * @param subId subscription id.
+     * @return the satellite ESOS supported true or false.
+     */
+    public boolean getSatelliteESOSSupported(int subId) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            return false;
+        }
+        SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager.getSubscriptionInfoInternal(
+                subId);
+        if (subInfo == null) {
+            return false;
+        }
+
+        return subInfo.getSatelliteESOSSupported() == 1;
+    }
+
+    /**
+     * Get whether the subscription is provisioned for OEM-enabled or carrier roaming NB-IOT
+     * satellite service or not.
+     *
+     * @param subId subscription id.
+     * @return {@code true} if it is provisioned for oem satellite service. {@code false} otherwise.
+     */
+    public boolean isSatelliteProvisionedForNonIpDatagram(int subId) {
+        SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager.getSubscriptionInfoInternal(
+                subId);
+
+        return subInfo.getIsSatelliteProvisionedForNonIpDatagram() == 1;
+    }
+
+    /**
      * checks whether esim bootstrap is activated for any of the available active subscription info
      * list.
      *
@@ -4633,9 +4769,16 @@
      */
     @Nullable
     private String getCurrentPackageName() {
+        if (mFeatureFlags.hsumPackageManager()) {
+            PackageManager pm = mContext.createContextAsUser(Binder.getCallingUserHandle(), 0)
+                    .getPackageManager();
+            if (pm == null) return null;
+            String[] callingPackageNames = pm.getPackagesForUid(Binder.getCallingUid());
+            return (callingPackageNames == null) ? null : callingPackageNames[0];
+        }
         if (mPackageManager == null) return null;
-        String[] callingUids = mPackageManager.getPackagesForUid(Binder.getCallingUid());
-        return (callingUids == null) ? null : callingUids[0];
+        String[] callingPackageNames = mPackageManager.getPackagesForUid(Binder.getCallingUid());
+        return (callingPackageNames == null) ? null : callingPackageNames[0];
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
index 5f8ccb4..a3f34c4 100644
--- a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
@@ -16,14 +16,18 @@
 
 package com.android.internal.telephony.uicc;
 
+import static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
+
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.AsyncResult;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.telephony.Rlog;
 
 import java.util.ArrayList;
 
@@ -31,7 +35,9 @@
  * {@hide}
  */
 public abstract class IccFileHandler extends Handler implements IccConstants {
-    private static final boolean VDBG = false;
+    protected static final String LOG_TAG = "IccFileHandler";
+    private static final boolean VDBG = FORCE_VERBOSE_STATE_LOGGING ||
+            Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
 
     //from TS 11.11 9.1 or elsewhere
     static protected final int COMMAND_READ_BINARY = 0xb0;
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index ae93b09..57ea9b9 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.uicc;
 
+import static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -59,7 +61,6 @@
 public abstract class IccRecords extends Handler implements IccConstants {
     private static final String LOG_TAG = "IccRecords";
     protected static final boolean DBG = true;
-    private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
     protected static final boolean VDBG =  FORCE_VERBOSE_STATE_LOGGING ||
             Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
 
diff --git a/src/java/com/android/internal/telephony/uicc/InstallCarrierAppTrampolineActivity.java b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppTrampolineActivity.java
index 2c29266..a07c2ec 100644
--- a/src/java/com/android/internal/telephony/uicc/InstallCarrierAppTrampolineActivity.java
+++ b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppTrampolineActivity.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
@@ -107,7 +108,8 @@
         super.onActivityResult(requestCode, resultCode, data);
         if (requestCode == INSTALL_CARRIER_APP_DIALOG_REQUEST) {
             if (resultCode == DOWNLOAD_RESULT) {
-                startActivity(InstallCarrierAppUtils.getPlayStoreIntent(mPackageName));
+                startActivityAsUser(InstallCarrierAppUtils.getPlayStoreIntent(mPackageName),
+                        UserHandle.CURRENT);
             }
             finishNoAnimation();
         }
diff --git a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
index 9591a498..b705cbc 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
@@ -16,17 +16,23 @@
 
 package com.android.internal.telephony.uicc;
 
+import android.annotation.NonNull;
+
+import static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
+
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.os.AsyncResult;
 import android.os.Build;
 import android.os.Message;
+import android.os.UserHandle;
 import android.telephony.SubscriptionManager;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.gsm.SimTlv;
 import com.android.telephony.Rlog;
 
@@ -43,7 +49,6 @@
     protected static final String LOG_TAG = "IsimUiccRecords";
 
     private static final boolean DBG = true;
-    private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
     private static final boolean VDBG =  FORCE_VERBOSE_STATE_LOGGING ||
             Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
     private static final boolean DUMP_RECORDS = false;  // Note: PII is logged when this is true
@@ -64,6 +69,9 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private String auth_rsp;
 
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
     private static final int TAG_ISIM_VALUE = 0x80;     // From 3GPP TS 31.103
 
     @Override
@@ -78,9 +86,10 @@
                 + " mSmss TPMR=" + getSmssTpmrValue()) : "");
     }
 
-    public IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
+    public IsimUiccRecords(@NonNull UiccCardApplication app, @NonNull Context c,
+            @NonNull CommandsInterface ci, @NonNull FeatureFlags flags) {
         super(app, c, ci);
-
+        mFeatureFlags = flags;
         mRecordsRequested = false;  // No load request is made till SIM ready
         //todo: currently locked state for ISIM is not handled well and may cause app state to not
         //be broadcast
@@ -387,7 +396,11 @@
         Intent intent = new Intent(INTENT_ISIM_REFRESH);
         log("send ISim REFRESH: " + INTENT_ISIM_REFRESH);
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mParentApp.getPhoneId());
-        mContext.sendBroadcast(intent);
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(intent);
+        }
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/uicc/PinStorage.java b/src/java/com/android/internal/telephony/uicc/PinStorage.java
index e4a0cfa..e26050e 100644
--- a/src/java/com/android/internal/telephony/uicc/PinStorage.java
+++ b/src/java/com/android/internal/telephony/uicc/PinStorage.java
@@ -36,6 +36,7 @@
 import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_SUCCESS;
 import static com.android.internal.telephony.uicc.IccCardStatus.PinState.PINSTATE_ENABLED_NOT_VERIFIED;
 import static com.android.internal.telephony.uicc.IccCardStatus.PinState.PINSTATE_ENABLED_VERIFIED;
+import static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
 
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
@@ -56,6 +57,7 @@
 import android.telephony.TelephonyManager.SimState;
 import android.util.Base64;
 import android.util.IndentingPrintWriter;
+import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.R;
@@ -88,7 +90,8 @@
  */
 public class PinStorage extends Handler {
     private static final String TAG = "PinStorage";
-    private static final boolean VDBG = false;  // STOPSHIP if true
+    private static final boolean VDBG = FORCE_VERBOSE_STATE_LOGGING ||
+            Rlog.isLoggable(TAG, Log.VERBOSE);
 
     /**
      * Time duration in milliseconds to allow automatic PIN verification after reboot. All unused
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index a97b00b..4b0e63c 100644
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -19,6 +19,8 @@
 import static android.telephony.SmsManager.STATUS_ON_ICC_READ;
 import static android.telephony.SmsManager.STATUS_ON_ICC_UNREAD;
 
+import static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
+
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
@@ -48,6 +50,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * {@hide}
@@ -56,8 +59,7 @@
     protected static final String LOG_TAG = "SIMRecords";
 
     private static final boolean CRASH_RIL = false;
-    private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
-    private static final boolean VDBG =  FORCE_VERBOSE_STATE_LOGGING ||
+    private static final boolean VDBG = FORCE_VERBOSE_STATE_LOGGING ||
             Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
 
     // ***** Instance Variables
@@ -1278,15 +1280,22 @@
 
                 case EVENT_GET_FPLMN_SIZE_DONE:
                     ar = (AsyncResult) msg.obj;
+                    int key = msg.arg2;
+
+                    Message response;
+                    Pair<Message, Object> transaction = null;
+                    if (ar.exception != null && ar.userObj != null) {
+                        response = (Message) ar.userObj;
+                    } else {
+                        transaction = retrievePendingTransaction(key);
+                        response = Objects.requireNonNull(transaction.first);
+                    }
+
                     if (ar.exception != null) {
-                        Message response = (Message) ar.userObj;
                         AsyncResult.forMessage(response).exception = ar.exception;
                         response.sendToTarget();
                         break;
                     }
-                    int key = msg.arg2;
-                    Pair<Message, Object> transaction = retrievePendingTransaction(key);
-                    Message response = transaction.first;
                     List<String> fplmns = (List<String>) transaction.second;
                     int dataLength = (int) ar.result;
                     if (dataLength < 0 || dataLength % FPLMN_BYTE_SIZE != 0) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
index fe19e99..3bda29f 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.uicc;
 
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.AsyncResult;
@@ -29,6 +30,7 @@
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
@@ -105,11 +107,14 @@
     private RegistrantList mPinLockedRegistrants = new RegistrantList();
     private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
 
-    public UiccCardApplication(UiccProfile uiccProfile,
-                        IccCardApplicationStatus as,
-                        Context c,
-                        CommandsInterface ci) {
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
+    public UiccCardApplication(@NonNull UiccProfile uiccProfile,
+            @NonNull IccCardApplicationStatus as, @NonNull Context c, @NonNull CommandsInterface ci,
+            @NonNull FeatureFlags flags) {
         if (DBG) log("Creating UiccApp: " + as);
+        mFeatureFlags = flags;
         mUiccProfile = uiccProfile;
         mAppState = as.app_state;
         mAppType = as.app_type;
@@ -208,7 +213,7 @@
         } else if (type == AppType.APPTYPE_RUIM || type == AppType.APPTYPE_CSIM){
             return new RuimRecords(this, c, ci);
         } else if (type == AppType.APPTYPE_ISIM) {
-            return new IsimUiccRecords(this, c, ci);
+            return new IsimUiccRecords(this, c, ci, mFeatureFlags);
         } else {
             // Unknown app type (maybe detection is still in progress)
             return null;
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 84e84d9..dd71c44 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -19,6 +19,8 @@
 import static android.telephony.TelephonyManager.UNINITIALIZED_CARD_ID;
 import static android.telephony.TelephonyManager.UNSUPPORTED_CARD_ID;
 
+import static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
+
 import static java.util.Arrays.copyOf;
 
 import android.Manifest;
@@ -35,6 +37,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.RegistrantList;
+import android.os.UserHandle;
 import android.preference.PreferenceManager;
 import android.sysprop.TelephonyProperties;
 import android.telephony.AnomalyReporter;
@@ -64,6 +67,7 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.RadioConfig;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.uicc.euicc.EuiccCard;
@@ -129,9 +133,10 @@
  * See also {@link com.android.internal.telephony.IccCard}
  */
 public class UiccController extends Handler {
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false; //STOPSHIP if true
     private static final String LOG_TAG = "UiccController";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = FORCE_VERBOSE_STATE_LOGGING ||
+            Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
 
     public static final int INVALID_SLOT_ID = -1;
 
@@ -245,22 +250,26 @@
     // LocalLog buffer to hold important SIM related events for debugging
     private static LocalLog sLocalLog = new LocalLog(TelephonyUtils.IS_DEBUGGABLE ? 256 : 64);
 
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
     /**
      * API to make UiccController singleton if not already created.
      */
-    public static UiccController make(Context c) {
+    public static UiccController make(@NonNull Context c, @NonNull FeatureFlags flags) {
         synchronized (mLock) {
             if (mInstance != null) {
                 throw new RuntimeException("UiccController.make() should only be called once");
             }
-            mInstance = new UiccController(c);
+            mInstance = new UiccController(c, flags);
             return mInstance;
         }
     }
 
-    private UiccController(Context c) {
+    private UiccController(@NonNull Context c, @NonNull FeatureFlags flags) {
         if (DBG) log("Creating UiccController");
         mContext = c;
+        mFeatureFlags = flags;
         mCis = PhoneFactory.getCommandsInterfaces();
         int numPhysicalSlots = c.getResources().getInteger(
                 com.android.internal.R.integer.config_num_physical_slots);
@@ -292,7 +301,7 @@
             mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, i);
         }
 
-        mLauncher = new UiccStateChangedLauncher(c, this);
+        mLauncher = new UiccStateChangedLauncher(c, this, mFeatureFlags);
         mCardStrings = loadCardStrings();
         mDefaultEuiccCardId = UNINITIALIZED_CARD_ID;
 
@@ -780,9 +789,10 @@
         intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
         intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, state);
         intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
-        SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
+        int subId = SubscriptionManager.getSubscriptionId(phoneId);
+        SubscriptionManager.putPhoneIdAndMaybeSubIdExtra(intent, phoneId, subId);
         Rlog.d(LOG_TAG, "Broadcasting intent ACTION_SIM_STATE_CHANGED " + state + " reason "
-                + reason + " for phone: " + phoneId);
+                + reason + " for phone: " + phoneId + " sub: " + subId);
         IntentBroadcaster.getInstance().broadcastStickyIntent(mContext, intent, phoneId);
     }
 
@@ -798,7 +808,8 @@
             Intent intent = new Intent(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             intent.putExtra(TelephonyManager.EXTRA_SIM_STATE, state);
-            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
+            int subId = SubscriptionManager.getSubscriptionId(phoneId);
+            SubscriptionManager.putPhoneIdAndMaybeSubIdExtra(intent, phoneId, subId);
             // TODO(b/130664115) we manually populate this intent with the slotId. In the future we
             // should do a review of whether to make this public
             UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(phoneId);
@@ -811,8 +822,13 @@
             }
             Rlog.d(LOG_TAG, "Broadcasting intent ACTION_SIM_CARD_STATE_CHANGED "
                     + TelephonyManager.simStateToString(state) + " for phone: " + phoneId
-                    + " slot: " + slotId + " port: " + portIndex);
-            mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+                    + " slot: " + slotId + " port: " + portIndex + " sub: " + subId);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                        Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            } else {
+                mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            }
             TelephonyMetrics.getInstance().updateSimState(phoneId, state);
         }
     }
@@ -838,7 +854,8 @@
             Intent intent = new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             intent.putExtra(TelephonyManager.EXTRA_SIM_STATE, state);
-            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
+            int subId = SubscriptionManager.getSubscriptionId(phoneId);
+            SubscriptionManager.putPhoneIdAndMaybeSubIdExtra(intent, phoneId, subId);
             // TODO(b/130664115) we populate this intent with the actual slotId. In the future we
             // should do a review of whether to make this public
             UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(phoneId);
@@ -849,9 +866,14 @@
             }
             Rlog.d(LOG_TAG, "Broadcasting intent ACTION_SIM_APPLICATION_STATE_CHANGED "
                     + TelephonyManager.simStateToString(state)
-                    + " for phone: " + phoneId + " slot: " + slotId + "port: "
-                    + slot.getPortIndexFromPhoneId(phoneId));
-            mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+                    + " for phone: " + phoneId + " slot: " + slotId + " port: "
+                    + slot.getPortIndexFromPhoneId(phoneId) + " sub: " + subId);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                        Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            } else {
+                mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            }
             TelephonyMetrics.getInstance().updateSimState(phoneId, state);
         }
     }
@@ -1453,8 +1475,13 @@
         options.setBackgroundActivityStartsAllowed(true);
         Intent intent = new Intent(TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        mContext.sendBroadcast(intent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
-                options.toBundle());
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, options.toBundle());
+        } else {
+            mContext.sendBroadcast(intent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+                    options.toBundle());
+        }
     }
 
     private boolean hasActivePort(IccSimPortInfo[] simPortInfos) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccPort.java b/src/java/com/android/internal/telephony/uicc/UiccPort.java
index 9e341ef..905db70 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccPort.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccPort.java
@@ -31,7 +31,6 @@
 import com.android.internal.telephony.TelephonyComponentFactory;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.flags.FeatureFlagsImpl;
-import com.android.internal.telephony.flags.Flags;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -442,15 +441,13 @@
      * channel that may have been assigned to other client.
      */
     private void cleanupOpenLogicalChannelRecordsIfNeeded() {
-        if (Flags.cleanupOpenLogicalChannelRecordOnDispose()) {
-            synchronized (mOpenChannelRecords) {
-                for (OpenLogicalChannelRecord record : mOpenChannelRecords) {
-                    if (DBG) log("Clean up " + record);
-                    record.mRequest.binder.unlinkToDeath(record, /*flags=*/ 0);
-                    record.mRequest.binder = null;
-                }
-                mOpenChannelRecords.clear();
+        synchronized (mOpenChannelRecords) {
+            for (OpenLogicalChannelRecord record : mOpenChannelRecords) {
+                if (DBG) log("Clean up " + record);
+                record.mRequest.binder.unlinkToDeath(record, /*flags=*/ 0);
+                record.mRequest.binder = null;
             }
+            mOpenChannelRecords.clear();
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index a22f075..2551cb8 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -19,6 +19,7 @@
 import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT;
 import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_FAILURE;
 import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_SUCCESS;
+import static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -40,6 +41,7 @@
 import android.os.PersistableBundle;
 import android.os.Registrant;
 import android.os.RegistrantList;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
@@ -53,6 +55,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CarrierAppUtils;
@@ -67,6 +70,7 @@
 import com.android.internal.telephony.TelephonyStatsLog;
 import com.android.internal.telephony.cat.CatService;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
@@ -101,11 +105,13 @@
 public class UiccProfile extends IccCard {
     protected static final String LOG_TAG = "UiccProfile";
     protected static final boolean DBG = true;
-    private static final boolean VDBG = false; //STOPSHIP if true
+    private static final boolean VDBG = FORCE_VERBOSE_STATE_LOGGING ||
+            Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
 
     private static final String OPERATOR_BRAND_OVERRIDE_PREFIX = "operator_branding_";
 
-    private final @NonNull FeatureFlags mFlags;
+    @NonNull
+    private final FeatureFlags mFlags;
     // The lock object is created by UiccSlot that owns the UiccCard that owns this UiccProfile.
     // This is to share the lock between UiccSlot, UiccCard and UiccProfile for now.
     private final Object mLock;
@@ -1139,7 +1145,7 @@
                     //Create newly added Applications
                     if (i < ics.mApplications.length) {
                         mUiccApplications[i] = new UiccCardApplication(this,
-                                ics.mApplications[i], mContext, mCi);
+                                ics.mApplications[i], mContext, mCi, mFlags);
                     }
                 } else if (i >= ics.mApplications.length) {
                     //Delete removed applications
@@ -1155,7 +1161,9 @@
 
             // Reload the carrier privilege rules if necessary.
             log("Before privilege rules: " + mCarrierPrivilegeRules + " : " + ics.mCardState);
-            if (mCarrierPrivilegeRules == null && ics.mCardState == CardState.CARDSTATE_PRESENT) {
+            if (mCarrierPrivilegeRules == null && ics.mCardState == CardState.CARDSTATE_PRESENT && (
+                    !Flags.uiccAppCountCheckToCreateChannel()
+                            || mLastReportedNumOfUiccApplications > 0)) {
                 mCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(this,
                         mHandler.obtainMessage(EVENT_CARRIER_PRIVILEGES_LOADED));
             } else if (mCarrierPrivilegeRules != null
@@ -1349,7 +1357,7 @@
     private void promptInstallCarrierApp(String pkgName) {
         Intent showDialogIntent = InstallCarrierAppTrampolineActivity.get(mContext, pkgName);
         showDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(showDialogIntent);
+        mContext.startActivityAsUser(showDialogIntent, UserHandle.CURRENT);
     }
 
     private void onCarrierPrivilegesLoadedMessage() {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccStateChangedLauncher.java b/src/java/com/android/internal/telephony/uicc/UiccStateChangedLauncher.java
index 3ef421a..ff48fd1 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccStateChangedLauncher.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccStateChangedLauncher.java
@@ -16,15 +16,18 @@
 
 package com.android.internal.telephony.uicc;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.Message;
+import android.os.UserHandle;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import com.android.internal.R;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
 
 /**
@@ -46,7 +49,12 @@
     private UiccController mUiccController;
     private boolean[] mIsRestricted = null;
 
-    public UiccStateChangedLauncher(Context context, UiccController controller) {
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
+    public UiccStateChangedLauncher(Context context, UiccController controller,
+            @NonNull FeatureFlags flags) {
+        mFeatureFlags = flags;
         sDeviceProvisioningPackage = context.getResources().getString(
                 R.string.config_deviceProvisioningPackage);
         if (sDeviceProvisioningPackage != null && !sDeviceProvisioningPackage.isEmpty()) {
@@ -92,7 +100,11 @@
         Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         intent.setPackage(sDeviceProvisioningPackage);
         try {
-            mContext.sendBroadcast(intent);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            } else {
+                mContext.sendBroadcast(intent);
+            }
         } catch (Exception e) {
             Log.e(TAG, e.toString());
         }
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
index f42d5a2..b17a0fd 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
@@ -18,14 +18,16 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.SharedPreferences;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.preference.PreferenceManager;
 import android.telephony.IccOpenLogicalChannelResponse;
 import android.util.Base64;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.euicc.EuiccSession;
 import com.android.internal.telephony.uicc.IccIoResult;
 import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback;
 import com.android.internal.telephony.uicc.euicc.async.AsyncResultHelper;
@@ -34,14 +36,20 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.List;
-import java.util.NoSuchElementException;
 
 /**
  * This class sends a list of APDU commands to an AID on a UICC. A logical channel will be opened
  * before sending and closed after all APDU commands are sent. The complete response of the last
  * APDU command will be returned. If any APDU command returns an error status (other than
  * {@link #STATUS_NO_ERROR}) or causing an exception, an {@link ApduException} will be returned
- * immediately without sending the rest of commands. This class is thread-safe.
+ * immediately without sending the rest of commands.
+ *
+ * <p>If {@link EuiccSession} indicates ongoing session(s), the behavior changes: 1) before
+ * sending, check if a channel is opened already. If yes, reuse the channel and send APDU commands
+ * directly. If no, open a channel before sending. 2) The channel is closed when EuiccSession
+ * class ends all sessions, independent of APDU sending.
+ *
+ * <p>This class is thread-safe.
  *
  * @hide
  */
@@ -55,10 +63,11 @@
     // Status code of APDU response
     private static final int STATUS_NO_ERROR = 0x9000;
     private static final int SW1_NO_ERROR = 0x91;
+    private static final int STATUS_CHANNEL_CLOSED = 0x6881; // b/359336875
 
     private static final int WAIT_TIME_MS = 2000;
     private static final String CHANNEL_ID_PRE = "esim-channel";
-    private static final String ISD_R_AID = "A0000005591010FFFFFFFF8900000100";
+    static final String ISD_R_AID = "A0000005591010FFFFFFFF8900000100";
     private static final String CHANNEL_RESPONSE_ID_PRE = "esim-res-id";
 
     private static void logv(String msg) {
@@ -69,6 +78,10 @@
         Rlog.d(LOG_TAG, msg);
     }
 
+    private static void loge(String msg) {
+        Rlog.e(LOG_TAG, msg);
+    }
+
     private final String mAid;
     private final boolean mSupportExtendedApdu;
     private final OpenLogicalChannelInvocation mOpenChannel;
@@ -77,10 +90,16 @@
     private final Context mContext;
     private final String mChannelKey;
     private final String mChannelResponseKey;
+    // closeAnyOpenChannel() needs a handler for its async callbacks.
+    private final Handler mHandler;
 
-    // Lock for accessing mChannelOpened. We only allow to open a single logical channel at any
-    // time for an AID.
-    private final Object mChannelLock = new Object();
+    // Lock for accessing mChannelInUse. We only allow to open a single logical
+    // channel at any time for an AID and to invoke one command at any time.
+    // Only the thread (and its async callbacks) that sets mChannelInUse
+    // can open/close/send, and update mChannelOpened.
+    private final Object mChannelInUseLock = new Object();
+    @GuardedBy("mChannelInUseLock")
+    private boolean mChannelInUse;
     private boolean mChannelOpened;
 
     /**
@@ -88,6 +107,9 @@
      */
     public ApduSender(Context context, int phoneId, CommandsInterface ci, String aid,
             boolean supportExtendedApdu) {
+        if (!aid.equals(ISD_R_AID) && !"user".equals(Build.TYPE)) {
+            throw new IllegalArgumentException("Only ISD-R AID is supported.");
+        }
         mAid = aid;
         mContext = context;
         mSupportExtendedApdu = supportExtendedApdu;
@@ -96,7 +118,10 @@
         mTransmitApdu = new TransmitApduLogicalChannelInvocation(ci);
         mChannelKey = CHANNEL_ID_PRE + "_" + phoneId;
         mChannelResponseKey = CHANNEL_RESPONSE_ID_PRE + "_" + phoneId;
-        closeExistingChannelIfExists();
+        mHandler = new Handler();
+
+        mChannelInUse = false;
+        closeAnyOpenChannel();
     }
 
     /**
@@ -115,86 +140,125 @@
             RequestProvider requestProvider,
             ApduSenderResultCallback resultCallback,
             Handler handler) {
-        synchronized (mChannelLock) {
-            if (mChannelOpened) {
-                if (!Looper.getMainLooper().equals(Looper.myLooper())) {
-                    logd("Logical channel has already been opened. Wait.");
-                    try {
-                        mChannelLock.wait(WAIT_TIME_MS);
-                    } catch (InterruptedException e) {
-                        // nothing to do
-                    }
-                    if (mChannelOpened) {
-                        AsyncResultHelper.throwException(
-                                new ApduException("The logical channel is still in use."),
-                                resultCallback, handler);
-                        return;
-                    }
-                } else {
-                    AsyncResultHelper.throwException(
-                            new ApduException("The logical channel is in use."),
-                            resultCallback, handler);
-                    return;
-                }
-            }
-            mChannelOpened = true;
+        if (!acquireChannelLock()) {
+            AsyncResultHelper.throwException(
+                    new ApduException("The logical channel is still in use."),
+                    resultCallback,
+                    handler);
+            return;
         }
 
-        mOpenChannel.invoke(mAid, new AsyncResultCallback<IccOpenLogicalChannelResponse>() {
-            @Override
-            public void onResult(IccOpenLogicalChannelResponse openChannelResponse) {
-                int channel = openChannelResponse.getChannel();
-                int status = openChannelResponse.getStatus();
-                byte[] selectResponse = openChannelResponse.getSelectResponse();
-                if (mAid.equals(ISD_R_AID)
-                      && status == IccOpenLogicalChannelResponse.STATUS_NO_SUCH_ELEMENT) {
-                    channel = PreferenceManager.getDefaultSharedPreferences(mContext)
-                                .getInt(mChannelKey, IccOpenLogicalChannelResponse.INVALID_CHANNEL);
-                    if (channel != IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
-                        logv("Try to use already opened channel: " + channel);
-                        status = IccOpenLogicalChannelResponse.STATUS_NO_ERROR;
-                        String storedResponse = PreferenceManager
-                                .getDefaultSharedPreferences(mContext)
-                                      .getString(mChannelResponseKey, "");
-                        selectResponse = Base64.decode(storedResponse, Base64.DEFAULT);
-                    }
-                }
-                if (channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL
-                        || status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR) {
-                    synchronized (mChannelLock) {
-                        mChannelOpened = false;
-                        mChannelLock.notify();
-                    }
-                    resultCallback.onException(
-                            new ApduException("Failed to open logical channel opened for AID: "
-                                    + mAid + ", with status: " + status));
-                    return;
-                }
-
-                RequestBuilder builder = new RequestBuilder(channel, mSupportExtendedApdu);
-                Throwable requestException = null;
-                if (mAid.equals(ISD_R_AID)) {
-                   PreferenceManager.getDefaultSharedPreferences(mContext)
-                         .edit().putInt(mChannelKey, channel).apply();
-                   PreferenceManager.getDefaultSharedPreferences(mContext)
-                        .edit().putString(mChannelResponseKey,
-                           Base64.encodeToString(selectResponse, Base64.DEFAULT)).apply();
-                }
-                try {
-                    requestProvider.buildRequest(selectResponse, builder);
-                } catch (Throwable e) {
-                    requestException = e;
-                }
-                if (builder.getCommands().isEmpty() || requestException != null) {
-                    // Just close the channel if we don't have commands to send or an error
-                    // was encountered.
-                    closeAndReturn(channel, null /* response */, requestException, resultCallback,
-                            handler);
-                    return;
-                }
-                sendCommand(builder.getCommands(), 0 /* index */, resultCallback, handler);
+        boolean euiccSession = EuiccSession.get().hasSession();
+        // Case 1, channel was already opened AND EuiccSession is ongoing.
+        // sendCommand directly. Do not immediately close channel after sendCommand.
+        // Case 2, channel was already opened AND EuiccSession is not ongoing. This means
+        // EuiccSession#endSession is already called but closeAnyOpenChannel() is not
+        // yet executed because of waiting to acquire lock hold by this thread.
+        // sendCommand directly. Close channel immediately anyways after sendCommand.
+        // Case 3, channel is not open AND EuiccSession is ongoing. Open channel
+        // before sendCommand. Do not immediately close channel after sendCommand.
+        // Case 4, channel is not open AND EuiccSession is not ongoing. Open channel
+        // before sendCommand. Close channel immediately after sendCommand.
+        if (mChannelOpened) {  // Case 1 or 2
+            if (euiccSession) {
+                EuiccSession.get().noteChannelOpen(this);
             }
-        }, handler);
+            RequestBuilder builder = getRequestBuilderWithOpenedChannel(requestProvider,
+                    !euiccSession /* closeChannelImmediately */, resultCallback, handler);
+            if (builder == null) {
+                return;
+            }
+            sendCommand(builder.getCommands(), 0 /* index */,
+                    !euiccSession /* closeChannelImmediately */, resultCallback, handler);
+        } else {  // Case 3 or 4
+            if (euiccSession) {
+                EuiccSession.get().noteChannelOpen(this);
+            }
+            openChannel(requestProvider,
+                    !euiccSession /* closeChannelImmediately */, resultCallback, handler);
+        }
+    }
+
+    private RequestBuilder getRequestBuilderWithOpenedChannel(
+            RequestProvider requestProvider,
+            boolean closeChannelImmediately,
+            ApduSenderResultCallback resultCallback,
+            Handler handler) {
+        Throwable requestException = null;
+        int channel =
+                PreferenceManager.getDefaultSharedPreferences(mContext)
+                        .getInt(mChannelKey, IccOpenLogicalChannelResponse.INVALID_CHANNEL);
+        String storedResponse =
+                PreferenceManager.getDefaultSharedPreferences(mContext)
+                        .getString(mChannelResponseKey, "");
+        byte[] selectResponse = Base64.decode(storedResponse, Base64.DEFAULT);
+        RequestBuilder builder = new RequestBuilder(channel, mSupportExtendedApdu);
+        try {
+            requestProvider.buildRequest(selectResponse, builder);
+        } catch (Throwable e) {
+            requestException = e;
+        }
+        if (builder.getCommands().isEmpty() || requestException != null) {
+            logd("Release as commands are empty or exception occurred");
+            returnRespnseOrException(channel, closeChannelImmediately,
+                    null /* response */, requestException, resultCallback, handler);
+            return null;
+        }
+        return builder;
+    }
+
+    private void openChannel(
+            RequestProvider requestProvider,
+            boolean closeChannelImmediately,
+            ApduSenderResultCallback resultCallback,
+            Handler handler) {
+        mOpenChannel.invoke(mAid, new AsyncResultCallback<IccOpenLogicalChannelResponse>() {
+                    @Override
+                    public void onResult(IccOpenLogicalChannelResponse openChannelResponse) {
+                        int channel = openChannelResponse.getChannel();
+                        int status = openChannelResponse.getStatus();
+                        byte[] selectResponse = openChannelResponse.getSelectResponse();
+                        if (status == IccOpenLogicalChannelResponse.STATUS_NO_SUCH_ELEMENT) {
+                            channel = PreferenceManager.getDefaultSharedPreferences(mContext)
+                                            .getInt(mChannelKey,
+                                                    IccOpenLogicalChannelResponse.INVALID_CHANNEL);
+                            if (channel != IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
+                                logv("Try to use already opened channel: " + channel);
+                                status = IccOpenLogicalChannelResponse.STATUS_NO_ERROR;
+                                String storedResponse = PreferenceManager
+                                        .getDefaultSharedPreferences(mContext)
+                                              .getString(mChannelResponseKey, "");
+                                selectResponse = Base64.decode(storedResponse, Base64.DEFAULT);
+                            }
+                        }
+
+                        if (channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL
+                                || status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR) {
+                            mChannelOpened = false;
+                            resultCallback.onException(
+                                    new ApduException("Failed to open logical channel for AID: "
+                                            + mAid + ", with status: " + status));
+                            return;
+                        }
+                        PreferenceManager.getDefaultSharedPreferences(mContext)
+                                .edit()
+                                .putInt(mChannelKey, channel)
+                                .putString(mChannelResponseKey,
+                                    Base64.encodeToString(selectResponse, Base64.DEFAULT)).apply();
+                        mChannelOpened = true;
+
+                        RequestBuilder builder =
+                                getRequestBuilderWithOpenedChannel(requestProvider,
+                                        closeChannelImmediately, resultCallback, handler);
+                        if (builder == null) {
+                            return;
+                        }
+
+                        sendCommand(builder.getCommands(), 0 /* index */,
+                                closeChannelImmediately, resultCallback, handler);
+                    }
+                },
+                handler);
     }
 
     /**
@@ -207,6 +271,7 @@
     private void sendCommand(
             List<ApduCommand> commands,
             int index,
+            boolean closeChannelImmediately,
             ApduSenderResultCallback resultCallback,
             Handler handler) {
         ApduCommand command = commands.get(index);
@@ -221,9 +286,21 @@
                             public void onResult(IccIoResult fullResponse) {
                                 logv("Full APDU response: " + fullResponse);
                                 int status = (fullResponse.sw1 << 8) | fullResponse.sw2;
-                                if (status != STATUS_NO_ERROR && fullResponse.sw1 != SW1_NO_ERROR) {
-                                    closeAndReturn(command.channel, null /* response */,
-                                            new ApduException(status), resultCallback, handler);
+                                if (status != STATUS_NO_ERROR
+                                        && fullResponse.sw1 != SW1_NO_ERROR) {
+                                    if (status == STATUS_CHANNEL_CLOSED) {
+                                        // Channel is closed by EUICC e.g. REFRESH.
+                                        tearDownPreferences();
+                                        mChannelOpened = false;
+                                        // TODO: add retry
+                                    }
+                                    returnRespnseOrException(
+                                            command.channel,
+                                            closeChannelImmediately,
+                                            null /* response */,
+                                            new ApduException(status),
+                                            resultCallback,
+                                            handler);
                                     return;
                                 }
 
@@ -233,11 +310,17 @@
                                                 fullResponse);
                                 if (continueSendCommand) {
                                     // Sends the next command
-                                    sendCommand(commands, index + 1, resultCallback, handler);
+                                    sendCommand(commands, index + 1,
+                                            closeChannelImmediately, resultCallback, handler);
                                 } else {
                                     // Returns the result of the last command
-                                    closeAndReturn(command.channel, fullResponse.payload,
-                                            null /* exception */, resultCallback, handler);
+                                    returnRespnseOrException(
+                                            command.channel,
+                                            closeChannelImmediately,
+                                            fullResponse.payload,
+                                            null /* exception */,
+                                            resultCallback,
+                                            handler);
                                 }
                             }
                         }, handler);
@@ -286,6 +369,41 @@
                 }, handler);
     }
 
+    private void tearDownPreferences() {
+        PreferenceManager.getDefaultSharedPreferences(mContext)
+                .edit()
+                .remove(mChannelKey)
+                .remove(mChannelResponseKey)
+                .apply();
+    }
+
+    /**
+     * Fires the {@code resultCallback} to return a response or exception. Also
+     * closes the open logical channel if {@code closeChannelImmediately} is {@code true}.
+     */
+    private void returnRespnseOrException(
+            int channel,
+            boolean closeChannelImmediately,
+            @Nullable byte[] response,
+            @Nullable Throwable exception,
+            ApduSenderResultCallback resultCallback,
+            Handler handler) {
+        if (closeChannelImmediately) {
+            closeAndReturn(
+                    channel,
+                    response,
+                    exception,
+                    resultCallback,
+                    handler);
+        } else {
+            releaseChannelLockAndReturn(
+                    response,
+                    exception,
+                    resultCallback,
+                    handler);
+        }
+    }
+
     /**
      * Closes the opened logical channel.
      *
@@ -303,16 +421,9 @@
         mCloseChannel.invoke(channel, new AsyncResultCallback<Boolean>() {
             @Override
             public void onResult(Boolean aBoolean) {
-                synchronized (mChannelLock) {
-                    if (mAid.equals(ISD_R_AID)) {
-                      PreferenceManager.getDefaultSharedPreferences(mContext)
-                             .edit().remove(mChannelKey).apply();
-                      PreferenceManager.getDefaultSharedPreferences(mContext)
-                             .edit().remove(mChannelResponseKey).apply();
-                    }
-                    mChannelOpened = false;
-                    mChannelLock.notify();
-                }
+                tearDownPreferences();
+                mChannelOpened = false;
+                releaseChannelLock();
 
                 if (exception == null) {
                     resultCallback.onResult(response);
@@ -324,37 +435,97 @@
     }
 
     /**
-     * Cleanup the existing opened channel which was remainined opened earlier due
-     * to failure or crash.
+     * Cleanup the existing opened channel which remained opened earlier due
+     * to:
+     *
+     * <p> 1) onging EuiccSession. This will be called by {@link EuiccSession#endSession()}
+     * from non-main-thread. Or,
+     *
+     * <p> 2) telephony crash. This will be called by constructor from main-thread.
      */
-    private void closeExistingChannelIfExists() {
-        if (mCloseChannel != null) {
-            int channelId = PreferenceManager.getDefaultSharedPreferences(mContext)
+    public void closeAnyOpenChannel() {
+        if (!acquireChannelLock()) {
+            // This cannot happen for case 2) when called by constructor
+            loge("[closeAnyOpenChannel] failed to acquire channel lock");
+            return;
+        }
+        int channelId = PreferenceManager.getDefaultSharedPreferences(mContext)
                 .getInt(mChannelKey, IccOpenLogicalChannelResponse.INVALID_CHANNEL);
-            if (channelId != IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
-                logv("Trying to clean up the opened channel : " +  channelId);
-                synchronized (mChannelLock) {
-                    mChannelOpened = true;
-                    mChannelLock.notify();
+        if (channelId == IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
+            releaseChannelLock();
+            return;
+        }
+        logv("[closeAnyOpenChannel] closing the open channel : " +  channelId);
+        mCloseChannel.invoke(channelId, new AsyncResultCallback<Boolean>() {
+            @Override
+            public void onResult(Boolean isSuccess) {
+                if (isSuccess) {
+                    logv("[closeAnyOpenChannel] Channel closed successfully: " + channelId);
+                    tearDownPreferences();
                 }
-                mCloseChannel.invoke(channelId, new AsyncResultCallback<Boolean>() {
-                    @Override
-                    public void onResult(Boolean isSuccess) {
-                        if (isSuccess) {
-                          logv("Channel closed successfully: " +  channelId);
-                          PreferenceManager.getDefaultSharedPreferences(mContext)
-                                 .edit().remove(mChannelResponseKey).apply();
-                          PreferenceManager.getDefaultSharedPreferences(mContext)
-                                 .edit().remove(mChannelKey).apply();
-                       }
-
-                       synchronized (mChannelLock) {
-                           mChannelOpened = false;
-                           mChannelLock.notify();
-                      }
-                    }
-                }, new Handler());
+                // Even if CloseChannel failed, pretend that the channel is closed.
+                // So next send() will try open the channel again. If the channel is
+                // indeed still open, we use the channelId saved in sharedPref.
+                mChannelOpened = false;
+                releaseChannelLock();
             }
+        }, mHandler);
+    }
+
+    // releases channel and callback
+    private void releaseChannelLockAndReturn(
+            @Nullable byte[] response,
+            @Nullable Throwable exception,
+            ApduSenderResultCallback resultCallback,
+            Handler handler) {
+        handler.post(
+                () -> {
+                    releaseChannelLock();
+                    if (exception == null) {
+                        resultCallback.onResult(response);
+                    } else {
+                        resultCallback.onException(exception);
+                    }
+                });
+    }
+
+    private void releaseChannelLock() {
+        synchronized (mChannelInUseLock) {
+            logd("Channel lock released.");
+            mChannelInUse = false;
+            mChannelInUseLock.notify();
+        }
+    }
+
+    /**
+     * Acquires channel lock and returns {@code true} if successful.
+     *
+     * <p>It fails and returns {@code false} when:
+     * <ul>
+     *   <li>Called from main thread, and mChannelInUse=true, fails immediately.
+     *   <li>Called from non main thread, and mChannelInUse=true after 2 seconds waiting, fails.
+     * </ul>
+     */
+    private boolean acquireChannelLock() {
+        synchronized (mChannelInUseLock) {
+            if (mChannelInUse) {
+                if (!Looper.getMainLooper().equals(Looper.myLooper())) {
+                    logd("Logical channel is in use. Wait.");
+                    try {
+                        mChannelInUseLock.wait(WAIT_TIME_MS);
+                    } catch (InterruptedException e) {
+                        // nothing to do
+                    }
+                    if (mChannelInUse) {
+                        return false;
+                    }
+                } else {
+                    return false;
+                }
+            }
+            mChannelInUse = true;
+            logd("Channel lock acquired.");
+            return true;
         }
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java
index e06e4fe..cb37d88 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java
@@ -23,7 +23,6 @@
 import android.os.Bundle;
 import android.os.CarrierAssociatedAppEntry;
 import android.os.UserHandle;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.test.mock.MockContentProvider;
@@ -35,12 +34,9 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.telephony.flags.Flags;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
@@ -63,9 +59,6 @@
     private static final int USER_ID = 12345;
     private static final String CALLING_PACKAGE = "phone";
 
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
     // Mocked classes
     private Context mContext;
     private PackageManager mPackageManager;
@@ -86,7 +79,6 @@
 
     @Before
     public void setUp() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_HIDE_PREINSTALLED_CARRIER_APP_AT_MOST_ONCE);
         System.setProperty("dexmaker.dexcache",
                 InstrumentationRegistry.getTargetContext().getCacheDir().getPath());
         Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierDisplayNameResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierDisplayNameResolverTest.java
index 2ccfe0c..17fb829 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierDisplayNameResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierDisplayNameResolverTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 
@@ -271,4 +272,14 @@
         assertThat(data.shouldShowPlmn()).isTrue();
         assertThat(data.getPlmn()).isEqualTo(HOME_PLMN_NUMERIC);
     }
+
+    @Test
+    public void testCarrierDisplayNameData_enforceNonNullDataSpn() {
+        try {
+            CarrierDisplayNameData cdnd = new CarrierDisplayNameData.Builder()
+                    .setSpn("testSpn").build();
+            fail("Expected IAE");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
index 06b63a2..a860dff 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
@@ -52,6 +52,7 @@
 import android.net.Uri;
 import android.os.PersistableBundle;
 import android.os.Process;
+import android.os.UserHandle;
 import android.service.carrier.CarrierService;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
@@ -187,9 +188,15 @@
             pkg.packageName = pkgCertInfo.pkgName;
             pkg.signatures = new Signature[] {new Signature(pkgCertInfo.cert)};
 
-            when(mPackageManager.getPackageInfo(
-                    eq(pkgCertInfo.pkgName), eq(PM_FLAGS)))
-                    .thenReturn(pkg);
+            if (mFeatureFlags.supportCarrierServicesForHsum()) {
+                when(mPackageManager.getPackageInfoAsUser(
+                        eq(pkgCertInfo.pkgName), eq(PM_FLAGS), anyInt()))
+                        .thenReturn(pkg);
+            } else {
+                when(mPackageManager.getPackageInfo(
+                        eq(pkgCertInfo.pkgName), eq(PM_FLAGS)))
+                        .thenReturn(pkg);
+            }
             when(mPackageManager.getPackageUidAsUser(
                     eq(pkgCertInfo.pkgName), eq(pkgCertInfo.userInfo.id)))
                     .thenReturn(pkgCertInfo.uid);
@@ -213,8 +220,8 @@
         // Capture CarrierConfigChangeListener to emulate the carrier config change notification
         ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
                 ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
-        CarrierPrivilegesTracker cpt =
-                new CarrierPrivilegesTracker(mTestableLooper.getLooper(), mPhone, mContext);
+        CarrierPrivilegesTracker cpt = new CarrierPrivilegesTracker(mTestableLooper.getLooper(),
+                mPhone, mContext, mFeatureFlags);
         verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
                 listenerArgumentCaptor.capture());
         mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
@@ -579,8 +586,14 @@
 
         ResolveInfo pkg1ResolveInfo = new ResolveInfoBuilder().setActivity(PACKAGE_1).build();
         ResolveInfo pkg2ResolveInfo = new ResolveInfoBuilder().setActivity(PACKAGE_2).build();
-        when(mPackageManager.queryBroadcastReceivers(any(), anyInt())).thenReturn(
-                List.of(pkg1ResolveInfo, pkg2ResolveInfo));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            when(mPackageManager.queryBroadcastReceiversAsUser(any(), anyInt(),
+                    anyInt())).thenReturn(
+                    List.of(pkg1ResolveInfo, pkg2ResolveInfo));
+        } else {
+            when(mPackageManager.queryBroadcastReceivers(any(), anyInt())).thenReturn(
+                    List.of(pkg1ResolveInfo, pkg2ResolveInfo));
+        }
 
         // SIM is READY
         sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_READY);
@@ -712,8 +725,13 @@
         // Update PACKAGE_1 to have no signatures
         PackageInfo pkg = new PackageInfo();
         pkg.packageName = PACKAGE_1;
-        when(mPackageManager.getPackageInfo(eq(PACKAGE_1), eq(PM_FLAGS)))
-                .thenReturn(pkg);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            when(mPackageManager.getPackageInfoAsUser(eq(PACKAGE_1), eq(PM_FLAGS), anyInt()))
+                    .thenReturn(pkg);
+        } else {
+            when(mPackageManager.getPackageInfo(eq(PACKAGE_1), eq(PM_FLAGS)))
+                    .thenReturn(pkg);
+        }
 
         sendPackageChangedIntent(Intent.ACTION_PACKAGE_ADDED, PACKAGE_1);
         mTestableLooper.processAllMessages();
@@ -784,19 +802,35 @@
         setupInstalledPackages(
                 new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1),
                 new PackageCertInfo(PACKAGE_2, CERT_2, USER_1, UID_2));
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+        } else {
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+        }
         ResolveInfo resolveInfoPkg1 = new ResolveInfoBuilder().setService(PACKAGE_1).build();
-        doReturn(List.of(resolveInfoPkg1))
-                .when(mPackageManager)
-                .queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(resolveInfoPkg1))
+                    .when(mPackageManager)
+                    .queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            doReturn(List.of(resolveInfoPkg1))
+                    .when(mPackageManager)
+                    .queryIntentServices(any(), anyInt());
+        }
         mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
 
         // Package_1 is disabled
         when(mPackageManager.getApplicationEnabledSetting(eq(PACKAGE_1))).thenReturn(
                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
-        doReturn(List.of()).when(
-                mPackageManager).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of()).when(
+                    mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            doReturn(List.of()).when(
+                    mPackageManager).queryIntentServices(any(), anyInt());
+        }
         sendPackageChangedIntent(Intent.ACTION_PACKAGE_CHANGED, PACKAGE_1);
         mTestableLooper.processAllMessages();
 
@@ -807,8 +841,13 @@
         // Package_1 is re-enabled
         when(mPackageManager.getApplicationEnabledSetting(eq(PACKAGE_1))).thenReturn(
                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
-        doReturn(List.of(resolveInfoPkg1)).when(
-                mPackageManager).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(resolveInfoPkg1)).when(
+                    mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            doReturn(List.of(resolveInfoPkg1)).when(
+                    mPackageManager).queryIntentServices(any(), anyInt());
+        }
         sendPackageChangedIntent(Intent.ACTION_PACKAGE_CHANGED, PACKAGE_1);
         mTestableLooper.processAllMessages();
 
@@ -888,24 +927,45 @@
 
         ResolveInfo privilegeBroadcast = new ResolveInfoBuilder().setActivity(PACKAGE_1).build();
         ResolveInfo noPrivilegeBroadcast = new ResolveInfoBuilder().setActivity(PACKAGE_2).build();
-        when(mPackageManager.queryBroadcastReceivers(any(), anyInt())).thenReturn(
-                List.of(privilegeBroadcast, noPrivilegeBroadcast));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            when(mPackageManager.queryBroadcastReceiversAsUser(any(), anyInt(),
+                    anyInt())).thenReturn(
+                    List.of(privilegeBroadcast, noPrivilegeBroadcast));
+        } else {
+            when(mPackageManager.queryBroadcastReceivers(any(), anyInt())).thenReturn(
+                    List.of(privilegeBroadcast, noPrivilegeBroadcast));
+        }
 
         ResolveInfo privilegeActivity = new ResolveInfoBuilder().setActivity(PACKAGE_3).build();
         ResolveInfo noPrivilegeActivity = new ResolveInfoBuilder().setActivity(PACKAGE_4).build();
-        when(mPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(
-                List.of(privilegeActivity, noPrivilegeActivity));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            when(mPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), anyInt())).thenReturn(
+                    List.of(privilegeActivity, noPrivilegeActivity));
+        } else {
+            when(mPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(
+                    List.of(privilegeActivity, noPrivilegeActivity));
+        }
 
         ResolveInfo privilegeService = new ResolveInfoBuilder().setService(PACKAGE_5).build();
         ResolveInfo noPrivilegeService = new ResolveInfoBuilder().setService(PACKAGE_6).build();
         // Use doReturn instead of when/thenReturn which has NPE with unknown reason
-        doReturn(List.of(privilegeService, noPrivilegeService)).when(
-                mPackageManager).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(privilegeService, noPrivilegeService)).when(
+                    mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            doReturn(List.of(privilegeService, noPrivilegeService)).when(
+                    mPackageManager).queryIntentServices(any(), anyInt());
+        }
 
         ResolveInfo privilegeProvider = new ResolveInfoBuilder().setProvider(PACKAGE_7).build();
         ResolveInfo noPrivilegeProvider = new ResolveInfoBuilder().setProvider(PACKAGE_8).build();
-        when(mPackageManager.queryIntentContentProviders(any(), anyInt())).thenReturn(
-                List.of(privilegeProvider, noPrivilegeProvider));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            when(mPackageManager.queryIntentContentProvidersAsUser(any(), anyInt(), anyInt()))
+                    .thenReturn(List.of(privilegeProvider, noPrivilegeProvider));
+        } else {
+            when(mPackageManager.queryIntentContentProviders(any(), anyInt())).thenReturn(
+                    List.of(privilegeProvider, noPrivilegeProvider));
+        }
 
         mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
         Intent intent = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE);
@@ -934,11 +994,19 @@
         ResolveInfo privilegeService = new ResolveInfoBuilder().setService(PACKAGE_1).build();
         ResolveInfo noPrivilegeService = new ResolveInfoBuilder().setService(PACKAGE_2).build();
         // Use doReturn instead of when/thenReturn which has NPE with unknown reason
-        doReturn(List.of(privilegeService, noPrivilegeService)).when(
-                mPackageManager).queryIntentServices(any(), anyInt());
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(privilegeService, noPrivilegeService)).when(
+                    mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        } else {
+            doReturn(List.of(privilegeService, noPrivilegeService)).when(
+                    mPackageManager).queryIntentServices(any(), anyInt());
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        }
 
         // Get CS package name for the first time
         mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
@@ -947,7 +1015,11 @@
         mTestableLooper.processAllMessages();
 
         // Package manager should be queried from
-        verify(mPackageManager).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            verify(mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            verify(mPackageManager).queryIntentServices(any(), anyInt());
+        }
         assertEquals(PACKAGE_1, carrierServicePackageName);
         assertEquals(UID_1, carrierServiceUid);
 
@@ -958,7 +1030,11 @@
         mTestableLooper.processAllMessages();
 
         // It should return the same result, but didn't query package manager
-        verify(mPackageManager, never()).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            verify(mPackageManager, never()).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            verify(mPackageManager, never()).queryIntentServices(any(), anyInt());
+        }
         assertEquals(PACKAGE_1, carrierServicePackageName);
         assertEquals(UID_1, carrierServiceUid);
     }
@@ -978,12 +1054,21 @@
         ResolveInfo service1 = new ResolveInfoBuilder().setService(PACKAGE_1).build();
         ResolveInfo service2 = new ResolveInfoBuilder().setService(PACKAGE_2).build();
         // Use doReturn instead of when/thenReturn which has NPE with unknown reason
-        doReturn(List.of(service1, service2))
-                .when(mPackageManager)
-                .queryIntentServices(any(), anyInt());
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(service1, service2))
+                    .when(mPackageManager)
+                    .queryIntentServicesAsUser(any(), anyInt(), anyInt());
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        } else {
+            doReturn(List.of(service1, service2))
+                    .when(mPackageManager)
+                    .queryIntentServices(any(), anyInt());
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        }
 
         // Verify that neither carrier service (no privileges, or carrier-config based privileges)
         // are accepted.
@@ -992,7 +1077,11 @@
         int carrierServiceUid = mCarrierPrivilegesTracker.getCarrierServicePackageUid();
         mTestableLooper.processAllMessages();
 
-        verify(mPackageManager).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            verify(mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            verify(mPackageManager).queryIntentServices(any(), anyInt());
+        }
         assertNull(carrierServicePackageName);
         assertEquals(Process.INVALID_UID, carrierServiceUid);
     }
@@ -1008,11 +1097,19 @@
                 new PackageCertInfo(PACKAGE_3, CERT_1, USER_1, UID_1));
         // No CarrierService declared at all
         // Use doReturn instead of when/thenReturn which has NPE with unknown reason
-        doReturn(List.of()).when(
-                mPackageManager).queryIntentServices(any(), anyInt());
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of()).when(
+                    mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        } else {
+            doReturn(List.of()).when(
+                    mPackageManager).queryIntentServices(any(), anyInt());
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        }
 
         mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
         String carrierServicePackageName = mCarrierPrivilegesTracker.getCarrierServicePackageName();
@@ -1021,7 +1118,11 @@
 
         assertNull(carrierServicePackageName);
         assertEquals(Process.INVALID_UID, carrierServiceUid);
-        verify(mPackageManager).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            verify(mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            verify(mPackageManager).queryIntentServices(any(), anyInt());
+        }
     }
 
     @Test
@@ -1030,10 +1131,17 @@
         setupInstalledPackages(new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1));
         ResolveInfo carrierService = new ResolveInfoBuilder().setService(PACKAGE_1).build();
 
-        doReturn(List.of(carrierService))
-                .when(mPackageManager)
-                .queryIntentServices(any(), anyInt());
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(carrierService))
+                    .when(mPackageManager)
+                    .queryIntentServicesAsUser(any(), anyInt(), anyInt());
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+        } else {
+            doReturn(List.of(carrierService))
+                    .when(mPackageManager)
+                    .queryIntentServices(any(), anyInt());
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+        }
 
         // Set override, and verify the carrier service package was not set due to a lack of a
         // matching cert.
@@ -1056,11 +1164,19 @@
         ResolveInfo service1 = new ResolveInfoBuilder().setService(PACKAGE_1).build();
         ResolveInfo service2 = new ResolveInfoBuilder().setService(PACKAGE_2).build();
 
-        doReturn(List.of(service1, service2))
-                .when(mPackageManager)
-                .queryIntentServices(any(), anyInt());
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(service1, service2))
+                    .when(mPackageManager)
+                    .queryIntentServicesAsUser(any(), anyInt(), anyInt());
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+        } else {
+            doReturn(List.of(service1, service2))
+                    .when(mPackageManager)
+                    .queryIntentServices(any(), anyInt());
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+        }
 
         mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
 
@@ -1093,10 +1209,17 @@
         setupInstalledPackages(new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1));
         ResolveInfo carrierService = new ResolveInfoBuilder().setService(PACKAGE_1).build();
 
-        doReturn(List.of(carrierService))
-                .when(mPackageManager)
-                .queryIntentServices(any(), anyInt());
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(carrierService))
+                    .when(mPackageManager)
+                    .queryIntentServicesAsUser(any(), anyInt(), anyInt());
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+        } else {
+            doReturn(List.of(carrierService))
+                    .when(mPackageManager)
+                    .queryIntentServices(any(), anyInt());
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+        }
 
         // Set override, and expect that an invalid package name would not be selected as the
         // carrier config service.
@@ -1114,21 +1237,40 @@
     }
 
     private void sendSimCardStateChangedIntent(int phoneId, int simState) {
-        mContext.sendBroadcast(
-                new Intent(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED)
-                        .putExtra(EXTRA_SIM_STATE, simState)
-                        .putExtra(PhoneConstants.PHONE_KEY, phoneId));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            mContext.sendBroadcastAsUser(
+                    new Intent(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED)
+                            .putExtra(EXTRA_SIM_STATE, simState)
+                            .putExtra(PhoneConstants.PHONE_KEY, phoneId), UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(
+                    new Intent(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED)
+                            .putExtra(EXTRA_SIM_STATE, simState)
+                            .putExtra(PhoneConstants.PHONE_KEY, phoneId));
+        }
     }
 
     private void sendSimApplicationStateChangedIntent(int phoneId, int simState) {
-        mContext.sendBroadcast(
-                new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED)
-                        .putExtra(EXTRA_SIM_STATE, simState)
-                        .putExtra(PhoneConstants.PHONE_KEY, phoneId));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            mContext.sendBroadcastAsUser(
+                    new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED)
+                            .putExtra(EXTRA_SIM_STATE, simState)
+                            .putExtra(PhoneConstants.PHONE_KEY, phoneId), UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(
+                    new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED)
+                            .putExtra(EXTRA_SIM_STATE, simState)
+                            .putExtra(PhoneConstants.PHONE_KEY, phoneId));
+        }
     }
 
     private void sendPackageChangedIntent(String action, String pkgName) {
-        mContext.sendBroadcast(new Intent(action, new Uri.Builder().path(pkgName).build()));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            mContext.sendBroadcastAsUser(
+                    new Intent(action, new Uri.Builder().path(pkgName).build()), UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(new Intent(action, new Uri.Builder().path(pkgName).build()));
+        }
     }
 
     /** Returns the SHA-1 hash (as a hex String) for the given hex String. */
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java
index a3fcd4e..753f85c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java
@@ -98,7 +98,7 @@
         super.setUp(getClass().getSimpleName());
         ((MockContentResolver) mContext.getContentResolver()).addProvider(
                 CarrierId.AUTHORITY, new CarrierIdContentProvider());
-        mCarrierResolver = new CarrierResolver(mPhone);
+        mCarrierResolver = new CarrierResolver(mPhone, mFeatureFlags);
         mCarrierResolver.sendEmptyMessage(ICC_CHANGED_EVENT);
         processAllMessages();
         logd("CarrierResolverTest -Setup!");
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 4612ad9..70e3dee 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -541,6 +541,12 @@
         }
 
         @Override
+        public void sendBroadcastAsUser(Intent intent, UserHandle user,
+                String receiverPermission, Bundle options) {
+            sendBroadcast(intent);
+        }
+
+        @Override
         public void sendBroadcastMultiplePermissions(Intent intent,
                 String[] includePermissions, String[] excludePermissions) {
             sendBroadcast(intent);
@@ -681,6 +687,9 @@
 
         @Override
         public void startActivity(Intent intent) {}
+
+        @Override
+        public void startActivityAsUser(Intent intent, UserHandle user) {}
     }
 
     private final Multimap<String, ComponentName> mComponentNamesByAction =
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
index 8209f92..8dad3ec 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
@@ -23,7 +23,6 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.telephony.CellIdentityGsm;
 import android.telephony.CellInfo;
@@ -101,23 +100,7 @@
     }
 
     @Test @SmallTest
-    public void testNotifyDataActivity() throws Exception {
-        when(mFeatureFlags.notifyDataActivityChangedWithSlot()).thenReturn(false);
-        //mock data activity state
-        doReturn(TelephonyManager.DATA_ACTIVITY_NONE).when(mPhone).getDataActivityState();
-        mDefaultPhoneNotifierUT.notifyDataActivity(mPhone);
-        verify(mTelephonyRegistryManager).notifyDataActivityChanged(eq(0),
-                eq(TelephonyManager.DATA_ACTIVITY_NONE));
-
-        doReturn(1).when(mPhone).getSubId();
-        doReturn(TelephonyManager.DATA_ACTIVITY_IN).when(mPhone).getDataActivityState();
-        mDefaultPhoneNotifierUT.notifyDataActivity(mPhone);
-        verify(mTelephonyRegistryManager).notifyDataActivityChanged(eq(1),
-                eq(TelephonyManager.DATA_ACTIVITY_IN));
-    }
-    @Test @SmallTest
     public void testNotifyDataActivityWithSlot() throws Exception {
-        when(mFeatureFlags.notifyDataActivityChangedWithSlot()).thenReturn(true);
         //mock data activity state
         doReturn(TelephonyManager.DATA_ACTIVITY_NONE).when(mPhone).getDataActivityState();
         doReturn(PHONE_ID).when(mPhone).getPhoneId();
@@ -401,4 +384,13 @@
         verify(mTelephonyRegistryManager).notifyCarrierRoamingNtnModeChanged(
                 eq(subId), eq(true));
     }
+
+    @Test
+    @SmallTest
+    public void testCarrierRoamingNtnEligibleStateChanged() {
+        int subId = mPhone.getSubId();
+        mDefaultPhoneNotifierUT.notifyCarrierRoamingNtnEligibleStateChanged(mPhone, true);
+        verify(mTelephonyRegistryManager).notifyCarrierRoamingNtnEligibleStateChanged(
+                eq(subId), eq(true));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
index a13a92c..c923f69 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -131,11 +131,14 @@
                     + Telephony.SimInfo.COLUMN_SATELLITE_ENABLED + " INTEGER DEFAULT 0,"
                     + Telephony.SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER
                     + " INTEGER DEFAULT 1, "
-                    + Telephony.SimInfo.COLUMN_IS_NTN + " INTEGER DEFAULT 0,"
+                    + Telephony.SimInfo.COLUMN_IS_ONLY_NTN + " INTEGER DEFAULT 0,"
                     + Telephony.SimInfo.COLUMN_SERVICE_CAPABILITIES + " INTEGER DEFAULT 7,"
                     + Telephony.SimInfo.COLUMN_TRANSFER_STATUS + " INTEGER DEFAULT 0,"
                     + Telephony.SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS + " INTEGER DEFAULT 0,"
-                    + Telephony.SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS + " TEXT"
+                    + Telephony.SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS + " TEXT,"
+                    + Telephony.SimInfo.COLUMN_SATELLITE_ESOS_SUPPORTED + " INTEGER DEFAULT 0,"
+                    + Telephony.SimInfo.COLUMN_IS_SATELLITE_PROVISIONED_FOR_NON_IP_DATAGRAM
+                    + " INTEGER DEFAULT 0"
                     + ");";
         }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
index 45f8c12..e56ac90 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
@@ -21,10 +21,12 @@
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.*;
 
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.emergency.EmergencyNumber;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -313,4 +315,52 @@
         assertEquals(DisconnectCause.OUT_OF_SERVICE,
                 connection.disconnectCauseFromCode(CallFailCause.LOCAL_SERVICE_UNAVAILABLE));
     }
+
+    @Test
+    public void testUpdateEmergencyRouting() {
+        Bundle extras = new Bundle();
+        extras.putBoolean(PhoneConstants.EXTRA_USE_EMERGENCY_ROUTING, true);
+
+        DialArgs dialArgs = new DialArgs.Builder()
+                .setIsEmergency(true)
+                .setIntentExtras(extras)
+                .build();
+
+        doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+
+        connection = new GsmCdmaConnection(mPhone, "911", mCT, null, dialArgs);
+        // Not updated when category is unset.
+        assertEquals(getTestEmergencyNumber(), connection.getEmergencyNumberInfo());
+
+        extras.putInt(PhoneConstants.EXTRA_EMERGENCY_SERVICE_CATEGORY,
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
+
+        dialArgs = new DialArgs.Builder()
+                .setIsEmergency(true)
+                .setIntentExtras(extras)
+                .build();
+
+        connection = new GsmCdmaConnection(mPhone, "911", mCT, null, dialArgs);
+        // Not updated when category is EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED.
+        assertEquals(getTestEmergencyNumber(), connection.getEmergencyNumberInfo());
+
+        extras.putInt(PhoneConstants.EXTRA_EMERGENCY_SERVICE_CATEGORY,
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE);
+
+        dialArgs = new DialArgs.Builder()
+                .setIsEmergency(true)
+                .setIntentExtras(extras)
+                .build();
+
+        connection = new GsmCdmaConnection(mPhone, "911", mCT, null, dialArgs);
+
+        EmergencyNumber expectedNumber = new EmergencyNumber("911", "us", "30",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+                new ArrayList<String>(), EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+
+        // Updated when category is not EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED.
+        assertNotEquals(getTestEmergencyNumber(), connection.getEmergencyNumberInfo());
+        assertEquals(expectedNumber, connection.getEmergencyNumberInfo());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index ba08f8b..7735c97 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -2018,6 +2018,42 @@
 
     @Test
     @SmallTest
+    public void testUsageSettingUpdate_ResetToDefault() {
+        setupUsageSettingResources();
+        mPhoneUT.mCi = mMockCi;
+
+        SubscriptionInfoInternal si = makeSubscriptionInfoInternal(
+                false, SubscriptionManager.USAGE_SETTING_DATA_CENTRIC);
+        doReturn(si).when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+
+        mPhoneUT.updateUsageSetting();
+        processAllMessages();
+
+        verify(mMockCi).getUsageSetting(any());
+        mPhoneUT.sendMessage(mPhoneUT.obtainMessage(GsmCdmaPhone.EVENT_GET_USAGE_SETTING_DONE,
+                new AsyncResult(null,
+                        new int[]{SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC}, null)));
+        processAllMessages();
+
+        // Grab the message to ensure it returns the preferred value for updating the cache
+        ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mMockCi).setUsageSetting(
+                messageCaptor.capture(), eq(SubscriptionManager.USAGE_SETTING_DATA_CENTRIC));
+        AsyncResult.forMessage(messageCaptor.getValue());
+        messageCaptor.getValue().sendToTarget();
+        processAllMessages();
+
+        si = makeSubscriptionInfoInternal(false, SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC);
+        doReturn(si).when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+
+        mPhoneUT.updateUsageSetting();
+        processAllMessages();
+
+        verify(mMockCi).setUsageSetting(any(), eq(SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC));
+    }
+
+    @Test
+    @SmallTest
     public void testUsageSettingUpdate_DefaultOpportunistic() {
         setupUsageSettingResources();
         mPhoneUT.mCi = mMockCi;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
index f64155c..6c84b08 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
@@ -34,6 +34,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.os.Binder;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SmsMessage;
@@ -71,6 +72,7 @@
     PersistableBundle mBundle = new PersistableBundle();
     private static final int SUB_0 = 0;
     private static final String TAG = "ImsSmsDispatcherTest";
+    private int mCallingUserId;
 
     @Before
     public void setUp() throws Exception {
@@ -94,6 +96,7 @@
         mTrackerData = new HashMap<>(1);
         when(mSmsTracker.getData()).thenReturn(mTrackerData);
         verify(mSmsDispatchersController).setImsManager(mImsManager);
+        mCallingUserId = Binder.getCallingUserHandle().getIdentifier();
     }
 
     @After
@@ -333,8 +336,7 @@
         doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
 
         mImsSmsDispatcher.sendText("+15555551212", null, "MessageRef test",
-                null, null, null, null, false,
-                -1, false, -1, false, 0);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0);
         verify(mImsManager).sendSms(eq(token + 1), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
                 nullable(String.class), eq(false), (byte[]) any());
     }
@@ -349,8 +351,7 @@
         doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
 
         mImsSmsDispatcher.sendText("+15555551212", null, "MessageRef test",
-                null, null, null, null, false,
-                -1, false, -1, false, 0);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0);
         verify(mImsManager).sendSms(eq(token + 1), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
                 nullable(String.class), eq(false), (byte[]) any());
 
@@ -384,8 +385,7 @@
         doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
 
         mImsSmsDispatcher.sendText("+15555551212", null, "MessageRef test",
-                null, null, null, null, false,
-                -1, false, -1, false, 0);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0);
         verify(mImsManager).sendSms(eq(token + 1), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
                 nullable(String.class), eq(false), (byte[]) any());
 
@@ -423,8 +423,7 @@
         when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
         doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
         mImsSmsDispatcher.sendText("+15555551212", null, "Retry test",
-                null, null, null, null, false,
-                -1, false, -1, false, 0);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0);
         verify(mImsManager).sendSms(eq(token + 1), eq(messageRef), eq(SmsMessage.FORMAT_3GPP),
                 nullable(String.class), eq(false), (byte[]) any());
         assertEquals(2, mImsSmsDispatcher.getMaxRetryCountOverIms());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index e08abd9..f92643a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -1507,6 +1507,52 @@
     }
 
     @Test
+    public void testTransitionToNrIdle() throws Exception {
+        doReturn(true).when(mFeatureFlags).supportNrSaRrcIdle();
+        doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+        doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(mServiceState).getNrFrequencyRange();
+        ArrayList<PhysicalChannelConfig> physicalChannelConfigs = new ArrayList<>();
+        // use advanced band
+        physicalChannelConfigs.add(new PhysicalChannelConfig.Builder()
+                .setPhysicalCellId(1)
+                .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+                .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+                .setBand(41)
+                .build());
+        doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
+        mBundle.putIntArray(CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY,
+                new int[]{41});
+        mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
+                "connected_mmwave,any,10");
+        sendCarrierConfigChanged();
+
+        assertEquals("connected_mmwave", getCurrentState().getName());
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+                mNetworkTypeController.getOverrideNetworkType());
+
+        // empty PCC, switch to connected_rrc_idle,
+        // isNrAdvanced is still true(due to either advance band OR frequency)
+        physicalChannelConfigs.clear();
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, physicalChannelConfigs, null));
+        processAllMessages();
+
+        assertEquals("connected_rrc_idle", getCurrentState().getName());
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+                mNetworkTypeController.getOverrideNetworkType());
+        assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+        // Received an event update, verify should stay in idle because physical link didn't change,
+        // otherwise there is a loop between advance state and idle state.
+        mNetworkTypeController.sendMessage(0 /* EVENT_UPDATE */);
+        processAllMessages();
+
+        assertEquals("connected_rrc_idle", getCurrentState().getName());
+        assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+                mNetworkTypeController.getOverrideNetworkType());
+    }
+
+    @Test
     public void testSecondaryTimerAdvanceBand() throws Exception {
         doReturn(true).when(mFeatureFlags).supportNrSaRrcIdle();
         doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
@@ -1603,8 +1649,23 @@
                 mNetworkTypeController.getOverrideNetworkType());
         assertTrue(mNetworkTypeController.areAnyTimersActive());
 
+
+        // the timer has been reduced from 20 - 6s(advance band) to 5s(regular). Suppose passed 1s,
+        // a new PCC shouldn't affect the timer.
+        moveTimeForward(1 * 1000);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, Collections.emptyList(), null));
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, List.of(
+                        new PhysicalChannelConfig.Builder()
+                                .setPhysicalCellId(3)
+                                .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+                                .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+                                .build()), null));
+        processAllMessages();
+
         // Verify the timer has been reduced from 20 - 6s(advance band) to 5s(regular).
-        moveTimeForward(5 * 1000);
+        moveTimeForward(4 * 1000);
         processAllMessages();
 
         assertEquals("connected_rrc_idle", getCurrentState().getName());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
index 0e04aff..b09d90d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
@@ -203,26 +203,40 @@
         for (int i : enabledLogicalSlots) { expectedSlots.add(i); }
         assertEquals(expectedSlots, mPcm.getSlotsSupportingSimultaneousCellularCalls());
     }
+    @Test
+    @SmallTest
+    public void testUpdateSimultaneousCallingSupportBothInvalidSlotIds() throws Exception {
+        // Test case where both slot IDs are invalid (-1 and 5).
+        testUpdateSimultaneousCallingSupportWithInvalidSlots(Arrays.asList(-1, 5));
+    }
 
     @Test
     @SmallTest
-    public void testUpdateSimultaneousCallingSupport_invalidResponse_shouldFail() throws Exception {
-        doReturn(false).when(mFeatureFlags).simultaneousCallingIndications();
-        init(2);
-        mPcm.updateSimultaneousCallingSupport();
+    public void testUpdateSimultaneousCallingSupportOneInvalidSlotId() throws Exception {
+        // Test case where one slot ID is valid (1) and the other is invalid (2).
+        testUpdateSimultaneousCallingSupportWithInvalidSlots(Arrays.asList(1, 2));
+    }
 
-        // Have the modem send invalid phone slots -1 and 5:
-        List<Integer> invalidEnabledLogicalSlots = Arrays.asList(-1, 5);
-        ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
-        verify(mMockRadioConfig).updateSimultaneousCallingSupport(captor.capture());
-        Message msg = captor.getValue();
-        AsyncResult.forMessage(msg, invalidEnabledLogicalSlots, null);
-        msg.sendToTarget();
-        processAllMessages();
+    @Test
+    @SmallTest
+    public void testUpdateSimultaneousCallingSupportInvalidExtraSlotId() throws Exception {
+        // Test case where the number of slot IDs exceeds the phone count (2) and one slot ID is
+        // invalid (2).
+        testUpdateSimultaneousCallingSupportWithInvalidSlots(Arrays.asList(0, 1, 2));
+    }
 
-        // We would expect to DSDA to be disabled and mSlotsSupportingSimultaneousCellularCalls to
-        // have been cleared:
-        assertTrue(mPcm.getSlotsSupportingSimultaneousCellularCalls().isEmpty());
+    @Test
+    @SmallTest
+    public void testUpdateSimultaneousCallingSupportInvalidSingularSlotId() throws Exception {
+        // Test case where only a single, invalid slot ID (0) is provided.
+        testUpdateSimultaneousCallingSupportWithInvalidSlots(List.of(0));
+    }
+
+    @Test
+    @SmallTest
+    public void testUpdateSimultaneousCallingSupportInvalidEmptySlotIds() throws Exception {
+        // Test case where an empty list of slot IDs is provided.
+        testUpdateSimultaneousCallingSupportWithInvalidSlots(List.of());
     }
 
     /**
@@ -576,4 +590,28 @@
 
         assertEquals(capability, mPcm.getStaticPhoneCapability());
     }
+
+    private void testUpdateSimultaneousCallingSupportWithInvalidSlots(List<Integer> invalidSlots)
+            throws Exception {
+        doReturn(false).when(mFeatureFlags).simultaneousCallingIndications();
+        init(2);
+        mPcm.updateSimultaneousCallingSupport();
+
+        sendInvalidSlotsToModem(invalidSlots);
+        processAllMessages();
+
+        assertDsdaDisabledAndSlotsCleared();
+    }
+
+    private void sendInvalidSlotsToModem(List<Integer> invalidSlots) {
+        ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+        verify(mMockRadioConfig).updateSimultaneousCallingSupport(captor.capture());
+        Message msg = captor.getValue();
+        AsyncResult.forMessage(msg, invalidSlots, null);
+        msg.sendToTarget();
+    }
+
+    private void assertDsdaDisabledAndSlotsCleared() {
+        assertTrue(mPcm.getSlotsSupportingSimultaneousCellularCalls().isEmpty());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index bf9ced3..7a30984 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -628,6 +628,7 @@
     public void testFormatSingaporeInternational() {
         // Disable feature flag.
         mSetFlagsRule.disableFlags(Flags.FLAG_REMOVE_COUNTRY_CODE_FROM_LOCAL_SINGAPORE_CALLS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_NATIONAL_COUNTRY_CODE_FORMATTING_FOR_LOCAL_CALLS);
 
         // International call from a US iso
         assertEquals("+65 6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "US"));
@@ -636,6 +637,7 @@
         assertEquals("+65 6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "us"));
 
         // Enable feature flag
+        mSetFlagsRule.disableFlags(Flags.FLAG_NATIONAL_COUNTRY_CODE_FORMATTING_FOR_LOCAL_CALLS);
         mSetFlagsRule.enableFlags(Flags.FLAG_REMOVE_COUNTRY_CODE_FROM_LOCAL_SINGAPORE_CALLS);
 
         // Internal call from a US iso
@@ -644,6 +646,7 @@
         // Lowercase country iso
         assertEquals("+65 6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "us"));
         mSetFlagsRule.disableFlags(Flags.FLAG_REMOVE_COUNTRY_CODE_FROM_LOCAL_SINGAPORE_CALLS);
+        mSetFlagsRule.disableFlags(Flags.FLAG_NATIONAL_COUNTRY_CODE_FORMATTING_FOR_LOCAL_CALLS);
     }
 
     /**
@@ -655,14 +658,16 @@
     public void testFormatSingaporeNational() {
         // Disable feature flag.
         mSetFlagsRule.disableFlags(Flags.FLAG_REMOVE_COUNTRY_CODE_FROM_LOCAL_SINGAPORE_CALLS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_NATIONAL_COUNTRY_CODE_FORMATTING_FOR_LOCAL_CALLS);
 
         // Local call from a Singaporean number to a Singaporean number
-        assertEquals("+65 6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "SG"));
+        assertEquals("6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "SG"));
 
         // Lowercase country iso.
-        assertEquals("+65 6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "sg"));
+        assertEquals("6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "sg"));
 
         // Enable feature flag.
+        mSetFlagsRule.disableFlags(Flags.FLAG_NATIONAL_COUNTRY_CODE_FORMATTING_FOR_LOCAL_CALLS);
         mSetFlagsRule.enableFlags(Flags.FLAG_REMOVE_COUNTRY_CODE_FROM_LOCAL_SINGAPORE_CALLS);
 
         // Local call from a Singaporean number to a Singaporean number.
@@ -671,6 +676,44 @@
         // Lowercase country iso.
         assertEquals("6521 8000", PhoneNumberUtils.formatNumber("+6565218000", "sg"));
         mSetFlagsRule.disableFlags(Flags.FLAG_REMOVE_COUNTRY_CODE_FROM_LOCAL_SINGAPORE_CALLS);
+        mSetFlagsRule.disableFlags(Flags.FLAG_NATIONAL_COUNTRY_CODE_FORMATTING_FOR_LOCAL_CALLS);
+    }
+
+    @SmallTest
+    @Test
+    public void testFormatTaiwanNational() {
+        // Disable feature flag.
+        mSetFlagsRule.disableFlags(Flags.FLAG_NATIONAL_COUNTRY_CODE_FORMATTING_FOR_LOCAL_CALLS);
+        assertEquals("+886 2 8729 6000", PhoneNumberUtils.formatNumber("+886287296000", "TW"));
+        assertEquals("+886 2 8729 6000", PhoneNumberUtils.formatNumber("+886287296000", "tw"));
+        assertEquals("+886 988 102 544", PhoneNumberUtils.formatNumber("+886988102544", "TW"));
+        assertEquals("+886 988 102 544", PhoneNumberUtils.formatNumber("+886988102544", "tw"));
+
+        // Enable feature flag.
+        mSetFlagsRule.enableFlags(Flags.FLAG_NATIONAL_COUNTRY_CODE_FORMATTING_FOR_LOCAL_CALLS);
+        assertEquals("02 8729 6000", PhoneNumberUtils.formatNumber("+886287296000", "TW"));
+        assertEquals("02 8729 6000", PhoneNumberUtils.formatNumber("+886287296000", "tw"));
+        assertEquals("0988 102 544", PhoneNumberUtils.formatNumber("+886988102544", "TW"));
+        assertEquals("0988 102 544", PhoneNumberUtils.formatNumber("+886988102544", "tw"));
+        mSetFlagsRule.disableFlags(Flags.FLAG_NATIONAL_COUNTRY_CODE_FORMATTING_FOR_LOCAL_CALLS);
+    }
+
+    @SmallTest
+    @Test
+    public void testFormatTaiwanInternational() {
+        // Disable feature flag.
+        mSetFlagsRule.disableFlags(Flags.FLAG_NATIONAL_COUNTRY_CODE_FORMATTING_FOR_LOCAL_CALLS);
+        assertEquals("+886 2 8729 6000", PhoneNumberUtils.formatNumber("+886287296000", "US"));
+        assertEquals("+886 2 8729 6000", PhoneNumberUtils.formatNumber("+886287296000", "us"));
+        assertEquals("+886 988 102 544", PhoneNumberUtils.formatNumber("+886988102544", "US"));
+        assertEquals("+886 988 102 544", PhoneNumberUtils.formatNumber("+886988102544", "us"));
+
+        mSetFlagsRule.enableFlags(Flags.FLAG_NATIONAL_COUNTRY_CODE_FORMATTING_FOR_LOCAL_CALLS);
+        assertEquals("+886 2 8729 6000", PhoneNumberUtils.formatNumber("+886287296000", "US"));
+        assertEquals("+886 2 8729 6000", PhoneNumberUtils.formatNumber("+886287296000", "us"));
+        assertEquals("+886 988 102 544", PhoneNumberUtils.formatNumber("+886988102544", "US"));
+        assertEquals("+886 988 102 544", PhoneNumberUtils.formatNumber("+886988102544", "us"));
+        mSetFlagsRule.disableFlags(Flags.FLAG_NATIONAL_COUNTRY_CODE_FORMATTING_FOR_LOCAL_CALLS);
     }
 
     @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index e3da458..1465176 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -26,6 +26,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.nullable;
@@ -594,7 +595,7 @@
         sst.setRadioPowerForReason(false, false, false, false, reason);
         assertTrue(sst.getRadioPowerOffReasons().contains(reason));
         assertTrue(sst.getRadioPowerOffReasons().size() == 1);
-        verify(mSatelliteController).onCellularRadioPowerOffRequested();
+        verify(mSatelliteController).onSetCellularRadioPowerStateRequested(eq(false));
         clearInvocations(mSatelliteController);
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
@@ -602,7 +603,7 @@
                 TelephonyManager.RADIO_POWER_REASON_USER);
         assertTrue(sst.getRadioPowerOffReasons().contains(reason));
         assertTrue(sst.getRadioPowerOffReasons().size() == 1);
-        verify(mSatelliteController, never()).onCellularRadioPowerOffRequested();
+        verify(mSatelliteController, never()).onSetCellularRadioPowerStateRequested(anyBoolean());
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
 
@@ -610,7 +611,7 @@
         // had been turned off for.
         sst.setRadioPowerForReason(true, false, false, false, reason);
         assertTrue(sst.getRadioPowerOffReasons().isEmpty());
-        verify(mSatelliteController, never()).onCellularRadioPowerOffRequested();
+        verify(mSatelliteController).onSetCellularRadioPowerStateRequested(eq(true));
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
 
@@ -1928,6 +1929,8 @@
         sst.setRadioPower(false);
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
+        verify(mSatelliteController).onSetCellularRadioPowerStateRequested(eq(false));
+        verify(mSatelliteController).onPowerOffCellularRadioFailed();
         sst.requestShutdown();
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         assertFalse(mSimulatedCommands.getRadioState()
@@ -3082,6 +3085,7 @@
         doReturn(mImsPhone).when(mPhone).getImsPhone();
         doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM).when(mImsPhone)
                 .getImsRegistrationTech();
+        doReturn(true).when(mPhone).isImsRegistered();
         String[] formats = {CROSS_SIM_CALLING_VOICE_FORMAT, "%s"};
         Resources r = mContext.getResources();
         doReturn(formats).when(r).getStringArray(anyInt());
@@ -3245,6 +3249,69 @@
         assertThat(b.getBoolean(TelephonyManager.EXTRA_SHOW_PLMN)).isTrue();
     }
 
+    @Test
+    public void testUpdateSpnDisplayLegacy_CrossSimCallingDataOOS_displayOOS() {
+        mBundle.putBoolean(
+                CarrierConfigManager.KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL, false);
+        sendCarrierConfigUpdate(PHONE_ID);
+
+        // GSM phone
+        doReturn(true).when(mPhone).isPhoneTypeGsm();
+
+        // Reg tech is Cross Sim but both data and voice are OOS
+        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
+        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getDataRegistrationState();
+        doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mServiceState).getDataNetworkType();
+        doReturn(mImsPhone).when(mPhone).getImsPhone();
+        doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM).when(
+                mImsPhone).getImsRegistrationTech();
+        sst.mSS = mServiceState;
+
+        // wifi-calling is enabled
+        doReturn(true).when(mPhone).isWifiCallingEnabled();
+
+        // update the spn
+        sst.updateSpnDisplay();
+
+        // Plmn should be shown, and the string is "No service"
+        Bundle b = getExtrasFromLastSpnUpdateIntent();
+        assertThat(b.getString(TelephonyManager.EXTRA_PLMN))
+                .isEqualTo(CARRIER_NAME_DISPLAY_NO_SERVICE);
+        assertThat(b.getBoolean(TelephonyManager.EXTRA_SHOW_PLMN)).isTrue();
+    }
+
+    @Test
+    public void testUpdateSpnDisplayLegacy_CrossSimCallingVoiceOOS_displayOOS() {
+        mBundle.putBoolean(
+                CarrierConfigManager.KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL, false);
+        sendCarrierConfigUpdate(PHONE_ID);
+
+        // GSM phone
+        doReturn(true).when(mPhone).isPhoneTypeGsm();
+
+        // voice out of service but data in service (connected to Cross Sim IWLAN)
+        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
+        doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getDataRegistrationState();
+        doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mServiceState).getDataNetworkType();
+        doReturn(mImsPhone).when(mPhone).getImsPhone();
+        doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM).when(
+                mImsPhone).getImsRegistrationTech();
+        doReturn(false).when(mPhone).isImsRegistered();
+        sst.mSS = mServiceState;
+
+        // wifi-calling is enabled
+        doReturn(true).when(mPhone).isWifiCallingEnabled();
+
+        // update the spn
+        sst.updateSpnDisplay();
+
+        // Plmn should be shown, and the string is "No service"
+        Bundle b = getExtrasFromLastSpnUpdateIntent();
+        assertThat(b.getString(TelephonyManager.EXTRA_PLMN))
+                .isEqualTo(CARRIER_NAME_DISPLAY_NO_SERVICE);
+        assertThat(b.getBoolean(TelephonyManager.EXTRA_SHOW_PLMN)).isTrue();
+    }
+
     private Bundle getExtrasFromLastSpnUpdateIntent() {
         // Verify the spn update notification was sent
         ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimultaneousCallingTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SimultaneousCallingTrackerTest.java
index 879b184..054df07 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimultaneousCallingTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimultaneousCallingTrackerTest.java
@@ -254,7 +254,7 @@
 
     /**
      * Test that simultaneous calling is not supported when IMS is not registered and cellular
-     * simultaneous calling is only supported for one SIM subscription.
+     * simultaneous calling is not supported for any SIM subscriptions.
      */
     @Test
     @SmallTest
@@ -264,8 +264,8 @@
         init(2);
         setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
 
-        // Have the modem inform telephony that only phone slot 0 supports DSDA:
-        List<Integer> enabledLogicalSlots = List.of(0);
+        // Have the modem inform telephony that no phone slots currently support DSDA:
+        List<Integer> enabledLogicalSlots = List.of();
         setAndVerifySlotsSupportingSimultaneousCellularCalling(enabledLogicalSlots);
 
         // Trigger onSubscriptionsChanged by updating the subscription ID of a phone slot:
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java
index 06dbd0b..710f1ec 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java
@@ -31,6 +31,7 @@
 
 import android.compat.testing.PlatformCompatChangeRule;
 import android.content.pm.PackageManager;
+import android.os.Binder;
 import android.os.Build;
 import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
@@ -67,6 +68,7 @@
     private SmsController mSmsControllerUT;
     private final String smscAddrStr = "+1206313004";
     private String mCallingPackage;
+    private int mCallingUserId;
 
     @Before
     public void setUp() throws Exception {
@@ -74,6 +76,7 @@
         mAdnRecordCache = Mockito.mock(AdnRecordCache.class);
         mSmsControllerUT = new SmsController(mContext, mFeatureFlags);
         mCallingPackage = mContext.getOpPackageName();
+        mCallingUserId = Binder.getCallingUserHandle().getIdentifier();
 
         doReturn(true).when(mPackageManager).hasSystemFeature(
                 PackageManager.FEATURE_TELEPHONY_MESSAGING);
@@ -196,9 +199,9 @@
         doReturn(true).when(mSubscriptionManager)
                 .isSubscriptionAssociatedWithUser(eq(subId), any());
 
-        mSmsControllerUT.sendVisualVoicemailSmsForSubscriber(mCallingPackage,null ,
-                subId, null, 0, null, null);
-        verify(mIccSmsInterfaceManager).sendTextWithSelfPermissions(any(),
+        mSmsControllerUT.sendVisualVoicemailSmsForSubscriber(mCallingPackage, mCallingUserId,
+                null , subId, null, 0, null, null);
+        verify(mIccSmsInterfaceManager).sendTextWithSelfPermissions(any(), eq(mCallingUserId),
                 any(), any(), any(), any(), any(), any(), eq(false), eq(true));
     }
 
@@ -206,10 +209,11 @@
     public void sendVisualVoicemailSmsForSubscriber_phoneIsInEcm() {
         doReturn(true).when(mPhone).isInEcm();
 
-        mSmsControllerUT.sendVisualVoicemailSmsForSubscriber(mCallingPackage,null ,
-                1, null, 0, null, null);
+        mSmsControllerUT.sendVisualVoicemailSmsForSubscriber(mCallingPackage, mCallingUserId,
+                null , 1, null, 0, null, null);
         verify(mIccSmsInterfaceManager, never()).sendTextWithSelfPermissions(any(),
-                any(), any(), any(), any(), any(), any(), eq(false), eq(true));
+                eq(mCallingUserId), any(), any(), any(), any(), any(), any(),
+                eq(false), eq(true));
 
         doReturn(false).when(mPhone).isInEcm();
     }
@@ -223,7 +227,8 @@
         mSmsControllerUT.sendTextForSubscriber(subId, mCallingPackage, null, "1234",
                 null, "text", null, null, false, 0L, true, true);
         verify(mIccSmsInterfaceManager, Mockito.times(1))
-                .sendText(mCallingPackage, "1234", null, "text", null, null, false, 0L, true);
+                .sendText(mCallingPackage, mCallingUserId,
+                        "1234", null, "text", null, null, false, 0L, true);
     }
 
     @Test
@@ -239,7 +244,8 @@
         mSmsControllerUT.sendTextForSubscriber(subId, mCallingPackage, null, "1234",
                 null, "text", null, null, false, 0L, true, true);
         verify(mIccSmsInterfaceManager, Mockito.times(1))
-                .sendText(mCallingPackage, "1234", null, "text", null, null, false, 0L, true);
+                .sendText(mCallingPackage, mCallingUserId,
+                        "1234", null, "text", null, null, false, 0L, true);
     }
 
     @Test
@@ -255,7 +261,8 @@
         mSmsControllerUT.sendTextForSubscriber(subId, mCallingPackage, null, "1234",
                 null, "text", null, null, false, 0L, true, true);
         verify(mIccSmsInterfaceManager, Mockito.times(0))
-                .sendText(mCallingPackage, "1234", null, "text", null, null, false, 0L, true);
+                .sendText(mCallingPackage, mCallingUserId,
+                        "1234", null, "text", null, null, false, 0L, true);
     }
 
     @Test
@@ -320,6 +327,7 @@
         mSmsControllerUT.sendTextForSubscriber(subId, mCallingPackage, null, "1234",
                 null, "text", null, null, false, 0L, true, true);
         verify(mIccSmsInterfaceManager, Mockito.times(1))
-                .sendText(mCallingPackage, "1234", null, "text", null, null, false, 0L, true);
+                .sendText(mCallingPackage, mCallingUserId,
+                        "1234", null, "text", null, null, false, 0L, true);
     }
 }
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
index 414fb3b..acf793e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony;
 
 import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE;
+import static com.android.internal.telephony.SmsDispatchersController.PendingRequest;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -28,6 +29,7 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.isNull;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
@@ -64,6 +66,7 @@
 import com.android.internal.telephony.domainselection.SmsDomainSelectionConnection;
 import com.android.internal.telephony.emergency.EmergencyStateTracker;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.satellite.DatagramDispatcher;
 import com.android.internal.telephony.uicc.IccUtils;
 
 import org.junit.After;
@@ -94,30 +97,31 @@
             return getDomainSelectionConnectionHolder(emergency);
         }
 
-        public void testSendData(String callingPackage, String destAddr, String scAddr,
-                int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent,
-                boolean isForVvm) {
-            sendData(callingPackage, destAddr, scAddr,
+        public void testSendData(String callingPackage, int callingUser,
+                String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
+                PendingIntent deliveryIntent, boolean isForVvm) {
+            sendData(callingPackage, callingUser, destAddr, scAddr,
                     destPort, data, sentIntent, deliveryIntent, isForVvm);
         }
 
         public void testSendMultipartText(String destAddr, String scAddr,
                 ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
                 ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
-                boolean persistMessage, int priority, boolean expectMore, int validityPeriod,
-                long messageId) {
+                int callingUser, boolean persistMessage, int priority, boolean expectMore,
+                int validityPeriod, long messageId) {
             sendMultipartText(destAddr, scAddr, parts, sentIntents, deliveryIntents, messageUri,
-                    callingPkg, persistMessage, priority, expectMore, validityPeriod, messageId);
+                    callingPkg, callingUser, persistMessage, priority, expectMore,
+                    validityPeriod, messageId);
         }
 
         public void testNotifySmsSentToEmergencyStateTracker(String destAddr, long messageId,
                 boolean isOverIms, boolean isLastSmsPart) {
-            notifySmsSentToEmergencyStateTracker(destAddr, messageId, isOverIms, isLastSmsPart);
+            notifySmsSent(destAddr, messageId, isOverIms, isLastSmsPart, true/*success*/);
         }
 
         public void testNotifySmsSentFailedToEmergencyStateTracker(String destAddr,
                 long messageId, boolean isOverIms) {
-            notifySmsSentFailedToEmergencyStateTracker(destAddr, messageId, isOverIms);
+            notifySmsSent(destAddr, messageId, isOverIms, true/*isLastSmsPart*/, false/*success*/);
         }
 
         public void testNotifySmsReceivedViaImsToEmergencyStateTracker(String origAddr) {
@@ -134,10 +138,10 @@
         }
 
         @Override
-        public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
-                byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent,
-                boolean isForVvm) {
-            super.sendData(callingPackage, destAddr, scAddr, destPort,
+        public void sendData(String callingPackage, int callingUser, String destAddr,
+                String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
+                PendingIntent deliveryIntent, boolean isForVvm) {
+            super.sendData(callingPackage, callingUser, destAddr, scAddr, destPort,
                     data, sentIntent, deliveryIntent, isForVvm);
         }
 
@@ -161,10 +165,10 @@
         }
 
         @Override
-        public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
-                byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent,
+        public void sendData(String callingPackage, int callingUser, String destAddr, String scAddr,
+                int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent,
                 boolean isForVvm) {
-            super.sendData(callingPackage, destAddr, scAddr, destPort,
+            super.sendData(callingPackage, callingUser, destAddr, scAddr, destPort,
                     data, sentIntent, deliveryIntent, isForVvm);
         }
 
@@ -191,15 +195,21 @@
     private TestSmsDispatchersController mSmsDispatchersController;
     private boolean mInjectionCallbackTriggered = false;
     private CompletableFuture<Integer> mDscFuture;
+    private DatagramDispatcher mMockDatagramDispatcher;
+    private int mCallingUserId;
 
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
         mTracker = mock(SMSDispatcher.SmsTracker.class);
         mFeatureFlags = mock(FeatureFlags.class);
+        mMockDatagramDispatcher = mock(DatagramDispatcher.class);
+        replaceInstance(DatagramDispatcher.class, "sInstance", null,
+                mMockDatagramDispatcher);
         setupMockPackagePermissionChecks();
         mSmsDispatchersController = new TestSmsDispatchersController(mPhone, mSmsStorageMonitor,
             mSmsUsageMonitor, mTestableLooper.getLooper(), mFeatureFlags);
+        mCallingUserId = mContext.getUserId();
         processAllMessages();
     }
 
@@ -234,7 +244,7 @@
     @Test @SmallTest
     public void testReportSmsMemoryStatus() throws Exception {
         int eventReportMemoryStatusDone = 3;
-        SmsStorageMonitor smsStorageMonnitor = new SmsStorageMonitor(mPhone);
+        SmsStorageMonitor smsStorageMonnitor = new SmsStorageMonitor(mPhone, mFeatureFlags);
         Message result = smsStorageMonnitor.obtainMessage(eventReportMemoryStatusDone);
         ImsSmsDispatcher mImsSmsDispatcher = Mockito.mock(ImsSmsDispatcher.class);
         mSmsDispatchersController.setImsSmsDispatcher(mImsSmsDispatcher);
@@ -247,7 +257,7 @@
     @Test @SmallTest
     public void testReportSmsMemoryStatusFailure() throws Exception {
         int eventReportMemoryStatusDone = 3;
-        SmsStorageMonitor smsStorageMonnitor = new SmsStorageMonitor(mPhone);
+        SmsStorageMonitor smsStorageMonnitor = new SmsStorageMonitor(mPhone, mFeatureFlags);
         Message result = smsStorageMonnitor.obtainMessage(eventReportMemoryStatusDone);
         mSmsDispatchersController.setImsSmsDispatcher(null);
         mSmsDispatchersController.reportSmsMemoryStatus(result);
@@ -259,7 +269,7 @@
     public void testSendImsGmsTest() throws Exception {
         switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
         mSmsDispatchersController.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
-                null, null, null, null, false, -1, false, -1, false, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
         verify(mSimulatedCommandsVerifier).sendImsGsmSms(eq("038122F2"),
                 eq("0100038111F100001CD3F69C989EC3C3F431BA2C9F0FDF6EBAFCCD6697E5D4F29C0E"), eq(0), eq(0),
                 any(Message.class));
@@ -269,7 +279,7 @@
     public void testSendImsGmsTestWithOutDesAddr() throws Exception {
         switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
         mSmsDispatchersController.sendText(null, "222" /*scAddr*/, TAG,
-                null, null, null, null, false, -1, false, -1, false, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
         verify(mSimulatedCommandsVerifier, times(0)).sendImsGsmSms(anyString(), anyString(),
                 anyInt(), anyInt(), any(Message.class));
     }
@@ -278,7 +288,7 @@
     public void testSendImsCdmaTest() throws Exception {
         switchImsSmsFormat(PhoneConstants.PHONE_TYPE_CDMA);
         mSmsDispatchersController.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
-                null, null, null, null, false, -1, false, -1, false, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
         verify(mSimulatedCommandsVerifier).sendImsCdmaSms((byte[])any(), eq(0), eq(0),
                 any(Message.class));
     }
@@ -353,7 +363,8 @@
 
         mSmsDispatchersController.sendText("111", null /*scAddr*/, TAG,
                 null, null, null, "com.android.messaging",
-                false, -1, false, -1, false, 0L);
+                mContext.getUserId(), false, -1, false,
+                -1, false, 0L);
         byte[] smscbyte = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(
                 "222");
         String smsc = IccUtils.bytesToHexString(smscbyte);
@@ -462,7 +473,7 @@
         setUpEmergencyStateTracker(DisconnectCause.NOT_DISCONNECTED);
 
         mSmsDispatchersController.sendText("911", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
 
         SmsDispatchersController.DomainSelectionConnectionHolder holder =
@@ -479,8 +490,8 @@
 
         verify(mEmergencySmsDsc).finishSelection();
         verify(mImsSmsDispatcher).sendText(eq("911"), eq("2222"), eq("text"), eq(mSentIntent),
-                any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10), eq(false),
-                eq(1L), eq(false));
+                any(), any(), eq("test-app"), eq(mCallingUserId), eq(false),
+                eq(0), eq(false), eq(10), eq(false), eq(1L), eq(false));
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
         assertEquals(0, holder.getPendingRequests().size());
@@ -494,7 +505,7 @@
         setUpEmergencyStateTracker(DisconnectCause.OUT_OF_SERVICE);
 
         mSmsDispatchersController.sendText("911", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
 
         // Verify the domain selection requested regardless of the result of EmergencyStateTracker.
@@ -512,7 +523,7 @@
         ArrayList<PendingIntent> sentIntents = new ArrayList<>();
         ArrayList<PendingIntent> deliveryIntents = new ArrayList<>();
         mSmsDispatchersController.testSendMultipartText("911", "2222", parts, sentIntents,
-                deliveryIntents, null, "test-app", false, 0, false, 10, 1L);
+                deliveryIntents, null, "test-app", mCallingUserId, false, 0, false, 10, 1L);
         processAllMessages();
 
         SmsDispatchersController.DomainSelectionConnectionHolder holder =
@@ -529,8 +540,9 @@
 
         verify(mEmergencySmsDsc).finishSelection();
         verify(mImsSmsDispatcher).sendMultipartText(eq("911"), eq("2222"), eq(parts),
-                eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(false), eq(0),
-                eq(false), eq(10), eq(1L));
+                eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"),
+                eq(mCallingUserId), eq(false), eq(0), eq(false),
+                eq(10), eq(1L));
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
         assertEquals(0, holder.getPendingRequests().size());
@@ -703,7 +715,7 @@
         when(mImsSmsDispatcher.isAvailable()).thenReturn(true);
 
         mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
 
         SmsDispatchersController.DomainSelectionConnectionHolder holder =
@@ -728,8 +740,8 @@
         assertEquals(0, holder.getPendingRequests().size());
 
         verify(mImsSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent),
-                any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10), eq(false),
-                eq(1L), eq(false));
+                any(), any(), eq("test-app"), eq(mCallingUserId),
+                eq(false), eq(0), eq(false), eq(10), eq(false), eq(1L), eq(false));
     }
 
     @Test
@@ -743,7 +755,8 @@
         when(mImsSmsDispatcher.isEmergencySmsSupport(anyString())).thenReturn(true);
 
         mSmsDispatchersController.sendText("911", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false,
+                10, false, 1L, false);
         processAllMessages();
 
         SmsDispatchersController.DomainSelectionConnectionHolder holder =
@@ -768,8 +781,8 @@
         assertEquals(0, holder.getPendingRequests().size());
 
         verify(mImsSmsDispatcher).sendText(eq("911"), eq("2222"), eq("text"), eq(mSentIntent),
-                any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10), eq(false),
-                eq(1L), eq(false));
+                any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false), eq(10),
+                eq(false), eq(1L), eq(false));
     }
 
     @Test
@@ -779,7 +792,7 @@
         setUpSmsDispatchers();
 
         mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
 
         SmsDispatchersController.DomainSelectionConnectionHolder holder =
@@ -790,7 +803,7 @@
         assertEquals(1, holder.getPendingRequests().size());
 
         mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
 
         verify(mSmsDsc).requestDomainSelection(any(), any());
@@ -802,9 +815,10 @@
         processAllMessages();
 
         verify(mSmsDsc).finishSelection();
-        verify(mImsSmsDispatcher, times(2)).sendText(eq("1111"), eq("2222"), eq("text"),
-                eq(mSentIntent), any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10),
-                eq(false), eq(1L), eq(false));
+        verify(mImsSmsDispatcher, times(2)).sendText(eq("1111"), eq("2222"),
+                eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(mCallingUserId),
+                eq(false), eq(0), eq(false), eq(10), eq(false), eq(1L),
+                eq(false));
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
         assertEquals(0, holder.getPendingRequests().size());
@@ -819,13 +833,13 @@
         when(mImsSmsDispatcher.isAvailable()).thenReturn(true);
 
         mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
 
         // Expect that the domain selection is not executed and
         // ImsSmsDispatcher handles this text directly.
         verify(mImsSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"),
-                eq(mSentIntent), any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10),
-                eq(false), eq(1L), eq(false));
+                eq(mSentIntent), any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0),
+                eq(false), eq(10), eq(false), eq(1L), eq(false));
     }
 
     @Test
@@ -835,7 +849,7 @@
         setUpSmsDispatchers();
 
         mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
 
         SmsDispatchersController.DomainSelectionConnectionHolder holder =
@@ -873,7 +887,7 @@
 
         // Expect that new domain selection connection is created and domain selection is performed.
         mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
 
         verify(mSmsDsc).finishSelection();
@@ -888,8 +902,8 @@
 
         verify(newSmsDsc).finishSelection();
         verify(mImsSmsDispatcher, times(2)).sendText(eq("1111"), eq("2222"), eq("text"),
-                eq(mSentIntent), any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10),
-                eq(false), eq(1L), eq(false));
+                eq(mSentIntent), any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0),
+                eq(false), eq(10), eq(false), eq(1L), eq(false));
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
         assertEquals(0, holder.getPendingRequests().size());
@@ -904,7 +918,7 @@
         setUpEmergencyStateTracker(DisconnectCause.NOT_DISCONNECTED);
 
         mSmsDispatchersController.sendText("911", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
 
         SmsDispatchersController.DomainSelectionConnectionHolder holder =
@@ -944,7 +958,7 @@
 
         // Expect that new domain selection connection is created and domain selection is performed.
         mSmsDispatchersController.sendText("911", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
 
         verify(mEmergencySmsDsc).finishSelection();
@@ -959,8 +973,8 @@
 
         verify(newEmergencySmsDsc).finishSelection();
         verify(mImsSmsDispatcher, times(2)).sendText(eq("911"), eq("2222"), eq("text"),
-                eq(mSentIntent), any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10),
-                eq(false), eq(1L), eq(false));
+                eq(mSentIntent), any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false),
+                eq(10), eq(false), eq(1L), eq(false));
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
         assertEquals(0, holder.getPendingRequests().size());
@@ -991,7 +1005,7 @@
         // Expect that creating a domain selection connection is failed and
         // fallback to the legacy implementation.
         mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
 
         SmsDispatchersController.DomainSelectionConnectionHolder holder =
@@ -1002,8 +1016,8 @@
         assertEquals(0, holder.getPendingRequests().size());
 
         verify(mImsSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent),
-                any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10), eq(false),
-                eq(1L), eq(false));
+                any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0),
+                eq(false), eq(10), eq(false), eq(1L), eq(false));
     }
 
     @Test
@@ -1033,7 +1047,7 @@
         // Expect that creating a domain selection connection is failed and
         // fallback to the legacy implementation.
         mSmsDispatchersController.sendText("911", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
 
         SmsDispatchersController.DomainSelectionConnectionHolder holder =
@@ -1045,8 +1059,8 @@
         assertEquals(0, holder.getPendingRequests().size());
 
         verify(mImsSmsDispatcher).sendText(eq("911"), eq("2222"), eq("text"), eq(mSentIntent),
-                any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10), eq(false),
-                eq(1L), eq(false));
+                any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0), eq(false),
+                eq(10), eq(false), eq(1L), eq(false));
     }
 
     private void switchImsSmsFormat(int phoneType) {
@@ -1063,6 +1077,42 @@
         assertTrue(mSmsDispatchersController.setImsManager(imsManager));
     }
 
+    @Test
+    public void testSendSmsToDatagramDispatcher() {
+        when(mSatelliteController.isInCarrierRoamingNbIotNtn()).thenReturn(true);
+        mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
+        processAllMessages();
+        verify(mMockDatagramDispatcher).sendSms(any());
+
+        clearInvocations(mMockDatagramDispatcher);
+        ArrayList<String> parts = new ArrayList<>();
+        ArrayList<PendingIntent> sentIntents = new ArrayList<>();
+        ArrayList<PendingIntent> deliveryIntents = new ArrayList<>();
+        mSmsDispatchersController.testSendMultipartText("1111", "2222", parts, sentIntents,
+                deliveryIntents, null, "test-app", mCallingUserId, false, 0, false, 10, 1L);
+        processAllMessages();
+        verify(mMockDatagramDispatcher).sendSms(any());
+    }
+
+    @Test
+    public void testSendCarrierRoamingNbIotNtnText() {
+        PendingRequest pendingRequest = createPendingRequest();
+        switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
+
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(false);
+        mSmsDispatchersController.sendCarrierRoamingNbIotNtnText(pendingRequest);
+        processAllMessages();
+        verify(mSimulatedCommandsVerifier, times(0)).sendImsGsmSms(anyString(), anyString(),
+                anyInt(), anyInt(), any(Message.class));
+
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        mSmsDispatchersController.sendCarrierRoamingNbIotNtnText(pendingRequest);
+        processAllMessages();
+        verify(mSimulatedCommandsVerifier, times(1)).sendImsGsmSms(anyString(), anyString(),
+                anyInt(), anyInt(), any(Message.class));
+    }
+
     private void setUpDomainSelectionEnabled(boolean enabled) {
         mSmsDispatchersController.setDomainSelectionResolverProxy(
                 new SmsDispatchersController.DomainSelectionResolverProxy() {
@@ -1152,7 +1202,7 @@
 
         byte[] data = new byte[] { 0x01 };
         mSmsDispatchersController.testSendData(
-                "test-app", "1111", "2222", 8080, data, mSentIntent, null, false);
+                "test-app", mCallingUserId, "1111", "2222", 8080, data, mSentIntent, null, false);
         processAllMessages();
 
         SmsDispatchersController.DomainSelectionConnectionHolder holder =
@@ -1168,14 +1218,14 @@
 
         verify(mSmsDsc).finishSelection();
         if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
-            verify(mImsSmsDispatcher).sendData(eq("test-app"), eq("1111"), eq("2222"), eq(8080),
-                    eq(data), eq(mSentIntent), any(), eq(false));
+            verify(mImsSmsDispatcher).sendData(eq("test-app"), eq(0), eq("1111"), eq("2222"),
+                    eq(8080), eq(data), eq(mSentIntent), any(), eq(false));
         } else if (isCdmaMo) {
-            verify(mCdmaSmsDispatcher).sendData(eq("test-app"), eq("1111"), eq("2222"), eq(8080),
-                    eq(data), eq(mSentIntent), any(), eq(false));
+            verify(mCdmaSmsDispatcher).sendData(eq("test-app"), eq(0), eq("1111"), eq("2222"),
+                    eq(8080), eq(data), eq(mSentIntent), any(), eq(false));
         } else {
-            verify(mGsmSmsDispatcher).sendData(eq("test-app"), eq("1111"), eq("2222"), eq(8080),
-                    eq(data), eq(mSentIntent), any(), eq(false));
+            verify(mGsmSmsDispatcher).sendData(eq("test-app"), eq(0), eq("1111"), eq("2222"),
+                    eq(8080), eq(data), eq(mSentIntent), any(), eq(false));
         }
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
@@ -1188,7 +1238,7 @@
         setUpSmsDispatchers();
 
         mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
-                "test-app", false, 0, false, 10, false, 1L, false);
+                "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
 
         SmsDispatchersController.DomainSelectionConnectionHolder holder =
@@ -1205,16 +1255,16 @@
         verify(mSmsDsc).finishSelection();
         if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
             verify(mImsSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent),
-                    any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10), eq(false),
-                    eq(1L), eq(false));
+                    any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false), eq(10),
+                    eq(false), eq(1L), eq(false));
         } else if (isCdmaMo) {
             verify(mCdmaSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent),
-                    any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10), eq(false),
-                    eq(1L), eq(false));
+                    any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false), eq(10),
+                    eq(false), eq(1L), eq(false));
         } else {
             verify(mGsmSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent),
-                    any(), any(), eq("test-app"), eq(false), eq(0), eq(false), eq(10), eq(false),
-                    eq(1L), eq(false));
+                    any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false), eq(10),
+                    eq(false), eq(1L), eq(false));
         }
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
@@ -1230,7 +1280,7 @@
         ArrayList<PendingIntent> sentIntents = new ArrayList<>();
         ArrayList<PendingIntent> deliveryIntents = new ArrayList<>();
         mSmsDispatchersController.testSendMultipartText("1111", "2222", parts, sentIntents,
-                deliveryIntents, null, "test-app", false, 0, false, 10, 1L);
+                deliveryIntents, null, "test-app", mCallingUserId, false, 0, false, 10, 1L);
         processAllMessages();
 
         SmsDispatchersController.DomainSelectionConnectionHolder holder =
@@ -1247,16 +1297,17 @@
         verify(mSmsDsc).finishSelection();
         if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
             verify(mImsSmsDispatcher).sendMultipartText(eq("1111"), eq("2222"), eq(parts),
-                    eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(false), eq(0),
-                    eq(false), eq(10), eq(1L));
+                    eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(mCallingUserId),
+                    eq(false), eq(0), eq(false), eq(10), eq(1L));
         } else if (isCdmaMo) {
             verify(mCdmaSmsDispatcher).sendMultipartText(eq("1111"), eq("2222"), eq(parts),
-                    eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(false), eq(0),
+                    eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(mCallingUserId),
+                    eq(false), eq(0),
                     eq(false), eq(10), eq(1L));
         } else {
             verify(mGsmSmsDispatcher).sendMultipartText(eq("1111"), eq("2222"), eq(parts),
-                    eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(false), eq(0),
-                    eq(false), eq(10), eq(1L));
+                    eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(mCallingUserId),
+                    eq(false), eq(0), eq(false), eq(10), eq(1L));
         }
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
@@ -1321,4 +1372,18 @@
             verify(mGsmSmsDispatcher).sendSms(eq(mTracker));
         }
     }
+
+    private static <T> ArrayList<T> asArrayList(T object) {
+        ArrayList<T> list = new ArrayList<>();
+        list.add(object);
+        return list;
+    }
+
+    private PendingRequest createPendingRequest() {
+        return new PendingRequest(
+                SmsDispatchersController.PendingRequest.TYPE_TEXT, null, "test-app",
+                mCallingUserId, "1111", "2222", asArrayList(mSentIntent), asArrayList(null),
+                false, null, 0, asArrayList("text"), null,
+                false, 0, false, 10, 100L, false);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
index 4483c61..bdf7e78 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
@@ -52,7 +52,7 @@
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
-        mSmsStorageMonitor = new SmsStorageMonitor(mPhone);
+        mSmsStorageMonitor = new SmsStorageMonitor(mPhone, mFeatureFlags);
         mSmsStorageMonitor.setMaxRetries(MAX_RETRIES);
         mSmsStorageMonitor.setRetryDelayInMillis(RETRY_DELAY);
         processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
index ac92b8f..1e2b8ce 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
@@ -69,6 +69,7 @@
                 .setServiceCapabilities(SubscriptionManager.getServiceCapabilitiesSet(
                     SubscriptionManager.SERVICE_CAPABILITY_DATA_BITMASK))
                 .setTransferStatus(1)
+                .setSatelliteESOSSupported(true)
                 .build();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyCountryDetectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyCountryDetectorTest.java
index a2763fe..9b3777b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyCountryDetectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyCountryDetectorTest.java
@@ -39,6 +39,8 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
+import android.os.AsyncResult;
+import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
@@ -48,6 +50,9 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.telephony.flags.FeatureFlags;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -72,6 +77,8 @@
     LocaleTracker mMockLocaleTracker2;
     @Mock Location mMockLocation;
     @Mock Network mMockNetwork;
+    @Mock
+    private FeatureFlags mMockFeatureFlags;
 
     @Captor
     private ArgumentCaptor<LocationListener> mLocationListenerCaptor;
@@ -114,8 +121,9 @@
 
         when(mLocationManager.getProviders(true)).thenReturn(Arrays.asList("TEST_PROVIDER"));
 
+        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         mCountryDetectorUT = new TestTelephonyCountryDetector(
-                mLooper, mContext, mLocationManager, mConnectivityManager);
+                mLooper, mContext, mLocationManager, mConnectivityManager, mMockFeatureFlags);
         if (isGeoCoderImplemented()) {
             verify(mLocationManager).requestLocationUpdates(anyString(), anyLong(), anyFloat(),
                     mLocationListenerCaptor.capture());
@@ -137,8 +145,10 @@
         clearInvocations(mLocationManager);
         clearInvocations(mConnectivityManager);
         when(mMockLocaleTracker.getCurrentCountry()).thenReturn("US");
-        TelephonyCountryDetector inst1 = TelephonyCountryDetector.getInstance(mContext);
-        TelephonyCountryDetector inst2 = TelephonyCountryDetector.getInstance(mContext);
+        TelephonyCountryDetector inst1 = TelephonyCountryDetector
+                .getInstance(mContext, mMockFeatureFlags);
+        TelephonyCountryDetector inst2 = TelephonyCountryDetector
+                .getInstance(mContext, mMockFeatureFlags);
         assertEquals(inst1, inst2);
         if (isGeoCoderImplemented()) {
             verify(mLocationManager, never()).requestLocationUpdates(anyString(), anyLong(),
@@ -322,6 +332,31 @@
         verify(mLocationManager).removeUpdates(any(LocationListener.class));
     }
 
+    @Test
+    public void testRegisterUnregisterForWifiConnectivityStateChanged() {
+        WifiConnectivityStateChangedListener listener = new WifiConnectivityStateChangedListener(
+                mLooper);
+
+        mCountryDetectorUT.registerForWifiConnectivityStateChanged(listener,
+                1 /* EVENT_WIFI_CONNECTIVITY_STATE_CHANGED*/, null);
+
+        // Wi-fi becomes available
+        clearInvocations(mLocationManager);
+        mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true);
+        mNetworkCallbackCaptor.getValue().onAvailable(mMockNetwork);
+        mTestableLooper.processAllMessages();
+        assertTrue(listener.getIsWifiConnected());
+
+        // Wi-fi becomes not available
+        clearInvocations(mLocationManager);
+        mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, false);
+        mNetworkCallbackCaptor.getValue().onUnavailable();
+        mTestableLooper.processAllMessages();
+        assertFalse(listener.getIsWifiConnected());
+
+        mCountryDetectorUT.unregisterForWifiConnectivityStateChanged(listener);
+    }
+
     private static boolean isGeoCoderImplemented() {
         return Geocoder.isPresent();
     }
@@ -357,8 +392,9 @@
          * @param locationManager  The LocationManager instance.
          */
         TestTelephonyCountryDetector(Looper looper, Context context,
-                LocationManager locationManager, ConnectivityManager connectivityManager) {
-            super(looper, context, locationManager, connectivityManager);
+                LocationManager locationManager, ConnectivityManager connectivityManager,
+                FeatureFlags featureFlags) {
+            super(looper, context, locationManager, connectivityManager, featureFlags);
         }
 
         @Override
@@ -375,4 +411,37 @@
             return WAIT_FOR_LOCATION_UPDATE_REQUEST_QUOTA_RESET_TIMEOUT_MILLIS;
         }
     }
+
+    private static class WifiConnectivityStateChangedListener extends Handler {
+        private static final int EVENT_WIFI_CONNECTIVITY_STATE_CHANGED = 1;
+        private final Object mIsWifiConnectedLock = new Object();
+        @GuardedBy("mIsWifiConnectedLock")
+        private boolean mIsWifiConnected = false;
+
+        WifiConnectivityStateChangedListener(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_WIFI_CONNECTIVITY_STATE_CHANGED: {
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    synchronized (mIsWifiConnectedLock) {
+                        mIsWifiConnected = (boolean) ar.result;
+                    }
+                    break;
+                }
+
+                default:
+                    break;
+            }
+        }
+
+        public boolean getIsWifiConnected() {
+            synchronized (mIsWifiConnectedLock) {
+                return mIsWifiConnected;
+            }
+        }
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
index d4717dd..ac89501 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
@@ -37,6 +37,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.permission.LegacyPermissionManager;
@@ -558,6 +559,38 @@
                 UserHandle.SYSTEM));
     }
 
+    @Test
+    public void testIsSystemOrPhone_systemUser() {
+        assertTrue(TelephonyPermissions.isSystemOrPhone(Process.SYSTEM_UID));
+        assertTrue(TelephonyPermissions.isSystemOrPhone(Process.PHONE_UID));
+
+        assertFalse(TelephonyPermissions.isSystemOrPhone(1002));
+    }
+
+    @Test
+    public void testIsSystemOrPhone_nonSystemUser() {
+        assertTrue(TelephonyPermissions.isSystemOrPhone(1001000));
+        assertTrue(TelephonyPermissions.isSystemOrPhone(1001001));
+
+        assertFalse(TelephonyPermissions.isSystemOrPhone(1001002));
+    }
+
+    @Test
+    public void testIsRootOrShell_systemUser() {
+        assertTrue(TelephonyPermissions.isRootOrShell(Process.ROOT_UID));
+        assertTrue(TelephonyPermissions.isRootOrShell(Process.SHELL_UID));
+
+        assertFalse(TelephonyPermissions.isRootOrShell(1002));
+    }
+
+    @Test
+    public void testIsRootOrShell_nonSystemUser() {
+        assertTrue(TelephonyPermissions.isRootOrShell(1000000));
+        assertTrue(TelephonyPermissions.isRootOrShell(1002000));
+
+        assertFalse(TelephonyPermissions.isRootOrShell(1001002));
+    }
+
     // Put mMockTelephony into service cache so that TELEPHONY_SUPPLIER will get it.
     private void setTelephonyMockAsService() throws Exception {
         when(mMockTelephonyBinder.queryLocalInterface(anyString())).thenReturn(mMockTelephony);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
index 3e447a9..a645439 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -45,6 +45,8 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.Annotation;
 import android.telephony.BarringInfo;
@@ -75,10 +77,12 @@
 import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.telephony.flags.Flags;
 import com.android.server.TelephonyRegistry;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -94,6 +98,8 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class TelephonyRegistryTest extends TelephonyTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     // Mocked classes
     private SubscriptionInfo mMockSubInfo;
     private TelephonyRegistry.ConfigurationProvider mMockConfigurationProvider;
@@ -118,6 +124,7 @@
     private int mRegistrationFailReason;
     private Set<Integer> mSimultaneousCallingSubscriptions;
     private boolean mCarrierRoamingNtnMode;
+    private boolean mCarrierRoamingNtnEligible;
 
     // All events contribute to TelephonyRegistry#isPhoneStatePermissionRequired
     private static final Set<Integer> READ_PHONE_STATE_EVENTS;
@@ -295,6 +302,12 @@
             invocationCount.incrementAndGet();
             mCarrierRoamingNtnMode = active;
         }
+
+        @Override
+        public void onCarrierRoamingNtnEligibleStateChanged(boolean eligible) {
+            invocationCount.incrementAndGet();
+            mCarrierRoamingNtnEligible = eligible;
+        }
     }
 
     private void addTelephonyRegistryService() {
@@ -329,6 +342,10 @@
         mContextFixture.putStringArrayResource(
                 com.android.internal.R.array.config_serviceStateLocationAllowedPackages,
                 new String[0]);
+
+        UserInfo userInfo = new UserInfo(UserHandle.myUserId(), "" /* name */, 0 /* flags */);
+        doReturn(userInfo.id).when(mIActivityManager).getCurrentUserId();
+
         processAllMessages();
         assertEquals(mTelephonyRegistry.asBinder(),
                 ServiceManager.getService("telephony.registry"));
@@ -1585,4 +1602,20 @@
         processAllMessages();
         assertTrue(mCarrierRoamingNtnMode);
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public void testNotifyCarrierRoamingNtnEligibleStateChanged() {
+        int subId = INVALID_SUBSCRIPTION_ID;
+        doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
+        doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
+        int[] events = {TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED};
+
+        mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(),
+                mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
+
+        mTelephonyRegistry.notifyCarrierRoamingNtnEligibleStateChanged(subId, true);
+        processAllMessages();
+        assertTrue(mCarrierRoamingNtnEligible);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 38b4f77..d80c9a2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -580,6 +580,9 @@
         mNullCipherNotifier = Mockito.mock(NullCipherNotifier.class);
 
         doReturn(true).when(mFeatureFlags).minimalTelephonyCdmCheck();
+        doReturn(true).when(mFeatureFlags).supportNetworkProvider();
+        doReturn(true).when(mFeatureFlags).hsumBroadcast();
+        doReturn(true).when(mFeatureFlags).hsumPackageManager();
 
         TelephonyManager.disableServiceHandleCaching();
         PropertyInvalidatedCache.disableForTestMode();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java b/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java
index c3db35c..58e5617 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java
@@ -30,17 +30,23 @@
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
+import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Telephony;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.telephony.flags.FeatureFlags;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
 
 public class WapPushOverSmsTest extends TelephonyTest {
     // Mocked classes
@@ -48,6 +54,9 @@
 
     private WapPushOverSms mWapPushOverSmsUT;
 
+    @Mock private FeatureFlags mFeatureFlags;
+    private static final UserHandle MOCKED_MAIN_USER = UserHandle.of(10);
+
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
@@ -56,9 +65,14 @@
         // Note that this replaces only cached services in ServiceManager. If a service is not found
         // in the cache, a real instance is used.
         mServiceManagerMockedServices.put("isms", mISmsStub);
+        mFeatureFlags = Mockito.mock(FeatureFlags.class);
+        doReturn(true).when(mFeatureFlags).smsMmsDeliverBroadcastsRedirectToMainUser();
         doReturn(mISmsStub).when(mISmsStub).queryLocalInterface(anyString());
 
-        mWapPushOverSmsUT = new WapPushOverSms(mContext);
+        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        doReturn(MOCKED_MAIN_USER).when(userManager).getMainUser();
+
+        mWapPushOverSmsUT = new WapPushOverSms(mContext, mFeatureFlags);
     }
 
     @After
@@ -94,7 +108,7 @@
                 eq(AppOpsManager.OPSTR_RECEIVE_WAP_PUSH),
                 nullable(Bundle.class),
                 isNull(InboundSmsHandler.SmsBroadcastReceiver.class),
-                eq(UserHandle.SYSTEM),
+                eq(MOCKED_MAIN_USER),
                 anyInt());
         Intent intent = intentArgumentCaptor.getValue();
         assertEquals(Telephony.Sms.Intents.WAP_PUSH_DELIVER_ACTION, intent.getAction());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
index 24f7d2c..cab76f7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
@@ -83,6 +83,7 @@
     private InboundSmsTracker mInboundSmsTracker;
     private final byte[] mSmsPdu = new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
     private final int mSubId0 = 0;
+    private static final UserHandle MOCKED_MAIN_USER = UserHandle.of(10);
 
     private IState getCurrentState() {
         try {
@@ -108,10 +109,13 @@
 
         UserManager userManager = (UserManager) mContextFixture.getTestDouble().
                 getSystemService(Context.USER_SERVICE);
+        doReturn(MOCKED_MAIN_USER).when(userManager).getMainUser();
         doReturn(true).when(userManager).isUserUnlocked();
+        doReturn(true).when(mFeatureFlags).smsMmsDeliverBroadcastsRedirectToMainUser();
 
         try {
-            doReturn(new int[]{UserHandle.USER_SYSTEM}).when(mIActivityManager).getRunningUserIds();
+            doReturn(new int[]{0, MOCKED_MAIN_USER.getIdentifier()})
+                .when(mIActivityManager).getRunningUserIds();
         } catch (RemoteException re) {
             StringWriter reString = new StringWriter();
             re.printStackTrace(new PrintWriter(reString));
@@ -157,7 +161,8 @@
                 Telephony.Sms.CONTENT_URI.getAuthority(), mContentProvider);
 
         mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(mContext,
-            mSmsStorageMonitor, mPhone, null, mTestableLooper.getLooper());
+            mSmsStorageMonitor, mPhone, null, mTestableLooper.getLooper(),
+                mFeatureFlags);
         monitorTestableLooper(new TestableLooper(mCdmaInboundSmsHandler.getHandler().getLooper()));
         processAllMessages();
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
index 1f52cea..118adbc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
@@ -18,6 +18,7 @@
 
 import static org.mockito.Mockito.*;
 
+import android.os.Binder;
 import android.os.HandlerThread;
 import android.os.Message;
 
@@ -38,6 +39,7 @@
 
     private CdmaSMSDispatcher mCdmaSmsDispatcher;
     private CdmaSmsDispatcherTestHandler mCdmaSmsDispatcherTestHandler;
+    private int mCallingUserId;
 
     private class CdmaSmsDispatcherTestHandler extends HandlerThread {
 
@@ -63,6 +65,7 @@
         mCdmaSmsDispatcherTestHandler = new CdmaSmsDispatcherTestHandler(TAG);
         mCdmaSmsDispatcherTestHandler.start();
         waitUntilReady();
+        mCallingUserId = Binder.getCallingUserHandle().getIdentifier();
     }
 
     @After
@@ -84,14 +87,14 @@
     @Test @SmallTest
     public void testSendText() {
         mCdmaSmsDispatcher.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
-                null, null, null, null, false, -1, false, -1, false, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
         verify(mSimulatedCommandsVerifier).sendCdmaSms(any(byte[].class), any(Message.class));
     }
 
     @Test @SmallTest
     public void testSendTextWithOutDesAddr() {
         mCdmaSmsDispatcher.sendText(null, "222" /*scAddr*/, TAG,
-                null, null, null, null, false, -1, false, -1, false, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
         verify(mSimulatedCommandsVerifier, times(0)).sendImsGsmSms(anyString(), anyString(),
                 anyInt(), anyInt(), any(Message.class));
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
index 005b312..95cefd8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
@@ -146,7 +146,6 @@
 
     @Test
     public void testMeteredNetworkCapabilities() {
-        doReturn(true).when(mFeatureFlags).meteredEmbbUrlcc();
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[] {ApnSetting.TYPE_MMS_STRING, ApnSetting.TYPE_DEFAULT_STRING});
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
index 499c1f5..ee713c6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -159,7 +159,6 @@
     private static final int EVENT_SUBSCRIPTION_OVERRIDE = 23;
 
     // Mocked classes
-    private PhoneSwitcher mMockedPhoneSwitcher;
     protected ISub mMockedIsub;
     private DataNetworkControllerCallback mMockedDataNetworkControllerCallback;
     private DataRetryManagerCallback mMockedDataRetryManagerCallback;
@@ -855,7 +854,6 @@
     public void setUp() throws Exception {
         logd("DataNetworkControllerTest +Setup!");
         super.setUp(getClass().getSimpleName());
-        mMockedPhoneSwitcher = Mockito.mock(PhoneSwitcher.class);
         mMockedIsub = Mockito.mock(ISub.class);
         mMockedImsManager = mContext.getSystemService(ImsManager.class);
         mMockedImsMmTelManager = Mockito.mock(ImsMmTelManager.class);
@@ -878,7 +876,6 @@
         mMockedDataServiceManagers.put(AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
                 mMockedWlanDataServiceManager);
 
-        replaceInstance(PhoneSwitcher.class, "sPhoneSwitcher", null, mMockedPhoneSwitcher);
         doReturn(1).when(mMockedIsub).getDefaultDataSubId();
         doReturn(mMockedIsub).when(mIBinder).queryLocalInterface(anyString());
         doReturn(mPhone).when(mPhone).getImsPhone();
@@ -891,10 +888,11 @@
         doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
         doReturn(new SubscriptionInfoInternal.Builder().setId(1).build())
                 .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+
         doReturn(true).when(mFeatureFlags).carrierEnabledSatelliteFlag();
         doReturn(true).when(mFeatureFlags).satelliteInternet();
-        doReturn(true).when(mFeatureFlags)
-                .ignoreExistingNetworksForInternetAllowedChecking();
+        doReturn(true).when(mFeatureFlags).simDisabledGracefulTearDown();
+
         when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
         doReturn(true).when(mMockPackageManager).hasSystemFeature(anyString());
 
@@ -2844,7 +2842,6 @@
 
     @Test
     public void testHandoverDataNetworkNotAllowedByPolicyDelayDueToVoiceCall() throws Exception {
-        doReturn(true).when(mFeatureFlags).relaxHoTeardown();
         // Config delay IMS tear down enabled
         mCarrierConfig.putBoolean(CarrierConfigManager.KEY_DELAY_IMS_TEAR_DOWN_UNTIL_CALL_END_BOOL,
                 true);
@@ -3222,6 +3219,118 @@
     }
 
     @Test
+    public void testSetupDataNetworkWithCandidateProfileWithIncompatibleRetryDataProfile() throws Exception {
+        mDataNetworkControllerUT
+                .getDataRetryManager()
+                .registerCallback(mMockedDataRetryManagerCallback);
+        setFailedSetupDataResponse(mMockedWwanDataServiceManager,
+                DataFailCause.ONLY_IPV4_ALLOWED, 2500 /* mSec */, false);
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+        verify(mMockedWwanDataServiceManager, times(1)).setupDataCall(anyInt(),
+                any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+                any(), any(), anyBoolean(), any(Message.class));
+
+        moveTimeForward(2500);
+        processAllMessages();
+        ArgumentCaptor<DataRetryManager.DataSetupRetryEntry> retryEntry =
+                ArgumentCaptor.forClass(DataRetryManager.DataSetupRetryEntry.class);
+        verify(mMockedDataRetryManagerCallback, times(1))
+                .onDataNetworkSetupRetry(retryEntry.capture());
+
+        ArgumentCaptor<List<ThrottleStatus>> throttleStatusCaptor =
+                ArgumentCaptor.forClass(List.class);
+        verify(mMockedDataRetryManagerCallback)
+                .onThrottleStatusChanged(throttleStatusCaptor.capture());
+
+        assertThat(retryEntry.getValue().dataProfile).isNotNull();
+
+        assertThat(retryEntry.getValue().dataProfile).isEqualTo(mGeneralPurposeDataProfile);
+        doReturn(false)
+                .when(mDataProfileManager)
+                .isDataProfileCompatible(retryEntry.getValue().dataProfile);
+
+        doReturn(mDuplicatedGeneralPurposeDataProfile).when(mDataProfileManager)
+                .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class),
+                anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
+
+        setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager, 2);
+
+        mDataNetworkControllerUT
+                .getDataRetryManager()
+                .obtainMessage(
+                        6 /* EVENT_DATA_PROFILE_UNTHROTTLED*/,
+                        new AsyncResult(
+                                AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                                mGeneralPurposeDataProfile,
+                                null))
+                .sendToTarget();
+        processAllFutureMessages();
+
+        verify(mMockedWwanDataServiceManager, times(2)).setupDataCall(anyInt(),
+                any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+                any(), any(), anyBoolean(), any(Message.class));
+
+        verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        verifyConnectedNetworkHasDataProfile(mDuplicatedGeneralPurposeDataProfile);
+    }
+
+    @Test
+    public void testSetupDataNetworkRetryWithCompatibleRetryDataProfile() throws Exception {
+        mDataNetworkControllerUT
+                .getDataRetryManager()
+                .registerCallback(mMockedDataRetryManagerCallback);
+        setFailedSetupDataResponse(mMockedWwanDataServiceManager,
+                DataFailCause.ONLY_IPV4_ALLOWED, 2500 /* mSec */, false);
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+        verify(mMockedWwanDataServiceManager, times(1)).setupDataCall(anyInt(),
+                any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+                any(), any(), anyBoolean(), any(Message.class));
+
+        moveTimeForward(2500);
+        processAllMessages();
+        ArgumentCaptor<DataRetryManager.DataSetupRetryEntry> retryEntry =
+                ArgumentCaptor.forClass(DataRetryManager.DataSetupRetryEntry.class);
+        verify(mMockedDataRetryManagerCallback, times(1))
+                .onDataNetworkSetupRetry(retryEntry.capture());
+
+        ArgumentCaptor<List<ThrottleStatus>> throttleStatusCaptor =
+                ArgumentCaptor.forClass(List.class);
+        verify(mMockedDataRetryManagerCallback)
+                .onThrottleStatusChanged(throttleStatusCaptor.capture());
+
+        assertThat(retryEntry.getValue().dataProfile).isNotNull();
+
+        assertThat(retryEntry.getValue().dataProfile).isEqualTo(mGeneralPurposeDataProfile);
+
+        assertThat(mDataProfileManager.isDataProfileCompatible(retryEntry.getValue().dataProfile))
+                .isTrue();
+
+        setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager, 2);
+
+        mDataNetworkControllerUT
+                .getDataRetryManager()
+                .obtainMessage(
+                        6 /* EVENT_DATA_PROFILE_UNTHROTTLED*/,
+                        new AsyncResult(
+                                AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                                mGeneralPurposeDataProfile,
+                                null))
+                .sendToTarget();
+        processAllFutureMessages();
+
+        verify(mMockedWwanDataServiceManager, times(2)).setupDataCall(anyInt(),
+                any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+                any(), any(), anyBoolean(), any(Message.class));
+
+        verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        verifyConnectedNetworkHasDataProfile(mGeneralPurposeDataProfile);
+    }
+
+    @Test
     public void testSetupDataNetworkRetryFailed() {
         mDataNetworkControllerUT.getDataRetryManager()
                 .registerCallback(mMockedDataRetryManagerCallback);
@@ -3901,7 +4010,6 @@
 
     @Test
     public void testNonVoPStoVoPSImsSetup() throws Exception {
-        doReturn(true).when(mFeatureFlags).allowMmtelInNonVops();
         mDataNetworkControllerUT.getDataSettingsManager().setDataRoamingEnabled(true);
         // Config that allows non-vops bring up when Roaming
         mCarrierConfig.putIntArray(CarrierConfigManager.Ims
@@ -4203,7 +4311,7 @@
     }
 
     @Test
-    public void testImsGracefulTearDown() throws Exception {
+    public void testImsGracefulTearDownSimRemoval() throws Exception {
         setImsRegistered(true);
         setRcsRegistered(true);
 
@@ -4249,6 +4357,52 @@
     }
 
     @Test
+    public void testImsGracefulTearDownSimDisabled() throws Exception {
+        setImsRegistered(true);
+        setRcsRegistered(true);
+
+        NetworkCapabilities netCaps = new NetworkCapabilities();
+        netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+        netCaps.maybeMarkCapabilitiesRestricted();
+        netCaps.setRequestorPackageName(FAKE_MMTEL_PACKAGE);
+
+        NetworkRequest nativeNetworkRequest = new NetworkRequest(netCaps,
+                ConnectivityManager.TYPE_MOBILE, ++mNetworkRequestId, NetworkRequest.Type.REQUEST);
+        TelephonyNetworkRequest networkRequest = new TelephonyNetworkRequest(
+                nativeNetworkRequest, mPhone, mFeatureFlags);
+
+        mDataNetworkControllerUT.addNetworkRequest(networkRequest);
+
+        processAllMessages();
+        Mockito.clearInvocations(mPhone);
+
+        // SIM disabled
+        mDataNetworkControllerUT.obtainMessage(9/*EVENT_SIM_STATE_CHANGED*/,
+                TelephonyManager.SIM_STATE_NOT_READY, 0).sendToTarget();
+        processAllMessages();
+
+        // Make sure data network enters disconnecting state
+        ArgumentCaptor<PreciseDataConnectionState> pdcsCaptor =
+                ArgumentCaptor.forClass(PreciseDataConnectionState.class);
+        verify(mPhone).notifyDataConnection(pdcsCaptor.capture());
+        PreciseDataConnectionState pdcs = pdcsCaptor.getValue();
+        assertThat(pdcs.getState()).isEqualTo(TelephonyManager.DATA_DISCONNECTING);
+
+        // IMS de-registered. Now data network is safe to be torn down.
+        Mockito.clearInvocations(mPhone);
+        setImsRegistered(false);
+        setRcsRegistered(false);
+        processAllMessages();
+
+        // All data should be disconnected.
+        verifyAllDataDisconnected();
+        verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+        verify(mPhone).notifyDataConnection(pdcsCaptor.capture());
+        pdcs = pdcsCaptor.getValue();
+        assertThat(pdcs.getState()).isEqualTo(TelephonyManager.DATA_DISCONNECTED);
+    }
+
+    @Test
     public void testNoGracefulTearDownForEmergencyDataNetwork() throws Exception {
         setImsRegistered(true);
 
@@ -4908,14 +5062,10 @@
 
         NetworkRequest nativeNetworkRequest = new NetworkRequest(netCaps,
                 ConnectivityManager.TYPE_MOBILE, 0, NetworkRequest.Type.REQUEST);
-
-        mDataNetworkControllerUT.addNetworkRequest(new TelephonyNetworkRequest(
-                nativeNetworkRequest, mPhone, mFeatureFlags));
-        processAllMessages();
-
-        // Intentionally create a new telephony request with the original native network request.
         TelephonyNetworkRequest request = new TelephonyNetworkRequest(
                 nativeNetworkRequest, mPhone, mFeatureFlags);
+        mDataNetworkControllerUT.addNetworkRequest(request);
+        processAllMessages();
 
         mDataNetworkControllerUT.removeNetworkRequest(request);
         processAllFutureMessages();
@@ -4936,7 +5086,7 @@
         processAllMessages();
 
         // this slot is 0, modem preferred on slot 1
-        doReturn(1).when(mMockedPhoneSwitcher).getPreferredDataPhoneId();
+        doReturn(1).when(mPhoneSwitcher).getPreferredDataPhoneId();
 
         // Simulate telephony network factory remove request due to switch.
         mDataNetworkControllerUT.removeNetworkRequest(request);
@@ -4949,7 +5099,7 @@
     @Test
     public void testSetupDataOnNonDds() throws Exception {
         // this slot is 0, modem preferred on slot 1
-        doReturn(1).when(mMockedPhoneSwitcher).getPreferredDataPhoneId();
+        doReturn(1).when(mPhoneSwitcher).getPreferredDataPhoneId();
         TelephonyNetworkRequest request = createNetworkRequest(
                 NetworkCapabilities.NET_CAPABILITY_MMS);
 
@@ -5475,4 +5625,18 @@
         assertThat(mDataNetworkControllerUT.getInternetEvaluation(true/*ignoreExistingNetworks*/)
                 .containsDisallowedReasons()).isTrue();
     }
+
+    @Test
+    public void testRemoveNetworkRequestClearState() throws Exception {
+        TelephonyNetworkRequest request = createNetworkRequest(
+                NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        mDataNetworkControllerUT.addNetworkRequest(request);
+        processAllMessages();
+        verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        assertThat(request.getState()).isEqualTo(TelephonyNetworkRequest.REQUEST_STATE_SATISFIED);
+
+        mDataNetworkControllerUT.removeNetworkRequest(request);
+        processAllMessages();
+        assertThat(request.getState()).isEqualTo(TelephonyNetworkRequest.REQUEST_STATE_UNSATISFIED);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
index 09cfa6f..f832a6b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -86,6 +87,7 @@
 import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
 import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
 import com.android.internal.telephony.data.LinkBandwidthEstimator.LinkBandwidthEstimatorCallback;
+import com.android.internal.telephony.data.PhoneSwitcher.PhoneSwitcherCallback;
 import com.android.internal.telephony.metrics.DataCallSessionStats;
 import com.android.internal.telephony.test.SimulatedCommands;
 
@@ -238,14 +240,14 @@
     // Mocked classes
     private DataNetworkCallback mDataNetworkCallback;
     private DataCallSessionStats mDataCallSessionStats;
-    private PhoneSwitcher mMockedPhoneSwitcher;
-
     private final NetworkRegistrationInfo mIwlanNetworkRegistrationInfo =
             new NetworkRegistrationInfo.Builder()
                     .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN)
                     .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
                     .build();
 
+    private PhoneSwitcherCallback mPhoneSwitcherCallback;
+
     private void setSuccessfulSetupDataResponse(DataServiceManager dsm, int cid) {
         setSuccessfulSetupDataResponse(dsm, cid, Collections.emptyList(), null);
     }
@@ -379,14 +381,13 @@
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
+        doReturn(1).when(mPhone).getSubId();
         doReturn(mImsPhone).when(mPhone).getImsPhone();
         doReturn(mImsCT).when(mImsPhone).getCallTracker();
         doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
 
         mDataNetworkCallback = Mockito.mock(DataNetworkCallback.class);
         mDataCallSessionStats = Mockito.mock(DataCallSessionStats.class);
-        mMockedPhoneSwitcher = Mockito.mock(PhoneSwitcher.class);
-        replaceInstance(PhoneSwitcher.class, "sPhoneSwitcher", null, mMockedPhoneSwitcher);
         doAnswer(invocation -> {
             ((Runnable) invocation.getArguments()[0]).run();
             return null;
@@ -2418,10 +2419,12 @@
 
     @Test
     public void testMmsCapabilityRemovedWhenMmsPreferredOnIwlan() throws Exception {
-        doReturn(true).when(mFeatureFlags).forceIwlanMms();
-        doReturn(true).when(mDataConfigManager).isForceIwlanMmsFeatureEnabled();
         setupDataNetwork();
 
+        TelephonyNetworkAgent mockNetworkAgent = Mockito.mock(TelephonyNetworkAgent.class);
+        replaceInstance(DataNetwork.class, "mNetworkAgent",
+                mDataNetworkUT, mockNetworkAgent);
+
         assertThat(mDataNetworkUT.getNetworkCapabilities()
                 .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)).isTrue();
 
@@ -2466,11 +2469,13 @@
                 .onPreferredTransportChanged(NetworkCapabilities.NET_CAPABILITY_MMS, false);
         processAllMessages();
 
-        // Check if MMS capability is removed.
+        // Check if MMS capability is removed, and we don't recreat network agent which triggers
+        // powering comsuming internet validation.
         assertThat(mDataNetworkUT.getNetworkCapabilities()
                 .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)).isFalse();
+        verify(mockNetworkAgent, never()).abandon();
 
-        // Now QNS prefers MMS on IWLAN
+        // Now QNS prefers MMS on WWAN
         doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mAccessNetworksManager)
             .getPreferredTransportByNetworkCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
         accessNetworksManagerCallbackArgumentCaptor.getValue()
@@ -2657,4 +2662,35 @@
         assertThat(mDataNetworkUT.getNetworkCapabilities()
                 .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).isTrue();
     }
+
+    @Test
+    public void testPrimaryTransport() throws Exception {
+        doReturn(0).when(mPhoneSwitcher).getPreferredDataPhoneId();
+        setupDataNetwork();
+        TelephonyNetworkAgent mockNetworkAgent = Mockito.mock(TelephonyNetworkAgent.class);
+        replaceInstance(DataNetwork.class, "mNetworkAgent",
+                mDataNetworkUT, mockNetworkAgent);
+
+        ArgumentCaptor<PhoneSwitcherCallback> callbackCaptor =
+                ArgumentCaptor.forClass(PhoneSwitcherCallback.class);
+        verify(mPhoneSwitcher).registerCallback(callbackCaptor.capture());
+        mPhoneSwitcherCallback = callbackCaptor.getValue();
+
+        // Switch the preferred data subscription to another.
+        mPhoneSwitcherCallback.onPreferredDataPhoneIdChanged(1);
+        processAllMessages();
+
+        ArgumentCaptor<NetworkScore> networkScoreCaptor =
+                ArgumentCaptor.forClass(NetworkScore.class);
+        verify(mockNetworkAgent).sendNetworkScore(networkScoreCaptor.capture());
+        assertThat(networkScoreCaptor.getValue().isTransportPrimary()).isFalse();
+        clearInvocations(mockNetworkAgent);
+
+        // Switch back
+        mPhoneSwitcherCallback.onPreferredDataPhoneIdChanged(0);
+        processAllMessages();
+
+        verify(mockNetworkAgent).sendNetworkScore(networkScoreCaptor.capture());
+        assertThat(networkScoreCaptor.getValue().isTransportPrimary()).isTrue();
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
index 30ce46f..96d0c8cc6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
@@ -1046,7 +1046,6 @@
 
     @Test
     public void testSetPreferredDataProfile() {
-        doReturn(true).when(mFeatureFlags).refinePreferredDataProfileSelection();
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(
                 new NetworkRequest.Builder()
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
index acfa16d..78bb553 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
@@ -341,7 +341,6 @@
 
     @Test
     public void testDataSetupUnthrottling() throws Exception {
-        doReturn(true).when(mFeatureFlags).unthrottleCheckTransport();
         NetworkRequest request = new NetworkRequest.Builder()
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
                 .build();
@@ -807,7 +806,6 @@
 
     @Test
     public void testDataRetryLongTimer() {
-        doReturn(true).when(mFeatureFlags).useAlarmCallback();
         // Rule requires a long timer
         DataSetupRetryRule retryRule = new DataSetupRetryRule(
                 "capabilities=internet, retry_interval=120000, maximum_retries=2");
@@ -882,6 +880,40 @@
     }
 
     @Test
+    public void testTrafficDescriptorRequestRetry() {
+        DataSetupRetryRule retryRule = new DataSetupRetryRule(
+                "capabilities=PRIORITIZE_BANDWIDTH, retry_interval=200, maximum_retries=2");
+        doReturn(Collections.singletonList(retryRule)).when(mDataConfigManager)
+                .getDataSetupRetryRules();
+        mDataConfigManagerCallback.onCarrierConfigChanged();
+        processAllMessages();
+
+        NetworkRequest request = new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH)
+                .build();
+        TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone, mFeatureFlags);
+        DataNetworkController.NetworkRequestList
+                networkRequestList = new DataNetworkController.NetworkRequestList(tnr);
+
+        // failed and retry.
+        mDataRetryManagerUT.evaluateDataSetupRetry(mDataProfile1,
+                AccessNetworkConstants.TRANSPORT_TYPE_WWAN, networkRequestList, 123,
+                DataCallResponse.RETRY_DURATION_UNDEFINED);
+        processAllFutureMessages();
+
+        tnr = new TelephonyNetworkRequest(new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+                .build(), mPhone, mFeatureFlags);
+        assertThat(mDataRetryManagerUT.isSimilarNetworkRequestRetryScheduled(tnr,
+                AccessNetworkConstants.TRANSPORT_TYPE_WWAN)).isTrue();
+        assertThat(mDataRetryManagerUT.isSimilarNetworkRequestRetryScheduled(tnr,
+                AccessNetworkConstants.TRANSPORT_TYPE_WLAN)).isFalse();
+    }
+
+
+    @Test
     public void testRilCrashedReset() {
         testDataSetupRetryNetworkSuggestedNeverRetry();
         Mockito.clearInvocations(mDataRetryManagerCallbackMock, mDataProfileManager);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
index 3f18a3a..620cf39 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
@@ -42,6 +42,7 @@
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 
 import org.junit.After;
 import org.junit.Before;
@@ -184,6 +185,49 @@
     }
 
     @Test
+    public void testUpdateDataEnabledAndNotifyOverrideDdsChange() throws Exception {
+        // Mock 2nd phone the DDS phone.
+        int phone2Id = 1;
+        int phone2SubId = 2;
+        doReturn(phone2SubId).when(mSubscriptionManagerService).getDefaultDataSubId();
+        Phone phone2 = Mockito.mock(Phone.class);
+        doReturn(phone2Id).when(phone2).getPhoneId();
+        doReturn(phone2SubId).when(phone2).getSubId();
+        doReturn(phone2Id).when(mSubscriptionManagerService).getPhoneId(phone2SubId);
+        DataSettingsManager dataSettingsManager2 = Mockito.mock(DataSettingsManager.class);
+        doReturn(dataSettingsManager2).when(phone2).getDataSettingsManager();
+        doReturn(true).when(phone2).isUserDataEnabled();
+
+        mPhones = new Phone[] {mPhone, phone2};
+        replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+        ArgumentCaptor<SubscriptionManagerService.SubscriptionManagerServiceCallback>
+                callbackArgumentCaptor = ArgumentCaptor
+                .forClass(SubscriptionManagerService.SubscriptionManagerServiceCallback.class);
+
+        mDataSettingsManagerUT.sendEmptyMessage(11 /* EVENT_INITIALIZE */);
+        mDataSettingsManagerUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER, false, "");
+        processAllMessages();
+
+        // Verify listening to DDS change callback
+        verify(mSubscriptionManagerService, times(2))
+                .registerCallback(callbackArgumentCaptor.capture());
+        SubscriptionManagerService.SubscriptionManagerServiceCallback callback =
+                callbackArgumentCaptor.getValue();
+
+        // Mock the phone as nonDDS auto switch override enabled.
+        clearInvocations(mPhones);
+        mDataSettingsManagerUT.setMobileDataPolicy(
+                TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true);
+        processAllMessages();
+        verify(mPhone).notifyDataEnabled(true, TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
+
+        // The phone became DDS, data should be disabled
+        doReturn(mPhone.getSubId()).when(mSubscriptionManagerService).getDefaultDataSubId();
+        callback.onDefaultDataSubscriptionChanged(mPhone.getSubId());
+        verify(mPhone).notifyDataEnabled(false, TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
+    }
+
+    @Test
     public void testNotifyDataEnabledFromNewValidSubId() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         mDataSettingsManagerUT.registerCallback(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
index 2a1fedb..f7990b9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
@@ -52,18 +52,15 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
-import android.net.NetworkProvider;
 import android.net.NetworkRequest;
 import android.net.TelephonyNetworkSpecifier;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Messenger;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneCapability;
@@ -75,8 +72,6 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
-import androidx.test.filters.SmallTest;
-
 import com.android.ims.ImsException;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CommandException;
@@ -87,11 +82,11 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ServiceStateTracker;
-import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback;
 
 import org.junit.After;
 import org.junit.Before;
@@ -140,9 +135,6 @@
 
     private PhoneSwitcher mPhoneSwitcherUT;
     private SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;
-    private ConnectivityManager mConnectivityManager;
-    // The messenger of PhoneSwitcher used to receive network requests.
-    private Messenger mNetworkProviderMessenger = null;
     private Map<Integer, DataSettingsManager.DataSettingsManagerCallback>
             mDataSettingsManagerCallbacks;
     private int mDefaultDataSub = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -155,6 +147,7 @@
     private TelephonyDisplayInfo mTelephonyDisplayInfo = new TelephonyDisplayInfo(
             TelephonyManager.NETWORK_TYPE_NR,
             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false);
+    private SubscriptionManagerServiceCallback mSubscriptionManagerServiceCallback;
 
     @Before
     public void setUp() throws Exception {
@@ -203,14 +196,13 @@
         mServiceManagerMockedServices.put("isub", mIBinder);
 
         doReturn(mTelephonyDisplayInfo).when(mDisplayInfoController).getTelephonyDisplayInfo();
+        doReturn(true).when(mFeatureFlags).ddsCallback();
     }
 
     @After
     public void tearDown() throws Exception {
         mPhoneSwitcherUT = null;
         mSubChangedListener = null;
-        mConnectivityManager = null;
-        mNetworkProviderMessenger = null;
         mTelephonyDisplayInfo = null;
         super.tearDown();
     }
@@ -219,7 +211,6 @@
      * Test that a single phone case results in our phone being active and the RIL called
      */
     @Test
-    @SmallTest
     public void testRegister() throws Exception {
         initialize();
 
@@ -466,7 +457,6 @@
     }
 
     @Test
-    @SmallTest
     public void testAutoDataSwitch_exemptPingTest() throws Exception {
         initialize();
 
@@ -504,7 +494,6 @@
      * - don't switch phones when in emergency mode
      */
     @Test
-    @SmallTest
     public void testPrioritization() throws Exception {
         initialize();
 
@@ -539,7 +528,6 @@
      * wins (ie, switch to wifi).
      */
     @Test
-    @SmallTest
     public void testHigherPriorityDefault() throws Exception {
         initialize();
 
@@ -572,7 +560,6 @@
      * active one.
      */
     @Test
-    @SmallTest
     public void testSetPreferredData() throws Exception {
         initialize();
 
@@ -618,7 +605,6 @@
      * 3. CBRS requests OR Auto switch requests - only one case applies at a time
      */
     @Test
-    @SmallTest
     public void testSetPreferredDataCasePriority_CbrsWaitsForVoiceCall() throws Exception {
         initialize();
         setAllPhonesInactive();
@@ -672,7 +658,6 @@
     }
 
     @Test
-    @SmallTest
     public void testSetPreferredData_NoAutoSwitchWhenCbrs() throws Exception {
         initialize();
         setAllPhonesInactive();
@@ -726,7 +711,6 @@
     }
 
     @Test
-    @SmallTest
     public void testSetPreferredDataModemCommand() throws Exception {
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
         initialize();
@@ -825,7 +809,6 @@
     }
 
     @Test
-    @SmallTest
     public void testSetPreferredDataWithValidation() throws Exception {
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
         initialize();
@@ -886,7 +869,6 @@
     }
 
     @Test
-    @SmallTest
     public void testNonDefaultDataPhoneInCall_ImsCallOnLte_shouldSwitchDds() throws Exception {
         initialize();
         setAllPhonesInactive();
@@ -914,7 +896,6 @@
     }
 
     @Test
-    @SmallTest
     public void testNonDefaultDataPhoneInCall_ImsCallDialingOnLte_shouldSwitchDds()
             throws Exception {
         initialize();
@@ -948,7 +929,6 @@
         assertEquals(1, mPhoneSwitcherUT.getPreferredDataPhoneId());
     }
     @Test
-    @SmallTest
     public void testNonDefaultDataPhoneInCall_ImsCallIncomingOnLte_shouldSwitchDds()
             throws Exception {
         initialize();
@@ -977,7 +957,6 @@
     }
 
     @Test
-    @SmallTest
     public void testNonDefaultDataPhoneInCall_ImsCallOnWlan_shouldNotSwitchDds() throws Exception {
         initialize();
         setAllPhonesInactive();
@@ -1004,7 +983,6 @@
     }
 
     @Test
-    @SmallTest
     public void testNonDefaultDataPhoneInCall_ImsCallOnCrossSIM_HandoverToLTE() throws Exception {
         initialize();
         setAllPhonesInactive();
@@ -1167,7 +1145,6 @@
     }
 
     @Test
-    @SmallTest
     public void testNetworkRequestOnNonDefaultData() throws Exception {
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
         initialize();
@@ -1192,7 +1169,6 @@
     }
 
     @Test
-    @SmallTest
     public void testEmergencyOverrideSuccessBeforeCallStarts() throws Exception {
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
         initialize();
@@ -1213,7 +1189,6 @@
     }
 
     @Test
-    @SmallTest
     public void testEmergencyOverrideNoDdsChange() throws Exception {
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
         initialize();
@@ -1233,7 +1208,6 @@
     }
 
     @Test
-    @SmallTest
     public void testEmergencyOverrideEndSuccess() throws Exception {
         PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS = 500;
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
@@ -1271,7 +1245,6 @@
     }
 
     @Test
-    @SmallTest
     public void testEmergencyOverrideEcbmStartEnd() throws Exception {
         PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS = 500;
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
@@ -1321,7 +1294,6 @@
     }
 
     @Test
-    @SmallTest
     public void testEmergencyOverrideNoCallStart() throws Exception {
         PhoneSwitcher.DEFAULT_DATA_OVERRIDE_TIMEOUT_MS = 500;
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
@@ -1353,7 +1325,6 @@
     }
 
     @Test
-    @SmallTest
     public void testEmergencyOverrideMultipleOverrideRequests() throws Exception {
         PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS = 500;
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
@@ -1404,7 +1375,6 @@
     }
 
     @Test
-    @SmallTest
     public void testSetPreferredDataCallback() throws Exception {
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
         initialize();
@@ -1579,7 +1549,6 @@
     }
 
     @Test
-    @SmallTest
     public void testMultiSimConfigChange() throws Exception {
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
         mActiveModemCount = 1;
@@ -1607,7 +1576,6 @@
     }
 
     @Test
-    @SmallTest
     public void testValidationOffSwitch_shouldSwitchOnNetworkAvailable() throws Exception {
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
         initialize();
@@ -1648,7 +1616,6 @@
     }
 
     @Test
-    @SmallTest
     public void testValidationOffSwitch_shouldSwitchOnTimeOut() throws Exception {
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
         initialize();
@@ -1771,7 +1738,6 @@
     }
 
     @Test
-    @SmallTest
     public void testRegisterForImsRegistrationCallback() throws Exception {
         initialize();
         setAllPhonesInactive();
@@ -1799,7 +1765,6 @@
     }
 
     @Test
-    @SmallTest
     public void testReceivingImsRegistrationTech() throws Exception {
         doReturn(true).when(mFeatureFlags).changeMethodOfObtainingImsRegistrationRadioTech();
 
@@ -1880,9 +1845,6 @@
         doReturn(true).when(mPhone2).isDataAllowed();
         doReturn(true).when(mDataSettingsManager2).isDataEnabled();
 
-        // 3.1 No default network
-        doReturn(null).when(mConnectivityManager).getNetworkCapabilities(any());
-
         mPhoneSwitcherUT.sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
     }
 
@@ -2032,19 +1994,12 @@
         }
     }
 
-    private void sendDefaultDataSubChanged() {
-        final Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
-        mContext.sendBroadcast(intent);
-        processAllMessages();
-    }
-
     private void initialize() throws Exception {
         setNumPhones(mActiveModemCount, mSupportedModemCount);
 
         initializeSubControllerMock();
         initializeCommandInterfacesMock();
         initializeTelRegistryMock();
-        initializeConnManagerMock();
         initializeConfigMock();
 
         mPhoneSwitcherUT = new PhoneSwitcher(mMaxDataAttachModemCount, mContext, Looper.myLooper(),
@@ -2066,6 +2021,11 @@
         processAllMessages();
 
         verify(mTelephonyRegistryManager).addOnSubscriptionsChangedListener(any(), any());
+
+        ArgumentCaptor<SubscriptionManagerServiceCallback> callbackCaptor =
+                ArgumentCaptor.forClass(SubscriptionManagerServiceCallback.class);
+        verify(mSubscriptionManagerService).registerCallback(callbackCaptor.capture());
+        mSubscriptionManagerServiceCallback = callbackCaptor.getValue();
     }
 
     /**
@@ -2153,21 +2113,6 @@
      * Capture mNetworkProviderMessenger so that testing can request or release
      * network requests on PhoneSwitcher.
      */
-    private void initializeConnManagerMock() {
-        mConnectivityManager = (ConnectivityManager)
-                mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-
-        doAnswer(invocation -> {
-            mNetworkProviderMessenger =
-                    ((NetworkProvider) invocation.getArgument(0)).getMessenger();
-            return null;
-        }).when(mConnectivityManager).registerNetworkProvider(any());
-    }
-
-    /**
-     * Capture mNetworkProviderMessenger so that testing can request or release
-     * network requests on PhoneSwitcher.
-     */
     private void initializeSubControllerMock() throws Exception {
         doReturn(mDefaultDataSub).when(mSubscriptionManagerService).getDefaultDataSubId();
         doReturn(mDefaultDataSub).when(mMockedIsub).getDefaultDataSubId();
@@ -2231,7 +2176,8 @@
             doAnswer(invocation -> phone.getSubId() == mDefaultDataSub)
                     .when(phone).isUserDataEnabled();
         }
-        sendDefaultDataSubChanged();
+        mSubscriptionManagerServiceCallback.onDefaultDataSubscriptionChanged(defaultDataSub);
+        processAllMessages();
     }
 
     private void setSlotIndexToSubId(int slotId, int subId) {
@@ -2263,13 +2209,7 @@
         NetworkRequest networkRequest = new NetworkRequest(netCap, ConnectivityManager.TYPE_NONE,
                 0, NetworkRequest.Type.REQUEST);
 
-        Message message = Message.obtain();
-        message.what = android.net.NetworkProvider.CMD_REQUEST_NETWORK;
-        message.arg1 = score;
-        message.obj = networkRequest;
-        mNetworkProviderMessenger.send(message);
-        processAllMessages();
-
+        mPhoneSwitcherUT.onRequestNetwork(networkRequest);
         return networkRequest;
     }
 
@@ -2287,14 +2227,7 @@
         }
         NetworkRequest networkRequest = new NetworkRequest(netCap, ConnectivityManager.TYPE_NONE,
                 1, NetworkRequest.Type.REQUEST);
-
-        Message message = Message.obtain();
-        message.what = android.net.NetworkProvider.CMD_REQUEST_NETWORK;
-        message.arg1 = 50; // Score
-        message.obj = networkRequest;
-        mNetworkProviderMessenger.send(message);
-        processAllMessages();
-
+        mPhoneSwitcherUT.onRequestNetwork(networkRequest);
         return networkRequest;
     }
 
@@ -2302,10 +2235,6 @@
      * Tell PhoneSwitcher to release a network request.
      */
     private void releaseNetworkRequest(NetworkRequest networkRequest) throws Exception {
-        Message message = Message.obtain();
-        message.what = android.net.NetworkProvider.CMD_CANCEL_REQUEST;
-        message.obj = networkRequest;
-        mNetworkProviderMessenger.send(message);
-        processAllMessages();
+        mPhoneSwitcherUT.onReleaseNetwork(networkRequest);
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkProviderTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkProviderTest.java
new file mode 100644
index 0000000..d62ee2f
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkProviderTest.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.data;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.NonNull;
+import android.net.MatchAllNetworkSpecifier;
+import android.net.NetworkCapabilities;
+import android.net.NetworkProvider;
+import android.net.NetworkRequest;
+import android.net.NetworkScore;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.connectivity.android.net.INetworkOfferCallback;
+import android.os.Looper;
+import android.telephony.Annotation.NetCapability;
+import android.telephony.SubscriptionManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.data.PhoneSwitcher.PhoneSwitcherCallback;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Arrays;
+import java.util.Set;
+
+@RunWith(AndroidTestingRunner.class)
[email protected]
+public class TelephonyNetworkProviderTest extends TelephonyTest {
+
+    private TelephonyNetworkProvider mTelephonyNetworkProvider;
+
+    private PhoneSwitcherCallback mPhoneSwitcherCallback;
+
+    // Mocked classes
+    private DataNetworkController mDataNetworkController2;
+
+
+    /**
+     * Set the preferred data phone, which is supposed to take the network request.
+     *
+     * @param phoneId The phone id
+     */
+    private void setPreferredDataPhone(int phoneId) {
+        doAnswer(invocation -> {
+            TelephonyNetworkRequest request = (TelephonyNetworkRequest)
+                    invocation.getArguments()[0];
+            int id = (int) invocation.getArguments()[1];
+
+            logd("shouldApplyNetworkRequest: request phone id=" + id
+                    + ", preferred data phone id=" + phoneId);
+
+            TelephonyNetworkSpecifier specifier = (TelephonyNetworkSpecifier)
+                    request.getNetworkSpecifier();
+            if (specifier != null) {
+                int subId = specifier.getSubscriptionId();
+                logd("shouldApplyNetworkRequest: requested on sub " + subId);
+                if (subId == 1 && mPhone.getPhoneId() == id) {
+                    logd("shouldApplyNetworkRequest: matched phone 0");
+                    return true;
+                }
+                if (subId == 2 && mPhone2.getPhoneId() == id) {
+                    logd("shouldApplyNetworkRequest: matched phone 1");
+                    return true;
+                }
+                return false;
+            }
+
+            if (request.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) return true;
+            return id == phoneId;
+        }).when(mPhoneSwitcher).shouldApplyNetworkRequest(any(TelephonyNetworkRequest.class),
+                anyInt());
+    }
+
+    /**
+     * Create a simple network request with internet capability.
+     *
+     * @return The network request
+     */
+    @NonNull
+    private NetworkRequest createNetworkRequest() {
+        return createNetworkRequestForSub(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+                NetworkCapabilities.NET_CAPABILITY_INTERNET);
+    }
+
+    /**
+     * Create a network request with specified network capabilities.
+     *
+     * @param caps Network capabilities
+     *
+     * @return The network request
+     */
+    @NonNull
+    private NetworkRequest createNetworkRequest(@NetCapability int... caps) {
+        return createNetworkRequestForSub(SubscriptionManager.INVALID_SUBSCRIPTION_ID, caps);
+    }
+
+    /**
+     * Create a network request with subscription id specified.
+     *
+     * @param subId The subscription in for the network request
+     *
+     * @return The network request
+     */
+    @NonNull
+    private NetworkRequest createNetworkRequestForSub(int subId) {
+        return createNetworkRequestForSub(subId, NetworkCapabilities.NET_CAPABILITY_INTERNET);
+    }
+
+    /**
+     * Create the network request.
+     *
+     * @param subId The subscription id. {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if no
+     * @param caps Network capabilities in the network request need to specify.
+     *
+     * @return The network request
+     */
+    @NonNull
+    private NetworkRequest createNetworkRequestForSub(int subId, @NetCapability int... caps) {
+        NetworkRequest.Builder builder = new NetworkRequest.Builder();
+        Arrays.stream(caps).boxed().toList().forEach(builder::addCapability);
+        if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+            builder.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
+            builder.setSubscriptionIds(Set.of(subId));
+        }
+
+        return builder.build();
+    }
+
+    /** Clear all invocations from all DataNetworkControllers. */
+    private void resetInvocations() {
+        clearInvocations(mDataNetworkController);
+        clearInvocations(mDataNetworkController2);
+    }
+
+    /**
+     * Verify the request was sent to the correct phone's DataNetworkController.
+     *
+     * @param phoneId The id of the phone that the request is supposed to send
+     * @param request The network request
+     */
+    private void verifyRequestSentOnPhone(int phoneId, @NonNull NetworkRequest request) {
+        ArgumentCaptor<TelephonyNetworkRequest> requestCaptor =
+                ArgumentCaptor.forClass(TelephonyNetworkRequest.class);
+
+        for (Phone phone : PhoneFactory.getPhones()) {
+            if (phone.getPhoneId() == phoneId) {
+                verify(phone.getDataNetworkController(), times(1)
+                        .description("Did not request on phone " + phoneId))
+                        .addNetworkRequest(requestCaptor.capture());
+                assertThat(requestCaptor.getValue().getNativeNetworkRequest()).isEqualTo(request);
+            } else {
+                verifyNoRequestSentOnPhone(phone.getPhoneId());
+            }
+        }
+    }
+
+    /**
+     * Verify the request was released on the specified phone's DataNetworkController.
+     *
+     * @param phoneId The id of the phone that the request is supposed to send
+     * @param request The network request
+     */
+    private void verifyRequestReleasedOnPhone(int phoneId, @NonNull NetworkRequest request) {
+        ArgumentCaptor<TelephonyNetworkRequest> requestCaptor =
+                ArgumentCaptor.forClass(TelephonyNetworkRequest.class);
+
+        for (Phone phone : PhoneFactory.getPhones()) {
+            if (phone.getPhoneId() == phoneId) {
+                verify(phone.getDataNetworkController(), times(1)
+                        .description("Did not remove on phone " + phoneId))
+                        .removeNetworkRequest(requestCaptor.capture());
+                assertThat(requestCaptor.getValue().getNativeNetworkRequest()).isEqualTo(request);
+            } else {
+                verifyNoRequestReleasedOnPhone(phone.getPhoneId());
+            }
+        }
+    }
+
+    /**
+     * Verify there is no request sent on specified phone.
+     *
+     * @param phoneId The phone id
+     */
+    private void verifyNoRequestSentOnPhone(int phoneId) {
+        verify(PhoneFactory.getPhone(phoneId).getDataNetworkController(), never()
+                .description("Should not request on phone " + phoneId))
+                .addNetworkRequest(any(TelephonyNetworkRequest.class));
+    }
+
+    /**
+     * Verify there is no request released on specified phone.
+     *
+     * @param phoneId The phone id
+     */
+    private void verifyNoRequestReleasedOnPhone(int phoneId) {
+        verify(PhoneFactory.getPhone(phoneId).getDataNetworkController(), never()
+                .description("Should not release on phone " + phoneId))
+                .removeNetworkRequest(any(TelephonyNetworkRequest.class));
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        logd("TelephonyNetworkProviderTest +Setup!");
+        super.setUp(getClass().getSimpleName());
+        replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone, mPhone2});
+
+        mDataNetworkController2 = mock(DataNetworkController.class);
+
+        doReturn(0).when(mPhone).getPhoneId();
+        doReturn(1).when(mPhone).getSubId();
+        doReturn(1).when(mPhone2).getPhoneId();
+        doReturn(2).when(mPhone2).getSubId();
+
+        doReturn(mDataNetworkController2).when(mPhone2).getDataNetworkController();
+
+        setPreferredDataPhone(0);
+
+        doAnswer(invocation -> {
+            NetworkProvider provider = (NetworkProvider) invocation.getArguments()[0];
+            provider.setProviderId(1);
+            return 1;
+        }).when(mConnectivityManager).registerNetworkProvider(any(NetworkProvider.class));
+
+        mTelephonyNetworkProvider = new TelephonyNetworkProvider(Looper.myLooper(),
+                mContext, mFeatureFlags);
+
+        ArgumentCaptor<PhoneSwitcherCallback> callbackCaptor =
+                ArgumentCaptor.forClass(PhoneSwitcherCallback.class);
+        verify(mPhoneSwitcher).registerCallback(callbackCaptor.capture());
+        mPhoneSwitcherCallback = callbackCaptor.getValue();
+
+        logd("TelephonyNetworkProviderTest -Setup!");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        logd("tearDown");
+        super.tearDown();
+    }
+
+    @Test
+    public void testRegisterProvider() {
+        verify(mConnectivityManager).registerNetworkProvider(any(TelephonyNetworkProvider.class));
+
+        ArgumentCaptor<NetworkCapabilities> capsCaptor =
+                ArgumentCaptor.forClass(NetworkCapabilities.class);
+        verify(mConnectivityManager).offerNetwork(anyInt(), any(NetworkScore.class),
+                capsCaptor.capture(), any(INetworkOfferCallback.class));
+
+        NetworkCapabilities caps = capsCaptor.getValue();
+
+        TelephonyNetworkRequest.getAllSupportedNetworkCapabilities().forEach(
+                (cap) -> assertThat(caps.hasCapability(cap)).isTrue());
+        assertThat(caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)).isTrue();
+        assertThat(caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMTEL)).isTrue();
+        assertThat(caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)).isTrue();
+        assertThat(caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)).isTrue();
+        assertThat(caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).isTrue();
+        assertThat(caps.hasTransport(NetworkCapabilities.TRANSPORT_SATELLITE)).isTrue();
+
+        assertThat(caps.getNetworkSpecifier()).isInstanceOf(MatchAllNetworkSpecifier.class);
+    }
+
+    @Test
+    public void testRequestNetwork() {
+        NetworkRequest request = createNetworkRequest();
+        mTelephonyNetworkProvider.onNetworkNeeded(request);
+        // Should request on phone 0
+        verifyRequestSentOnPhone(0, request);
+    }
+
+    @Test
+    public void testReleaseNetwork() {
+        NetworkRequest request = createNetworkRequest();
+        mTelephonyNetworkProvider.onNetworkNeeded(request);
+        // Should request on phone 0
+        verifyRequestSentOnPhone(0, request);
+        resetInvocations();
+
+        // Now release the network request.
+        mTelephonyNetworkProvider.onNetworkUnneeded(request);
+        // Should release on phone 0
+        verifyRequestReleasedOnPhone(0, request);
+        resetInvocations();
+
+        // Release the same request again should not result in another remove
+        mTelephonyNetworkProvider.onNetworkUnneeded(request);
+        verifyNoRequestReleasedOnPhone(0);
+        verifyNoRequestReleasedOnPhone(1);
+    }
+
+    @Test
+    public void testRequestNetworkDuplicate() {
+        NetworkRequest request = createNetworkRequest();
+        mTelephonyNetworkProvider.onNetworkNeeded(request);
+        // Should request on phone 0
+        verifyRequestSentOnPhone(0, request);
+
+        resetInvocations();
+        // send the same request again should be blocked.
+        mTelephonyNetworkProvider.onNetworkNeeded(request);
+        verifyNoRequestSentOnPhone(0);
+        verifyNoRequestSentOnPhone(1);
+    }
+
+    @Test
+    public void testRequestNetworkPreferredPhone1() {
+        setPreferredDataPhone(1);
+
+        NetworkRequest request = createNetworkRequest();
+        mTelephonyNetworkProvider.onNetworkNeeded(request);
+        // Should request on phone 1
+        verifyRequestSentOnPhone(1, request);
+    }
+
+    @Test
+    public void testRequestEmergencyNetwork() {
+        setPreferredDataPhone(1);
+
+        NetworkRequest request = createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_EIMS);
+        mTelephonyNetworkProvider.onNetworkNeeded(request);
+        // Should request on phone 0
+        verifyRequestSentOnPhone(0, request);
+    }
+
+    @Test
+    public void testRequestNetworkOnSpecifiedSub() {
+        NetworkRequest request = createNetworkRequestForSub(1);
+        mTelephonyNetworkProvider.onNetworkNeeded(request);
+        verifyRequestSentOnPhone(0, request);
+
+        resetInvocations();
+        request = createNetworkRequestForSub(2);
+        mTelephonyNetworkProvider.onNetworkNeeded(request);
+        // Should request on phone 1
+        verifyRequestSentOnPhone(1, request);
+    }
+
+    @Test
+    public void testPreferredDataSwitch() {
+        NetworkRequest request = createNetworkRequest();
+        mTelephonyNetworkProvider.onNetworkNeeded(request);
+        // Should request on phone 0
+        verifyRequestSentOnPhone(0, request);
+        resetInvocations();
+
+        // Now switch from phone 0 to phone 1
+        setPreferredDataPhone(1);
+        mPhoneSwitcherCallback.onPreferredDataPhoneIdChanged(1);
+        verifyRequestReleasedOnPhone(0, request);
+        verifyRequestSentOnPhone(1, request);
+        resetInvocations();
+
+        // Now switch back to phone 0
+        setPreferredDataPhone(0);
+        mPhoneSwitcherCallback.onPreferredDataPhoneIdChanged(0);
+        verifyRequestReleasedOnPhone(1, request);
+        verifyRequestSentOnPhone(0, request);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkRequestTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkRequestTest.java
index 9f11a3a..27c87e2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkRequestTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkRequestTest.java
@@ -189,7 +189,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)
                         .build(), mPhone, mFeatureFlags);
-        assertThat(request.getApnTypeNetworkCapability())
+        assertThat(request.getHighestPriorityApnTypeNetworkCapability())
                 .isEqualTo(NetworkCapabilities.NET_CAPABILITY_SUPL);
 
         request = new TelephonyNetworkRequest(
@@ -198,7 +198,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
                         .build(), mPhone, mFeatureFlags);
-        assertThat(request.getApnTypeNetworkCapability())
+        assertThat(request.getHighestPriorityApnTypeNetworkCapability())
                 .isEqualTo(NetworkCapabilities.NET_CAPABILITY_FOTA);
 
         request = new TelephonyNetworkRequest(
@@ -207,7 +207,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
                         .build(), mPhone, mFeatureFlags);
-        assertThat(request.getApnTypeNetworkCapability())
+        assertThat(request.getHighestPriorityApnTypeNetworkCapability())
                 .isEqualTo(NetworkCapabilities.NET_CAPABILITY_EIMS);
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
index 5fd7a77..2f3fabf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
@@ -17,8 +17,10 @@
 
 import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
 import static android.telephony.AccessNetworkConstants.AccessNetworkType.UTRAN;
+import static android.telephony.DomainSelectionService.SCAN_TYPE_FULL_SERVICE;
 import static android.telephony.DomainSelectionService.SCAN_TYPE_NO_PREFERENCE;
 import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_SMS;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -46,6 +48,7 @@
 import android.telephony.DisconnectCause;
 import android.telephony.DomainSelectionService;
 import android.telephony.EmergencyRegistrationResult;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PreciseDisconnectCause;
 import android.telephony.data.ApnSetting;
 import android.telephony.ims.ImsReasonInfo;
@@ -55,6 +58,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.telephony.CallFailCause;
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.IDomainSelector;
 import com.android.internal.telephony.ITransportSelectorCallback;
 import com.android.internal.telephony.ITransportSelectorResultCallback;
@@ -83,6 +88,7 @@
 
     private static final String TELECOM_CALL_ID1 = "TC1";
 
+    private CommandsInterface mCi;
     private DomainSelectionController mDomainSelectionController;
     private DomainSelectionConnection.DomainSelectionConnectionCallback mConnectionCallback;
     private DomainSelectionConnection mDsc;
@@ -91,6 +97,9 @@
     public void setUp() throws Exception {
         super.setUp(this.getClass().getSimpleName());
 
+        mCi = Mockito.mock(CommandsInterface.class);
+        mPhone.mCi = mCi;
+
         mDomainSelectionController = Mockito.mock(DomainSelectionController.class);
         doReturn(true).when(mDomainSelectionController).selectDomain(any(), any());
         mConnectionCallback =
@@ -187,6 +196,7 @@
 
         verify(mPhone).registerForEmergencyNetworkScan(
                 handlerCaptor.capture(), eventCaptor.capture(), any());
+        verify(mCi).registerForModemReset(any(), anyInt(), any());
 
         int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN };
 
@@ -235,6 +245,7 @@
         processAllMessages();
 
         verify(mPhone).registerForEmergencyNetworkScan(any(), anyInt(), any());
+        verify(mCi).registerForModemReset(any(), anyInt(), any());
         verify(mPhone).triggerEmergencyNetworkScan(any(), anyInt(), any());
 
         wwanCallback.onCancel();
@@ -280,6 +291,7 @@
         verify(mPhone).cancelEmergencyNetworkScan(eq(true), msgCaptor.capture());
 
         verify(mPhone, times(0)).registerForEmergencyNetworkScan(any(), anyInt(), any());
+        verify(mCi, times(0)).registerForModemReset(any(), anyInt(), any());
         verify(mPhone, times(0)).triggerEmergencyNetworkScan(any(), anyInt(), any());
 
         Message msg = msgCaptor.getValue();
@@ -295,6 +307,7 @@
 
         verify(mPhone).registerForEmergencyNetworkScan(
                 handlerCaptor.capture(), eventCaptor.capture(), any());
+        verify(mCi).registerForModemReset(any(), anyInt(), any());
 
         int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN };
 
@@ -350,6 +363,7 @@
 
         verify(mPhone).cancelEmergencyNetworkScan(eq(true), msgCaptor.capture());
         verify(mPhone, times(0)).registerForEmergencyNetworkScan(any(), anyInt(), any());
+        verify(mCi, times(0)).registerForModemReset(any(), anyInt(), any());
         verify(mPhone, times(0)).triggerEmergencyNetworkScan(any(), anyInt(), any());
 
         Message msg = msgCaptor.getValue();
@@ -363,6 +377,7 @@
 
         // Verify that scan is requested.
         verify(mPhone).registerForEmergencyNetworkScan(any(), anyInt(), any());
+        verify(mCi).registerForModemReset(any(), anyInt(), any());
         verify(mPhone).triggerEmergencyNetworkScan(any(), anyInt(), any());
 
         // Cancele scan after reset completes.
@@ -410,6 +425,7 @@
 
         verify(mPhone).cancelEmergencyNetworkScan(eq(true), msgCaptor.capture());
         verify(mPhone, times(0)).registerForEmergencyNetworkScan(any(), anyInt(), any());
+        verify(mCi, times(0)).registerForModemReset(any(), anyInt(), any());
         verify(mPhone, times(0)).triggerEmergencyNetworkScan(any(), anyInt(), any());
 
         Message msg = msgCaptor.getValue();
@@ -430,11 +446,270 @@
 
         // Verify there is no scan request after reset completes.
         verify(mPhone, times(0)).registerForEmergencyNetworkScan(any(), anyInt(), any());
+        verify(mCi, times(0)).registerForModemReset(any(), anyInt(), any());
         verify(mPhone, times(0)).triggerEmergencyNetworkScan(any(), anyInt(), any());
     }
 
     @Test
     @SmallTest
+    public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanAndRadioNotAvailableError()
+            throws Exception {
+        mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+                mDomainSelectionController);
+
+        ITransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+        assertNotNull(transportCallback);
+
+        DomainSelectionService.SelectionAttributes attr = getSelectionAttributes(
+                mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_CALLING, true,
+                false, 0, TELECOM_CALL_ID1, null, null, null);
+
+        mDsc.selectDomain(attr);
+
+        IDomainSelector domainSelector = Mockito.mock(IDomainSelector.class);
+        transportCallback.onCreated(domainSelector);
+
+        IWwanSelectorCallback wwanCallback = onWwanSelected(transportCallback);
+
+        assertNotNull(wwanCallback);
+
+        int[] preferredNetworks = new int[] { EUTRAN, UTRAN };
+        int scanType = SCAN_TYPE_NO_PREFERENCE;
+        IWwanSelectorResultCallback resultCallback =
+                Mockito.mock(IWwanSelectorResultCallback.class);
+
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+                false, resultCallback);
+        processAllMessages();
+
+        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+        ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
+
+        verify(mPhone).registerForEmergencyNetworkScan(handlerCaptor.capture(), anyInt(), any());
+        verify(mCi).registerForModemReset(any(), anyInt(), any());
+        processAllMessages();
+
+        int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN };
+
+        verify(mPhone).triggerEmergencyNetworkScan(eq(expectedPreferredNetworks),
+                eq(scanType), msgCaptor.capture());
+
+        Handler handler = handlerCaptor.getValue();
+        Message msg = msgCaptor.getValue();
+
+        assertNotNull(handler);
+        assertNotNull(msg);
+
+        CommandException commandException =
+                new CommandException(CommandException.Error.RADIO_NOT_AVAILABLE);
+        handler.sendMessage(handler.obtainMessage(msg.what,
+                new AsyncResult((Integer) scanType, null, commandException)));
+        processAllMessages();
+
+        verify(resultCallback, times(0)).onComplete(any());
+        verify(mPhone).unregisterForEmergencyNetworkScan(any());
+        verify(mCi).unregisterForModemReset(any());
+        verify(mPhone, times(0)).cancelEmergencyNetworkScan(anyBoolean(), any());
+        verify(mDomainSelectionController).removeConnection(eq(mDsc));
+    }
+
+    @Test
+    @SmallTest
+    public void testModemResetOnRequestEmergencyNetworkDuringNetworkScan()
+            throws Exception {
+        mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+                mDomainSelectionController);
+
+        ITransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+        assertNotNull(transportCallback);
+
+        DomainSelectionService.SelectionAttributes attr = getSelectionAttributes(
+                mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_CALLING, true,
+                false, 0, TELECOM_CALL_ID1, null, null, null);
+
+        mDsc.selectDomain(attr);
+
+        IDomainSelector domainSelector = Mockito.mock(IDomainSelector.class);
+        transportCallback.onCreated(domainSelector);
+
+        IWwanSelectorCallback wwanCallback = onWwanSelected(transportCallback);
+
+        assertNotNull(wwanCallback);
+
+        int[] preferredNetworks = new int[] { EUTRAN, UTRAN };
+        int scanType = SCAN_TYPE_FULL_SERVICE;
+        IWwanSelectorResultCallback resultCallback =
+                Mockito.mock(IWwanSelectorResultCallback.class);
+
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, false,
+                resultCallback);
+        processAllMessages();
+
+        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+        ArgumentCaptor<Integer> eventCaptor = ArgumentCaptor.forClass(Integer.class);
+
+        verify(mPhone).registerForEmergencyNetworkScan(any(), anyInt(), any());
+        verify(mCi).registerForModemReset(handlerCaptor.capture(), eventCaptor.capture(),
+                any());
+        processAllMessages();
+
+        int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN };
+
+        verify(mPhone).triggerEmergencyNetworkScan(eq(expectedPreferredNetworks),
+                eq(scanType), any());
+
+        Handler handler = handlerCaptor.getValue();
+        Integer event = eventCaptor.getValue();
+
+        assertNotNull(handler);
+        assertNotNull(event);
+
+        handler.sendMessage(handler.obtainMessage(event.intValue(),
+                new AsyncResult(null, null, null)));
+        processAllMessages();
+
+        verify(resultCallback, times(0)).onComplete(any());
+        verify(mPhone).unregisterForEmergencyNetworkScan(any());
+        verify(mCi).unregisterForModemReset(any());
+        verify(mPhone).cancelEmergencyNetworkScan(anyBoolean(), any());
+    }
+
+    @Test
+    @SmallTest
+    public void testModemResetOnRequestEmergencyNetworkDuringNetworkScanForSms()
+            throws Exception {
+        mDsc = createConnection(mPhone, SELECTOR_TYPE_SMS, true,
+                mDomainSelectionController);
+
+        ITransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+        assertNotNull(transportCallback);
+
+        DomainSelectionService.SelectionAttributes attr = getSelectionAttributes(
+                mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_SMS, true,
+                false, 0, TELECOM_CALL_ID1, null, null, null);
+
+        mDsc.selectDomain(attr);
+
+        IDomainSelector domainSelector = Mockito.mock(IDomainSelector.class);
+        transportCallback.onCreated(domainSelector);
+
+        IWwanSelectorCallback wwanCallback = onWwanSelected(transportCallback);
+
+        assertNotNull(wwanCallback);
+
+        int[] preferredNetworks = new int[] { EUTRAN, UTRAN };
+        int scanType = SCAN_TYPE_FULL_SERVICE;
+        IWwanSelectorResultCallback resultCallback =
+                Mockito.mock(IWwanSelectorResultCallback.class);
+
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, false,
+                resultCallback);
+        processAllMessages();
+
+        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+        ArgumentCaptor<Integer> eventCaptor = ArgumentCaptor.forClass(Integer.class);
+
+        verify(mPhone).registerForEmergencyNetworkScan(any(), anyInt(), any());
+        verify(mCi).registerForModemReset(handlerCaptor.capture(), eventCaptor.capture(),
+                any());
+        processAllMessages();
+
+        int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN };
+
+        verify(mPhone).triggerEmergencyNetworkScan(eq(expectedPreferredNetworks),
+                eq(scanType), any());
+
+        Handler handler = handlerCaptor.getValue();
+        Integer event = eventCaptor.getValue();
+
+        assertNotNull(handler);
+        assertNotNull(event);
+
+        handler.sendMessage(handler.obtainMessage(event.intValue(),
+                new AsyncResult(null, null, null)));
+        processAllMessages();
+
+        verify(mPhone, times(0)).unregisterForEmergencyNetworkScan(any());
+        verify(mCi, times(0)).unregisterForModemReset(any());
+        verify(mPhone, times(0)).cancelEmergencyNetworkScan(anyBoolean(), any());
+    }
+
+    @Test
+    @SmallTest
+    public void testModemResetOnRequestEmergencyNetworkAfterNetworkScanDone()
+            throws Exception {
+        mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+                mDomainSelectionController);
+
+        ITransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+        assertNotNull(transportCallback);
+
+        DomainSelectionService.SelectionAttributes attr = getSelectionAttributes(
+                mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_CALLING, true,
+                false, 0, TELECOM_CALL_ID1, null, null, null);
+
+        mDsc.selectDomain(attr);
+
+        IDomainSelector domainSelector = Mockito.mock(IDomainSelector.class);
+        transportCallback.onCreated(domainSelector);
+
+        IWwanSelectorCallback wwanCallback = onWwanSelected(transportCallback);
+
+        assertNotNull(wwanCallback);
+
+        int[] preferredNetworks = new int[] { EUTRAN, UTRAN };
+        int scanType = SCAN_TYPE_FULL_SERVICE;
+        IWwanSelectorResultCallback resultCallback =
+                Mockito.mock(IWwanSelectorResultCallback.class);
+
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, false,
+                resultCallback);
+        processAllMessages();
+
+        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+        ArgumentCaptor<Integer> scanEventCaptor = ArgumentCaptor.forClass(Integer.class);
+        ArgumentCaptor<Integer> resetEventCaptor = ArgumentCaptor.forClass(Integer.class);
+
+        verify(mPhone).registerForEmergencyNetworkScan(any(), scanEventCaptor.capture(), any());
+        verify(mCi).registerForModemReset(handlerCaptor.capture(),
+                resetEventCaptor.capture(), any());
+        processAllMessages();
+
+        int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN };
+
+        verify(mPhone).triggerEmergencyNetworkScan(eq(expectedPreferredNetworks),
+                eq(scanType), any());
+
+        Handler handler = handlerCaptor.getValue();
+        Integer scanEvent = scanEventCaptor.getValue();
+        Integer resetEvent = resetEventCaptor.getValue();
+
+        assertNotNull(handler);
+        assertNotNull(scanEvent);
+        assertNotNull(resetEvent);
+
+        EmergencyRegistrationResult regResult =
+                new EmergencyRegistrationResult(UTRAN, 0, 0, true, false, 0, 0, "", "", "");
+        handler.sendMessage(handler.obtainMessage(scanEvent,
+                new AsyncResult(null, regResult, null)));
+        processAllMessages();
+        wwanCallback.onDomainSelected(NetworkRegistrationInfo.DOMAIN_CS_PS, true);
+
+        handler.sendMessage(handler.obtainMessage(resetEvent, new AsyncResult(null, null, null)));
+        processAllMessages();
+
+        verify(resultCallback).onComplete(any());
+        verify(mPhone, times(0)).unregisterForEmergencyNetworkScan(any());
+        verify(mCi, times(0)).unregisterForModemReset(any());
+        verify(mPhone, times(0)).cancelEmergencyNetworkScan(anyBoolean(), any());
+    }
+
+    @Test
+    @SmallTest
     public void testDomainSelectorCancelSelection() throws Exception {
         mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true,
                 mDomainSelectionController);
@@ -601,6 +876,7 @@
 
         verify(mPhone).registerForEmergencyNetworkScan(
                 handlerCaptor.capture(), eventCaptor.capture(), any());
+        verify(mCi).registerForModemReset(any(), anyInt(), any());
 
         int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN };
 
@@ -686,6 +962,7 @@
 
         verify(mPhone).registerForEmergencyNetworkScan(
                 handlerCaptor.capture(), eventCaptor.capture(), any());
+        verify(mCi).registerForModemReset(any(), anyInt(), any());
 
         int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN };
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
index 0eb5c13..25e9715 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
@@ -180,7 +180,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
                 .forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
-                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
+                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS), eq(false));
         // isOkToCall() should return true when IN_SERVICE state
         assertFalse(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
@@ -239,7 +239,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
                 .forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
-                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
+                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS), eq(false));
         // onTimeout should return true when radion on
         assertFalse(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
@@ -287,7 +287,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
                 .forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
-                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
+                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS), eq(false));
 
         // Hangup the call
         emergencyStateTracker.endCall(mTestConnection1);
@@ -323,7 +323,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
                 .forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
-                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
+                eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS), eq(false));
         // Verify future completes with DisconnectCause.POWER_OFF if radio not ready
         CompletableFuture<Void> unused = future.thenAccept((result) -> {
             assertEquals((Integer) result, (Integer) DisconnectCause.POWER_OFF);
@@ -359,7 +359,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
                 .forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
-                eq(false), eq(0));
+                eq(false), eq(0), eq(false));
         // isOkToCall() should return true once satellite modem is off
         assertFalse(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, false));
@@ -400,7 +400,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
                 .forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
-                eq(false), eq(0));
+                eq(false), eq(0), eq(false));
         // Verify future completes with DisconnectCause.POWER_OFF if radio not ready
         CompletableFuture<Void> unused = future.thenAccept((result) -> {
             assertEquals((Integer) result, (Integer) DisconnectCause.SATELLITE_ENABLED);
@@ -428,7 +428,7 @@
 
         // Radio already on so shouldn't trigger this
         verify(mRadioOnHelper, never()).triggerRadioOnAndListen(any(), anyBoolean(), any(),
-                anyBoolean(), eq(0));
+                anyBoolean(), eq(0), eq(false));
         // Carrier supports control-plane fallback, so no DDS switch
         verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
     }
@@ -3168,7 +3168,7 @@
         // Wait for the radio off for all phones
         verify(mSST, times(2)).registerForVoiceRegStateOrRatChanged(any(), anyInt(), any());
         verify(mRadioOnHelper, never()).triggerRadioOnAndListen(any(), anyBoolean(), any(),
-                anyBoolean(), eq(0));
+                anyBoolean(), eq(0), eq(false));
     }
 
     /**
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java
index 037f82c..bb349a7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java
@@ -90,11 +90,10 @@
         waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
 
         verify(mMockPhone).unregisterForServiceStateChanged(any(Handler.class));
-        verify(mSatelliteController).unregisterForModemStateChanged(anyInt(), any());
+        verify(mSatelliteController).unregisterForModemStateChanged(any());
         verify(mMockPhone).registerForServiceStateChanged(any(Handler.class),
                 eq(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED), isNull());
-        verify(mSatelliteController, never()).registerForSatelliteModemStateChanged(
-                anyInt(), any());
+        verify(mSatelliteController, never()).registerForSatelliteModemStateChanged(any());
 
         verify(mMockCi).registerForOffOrNotAvailable(any(Handler.class),
                 eq(RadioOnStateListener.MSG_RADIO_OFF_OR_NOT_AVAILABLE), isNull());
@@ -110,8 +109,8 @@
 
         waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
 
-        verify(mSatelliteController).unregisterForModemStateChanged(anyInt(), any());
-        verify(mSatelliteController).registerForSatelliteModemStateChanged(anyInt(), any());
+        verify(mSatelliteController).unregisterForModemStateChanged(any());
+        verify(mSatelliteController).registerForSatelliteModemStateChanged(any());
     }
 
     /**
@@ -205,7 +204,7 @@
         verify(mCallback).onComplete(eq(mListener), eq(false));
         verify(mMockPhone, times(2)).setRadioPower(eq(true), eq(false), eq(false), eq(false));
         verify(mSatelliteController, never()).requestSatelliteEnabled(
-                anyInt(), eq(false), eq(false), eq(false), any());
+                eq(false), eq(false), eq(false), any());
     }
 
     @Test
@@ -225,7 +224,7 @@
         verify(mCallback).onComplete(eq(mListener), eq(false));
         verify(mMockPhone, times(2)).setRadioPower(eq(true), eq(true), eq(true), eq(false));
         verify(mSatelliteController, never()).requestSatelliteEnabled(
-                anyInt(), eq(false), eq(false), eq(false), any());
+                eq(false), eq(false), eq(false), any());
     }
 
     @Test
@@ -246,7 +245,7 @@
         verify(mCallback).onComplete(eq(mListener), eq(false));
         verify(mMockPhone, times(2)).setRadioPower(eq(true), eq(true), eq(true), eq(false));
         verify(mSatelliteController, times(2)).requestSatelliteEnabled(
-                anyInt(), eq(false), eq(false), eq(false), any());
+                eq(false), eq(false), eq(false), any());
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
index e374551..879118f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
@@ -41,7 +41,6 @@
 import android.Manifest;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
-import android.app.admin.flags.Flags;
 import android.compat.testing.PlatformCompatChangeRule;
 import android.content.Context;
 import android.content.Intent;
@@ -49,6 +48,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.UserManager;
@@ -87,8 +87,13 @@
 import org.junit.Test;
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.mockito.stubbing.Answer;
 import org.mockito.stubbing.Stubber;
 
@@ -106,6 +111,8 @@
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    @Rule
+    public MockitoRule rule = MockitoJUnit.rule();
 
     private static final DownloadableSubscription SUBSCRIPTION =
             DownloadableSubscription.forActivationCode("abcde");
@@ -145,8 +152,8 @@
     private static final long AVAILABLE_MEMORY = 123L;
 
     // Mocked classes
-    private EuiccConnector mMockConnector;
-    private UiccSlot mUiccSlot;
+    @Mock private EuiccConnector mMockConnector;
+    @Captor private ArgumentCaptor<Bundle> mBundleCaptor;
 
     private TestEuiccController mController;
     private int mSavedEuiccProvisionedValue;
@@ -218,8 +225,6 @@
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
-        mMockConnector = Mockito.mock(EuiccConnector.class);
-        mUiccSlot = Mockito.mock(UiccSlot.class);
         mController = new TestEuiccController(mContext, mMockConnector, mFeatureFlags);
 
         PackageInfo pi = new PackageInfo();
@@ -580,8 +585,15 @@
     public void testDownloadSubscription_success() throws Exception {
         setHasWriteEmbeddedPermission(true);
         setUpUiccSlotData();
+
         callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
                 EuiccService.RESULT_OK, 0 /* resolvableError */, "whatever" /* callingPackage */);
+
+        verify(mMockConnector).downloadSubscription(anyInt(), anyInt(),
+                any(), eq(true), anyBoolean(), mBundleCaptor.capture(), any());
+        assertEquals(
+                "whatever",
+                mBundleCaptor.getValue().getString(EuiccService.EXTRA_PACKAGE_NAME));
         verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
         // switchAfterDownload = true so no refresh should occur.
         assertFalse(mController.mCalledRefreshSubscriptionsAndSendResult);
@@ -591,8 +603,15 @@
     @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
     public void testDownloadSubscription_noSwitch_success() throws Exception {
         setHasWriteEmbeddedPermission(true);
+
         callDownloadSubscription(SUBSCRIPTION, false /* switchAfterDownload */, true /* complete */,
                 EuiccService.RESULT_OK, 0 /* resolvableError */, "whatever" /* callingPackage */);
+
+        verify(mMockConnector).downloadSubscription(anyInt(), anyInt(),
+                any(), eq(false), anyBoolean(), mBundleCaptor.capture(), any());
+        assertEquals(
+                "whatever",
+                mBundleCaptor.getValue().getString(EuiccService.EXTRA_PACKAGE_NAME));
         verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
         assertTrue(mController.mCalledRefreshSubscriptionsAndSendResult);
     }
@@ -857,7 +876,6 @@
     @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
     public void testDownloadSubscription_noAdminPermission()
             throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         setHasWriteEmbeddedPermission(false);
         setHasManageDevicePolicyManagedSubscriptionsPermission(false);
         setUpUiccSlotData();
@@ -883,7 +901,6 @@
     @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
     public void testDownloadSubscription_adminPermission()
             throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         setHasManageDevicePolicyManagedSubscriptionsPermission(true);
         setHasWriteEmbeddedPermission(false);
 
@@ -898,7 +915,6 @@
     @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
     public void testDownloadSubscription_adminPermission_usingSwitchAfterDownload_fails()
             throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         setHasWriteEmbeddedPermission(false);
         setHasManageDevicePolicyManagedSubscriptionsPermission(true);
         setUpUiccSlotData();
@@ -924,7 +940,6 @@
     @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
     public void testDownloadSubscription_profileOwner_usingSwitchAfterDownload_fails()
             throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         setHasWriteEmbeddedPermission(false);
         setHasManageDevicePolicyManagedSubscriptionsPermission(true);
         setUpUiccSlotData();
@@ -953,7 +968,6 @@
     @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
     public void testDownloadSubscription_orgOwnedProfileOwner_usingSwitchAfterDownload_success()
             throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         setHasWriteEmbeddedPermission(false);
         setHasManageDevicePolicyManagedSubscriptionsPermission(true);
         setUpUiccSlotData();
@@ -979,7 +993,6 @@
     @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
     public void testDownloadSubscription_deviceOwner_usingSwitchAfterDownload_success()
             throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         setHasWriteEmbeddedPermission(false);
         setHasManageDevicePolicyManagedSubscriptionsPermission(true);
         setUpUiccSlotData();
@@ -1005,7 +1018,6 @@
     @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
     public void testDownloadSubscription_onlyAdminManagedAllowed_callerNotAdmin_error()
             throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         setHasManageDevicePolicyManagedSubscriptionsPermission(false);
         setHasWriteEmbeddedPermission(true);
         doReturn(true)
@@ -1021,27 +1033,8 @@
 
     @Test
     @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
-    public void testDownloadSubscription_onlyAdminManagedAllowed_callerNotAdmin_disabled_success()
-            throws Exception {
-        mSetFlagsRule.disableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
-        setHasManageDevicePolicyManagedSubscriptionsPermission(false);
-        setHasWriteEmbeddedPermission(true);
-        doReturn(true)
-                .when(mUserManager)
-                .hasUserRestriction(UserManager.DISALLOW_SIM_GLOBALLY);
-
-        callDownloadSubscription(SUBSCRIPTION, false /* switchAfterDownload */, true /* complete */,
-                EuiccService.RESULT_OK, 0 /* resolvableError */, "whatever" /* callingPackage */);
-
-        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
-        assertTrue(mController.mCalledRefreshSubscriptionsAndSendResult);
-    }
-
-    @Test
-    @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
     public void testDownloadSubscription_onlyAdminManagedAllowed_callerIsAdmin_success()
             throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         setHasManageDevicePolicyManagedSubscriptionsPermission(true);
         setHasWriteEmbeddedPermission(false);
         doReturn(true)
@@ -1102,7 +1095,6 @@
 
     @Test
     public void testDeleteSubscription_adminOwned_success() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         setHasWriteEmbeddedPermission(false);
         setHasManageDevicePolicyManagedSubscriptionsPermission(true);
         String callingPackage = "whatever";
@@ -1126,33 +1118,7 @@
     }
 
     @Test
-    public void testDeleteSubscription_adminOwned_featureDisabled_success() throws Exception {
-        mSetFlagsRule.disableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
-        setHasWriteEmbeddedPermission(true);
-        setHasManageDevicePolicyManagedSubscriptionsPermission(false);
-        String callingPackage = "whatever";
-        SubscriptionInfo subInfo1 = new SubscriptionInfo.Builder()
-                .setId(SUBSCRIPTION_ID)
-                .setEmbedded(true)
-                .setIccId(ICC_ID)
-                .setCardId(CARD_ID)
-                .setPortIndex(TelephonyManager.DEFAULT_PORT_INDEX)
-                .setGroupOwner(callingPackage)
-                .build();
-        ArrayList<SubscriptionInfo> subInfos = new ArrayList<>(Arrays.asList(subInfo1));
-        when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(subInfos);
-
-        callDeleteSubscription(
-                SUBSCRIPTION_ID, ICC_ID, true /* complete */,
-                0 /* result */, callingPackage /* callingPackage */);
-
-        verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK,
-                0 /* detailedCode */);
-    }
-
-    @Test
     public void testDeleteSubscription_adminOwned_noPermissions_error() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         setHasWriteEmbeddedPermission(false);
         setHasManageDevicePolicyManagedSubscriptionsPermission(false);
         String callingPackage = "whatever";
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccSessionTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccSessionTest.java
new file mode 100644
index 0000000..f91088c
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccSessionTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.euicc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.never;
+
+import android.app.PendingIntent;
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.service.euicc.DownloadSubscriptionResult;
+import android.service.euicc.EuiccService;
+import android.service.euicc.GetDefaultDownloadableSubscriptionListResult;
+import android.service.euicc.GetDownloadableSubscriptionMetadataResult;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccAccessRule;
+import android.telephony.UiccCardInfo;
+import android.telephony.UiccPortInfo;
+import android.telephony.euicc.DownloadableSubscription;
+import android.telephony.euicc.EuiccInfo;
+import android.telephony.euicc.EuiccManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.euicc.EuiccConnector.GetOtaStatusCommandCallback;
+import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback;
+import com.android.internal.telephony.uicc.euicc.apdu.ApduSender;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.uicc.UiccSlot;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.mockito.stubbing.Stubber;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class EuiccSessionTest extends TelephonyTest {
+    @Rule
+    public final TestRule compatChangeRule = new PlatformCompatChangeRule();
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    @Rule
+    public final MockitoRule rule = MockitoJUnit.rule();
+
+    private static final String SESSION_ID_1 = "SESSION_ID_1";
+    private static final String SESSION_ID_2 = "SESSION_ID_2";
+
+    private EuiccSession mEuiccSession;
+    @Mock private ApduSender mApduSender;
+
+    @Before
+    public void setUp() throws Exception {
+        mEuiccSession = new EuiccSession();
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER)
+    public void startOneSession_featureDisabled_noop() throws Exception {
+        mEuiccSession.startSession(SESSION_ID_1);
+        mEuiccSession.noteChannelOpen(mApduSender);
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+
+        mEuiccSession.endSession(SESSION_ID_1);
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+        verify(mApduSender, never()).closeAnyOpenChannel();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER)
+    public void startOneSession_endSession_hasSession() throws Exception {
+        mEuiccSession.startSession(SESSION_ID_1);
+        mEuiccSession.noteChannelOpen(mApduSender);
+
+        assertThat(mEuiccSession.hasSession()).isTrue();
+
+        mEuiccSession.endSession(SESSION_ID_2);
+
+        assertThat(mEuiccSession.hasSession()).isTrue();
+        verify(mApduSender, never()).closeAnyOpenChannel();
+
+        mEuiccSession.endSession(SESSION_ID_1);
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+        verify(mApduSender).closeAnyOpenChannel();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER)
+    public void startTwoSession_endSession_hasSession() throws Exception {
+        mEuiccSession.startSession(SESSION_ID_1);
+        mEuiccSession.noteChannelOpen(mApduSender);
+        mEuiccSession.startSession(SESSION_ID_2);
+
+        assertThat(mEuiccSession.hasSession()).isTrue();
+
+        mEuiccSession.endSession(SESSION_ID_1);
+        verify(mApduSender, never()).closeAnyOpenChannel();
+
+        assertThat(mEuiccSession.hasSession()).isTrue();
+
+        mEuiccSession.endSession(SESSION_ID_2);
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+        verify(mApduSender).closeAnyOpenChannel();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER)
+    public void noteChannelOpen_noSession_noop() throws Exception {
+        // noteChannelOpen called without a session started
+        mEuiccSession.noteChannelOpen(mApduSender);
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+
+        mEuiccSession.endSession(SESSION_ID_1);
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+        verify(mApduSender, never()).closeAnyOpenChannel();
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index c261afe..e542a41 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -44,6 +44,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.AsyncResult;
@@ -109,6 +110,7 @@
     private static final String RAW_TABLE_NAME = "raw";
     private static final Uri sRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI,
             RAW_TABLE_NAME);
+    private static final UserHandle MOCKED_MAIN_USER = UserHandle.of(0);
 
     private final String mMessageBody = "This is the message body of a single-part message";
     private final String mMessageBodyPart1 = "This is the first part of a multi-part message";
@@ -165,15 +167,19 @@
         mSmsFilter = Mockito.mock(InboundSmsHandler.SmsFilter.class);
         mSmsFilter2 = Mockito.mock(InboundSmsHandler.SmsFilter.class);
 
+        when(mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser()).thenReturn(true);
         doReturn(true).when(mTelephonyManager).getSmsReceiveCapableForPhone(anyInt(), anyBoolean());
         doReturn(true).when(mSmsStorageMonitor).isStorageAvailable();
 
         UserManager userManager = (UserManager)mContext.getSystemService(Context.USER_SERVICE);
         doReturn(true).when(userManager).isUserUnlocked();
+        doReturn(true).when(userManager).isUserUnlocked(any(UserHandle.class));
         doReturn(true).when(userManager).isUserRunning(any(UserHandle.class));
 
         List<UserHandle> userHandles = new ArrayList();
-        userHandles.add(UserHandle.SYSTEM);
+        userHandles.add(MOCKED_MAIN_USER);
+
+        doReturn(MOCKED_MAIN_USER).when(userManager).getMainUser();
         doReturn(userHandles).when(userManager).getUserHandles(anyBoolean());
 
         mSmsMessage.mWrappedSmsMessage = mGsmSmsMessage;
@@ -204,7 +210,7 @@
                 Telephony.Sms.CONTENT_URI.getAuthority(), mContentProvider);
 
         mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(mContext,
-                mSmsStorageMonitor, mPhone, mTestableLooper.getLooper());
+                mSmsStorageMonitor, mPhone, mTestableLooper.getLooper(), mFeatureFlags);
         mSmsFilters = new ArrayList<>();
         mSmsFilters.add(mSmsFilter);
         mSmsFilters.add(mSmsFilter2);
@@ -331,6 +337,33 @@
 
     @Test
     @MediumTest
+    public void testNewSMSProcessedThroughMainUser() {
+        transitionFromStartupToIdle();
+        UserHandle mockedMainSecUser = UserHandle.of(10);
+
+        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        List<UserHandle> userHandles = new ArrayList();
+        userHandles.add(mockedMainSecUser);
+        doReturn(mockedMainSecUser).when(userManager).getMainUser();
+        doReturn(userHandles).when(userManager).getUserHandles(anyBoolean());
+
+        // send new SMS to state machine and verify that it triggers SMS_DELIVER_ACTION
+        // and context being used is from the MAIN user
+        sendNewSms();
+
+        // Verify that the context created to send the broadcast is created from the MAIN user.
+        try {
+            verify(mContext).createPackageContextAsUser(any(String.class),
+                    any(Integer.class), eq(mockedMainSecUser));
+        } catch (PackageManager.NameNotFoundException e) {
+            fail(e.toString());
+        }
+
+        verifySmsIntentBroadcasts(0);
+    }
+
+    @Test
+    @MediumTest
     public void testNewSmsFromBlockedNumber_noBroadcastsSent() {
         String blockedNumber = "1234567890";
         mFakeBlockedNumberContentProvider.mBlockedNumbers.add(blockedNumber);
@@ -352,6 +385,7 @@
         // user locked
         UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         doReturn(false).when(userManager).isUserUnlocked();
+        doReturn(false).when(userManager).isUserUnlocked(any(UserHandle.class));
 
         transitionFromStartupToIdle();
 
@@ -366,10 +400,19 @@
         // New message notification should be shown.
         NotificationManager notificationManager =
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        verify(notificationManager).notify(
-                eq(InboundSmsHandler.NOTIFICATION_TAG),
-                eq(InboundSmsHandler.NOTIFICATION_ID_NEW_MESSAGE),
-                any(Notification.class));
+        if (mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser()) {
+            verify(notificationManager).notifyAsUser(
+                    eq(InboundSmsHandler.NOTIFICATION_TAG),
+                    eq(InboundSmsHandler.NOTIFICATION_ID_NEW_MESSAGE),
+                    any(Notification.class),
+                    eq(MOCKED_MAIN_USER));
+        } else {
+            verify(notificationManager).notify(
+                    eq(InboundSmsHandler.NOTIFICATION_TAG),
+                    eq(InboundSmsHandler.NOTIFICATION_ID_NEW_MESSAGE),
+                    any(Notification.class));
+
+        }
     }
 
     @Test
@@ -381,6 +424,7 @@
         // user locked
         UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         doReturn(false).when(userManager).isUserUnlocked();
+        doReturn(false).when(userManager).isUserUnlocked(any(UserHandle.class));
 
         transitionFromStartupToIdle();
 
@@ -395,10 +439,19 @@
         // No new message notification should be shown.
         NotificationManager notificationManager =
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        verify(notificationManager, never()).notify(
-                eq(InboundSmsHandler.NOTIFICATION_TAG),
-                eq(InboundSmsHandler.NOTIFICATION_ID_NEW_MESSAGE),
-                any(Notification.class));
+        if (mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser()) {
+            verify(notificationManager, never()).notifyAsUser(
+                    eq(InboundSmsHandler.NOTIFICATION_TAG),
+                    eq(InboundSmsHandler.NOTIFICATION_ID_NEW_MESSAGE),
+                    any(Notification.class),
+                    eq(MOCKED_MAIN_USER));
+        } else {
+            verify(notificationManager, never()).notify(
+                    eq(InboundSmsHandler.NOTIFICATION_TAG),
+                    eq(InboundSmsHandler.NOTIFICATION_ID_NEW_MESSAGE),
+                    any(Notification.class));
+
+        }
     }
 
     @Test
@@ -1042,16 +1095,23 @@
         mContentProvider.insert(sRawUri, mInboundSmsTracker.getContentValues());
 
         // user locked
-        UserManager userManager = (UserManager)mContext.getSystemService(Context.USER_SERVICE);
+        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         doReturn(false).when(userManager).isUserUnlocked();
+        doReturn(false).when(userManager).isUserUnlocked(any(UserHandle.class));
 
-        SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
+        SmsBroadcastUndelivered.initialize(
+                mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler, mFeatureFlags);
 
-        // verify that a broadcast receiver is registered for current user (user == null) based on
-        // implementation in ContextFixture. registerReceiver may be called more than once (for
-        // example by GsmInboundSmsHandler if TEST_MODE is true)
-        verify(mContext, atLeastOnce()).registerReceiver(any(BroadcastReceiver.class),
-                any(IntentFilter.class));
+        if (mFeatureFlags.smsMmsDeliverBroadcastsRedirectToMainUser()) {
+            verify(mContext).registerReceiverAsUser(any(BroadcastReceiver.class),
+                    any(UserHandle.class), any(IntentFilter.class), any(), any());
+        } else {
+            // verify that a broadcast receiver is registered for current user (user == null) based
+            // on implementation in ContextFixture. registerReceiver may be called more than once
+            // (for example by GsmInboundSmsHandler if TEST_MODE is true)
+            verify(mContext, atLeastOnce()).registerReceiver(any(BroadcastReceiver.class),
+                    any(IntentFilter.class));
+        }
 
         // wait for ScanRawTableThread
         waitForMs(100);
@@ -1062,7 +1122,10 @@
 
         // when user unlocks the device, the message in db should be broadcast
         doReturn(true).when(userManager).isUserUnlocked();
-        mContext.sendBroadcast(new Intent(Intent.ACTION_USER_UNLOCKED));
+        doReturn(true).when(userManager).isUserUnlocked(any(UserHandle.class));
+        mContext.sendBroadcast(
+                new Intent(Intent.ACTION_USER_UNLOCKED)
+                        .putExtra(Intent.EXTRA_USER_HANDLE, MOCKED_MAIN_USER.getIdentifier()));
         // wait for ScanRawTableThread
         waitForMs(100);
         processAllMessages();
@@ -1097,7 +1160,8 @@
         // add a fake entry to db
         mContentProvider.insert(sRawUri, mInboundSmsTracker.getContentValues());
 
-        SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
+        SmsBroadcastUndelivered.initialize(
+                mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler, mFeatureFlags);
 
         // wait for ScanRawTableThread
         waitForMs(100);
@@ -1112,7 +1176,8 @@
     @MediumTest
     public void testBroadcastUndeliveredDeleted() throws Exception {
         replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null);
-        SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
+        SmsBroadcastUndelivered.initialize(
+                mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler, mFeatureFlags);
         mInboundSmsTracker = new InboundSmsTracker(
                 mContext,
                 mSmsPdu, /* pdu */
@@ -1138,7 +1203,9 @@
         mContentProvider.insert(sRawUri, rawSms);
 
         //when user unlocks the device, broadcast should not be sent for new message
-        mContext.sendBroadcast(new Intent(Intent.ACTION_USER_UNLOCKED));
+        mContext.sendBroadcast(
+                new Intent(Intent.ACTION_USER_UNLOCKED)
+                        .putExtra(Intent.EXTRA_USER_HANDLE, MOCKED_MAIN_USER.getIdentifier()));
         // wait for ScanRawTableThread
         waitForMs(100);
         processAllMessages();
@@ -1165,7 +1232,8 @@
                 when(mTelephonyComponentFactory).makeInboundSmsTracker(any(Context.class),
                         any(Cursor.class), anyBoolean());
 
-        SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
+        SmsBroadcastUndelivered.initialize(
+                mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler, mFeatureFlags);
         // wait for ScanRawTableThread
         waitForMs(100);
         processAllMessages();
@@ -1183,7 +1251,8 @@
         mContentProvider.insert(sRawUri, mInboundSmsTracker.getContentValues());
         mContentProvider.insert(sRawUri, mInboundSmsTrackerSub1.getContentValues());
 
-        SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
+        SmsBroadcastUndelivered.initialize(
+                mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler, mFeatureFlags);
         // wait for ScanRawTableThread
         waitForMs(100);
         processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
index 8374daa..8a10fde 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -45,6 +45,7 @@
 import android.content.pm.ServiceInfo;
 import android.location.Country;
 import android.location.CountryDetector;
+import android.os.Binder;
 import android.os.HandlerThread;
 import android.os.Message;
 import android.os.RemoteException;
@@ -114,6 +115,7 @@
 
     private GsmSMSDispatcher mGsmSmsDispatcher;
     private GsmSmsDispatcherTestHandler mGsmSmsDispatcherTestHandler;
+    private int mCallingUserId;
 
     private class GsmSmsDispatcherTestHandler extends HandlerThread {
 
@@ -149,6 +151,7 @@
         waitUntilReady();
         mGsmSmsDispatcher = new GsmSMSDispatcher(mPhone, mSmsDispatchersController,
                 mGsmInboundSmsHandler);
+        mCallingUserId = Binder.getCallingUserHandle().getIdentifier();
         processAllMessages();
     }
 
@@ -177,7 +180,7 @@
                 .thenReturn(new Country("US", Country.COUNTRY_SOURCE_SIM));
 
         mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms",
-                null, null, null, null, false, -1, false, -1, false, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
 
         verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(), any(Message.class));
         // Blocked number provider is notified about the emergency contact asynchronously.
@@ -197,7 +200,7 @@
 
         mGsmSmsDispatcher.sendText(
                 getEmergencyNumberFromSystemPropertiesOrDefault(), "121" /*scAddr*/, "test sms",
-                null, null, null, null, false, -1, false, -1, false, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
 
         verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(), any(Message.class));
         // Blocked number provider is notified about the emergency contact asynchronously.
@@ -245,7 +248,7 @@
         // send invalid dest address: +
         mReceivedTestIntent = false;
         mGsmSmsDispatcher.sendText("+", "222" /*scAddr*/, TAG,
-                pendingIntent, null, null, null, false, -1, false, -1, false, 0L);
+                pendingIntent, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
         waitForMs(500);
         verify(mSimulatedCommandsVerifier, times(0)).sendSMS(anyString(), anyString(),
                 any(Message.class));
@@ -310,7 +313,7 @@
         // send SMS and check sentIntent
         mReceivedTestIntent = false;
         mGsmSmsDispatcher.sendMultipartText("+123" /*destAddr*/, "222" /*scAddr*/, parts,
-                sentIntents, null, null, null, false, -1, false, -1, 0L);
+                sentIntents, null, null, null, mCallingUserId, false, -1, false, -1, 0L);
 
         waitForMs(500);
         synchronized (mLock) {
@@ -390,7 +393,7 @@
         mReceivedTestIntent = false;
 
         mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms",
-                pendingIntent, null, null, null, false, -1, false, -1, false, 0L);
+                pendingIntent, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
         processAllMessages();
         synchronized (mLock) {
             if (!mReceivedTestIntent) {
@@ -414,7 +417,7 @@
         mGsmSmsDispatcher.mCarrierMessagingTimeout = 100;
 
         mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms",
-                null, null, null, null, false, -1, false, -1, false, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
         // wait for timeout
         waitForMs(150);
         verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(), any(Message.class));
@@ -428,7 +431,7 @@
         mockUiccWithCarrierApp();
 
         mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms",
-                null, null, null, null, false, -1, false, -1, false, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
         processAllMessages();
         verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(), any(Message.class));
     }
@@ -452,7 +455,8 @@
         sentIntents.add(sentIntent2);
 
         mGsmSmsDispatcher.sendMultipartText("6501002000" /*destAddr*/, "222" /*scAddr*/, parts,
-                withSentIntents ? sentIntents : null, null, null, null, false, -1, false, -1, 0L);
+                withSentIntents ? sentIntents : null, null, null, null, mCallingUserId,
+                false, -1, false, -1, 0L);
     }
 
     @Test
@@ -524,7 +528,7 @@
         }
 
         mGsmSmsDispatcher.sendText("111", "222" /*scAddr*/, TAG,
-                null, null, null, null, false, -1, false, -1, false, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
 
         ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
         verify(mSimulatedCommandsVerifier).sendSMS(anyString(), pduCaptor.capture(),
@@ -545,7 +549,7 @@
             messageRef += parts.size();
         }
         mGsmSmsDispatcher.sendMultipartText("6501002000" /*destAddr*/, "222" /*scAddr*/, parts,
-                null, null, null, null, false, -1, false, -1, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, 0L);
         waitForMs(150);
         ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
 
@@ -561,14 +565,14 @@
     @Test
     public void testSendTextWithMessageRefNegativeBoundaryCondition() throws Exception {
         mIsimUiccRecords = new IsimUiccRecords(mUiccCardApplication3gpp, mContext,
-                mSimulatedCommands);
+                mSimulatedCommands, mFeatureFlags);
         doReturn(mIsimUiccRecords).when(mPhone).getIccRecords();
         Message msg = mGsmSmsDispatcher.obtainMessage(17);
         mPhone.getIccRecords().setSmssTpmrValue(-1, msg);
         mSubscriptionManagerService.setLastUsedTPMessageReference(mPhone.getSubId(), -1);
 
         mGsmSmsDispatcher.sendText("111", "222" /*scAddr*/, TAG,
-                null, null, null, null, false, -1, false, -1, false, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
 
         ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
         verify(mSimulatedCommandsVerifier).sendSMS(anyString(), pduCaptor.capture(),
@@ -580,12 +584,12 @@
     @Test
     public void testSendTextWithMessageRefMaxBoundaryCondition() throws Exception {
         mIsimUiccRecords = new IsimUiccRecords(mUiccCardApplication3gpp, mContext,
-                mSimulatedCommands);
+                mSimulatedCommands, mFeatureFlags);
         doReturn(mIsimUiccRecords).when(mPhone).getIccRecords();
         Message msg = mGsmSmsDispatcher.obtainMessage(17);
         mPhone.getIccRecords().setSmssTpmrValue(255, msg);
         mGsmSmsDispatcher.sendText("111", "222" /*scAddr*/, TAG,
-                null, null, null, null, false, -1, false, -1, false, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L);
 
         ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
         verify(mSimulatedCommandsVerifier).sendSMS(anyString(), pduCaptor.capture(),
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsNrSaModeHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsNrSaModeHandlerTest.java
index 3b362b1..8cf422e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsNrSaModeHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsNrSaModeHandlerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.telephony.imsphone;
 
-import static android.telephony.CarrierConfigManager.CARRIER_NR_AVAILABILITY_NSA;
 import static android.telephony.CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA;
 import static android.telephony.CarrierConfigManager.Ims.KEY_NR_SA_DISABLE_POLICY_INT;
 import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_NONE;
@@ -24,6 +23,7 @@
 import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_WFC_ESTABLISHED;
 import static android.telephony.CarrierConfigManager.Ims.NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED;
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
 
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.os.Handler;
@@ -55,7 +56,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Arrays;
 import java.util.Set;
 
 @RunWith(AndroidTestingRunner.class)
@@ -69,7 +69,7 @@
     private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
     private Handler mPreciseCallStateHandler;
 
-    private Phone mPassthroughPhone;
+    private Phone mDefaultPhone;
 
     @Mock private ImsPhoneCall mForegroundCall;
     @Mock private ImsPhoneCall mBackgroundCall;
@@ -77,8 +77,7 @@
     private Call.State mIdleState = ImsPhoneCall.State.IDLE;
 
     private int mAnyInt = 0;
-    private final Set<String> mFeatureTags =
-            new ArraySet<String>(Arrays.asList(ImsNrSaModeHandler.MMTEL_FEATURE_TAG));
+    private final Set<String> mFeatureTags = new ArraySet<String>();
 
     @Before
     public void setUp() throws Exception {
@@ -96,12 +95,12 @@
         doReturn(mContextFixture.getCarrierConfigBundle()).when(mCarrierConfigManager)
                 .getConfigForSubId(anyInt(), any());
 
-        mPassthroughPhone = new GsmCdmaPhone(
+        mDefaultPhone = new GsmCdmaPhone(
                 mContext, mSimulatedCommands, mNotifier, true, 0,
                 PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory,
                 (c, p) -> mImsManager, mFeatureFlags);
 
-        doReturn(mPassthroughPhone).when(mImsPhone).getDefaultPhone();
+        doReturn(mDefaultPhone).when(mImsPhone).getDefaultPhone();
 
         doReturn(mForegroundCall).when(mImsPhone).getForegroundCall();
         doReturn(mBackgroundCall).when(mImsPhone).getBackgroundCall();
@@ -140,19 +139,19 @@
     }
 
     @Test
-    public void testOnImsRegisteredWithSaDisablePolicyNone() {
+    public void testOnImsRegisteredWithNrSaCapabilityAndVoiceCapabilityAndSaDisablePolicyNone() {
         mContextFixture.getCarrierConfigBundle().putInt(
                 KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_NONE);
         mContextFixture.getCarrierConfigBundle().putIntArray(
                 KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
 
         mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
-
-        mTestImsNrSaModeHandler.setVowifiRegStatus(false);
+        mTestImsNrSaModeHandler.updateImsCapability(CAPABILITY_TYPE_VOICE);
+        mTestImsNrSaModeHandler.setWifiRegStatus(false);
 
         mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
 
-        assertFalse(mTestImsNrSaModeHandler.isVowifiRegistered());
+        assertFalse(mTestImsNrSaModeHandler.isWifiRegistered());
     }
 
     @Test
@@ -163,17 +162,24 @@
                 KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
 
         mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+        mTestImsNrSaModeHandler.updateImsCapability(CAPABILITY_TYPE_VOICE);
 
         verify(mImsPhone).registerForPreciseCallStateChanged(any(), anyInt(), any());
 
         mSimulatedCommands.setN1ModeEnabled(false, null);
-        mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+        mTestImsNrSaModeHandler.setNrSaDisabledForWfc(true);
+        mTestImsNrSaModeHandler.setWifiRegStatus(true);
         mTestImsNrSaModeHandler.setImsCallStatus(true);
 
         mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_NONE, mFeatureTags);
 
-        assertFalse(mTestImsNrSaModeHandler.isVowifiRegistered());
         assertTrue(mSimulatedCommands.isN1ModeEnabled());
+        assertFalse(mTestImsNrSaModeHandler.isWifiRegistered());
+
+        mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
+
+        assertFalse(mSimulatedCommands.isN1ModeEnabled());
+        assertTrue(mTestImsNrSaModeHandler.isWifiRegistered());
     }
 
     @Test
@@ -185,54 +191,42 @@
                 KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
 
         mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+        mTestImsNrSaModeHandler.updateImsCapability(CAPABILITY_TYPE_VOICE);
 
         verify(mImsPhone).registerForPreciseCallStateChanged(any(), anyInt(), any());
 
         mSimulatedCommands.setN1ModeEnabled(true, null);
-        mTestImsNrSaModeHandler.setVowifiRegStatus(false);
+        mTestImsNrSaModeHandler.setWifiRegStatus(false);
         mTestImsNrSaModeHandler.setImsCallStatus(true);
         mSimulatedCommands.setVonrEnabled(true);
 
         mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
         processAllMessages();
 
-        assertTrue(mTestImsNrSaModeHandler.isVowifiRegistered());
         assertTrue(mSimulatedCommands.isN1ModeEnabled());
+        assertTrue(mTestImsNrSaModeHandler.isWifiRegistered());
 
         mSimulatedCommands.setN1ModeEnabled(true, null);
-        mTestImsNrSaModeHandler.setVowifiRegStatus(false);
+        mTestImsNrSaModeHandler.setWifiRegStatus(false);
         mTestImsNrSaModeHandler.setImsCallStatus(true);
         mSimulatedCommands.setVonrEnabled(false);
 
         mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
         processAllMessages();
 
-        assertTrue(mTestImsNrSaModeHandler.isVowifiRegistered());
         assertFalse(mSimulatedCommands.isN1ModeEnabled());
+        assertTrue(mTestImsNrSaModeHandler.isWifiRegistered());
 
         mSimulatedCommands.setN1ModeEnabled(true, null);
-        mTestImsNrSaModeHandler.setVowifiRegStatus(false);
+        mTestImsNrSaModeHandler.setWifiRegStatus(false);
         mTestImsNrSaModeHandler.setImsCallStatus(true);
-        mSimulatedCommands.setVonrEnabled(false);
+        mSimulatedCommands.setVonrEnabled(true);
 
-        mFeatureTags.remove(ImsNrSaModeHandler.MMTEL_FEATURE_TAG);
-        mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
+        mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_NONE, mFeatureTags);
         processAllMessages();
 
-        assertFalse(mTestImsNrSaModeHandler.isVowifiRegistered());
         assertTrue(mSimulatedCommands.isN1ModeEnabled());
-
-        mSimulatedCommands.setN1ModeEnabled(true, null);
-        mTestImsNrSaModeHandler.setVowifiRegStatus(false);
-        mTestImsNrSaModeHandler.setImsCallStatus(true);
-        mSimulatedCommands.setVonrEnabled(false);
-
-        mFeatureTags.add(ImsNrSaModeHandler.MMTEL_FEATURE_TAG);
-        mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
-        processAllMessages();
-
-        assertTrue(mTestImsNrSaModeHandler.isVowifiRegistered());
-        assertFalse(mSimulatedCommands.isN1ModeEnabled());
+        assertFalse(mTestImsNrSaModeHandler.isWifiRegistered());
     }
 
     @Test
@@ -243,26 +237,50 @@
                 KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
 
         mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+        mTestImsNrSaModeHandler.updateImsCapability(CAPABILITY_TYPE_VOICE);
+
+        verify(mImsPhone).unregisterForPreciseCallStateChanged(mTestImsNrSaModeHandler);
 
         mSimulatedCommands.setN1ModeEnabled(true, null);
-        mTestImsNrSaModeHandler.setVowifiRegStatus(false);
+        mTestImsNrSaModeHandler.setWifiRegStatus(false);
 
         mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
 
-        assertTrue(mTestImsNrSaModeHandler.isVowifiRegistered());
         assertFalse(mSimulatedCommands.isN1ModeEnabled());
+        assertTrue(mTestImsNrSaModeHandler.isWifiRegistered());
 
         mSimulatedCommands.setN1ModeEnabled(false, null);
-        mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+        mTestImsNrSaModeHandler.setWifiRegStatus(true);
 
         mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_NONE, mFeatureTags);
 
-        assertFalse(mTestImsNrSaModeHandler.isVowifiRegistered());
         assertTrue(mSimulatedCommands.isN1ModeEnabled());
+        assertFalse(mTestImsNrSaModeHandler.isWifiRegistered());
     }
 
     @Test
-    public void testOnImsUnregisteredDoNothingIfNotVowifiRegNoti() {
+    public void testOnImsRegisteredWithoutNrSaCapabilityWithSaDisablePolicyVowifiRegistered() {
+        mContextFixture.getCarrierConfigBundle().putInt(
+                KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED);
+        mContextFixture.getCarrierConfigBundle().putIntArray(
+                KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{});
+
+        mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+        mTestImsNrSaModeHandler.updateImsCapability(CAPABILITY_TYPE_VOICE);
+
+        verify(mImsPhone, times(0)).unregisterForPreciseCallStateChanged(mTestImsNrSaModeHandler);
+
+        mSimulatedCommands.setN1ModeEnabled(true, null);
+        mTestImsNrSaModeHandler.setWifiRegStatus(false);
+
+        mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
+
+        assertTrue(mSimulatedCommands.isN1ModeEnabled());
+        assertFalse(mTestImsNrSaModeHandler.isWifiRegistered());
+    }
+
+    @Test
+    public void testOnImsRegisteredWithoutVoiceCapabilityWithSaDisablePolicyVowifiRegistered() {
         mContextFixture.getCarrierConfigBundle().putInt(
                 KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED);
         mContextFixture.getCarrierConfigBundle().putIntArray(
@@ -270,37 +288,100 @@
 
         mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
 
-        mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+        verify(mImsPhone).unregisterForPreciseCallStateChanged(mTestImsNrSaModeHandler);
 
-        mTestImsNrSaModeHandler.onImsUnregistered(REGISTRATION_TECH_NONE);
+        mSimulatedCommands.setN1ModeEnabled(true, null);
+        mTestImsNrSaModeHandler.setWifiRegStatus(false);
 
-        assertTrue(mTestImsNrSaModeHandler.isVowifiRegistered());
+        mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_IWLAN, mFeatureTags);
+
+        assertTrue(mSimulatedCommands.isN1ModeEnabled());
+        assertTrue(mTestImsNrSaModeHandler.isWifiRegistered());
     }
 
     @Test
-    public void testOnImsUnregisteredWithSaDisablePolicyVowifiRegistered() {
+    public void testOnImsUnregisteredWithoutNrSaCapability() {
         mContextFixture.getCarrierConfigBundle().putInt(
                 KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED);
         mContextFixture.getCarrierConfigBundle().putIntArray(
-                KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+                KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{});
 
         mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
 
-        mSimulatedCommands.setN1ModeEnabled(false, null);
-        mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+        mTestImsNrSaModeHandler.setWifiRegStatus(true);
 
         mTestImsNrSaModeHandler.onImsUnregistered(REGISTRATION_TECH_IWLAN);
 
-        assertFalse(mTestImsNrSaModeHandler.isVowifiRegistered());
-        assertTrue(mSimulatedCommands.isN1ModeEnabled());
+        assertTrue(mTestImsNrSaModeHandler.isWifiRegistered());
+    }
+
+
+    @Test
+    public void testOnImsUnregisteredWithSaDisablePolicyNone() {
+        mContextFixture.getCarrierConfigBundle().putInt(
+                KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_NONE);
+        mContextFixture.getCarrierConfigBundle().putIntArray(
+                KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+        mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+        mTestImsNrSaModeHandler.setWifiRegStatus(true);
+
+        mTestImsNrSaModeHandler.onImsUnregistered(REGISTRATION_TECH_IWLAN);
+
+        assertTrue(mTestImsNrSaModeHandler.isWifiRegistered());
+    }
+
+    @Test
+    public void testOnImsUnregisteredWithoutRadioTechIwlan() {
+        mContextFixture.getCarrierConfigBundle().putInt(
+                KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED);
+        mContextFixture.getCarrierConfigBundle().putIntArray(
+                KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+        mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+        mTestImsNrSaModeHandler.setWifiRegStatus(true);
+
+        mTestImsNrSaModeHandler.onImsUnregistered(NR_SA_DISABLE_POLICY_NONE);
+
+        assertTrue(mTestImsNrSaModeHandler.isWifiRegistered());
+    }
+
+    @Test
+    public void testOnImsUnregisteredWithoutWifiResistring() {
+        mContextFixture.getCarrierConfigBundle().putInt(
+                KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED);
+        mContextFixture.getCarrierConfigBundle().putIntArray(
+                KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+        mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+
+        mTestImsNrSaModeHandler.setWifiRegStatus(false);
+
+        mTestImsNrSaModeHandler.onImsUnregistered(REGISTRATION_TECH_IWLAN);
+
+        assertFalse(mTestImsNrSaModeHandler.isWifiRegistered());
+    }
+
+    @Test
+    public void testOnImsUnregisteredWithNrSaCapabilityAndVoiceCapabilityAndRadioTechIwlan() {
+        mContextFixture.getCarrierConfigBundle().putInt(
+                KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_VOWIFI_REGISTERED);
+        mContextFixture.getCarrierConfigBundle().putIntArray(
+                KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+        mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+        mTestImsNrSaModeHandler.updateImsCapability(CAPABILITY_TYPE_VOICE);
 
         mSimulatedCommands.setN1ModeEnabled(false, null);
-        mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+        mTestImsNrSaModeHandler.setNrSaDisabledForWfc(true);
+        mTestImsNrSaModeHandler.setWifiRegStatus(true);
 
-        mTestImsNrSaModeHandler.onImsUnregistered(REGISTRATION_TECH_NONE);
+        mTestImsNrSaModeHandler.onImsUnregistered(REGISTRATION_TECH_IWLAN);
 
-        assertTrue(mTestImsNrSaModeHandler.isVowifiRegistered());
-        assertFalse(mSimulatedCommands.isN1ModeEnabled());
+        assertTrue(mSimulatedCommands.isN1ModeEnabled());
+        assertFalse(mTestImsNrSaModeHandler.isWifiRegistered());
     }
 
     @Test
@@ -311,12 +392,13 @@
                 KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
 
         mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+        mTestImsNrSaModeHandler.updateImsCapability(CAPABILITY_TYPE_VOICE);
 
         verify(mImsPhone).registerForPreciseCallStateChanged(
                 mPreciseCallStateHandlerCaptor.capture(), anyInt(), any());
         mPreciseCallStateHandler = mPreciseCallStateHandlerCaptor.getValue();
 
-        mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+        mTestImsNrSaModeHandler.setWifiRegStatus(true);
         mSimulatedCommands.setN1ModeEnabled(true, null);
 
         mPreciseCallStateHandler.handleMessage(mPreciseCallStateHandler.obtainMessage(101));
@@ -324,38 +406,55 @@
         assertTrue(mTestImsNrSaModeHandler.isImsCallOngoing());
         assertFalse(mSimulatedCommands.isN1ModeEnabled());
 
-        mTestImsNrSaModeHandler.setVowifiRegStatus(false);
-        mSimulatedCommands.setN1ModeEnabled(true, null);
-
-        doReturn(mActiveState).when(mForegroundCall).getState();
-        doReturn(mActiveState).when(mBackgroundCall).getState();
-        mPreciseCallStateHandler.handleMessage(mPreciseCallStateHandler.obtainMessage(101));
-
-        assertTrue(mTestImsNrSaModeHandler.isImsCallOngoing());
-        assertTrue(mSimulatedCommands.isN1ModeEnabled());
-
-        mTestImsNrSaModeHandler.setVowifiRegStatus(false);
-        mTestImsNrSaModeHandler.setImsCallStatus(false);
-        mSimulatedCommands.setN1ModeEnabled(true, null);
-
         doReturn(mIdleState).when(mForegroundCall).getState();
         doReturn(mIdleState).when(mBackgroundCall).getState();
         mPreciseCallStateHandler.handleMessage(mPreciseCallStateHandler.obtainMessage(101));
 
         assertFalse(mTestImsNrSaModeHandler.isImsCallOngoing());
         assertTrue(mSimulatedCommands.isN1ModeEnabled());
-
-        mTestImsNrSaModeHandler.setVowifiRegStatus(true);
-        mTestImsNrSaModeHandler.setImsCallStatus(true);
-        mSimulatedCommands.setN1ModeEnabled(false, null);
-        mPreciseCallStateHandler.handleMessage(mPreciseCallStateHandler.obtainMessage(101));
-
-        assertFalse(mTestImsNrSaModeHandler.isImsCallOngoing());
-        assertTrue(mSimulatedCommands.isN1ModeEnabled());
     }
 
     @Test
-    public void testUnregisterForPreciseCallStateChangeIfNeeded() {
+    public void testOnPreciseCallStateChangedWithSaDisablePolicyWfcEstablishedWithVonrDisabled() {
+        mContextFixture.getCarrierConfigBundle().putInt(
+                KEY_NR_SA_DISABLE_POLICY_INT,
+                NR_SA_DISABLE_POLICY_WFC_ESTABLISHED_WHEN_VONR_DISABLED);
+        mContextFixture.getCarrierConfigBundle().putIntArray(
+                KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
+
+        mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
+        mTestImsNrSaModeHandler.updateImsCapability(CAPABILITY_TYPE_VOICE);
+
+        verify(mImsPhone).registerForPreciseCallStateChanged(
+                mPreciseCallStateHandlerCaptor.capture(), anyInt(), any());
+        mPreciseCallStateHandler = mPreciseCallStateHandlerCaptor.getValue();
+
+        mTestImsNrSaModeHandler.setWifiRegStatus(true);
+        mSimulatedCommands.setN1ModeEnabled(true, null);
+        mSimulatedCommands.setVonrEnabled(false);
+
+        mPreciseCallStateHandler.handleMessage(mPreciseCallStateHandler.obtainMessage(101));
+        processAllMessages();
+
+        assertFalse(mSimulatedCommands.isN1ModeEnabled());
+
+        doReturn(mIdleState).when(mForegroundCall).getState();
+        doReturn(mIdleState).when(mBackgroundCall).getState();
+        mPreciseCallStateHandler.handleMessage(mPreciseCallStateHandler.obtainMessage(101));
+
+        assertTrue(mSimulatedCommands.isN1ModeEnabled());
+
+        doReturn(mActiveState).when(mForegroundCall).getState();
+        doReturn(mActiveState).when(mBackgroundCall).getState();
+        mPreciseCallStateHandler.handleMessage(mPreciseCallStateHandler.obtainMessage(101));
+        mTestImsNrSaModeHandler.setImsCallStatus(false);
+        processAllMessages();
+
+        assertTrue(mSimulatedCommands.isN1ModeEnabled());
+    }
+
+    @Test
+    public void testOnCarrierConfigChangedRegisterOrUnregisterListenerForPreciseCallStateChange() {
         mContextFixture.getCarrierConfigBundle().putInt(
                 KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_WFC_ESTABLISHED);
         mContextFixture.getCarrierConfigBundle().putIntArray(
@@ -376,20 +475,29 @@
     }
 
     @Test
-    public void testNrSaModeIsNotHandledWhenNotSupported() {
+    public void testUpdateImsCapabilityWithSaDisablePolicyWfcEstablished() {
         mContextFixture.getCarrierConfigBundle().putInt(
                 KEY_NR_SA_DISABLE_POLICY_INT, NR_SA_DISABLE_POLICY_WFC_ESTABLISHED);
         mContextFixture.getCarrierConfigBundle().putIntArray(
-                KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_NSA});
+                KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, new int[]{CARRIER_NR_AVAILABILITY_SA});
 
         mCarrierConfigChangeListener.onCarrierConfigChanged(mAnyInt, mAnyInt, mAnyInt, mAnyInt);
 
-        mSimulatedCommands.setN1ModeEnabled(false, null);
-        mTestImsNrSaModeHandler.setVowifiRegStatus(true);
+        verify(mImsPhone).registerForPreciseCallStateChanged(
+                mPreciseCallStateHandlerCaptor.capture(), anyInt(), any());
+        mPreciseCallStateHandler = mPreciseCallStateHandlerCaptor.getValue();
 
-        mTestImsNrSaModeHandler.onImsRegistered(REGISTRATION_TECH_NONE, mFeatureTags);
+        mTestImsNrSaModeHandler.setWifiRegStatus(true);
+        mSimulatedCommands.setN1ModeEnabled(true, null);
+        mPreciseCallStateHandler.handleMessage(mPreciseCallStateHandler.obtainMessage(101));
 
-        assertFalse(mTestImsNrSaModeHandler.isVowifiRegistered());
+        assertTrue(mTestImsNrSaModeHandler.isImsCallOngoing());
+        assertTrue(mSimulatedCommands.isN1ModeEnabled());
+
+        mTestImsNrSaModeHandler.updateImsCapability(CAPABILITY_TYPE_VOICE);
         assertFalse(mSimulatedCommands.isN1ModeEnabled());
+
+        mTestImsNrSaModeHandler.updateImsCapability(0);
+        assertTrue(mSimulatedCommands.isN1ModeEnabled());
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index 6056112..a74a408 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -1477,6 +1477,17 @@
 
     @Test
     @SmallTest
+    public void testSipRequestCancelled() {
+        doReturn(true).when(mFeatureFlags).remapDisconnectCauseSipRequestCancelled();
+
+        assertEquals(DisconnectCause.NORMAL,
+                mCTUT.getDisconnectCauseFromReasonInfo(
+                        new ImsReasonInfo(ImsReasonInfo.CODE_SIP_REQUEST_CANCELLED, 0),
+                        Call.State.DIALING));
+    }
+
+    @Test
+    @SmallTest
     public void testLowBatteryDisconnectDialing() {
         assertEquals(DisconnectCause.DIAL_LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
                 new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, 0), Call.State.DIALING));
@@ -2774,6 +2785,32 @@
         assertNetworkStatsEquals(expectedStats, uidStatsCaptor.getValue());
     }
 
+    @Test
+    public void testPreventHangupDuringCallMerge() {
+        // Enable feature flag
+        doReturn(true).when(mFeatureFlags).preventHangupDuringCallMerge();
+
+        // Change carrier config to allow call hold for 2nd call setup
+        PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
+        bundle.putBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_VIDEO_CALL_BOOL, true);
+        mCTUT.updateCarrierConfigCache(bundle);
+
+        // Place a call.
+        placeCallAndMakeActive();
+        // Place a 2nd call
+        ImsPhoneConnection imsPhoneConnection = placeCallAndMakeActive();
+
+        // Try call merge
+        mCTUT.conference();
+
+        try {
+            mCTUT.hangup(imsPhoneConnection.getCall());
+            fail("Expect CallStateException but not");
+        } catch  (CallStateException e) {
+            // Expected exception
+        }
+    }
+
     private ImsPhoneConnection placeCallAndMakeActive() {
         ImsPhoneConnection connection = placeCall();
         ImsCall imsCall = connection.getImsCall();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java
index 3c1b045..6f42350 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java
@@ -19,10 +19,13 @@
 import static junit.framework.Assert.fail;
 
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.os.AsyncResult;
@@ -35,6 +38,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.flags.FeatureFlags;
 
@@ -54,6 +58,9 @@
     private static final String TEST_DIAL_STRING_SERVICE_CODE = "*67911";
     private static final String TEST_DIAL_STRING_NO_SERVICE_CODE = "*767911";
     private static final String TEST_DIAL_STRING_NON_EMERGENCY_NUMBER = "11976";
+    private static final String TEST_DIAL_NUMBER = "123456789";
+    private static final String TEST_DIAL_STRING_CFNRY_ACTIVE_CODE = "**61*" + TEST_DIAL_NUMBER;
+    private static final String TEST_DIAL_STRING_CFNRY_DEACTIVE_CODE = "##61#";
     private ImsPhoneMmiCode mImsPhoneMmiCode;
     private ImsPhone mImsPhoneUT;
 
@@ -90,7 +97,7 @@
         // Test *67911 is treated as temporary mode CLIR
         doReturn(true).when(mTelephonyManager).isEmergencyNumber(TEST_DIAL_STRING_SERVICE_CODE);
         mImsPhoneMmiCode = ImsPhoneMmiCode.newFromDialString(TEST_DIAL_STRING_SERVICE_CODE,
-                mImsPhoneUT);
+                mImsPhoneUT, mFeatureFlags);
         assertTrue(mImsPhoneMmiCode.isTemporaryModeCLIR());
     }
 
@@ -99,7 +106,7 @@
         // Test if prefix isn't *67 or *82
         doReturn(true).when(mTelephonyManager).isEmergencyNumber(TEST_DIAL_STRING_NO_SERVICE_CODE);
         mImsPhoneMmiCode = ImsPhoneMmiCode.newFromDialString(TEST_DIAL_STRING_NO_SERVICE_CODE,
-                mImsPhoneUT);
+                mImsPhoneUT, mFeatureFlags);
         assertTrue(mImsPhoneMmiCode == null);
     }
 
@@ -107,13 +114,14 @@
     public void testIsTemporaryModeCLIRForNonEmergencyNumber() {
         // Test if dialing string isn't an emergency number
         mImsPhoneMmiCode = ImsPhoneMmiCode.newFromDialString(TEST_DIAL_STRING_NON_EMERGENCY_NUMBER,
-                mImsPhoneUT);
+                mImsPhoneUT, mFeatureFlags);
         assertTrue(mImsPhoneMmiCode == null);
     }
 
     @Test
     public void testNoCrashOnEmptyMessage() {
-        ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(null, true, mImsPhoneUT);
+        ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(null, true, mImsPhoneUT,
+                mFeatureFlags);
         try {
             mmi.onUssdFinishedError();
         } catch (Exception e) {
@@ -134,7 +142,8 @@
     @Test
     @SmallTest
     public void testOperationNotSupported() {
-        mImsPhoneMmiCode = ImsPhoneMmiCode.newNetworkInitiatedUssd(null, true, mImsPhoneUT);
+        mImsPhoneMmiCode = ImsPhoneMmiCode.newNetworkInitiatedUssd(null, true, mImsPhoneUT,
+                mFeatureFlags);
 
         // Emulate request not supported from the network.
         AsyncResult ar = new AsyncResult(null, null,
@@ -143,4 +152,80 @@
         verify(mContext.getResources()).getText(
                 eq(com.android.internal.R.string.mmiErrorNotSupported));
     }
+
+    @Test
+    @SmallTest
+    public void testActivationCfnrWithCfnry() throws Exception {
+        doNothing().when(mImsPhone).setCallForwardingOption(
+                anyInt(), anyInt(), any(), anyInt(), anyInt(), any());
+
+        doReturn(true).when(mFeatureFlags).useCarrierConfigForCfnryTimeViaMmi();
+
+        int carrierConfigTime = 40;
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(CarrierConfigManager
+                .KEY_NO_REPLY_TIMER_FOR_CFNRY_SEC_INT, carrierConfigTime);
+        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+
+        int mmiTime = 30;
+        String withoutTimeCode = TEST_DIAL_STRING_CFNRY_ACTIVE_CODE + "#";
+        String withTimeCode = TEST_DIAL_STRING_CFNRY_ACTIVE_CODE + "**" + mmiTime + "#";
+
+        mImsPhoneMmiCode = ImsPhoneMmiCode.newFromDialString(withoutTimeCode, mImsPhoneUT,
+                mFeatureFlags);
+        // For verification, replace the internal object of target with mock
+        replaceInstance(ImsPhoneMmiCode.class, "mPhone", mImsPhoneMmiCode, mImsPhone);
+
+        mImsPhoneMmiCode.processCode();
+        verify(mImsPhone, times(1)).setCallForwardingOption(
+                eq(CommandsInterface.CF_ACTION_REGISTRATION),
+                anyInt(),
+                eq(TEST_DIAL_NUMBER),
+                anyInt(),
+                eq(carrierConfigTime),
+                any());
+
+        mImsPhoneMmiCode = ImsPhoneMmiCode.newFromDialString(withTimeCode, mImsPhoneUT,
+                mFeatureFlags);
+        // For verification, replace the internal object of target with mock
+        replaceInstance(ImsPhoneMmiCode.class, "mPhone", mImsPhoneMmiCode, mImsPhone);
+
+        mImsPhoneMmiCode.processCode();
+        verify(mImsPhone, times(1)).setCallForwardingOption(
+                eq(CommandsInterface.CF_ACTION_REGISTRATION),
+                anyInt(),
+                eq(TEST_DIAL_NUMBER),
+                anyInt(),
+                eq(mmiTime),
+                any());
+    }
+
+    @Test
+    @SmallTest
+    public void testDeactivationCfnrWithCfnry() throws Exception {
+        doNothing().when(mImsPhone).setCallForwardingOption(
+                anyInt(), anyInt(), any(), anyInt(), anyInt(), any());
+
+        doReturn(true).when(mFeatureFlags).useCarrierConfigForCfnryTimeViaMmi();
+
+        int carrierConfigTime = 40;
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(CarrierConfigManager
+                .KEY_NO_REPLY_TIMER_FOR_CFNRY_SEC_INT, carrierConfigTime);
+        doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+
+        mImsPhoneMmiCode = ImsPhoneMmiCode.newFromDialString(TEST_DIAL_STRING_CFNRY_DEACTIVE_CODE,
+                mImsPhoneUT, mFeatureFlags);
+        // For verification, replace the internal object of target with mock
+        replaceInstance(ImsPhoneMmiCode.class, "mPhone", mImsPhoneMmiCode, mImsPhone);
+
+        mImsPhoneMmiCode.processCode();
+        verify(mImsPhone, times(1)).setCallForwardingOption(
+                eq(CommandsInterface.CF_ACTION_ERASURE),
+                anyInt(),
+                eq(null),
+                anyInt(),
+                eq(carrierConfigTime),
+                any());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
index 8ee3a37..e371c59 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -1146,6 +1146,8 @@
         mSatelliteController1.countOfDemoModeIncomingDatagramFail = 2;
         mSatelliteController1.countOfDatagramTypeKeepAliveSuccess = 1;
         mSatelliteController1.countOfDatagramTypeKeepAliveFail = 2;
+        mSatelliteController1.isProvisioned = true;
+        mSatelliteController1.carrierId = 1;
 
         mSatelliteController2 = new SatelliteController();
         mSatelliteController2.countOfSatelliteServiceEnablementsSuccess = 2 + 1;
@@ -1173,6 +1175,8 @@
         mSatelliteController2.countOfDemoModeIncomingDatagramFail = 3;
         mSatelliteController2.countOfDatagramTypeKeepAliveSuccess = 4;
         mSatelliteController2.countOfDatagramTypeKeepAliveFail = 5;
+        mSatelliteController2.isProvisioned = false;
+        mSatelliteController2.carrierId = 10;
 
         // SatelliteController atom has one data point
         mSatelliteControllers =
@@ -1182,12 +1186,12 @@
 
         mSatelliteSession1 = new SatelliteSession();
         mSatelliteSession1.satelliteServiceInitializationResult =
-                SatelliteProtoEnums.SATELLITE_ERROR_NONE;
+                SatelliteProtoEnums.SATELLITE_RESULT_SUCCESS;
         mSatelliteSession1.satelliteTechnology =
                 SatelliteProtoEnums.NT_RADIO_TECHNOLOGY_PROPRIETARY;
         mSatelliteSession1.count = 1;
         mSatelliteSession1.satelliteServiceTerminationResult =
-                SatelliteProtoEnums.SATELLITE_ERROR_NONE;
+                SatelliteProtoEnums.SATELLITE_RESULT_SUCCESS;
         mSatelliteSession1.initializationProcessingTimeMillis = 100;
         mSatelliteSession1.terminationProcessingTimeMillis = 200;
         mSatelliteSession1.sessionDurationSeconds = 3;
@@ -1197,15 +1201,19 @@
         mSatelliteSession1.countOfIncomingDatagramFailed = 0;
         mSatelliteSession1.isDemoMode = false;
         mSatelliteSession1.maxNtnSignalStrengthLevel = 2;
+        mSatelliteSession1.carrierId = 2;
+        mSatelliteSession1.countOfSatelliteNotificationDisplayed = 4;
+        mSatelliteSession1.countOfAutoExitDueToScreenOff = 6;
+        mSatelliteSession1.countOfAutoExitDueToTnNetwork = 7;
 
         mSatelliteSession2 = new SatelliteSession();
         mSatelliteSession2.satelliteServiceInitializationResult =
-                SatelliteProtoEnums.SATELLITE_MODEM_ERROR;
+                SatelliteProtoEnums.SATELLITE_RESULT_MODEM_ERROR;
         mSatelliteSession2.satelliteTechnology =
                 SatelliteProtoEnums.NT_RADIO_TECHNOLOGY_NB_IOT_NTN;
         mSatelliteSession2.count = 1;
         mSatelliteSession2.satelliteServiceTerminationResult =
-                SatelliteProtoEnums.SATELLITE_ERROR_NONE;
+                SatelliteProtoEnums.SATELLITE_RESULT_SUCCESS;
         mSatelliteSession2.initializationProcessingTimeMillis = 300;
         mSatelliteSession2.terminationProcessingTimeMillis = 100;
         mSatelliteSession2.sessionDurationSeconds = 10;
@@ -1215,6 +1223,10 @@
         mSatelliteSession2.countOfIncomingDatagramFailed = 1;
         mSatelliteSession2.isDemoMode = true;
         mSatelliteSession2.maxNtnSignalStrengthLevel = 4;
+        mSatelliteSession2.carrierId = 20;
+        mSatelliteSession2.countOfSatelliteNotificationDisplayed = 40;
+        mSatelliteSession2.countOfAutoExitDueToScreenOff = 60;
+        mSatelliteSession2.countOfAutoExitDueToTnNetwork = 70;
 
         mSatelliteSessions =
                 new SatelliteSession[] {
@@ -1222,16 +1234,18 @@
                 };
 
         mSatelliteIncomingDatagram1 = new SatelliteIncomingDatagram();
-        mSatelliteIncomingDatagram1.resultCode = SatelliteProtoEnums.SATELLITE_ERROR_NONE;
+        mSatelliteIncomingDatagram1.resultCode = SatelliteProtoEnums.SATELLITE_RESULT_SUCCESS;
         mSatelliteIncomingDatagram1.datagramSizeBytes = 1 * 1024;
         mSatelliteIncomingDatagram1.datagramTransferTimeMillis = 3 * 1000;
         mSatelliteIncomingDatagram1.isDemoMode = false;
+        mSatelliteIncomingDatagram1.carrierId = 1;
 
         mSatelliteIncomingDatagram2 = new SatelliteIncomingDatagram();
-        mSatelliteIncomingDatagram2.resultCode = SatelliteProtoEnums.SATELLITE_MODEM_ERROR;
+        mSatelliteIncomingDatagram2.resultCode = SatelliteProtoEnums.SATELLITE_RESULT_MODEM_ERROR;
         mSatelliteIncomingDatagram2.datagramSizeBytes = 512;
         mSatelliteIncomingDatagram2.datagramTransferTimeMillis = 1 * 1000;
-        mSatelliteIncomingDatagram1.isDemoMode = true;
+        mSatelliteIncomingDatagram2.isDemoMode = true;
+        mSatelliteIncomingDatagram2.carrierId = 10;
 
         mSatelliteIncomingDatagrams =
                 new SatelliteIncomingDatagram[] {
@@ -1241,18 +1255,20 @@
         mSatelliteOutgoingDatagram1 = new SatelliteOutgoingDatagram();
         mSatelliteOutgoingDatagram1.datagramType =
                 SatelliteProtoEnums.DATAGRAM_TYPE_LOCATION_SHARING;
-        mSatelliteOutgoingDatagram1.resultCode = SatelliteProtoEnums.SATELLITE_ERROR_NONE;
+        mSatelliteOutgoingDatagram1.resultCode = SatelliteProtoEnums.SATELLITE_RESULT_SUCCESS;
         mSatelliteOutgoingDatagram1.datagramSizeBytes = 1 * 1024;
         mSatelliteOutgoingDatagram1.datagramTransferTimeMillis = 3 * 1000;
         mSatelliteOutgoingDatagram1.isDemoMode = false;
+        mSatelliteOutgoingDatagram1.carrierId = 1;
 
         mSatelliteOutgoingDatagram2 = new SatelliteOutgoingDatagram();
         mSatelliteOutgoingDatagram2.datagramType =
                 SatelliteProtoEnums.DATAGRAM_TYPE_SOS_MESSAGE;
-        mSatelliteOutgoingDatagram2.resultCode = SatelliteProtoEnums.SATELLITE_MODEM_ERROR;
+        mSatelliteOutgoingDatagram2.resultCode = SatelliteProtoEnums.SATELLITE_RESULT_MODEM_ERROR;
         mSatelliteOutgoingDatagram2.datagramSizeBytes = 512;
         mSatelliteOutgoingDatagram2.datagramTransferTimeMillis = 1 * 1000;
-        mSatelliteOutgoingDatagram1.isDemoMode = true;
+        mSatelliteOutgoingDatagram2.isDemoMode = true;
+        mSatelliteOutgoingDatagram2.carrierId = 10;
 
         mSatelliteOutgoingDatagrams =
                 new SatelliteOutgoingDatagram[] {
@@ -1260,17 +1276,19 @@
                 };
 
         mSatelliteProvision1 = new SatelliteProvision();
-        mSatelliteProvision1.resultCode = SatelliteProtoEnums.SATELLITE_ERROR_NONE;
+        mSatelliteProvision1.resultCode = SatelliteProtoEnums.SATELLITE_RESULT_SUCCESS;
         mSatelliteProvision1.provisioningTimeSec = 3 * 60;
         mSatelliteProvision1.isProvisionRequest = true;
         mSatelliteProvision1.isCanceled = false;
+        mSatelliteProvision1.carrierId = 1;
 
         mSatelliteProvision2 = new SatelliteProvision();
         mSatelliteProvision2.resultCode =
-                SatelliteProtoEnums.SATELLITE_SERVICE_NOT_PROVISIONED;
+                SatelliteProtoEnums.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED;
         mSatelliteProvision2.provisioningTimeSec = 0;
         mSatelliteProvision2.isProvisionRequest = false;
         mSatelliteProvision2.isCanceled = true;
+        mSatelliteProvision2.carrierId = 10;
 
         mSatelliteProvisions =
                 new SatelliteProvision[] {
@@ -1286,6 +1304,8 @@
         mSatelliteSosMessageRecommender1.isMultiSim = true;
         mSatelliteSosMessageRecommender1.recommendingHandoverType = 1;
         mSatelliteSosMessageRecommender1.isSatelliteAllowedInCurrentLocation = true;
+        mSatelliteSosMessageRecommender1.isWifiConnected = true;
+        mSatelliteSosMessageRecommender1.carrierId = 1;
         mSatelliteSosMessageRecommender1.count = 1;
 
         mSatelliteSosMessageRecommender2 = new SatelliteSosMessageRecommender();
@@ -1297,6 +1317,9 @@
         mSatelliteSosMessageRecommender2.isMultiSim = false;
         mSatelliteSosMessageRecommender2.recommendingHandoverType = 0;
         mSatelliteSosMessageRecommender2.isSatelliteAllowedInCurrentLocation = true;
+        mSatelliteSosMessageRecommender2.isWifiConnected = false;
+        mSatelliteSosMessageRecommender2.carrierId = 2;
+
         mSatelliteSosMessageRecommender2.count = 1;
 
         mSatelliteSosMessageRecommenders =
@@ -1352,6 +1375,8 @@
         mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapMinSec = 2;
         mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapAvgSec = 3;
         mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapMaxSec = 4;
+        mCarrierRoamingSatelliteControllerStats1.carrierId = 1;
+        mCarrierRoamingSatelliteControllerStats1.isDeviceEntitled = true;
 
         mCarrierRoamingSatelliteControllerStats2 = new CarrierRoamingSatelliteControllerStats();
         mCarrierRoamingSatelliteControllerStats2.configDataSource =
@@ -1362,6 +1387,8 @@
         mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapMinSec = 5;
         mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapAvgSec = 10;
         mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapMaxSec = 15;
+        mCarrierRoamingSatelliteControllerStats2.carrierId = 10;
+        mCarrierRoamingSatelliteControllerStats2.isDeviceEntitled = false;
 
         // CarrierRoamingSatelliteController has one data point
         mCarrierRoamingSatelliteControllerStats = new CarrierRoamingSatelliteControllerStats[] {
@@ -1380,7 +1407,7 @@
         mSatelliteEntitlement2.result = 1;
         mSatelliteEntitlement2.entitlementStatus =
                 SatelliteProtoEnums.SATELLITE_ENTITLEMENT_STATUS_DISABLED;
-        mSatelliteEntitlement1.isRetry = true;
+        mSatelliteEntitlement2.isRetry = true;
         mSatelliteEntitlement2.count = 1;
 
         mSatelliteEntitlements = new SatelliteEntitlement[] {mSatelliteEntitlement1,
@@ -1414,10 +1441,11 @@
         mSatelliteAccessController1.resultCode = SATELLITE_RESULT_SUCCESS;
         mSatelliteAccessController1.countryCodes = new String[]{"AB", "CD"};
         mSatelliteAccessController1.configDataSource = CONFIG_DATA_SOURCE_DEVICE_CONFIG;
+        mSatelliteAccessController1.carrierId = 1;
 
         mSatelliteAccessController2 = new SatelliteAccessController();
-        mSatelliteAccessController1.accessControlType = ACCESS_CONTROL_TYPE_CURRENT_LOCATION;
-        mSatelliteAccessController1.locationQueryTimeMillis = TimeUnit.SECONDS.toMillis(4);
+        mSatelliteAccessController2.accessControlType = ACCESS_CONTROL_TYPE_CURRENT_LOCATION;
+        mSatelliteAccessController2.locationQueryTimeMillis = TimeUnit.SECONDS.toMillis(4);
         mSatelliteAccessController2.onDeviceLookupTimeMillis = TimeUnit.SECONDS.toMillis(5);
         mSatelliteAccessController2.totalCheckingTimeMillis = TimeUnit.SECONDS.toMillis(6);
         mSatelliteAccessController2.isAllowed = false;
@@ -1425,6 +1453,7 @@
         mSatelliteAccessController2.resultCode = SATELLITE_RESULT_SUCCESS;
         mSatelliteAccessController2.countryCodes = new String[]{"EF", "GH"};
         mSatelliteAccessController2.configDataSource = CONFIG_DATA_SOURCE_CONFIG_UPDATER;
+        mSatelliteAccessController2.carrierId = 10;
 
         mSatelliteAccessControllers = new SatelliteAccessController[]{
                 mSatelliteAccessController1, mSatelliteAccessController2
@@ -4421,6 +4450,8 @@
         expected.countOfDatagramTypeKeepAliveFail =
                 mSatelliteController1.countOfDatagramTypeKeepAliveFail
                         + mSatelliteController2.countOfDatagramTypeKeepAliveFail;
+        expected.isProvisioned = mSatelliteController2.isProvisioned;
+        expected.carrierId = mSatelliteController2.carrierId;
 
         // Service state and service switch should be added successfully
         verifyCurrentStateSavedToFileOnce();
@@ -4973,6 +5004,8 @@
                 mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapAvgSec;
         expected.satelliteSessionGapMaxSec =
                 mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapMaxSec;
+        expected.carrierId = mCarrierRoamingSatelliteControllerStats2.carrierId;
+        expected.isDeviceEntitled = mCarrierRoamingSatelliteControllerStats2.isDeviceEntitled;
 
         verifyCurrentStateSavedToFileOnce();
         CarrierRoamingSatelliteControllerStats[] output =
@@ -5825,6 +5858,18 @@
                 expectedStats.countOfDemoModeIncomingDatagramSuccess);
         assertEquals(tested[0].countOfDemoModeIncomingDatagramFail,
                 expectedStats.countOfDemoModeIncomingDatagramFail);
+        assertEquals(tested[0].countOfDatagramTypeKeepAliveSuccess,
+                expectedStats.countOfDatagramTypeKeepAliveSuccess);
+        assertEquals(tested[0].countOfDatagramTypeKeepAliveFail,
+                expectedStats.countOfDatagramTypeKeepAliveFail);
+        assertEquals(tested[0].countOfAllowedSatelliteAccess,
+                expectedStats.countOfAllowedSatelliteAccess);
+        assertEquals(tested[0].countOfDisallowedSatelliteAccess,
+                expectedStats.countOfDisallowedSatelliteAccess);
+        assertEquals(tested[0].countOfSatelliteAccessCheckFail,
+                expectedStats.countOfSatelliteAccessCheckFail);
+        assertEquals(tested[0].isProvisioned, expectedStats.isProvisioned);
+        assertEquals(tested[0].carrierId, expectedStats.carrierId);
     }
 
     private static void assertHasStatsAndCount(
@@ -5851,7 +5896,14 @@
                         == expectedStats.countOfIncomingDatagramSuccess
                     && stats.countOfIncomingDatagramFailed
                         == expectedStats.countOfIncomingDatagramFailed
-                    && stats.isDemoMode == expectedStats.isDemoMode) {
+                    && stats.isDemoMode == expectedStats.isDemoMode
+                    && stats.carrierId == expectedStats.carrierId
+                    && stats.countOfSatelliteNotificationDisplayed
+                    == expectedStats.countOfSatelliteNotificationDisplayed
+                    && stats.countOfAutoExitDueToScreenOff
+                    == expectedStats.countOfAutoExitDueToScreenOff
+                    && stats.countOfAutoExitDueToTnNetwork
+                    == expectedStats.countOfAutoExitDueToTnNetwork) {
                 actualCount = stats.count;
             }
         }
@@ -5868,7 +5920,8 @@
                     && stats.datagramSizeBytes == expectedStats.datagramSizeBytes
                     && stats.datagramTransferTimeMillis
                         == expectedStats.datagramTransferTimeMillis
-                    && stats.isDemoMode == expectedStats.isDemoMode) {
+                    && stats.isDemoMode == expectedStats.isDemoMode
+                    && stats.carrierId == expectedStats.carrierId) {
                 actualCount++;
             }
         }
@@ -5886,7 +5939,8 @@
                     && stats.datagramSizeBytes == expectedStats.datagramSizeBytes
                     && stats.datagramTransferTimeMillis
                         == expectedStats.datagramTransferTimeMillis
-                    && stats.isDemoMode == expectedStats.isDemoMode) {
+                    && stats.isDemoMode == expectedStats.isDemoMode
+                    && stats.carrierId == expectedStats.carrierId) {
                 actualCount++;
             }
         }
@@ -5902,7 +5956,8 @@
             if (stats.resultCode == expectedStats.resultCode
                     && stats.provisioningTimeSec == expectedStats.provisioningTimeSec
                     && stats.isProvisionRequest == expectedStats.isProvisionRequest
-                    && stats.isCanceled == expectedStats.isCanceled) {
+                    && stats.isCanceled == expectedStats.isCanceled
+                    && stats.carrierId == expectedStats.carrierId) {
                 actualCount++;
             }
         }
@@ -5923,7 +5978,9 @@
                     && stats.isMultiSim == expectedStats.isMultiSim
                     && stats.recommendingHandoverType == expectedStats.recommendingHandoverType
                     && stats.isSatelliteAllowedInCurrentLocation
-                    == expectedStats.isSatelliteAllowedInCurrentLocation) {
+                    == expectedStats.isSatelliteAllowedInCurrentLocation
+                    && stats.isWifiConnected == expectedStats.isWifiConnected
+                    && stats.carrierId == expectedStats.carrierId) {
                 actualCount = stats.count;
             }
         }
@@ -5945,7 +6002,8 @@
                     && stats.isEmergency == expectedStats.isEmergency
                     && stats.resultCode == expectedStats.resultCode
                     && Arrays.equals(stats.countryCodes, expectedStats.countryCodes)
-                    && stats.configDataSource == expectedStats.configDataSource) {
+                    && stats.configDataSource == expectedStats.configDataSource
+                    && stats.carrierId == expectedStats.carrierId) {
                 actualCount++;
             }
         }
@@ -6205,6 +6263,8 @@
         assertEquals(tested[0].satelliteSessionGapMinSec, expectedStats.satelliteSessionGapMinSec);
         assertEquals(tested[0].satelliteSessionGapAvgSec, expectedStats.satelliteSessionGapAvgSec);
         assertEquals(tested[0].satelliteSessionGapMaxSec, expectedStats.satelliteSessionGapMaxSec);
+        assertEquals(tested[0].carrierId, expectedStats.carrierId);
+        assertEquals(tested[0].isDeviceEntitled, expectedStats.isDeviceEntitled);
     }
 
     private static void assertHasStatsAndCount(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
index cda96ef..5740336 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
@@ -23,6 +23,7 @@
 import static com.android.internal.telephony.satellite.SatelliteConstants.ACCESS_CONTROL_TYPE_CACHED_COUNTRY_CODE;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
@@ -102,6 +103,10 @@
                         .setCountOfDemoModeIncomingDatagramFail(2)
                         .setCountOfDatagramTypeKeepAliveSuccess(1)
                         .setCountOfDatagramTypeKeepAliveFail(2)
+                        .setCountOfAllowedSatelliteAccess(1)
+                        .setCountOfDisallowedSatelliteAccess(2)
+                        .setCountOfSatelliteAccessCheckFail(3)
+                        .setIsProvisioned(true)
                         .build();
 
         mSatelliteStats.onSatelliteControllerMetrics(param);
@@ -160,6 +165,59 @@
                 stats.countOfDatagramTypeKeepAliveSuccess);
         assertEquals(param.getCountOfDatagramTypeKeepAliveFail(),
                 stats.countOfDatagramTypeKeepAliveFail);
+        assertEquals(param.getCountOfAllowedSatelliteAccess(), stats.countOfAllowedSatelliteAccess);
+        assertEquals(param.getCountOfDisallowedSatelliteAccess(),
+                stats.countOfDisallowedSatelliteAccess);
+        assertEquals(param.getCountOfSatelliteAccessCheckFail(),
+                stats.countOfSatelliteAccessCheckFail);
+        assertEquals(param.isProvisioned(), stats.isProvisioned);
+
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void onSatelliteControllerMetrics_isProvisioned() throws Exception {
+        SatelliteStats.SatelliteControllerParams param =
+                new SatelliteStats.SatelliteControllerParams.Builder()
+                        .setCountOfSatelliteServiceEnablementsSuccess(2)
+                        .setIsProvisioned(true)
+                        .build();
+        mSatelliteStats.onSatelliteControllerMetrics(param);
+
+        ArgumentCaptor<SatelliteController> captor =
+                ArgumentCaptor.forClass(SatelliteController.class);
+        verify(mPersistAtomsStorage, times(1)).addSatelliteControllerStats(captor.capture());
+        SatelliteController stats = captor.getValue();
+        assertEquals(param.getCountOfSatelliteServiceEnablementsSuccess(),
+                stats.countOfSatelliteServiceEnablementsSuccess);
+        assertEquals(param.isProvisioned(), stats.isProvisioned);
+
+        param = new SatelliteStats.SatelliteControllerParams.Builder()
+                .setCountOfSatelliteServiceEnablementsSuccess(2)
+                .build();
+        mSatelliteStats.onSatelliteControllerMetrics(param);
+
+        captor = ArgumentCaptor.forClass(SatelliteController.class);
+        verify(mPersistAtomsStorage, times(2)).addSatelliteControllerStats(captor.capture());
+        stats = captor.getValue();
+        // count should be added
+        assertEquals(2, stats.countOfSatelliteServiceEnablementsSuccess);
+        // isProvisioned value should not be updated
+        assertEquals(true, stats.isProvisioned);
+
+        param = new SatelliteStats.SatelliteControllerParams.Builder()
+                .setCountOfSatelliteServiceEnablementsSuccess(2)
+                .setIsProvisioned(false)
+                .build();
+        mSatelliteStats.onSatelliteControllerMetrics(param);
+
+        captor = ArgumentCaptor.forClass(SatelliteController.class);
+        verify(mPersistAtomsStorage, times(3)).addSatelliteControllerStats(captor.capture());
+        stats = captor.getValue();
+        // count should be added
+        assertEquals(2, stats.countOfSatelliteServiceEnablementsSuccess);
+        // isProvisioned should be updated
+        assertEquals(false, stats.isProvisioned);
 
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
@@ -169,9 +227,9 @@
         SatelliteStats.SatelliteSessionParams param =
                 new SatelliteStats.SatelliteSessionParams.Builder()
                         .setSatelliteServiceInitializationResult(
-                                SatelliteProtoEnums.SATELLITE_ERROR_NONE)
+                                SatelliteProtoEnums.SATELLITE_RESULT_SUCCESS)
                         .setSatelliteTechnology(SatelliteProtoEnums.NT_RADIO_TECHNOLOGY_PROPRIETARY)
-                        .setTerminationResult(SatelliteProtoEnums.SATELLITE_ERROR_NONE)
+                        .setTerminationResult(SatelliteProtoEnums.SATELLITE_RESULT_SUCCESS)
                         .setInitializationProcessingTime(100)
                         .setTerminationProcessingTime(200)
                         .setSessionDuration(3)
@@ -212,7 +270,7 @@
     public void onSatelliteIncomingDatagramMetrics_withAtoms() throws Exception {
         SatelliteStats.SatelliteIncomingDatagramParams param =
                 new SatelliteStats.SatelliteIncomingDatagramParams.Builder()
-                        .setResultCode(SatelliteProtoEnums.SATELLITE_ERROR_NONE)
+                        .setResultCode(SatelliteProtoEnums.SATELLITE_RESULT_SUCCESS)
                         .setDatagramSizeBytes(1 * 1024)
                         .setDatagramTransferTimeMillis(3 * 1000)
                         .setIsDemoMode(true)
@@ -236,7 +294,7 @@
         SatelliteStats.SatelliteOutgoingDatagramParams param =
                 new SatelliteStats.SatelliteOutgoingDatagramParams.Builder()
                         .setDatagramType(SatelliteProtoEnums.DATAGRAM_TYPE_LOCATION_SHARING)
-                        .setResultCode(SatelliteProtoEnums.SATELLITE_ERROR_NONE)
+                        .setResultCode(SatelliteProtoEnums.SATELLITE_RESULT_SUCCESS)
                         .setDatagramSizeBytes(1 * 1024)
                         .setDatagramTransferTimeMillis(3 * 1000)
                         .setIsDemoMode(true)
@@ -261,7 +319,7 @@
         SatelliteStats.SatelliteProvisionParams param =
                 new SatelliteStats.SatelliteProvisionParams.Builder()
                         .setResultCode(
-                                SatelliteProtoEnums.SATELLITE_SERVICE_PROVISION_IN_PROGRESS)
+                                SatelliteProtoEnums.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS)
                         .setProvisioningTimeSec(5 * 1000)
                         .setIsProvisionRequest(true)
                         .setIsCanceled(false)
@@ -292,6 +350,8 @@
                         .setIsMultiSim(false)
                         .setRecommendingHandoverType(0)
                         .setIsSatelliteAllowedInCurrentLocation(true)
+                        .setIsWifiConnected(true)
+                        .setCarrierId(1)
                         .build();
 
         mSatelliteStats.onSatelliteSosMessageRecommender(param);
@@ -309,6 +369,8 @@
         assertEquals(param.getRecommendingHandoverType(), stats.recommendingHandoverType);
         assertEquals(param.isSatelliteAllowedInCurrentLocation(),
                 stats.isSatelliteAllowedInCurrentLocation);
+        assertEquals(param.isWifiConnected(), stats.isWifiConnected);
+        assertEquals(param.getCarrierId(), stats.carrierId);
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
 
@@ -372,6 +434,8 @@
                         .setSatelliteSessionGapMinSec(15)
                         .setSatelliteSessionGapAvgSec(30)
                         .setSatelliteSessionGapMaxSec(45)
+                        .setCarrierId(10)
+                        .setIsDeviceEntitled(true)
                         .build();
 
         mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(param);
@@ -390,6 +454,70 @@
         assertEquals(param.getSatelliteSessionGapMinSec(), stats.satelliteSessionGapMinSec);
         assertEquals(param.getSatelliteSessionGapAvgSec(), stats.satelliteSessionGapAvgSec);
         assertEquals(param.getSatelliteSessionGapMaxSec(), stats.satelliteSessionGapMaxSec);
+        assertEquals(param.getCarrierId(), stats.carrierId);
+        assertEquals(param.isDeviceEntitled(), stats.isDeviceEntitled);
+
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void onCarrierRoamingSatelliteControllerStatsMetrics_testStaticFields()
+            throws Exception {
+        SatelliteStats.CarrierRoamingSatelliteControllerStatsParams param =
+                new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                        .setConfigDataSource(4)
+                        .setCountOfEntitlementStatusQueryRequest(6)
+                        .setCountOfSatelliteConfigUpdateRequest(2)
+                        .setCountOfSatelliteNotificationDisplayed(1)
+                        .setSatelliteSessionGapMinSec(15)
+                        .setSatelliteSessionGapAvgSec(30)
+                        .setSatelliteSessionGapMaxSec(45)
+                        .setCarrierId(10)
+                        .setIsDeviceEntitled(true)
+                        .build();
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(param);
+
+        ArgumentCaptor<CarrierRoamingSatelliteControllerStats> captor =
+                ArgumentCaptor.forClass(CarrierRoamingSatelliteControllerStats.class);
+        verify(mPersistAtomsStorage, times(1)).addCarrierRoamingSatelliteControllerStats(
+                captor.capture());
+        CarrierRoamingSatelliteControllerStats stats = captor.getValue();
+        assertEquals(param.getCountOfEntitlementStatusQueryRequest(),
+                stats.countOfEntitlementStatusQueryRequest);
+        assertEquals(param.getCarrierId(), stats.carrierId);
+        assertEquals(param.isDeviceEntitled(), stats.isDeviceEntitled);
+
+        param = new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                .setCountOfSatelliteConfigUpdateRequest(2)
+                .build();
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(param);
+
+        captor = ArgumentCaptor.forClass(CarrierRoamingSatelliteControllerStats.class);
+        verify(mPersistAtomsStorage, times(2)).addCarrierRoamingSatelliteControllerStats(
+                captor.capture());
+        stats = captor.getValue();
+        // count should be added
+        assertEquals(2, stats.countOfSatelliteConfigUpdateRequest);
+        // static values should not be updated
+        assertEquals(10, stats.carrierId);
+        assertEquals(true, stats.isDeviceEntitled);
+
+        param = new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                .setCountOfSatelliteConfigUpdateRequest(2)
+                .setCarrierId(20)
+                .setIsDeviceEntitled(false)
+                .build();
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(param);
+
+        captor = ArgumentCaptor.forClass(CarrierRoamingSatelliteControllerStats.class);
+        verify(mPersistAtomsStorage, times(3)).addCarrierRoamingSatelliteControllerStats(
+                captor.capture());
+        stats = captor.getValue();
+        // count should be added
+        assertEquals(2, stats.countOfSatelliteConfigUpdateRequest);
+        // static values should be updated
+        assertEquals(20, stats.carrierId);
+        assertEquals(false, stats.isDeviceEntitled);
 
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
index 04d140c..4347869 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
@@ -30,6 +30,10 @@
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_DISCONNECTED;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_UNKNOWN;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -268,6 +272,8 @@
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
         expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
         expectedCall.callDuration = VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
@@ -319,6 +325,8 @@
         expectedCall.setupDurationMillis = 200;
         expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
         expectedCall.callDuration = VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_DISCONNECTED;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 2200L, 1L);
@@ -363,6 +371,8 @@
         expectedCall.setupDurationMillis = 200;
         expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
         expectedCall.callDuration = VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_DISCONNECTED;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 2200L, 1L);
@@ -411,6 +421,8 @@
         expectedCall.disconnectExtraMessage = "normal call clearing";
         expectedCall.callDuration =
                 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_ONE_MINUTE;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 100000L, 1L);
@@ -569,6 +581,8 @@
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.callDuration =
                 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_FIVE_MINUTES;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
@@ -628,6 +642,8 @@
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.isMultiSim = false; // DSDS with one active SIM profile should not count
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_UNKNOWN;
 
         mVoiceCallSessionStats0.setTimeMillis(2000L);
         doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -860,6 +876,8 @@
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.callDuration =
                 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_TEN_MINUTES;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
 
         mVoiceCallSessionStats0.setTimeMillis(2000L);
         doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -918,6 +936,8 @@
         expectedCall.callDuration =
                 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_THIRTY_MINUTES;
         expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsageLte =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 4000L, 1L);
@@ -996,6 +1016,8 @@
         expectedCall.callDuration =
                 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_THIRTY_MINUTES;
         expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsageLte =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 4000L, 1L);
@@ -1130,7 +1152,8 @@
         expectedCall.rttEnabled = true;
         expectedCall.callDuration =
                 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_MORE_THAN_ONE_HOUR;
-
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         mVoiceCallSessionStats0.setTimeMillis(2000L);
         doReturn(Call.State.INCOMING).when(mImsCall0).getState();
         doReturn(Call.State.INCOMING).when(mImsConnection0).getState();
@@ -1187,6 +1210,8 @@
         expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
         expectedCall0.bandAtEnd = 0;
         expectedCall0.lastKnownRat = TelephonyManager.NETWORK_TYPE_HSPA;
+        expectedCall0.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
         // call 1 starts later, MT
         doReturn(true).when(mImsConnection1).isIncoming();
         doReturn(60000L).when(mImsConnection1).getCreateTime();
@@ -1210,6 +1235,8 @@
         expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
         expectedCall1.bandAtEnd = 0;
         expectedCall1.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
+        expectedCall1.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsageLte =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
@@ -1319,6 +1346,8 @@
         expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
         expectedCall0.bandAtEnd = 0;
         expectedCall0.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
+        expectedCall0.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
         // call 1 starts later, MT
         doReturn(true).when(mImsConnection1).isIncoming();
         doReturn(60000L).when(mImsConnection1).getCreateTime();
@@ -1342,6 +1371,8 @@
         expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
         expectedCall1.bandAtEnd = 0;
         expectedCall1.lastKnownRat = TelephonyManager.NETWORK_TYPE_HSPA;
+        expectedCall1.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsageLte =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
@@ -1449,6 +1480,8 @@
         expectedCall0.ratSwitchCount = 0L;
         expectedCall0.ratSwitchCountAfterConnected = 0L;
         expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_LTE;
+        expectedCall0.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
         // call 1 starts later, MT
         doReturn(true).when(mImsConnection1).isIncoming();
         doReturn(60000L).when(mImsConnection1).getCreateTime();
@@ -1472,6 +1505,8 @@
         expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
         expectedCall1.bandAtEnd = 0;
         expectedCall1.lastKnownRat = TelephonyManager.NETWORK_TYPE_HSPA;
+        expectedCall1.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsageLte =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
@@ -1571,6 +1606,8 @@
         expectedCall.disconnectExtraMessage = "normal call clearing";
         expectedCall.callDuration =
                 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_ONE_MINUTE;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_IWLAN, 2000L, 100000L, 1L);
@@ -1631,6 +1668,8 @@
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
         VoiceCallRatUsage expectedRatUsageLte =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
@@ -1695,6 +1734,8 @@
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
         expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_DISCONNECTED;
         VoiceCallRatUsage expectedRatUsageLte =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
@@ -1753,6 +1794,8 @@
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
         VoiceCallRatUsage expectedRatUsageLte =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
@@ -1818,6 +1861,8 @@
                 (1L << AudioCodec.AUDIO_CODEC_AMR) | (1L << AudioCodec.AUDIO_CODEC_AMR_WB);
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_UNKNOWN;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 2500L, 15000L, 1L);
@@ -1871,6 +1916,8 @@
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.bandAtEnd = 0;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 2500L, 100000L, 1L);
@@ -1934,6 +1981,8 @@
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsageLte =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 10000L, 1L);
@@ -2009,6 +2058,8 @@
         expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
@@ -2087,6 +2138,8 @@
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsageLte =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 7000L, 1L);
@@ -2185,6 +2238,8 @@
         expectedCall0.srvccCompleted = true;
         expectedCall0.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
         expectedCall0.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
+        expectedCall0.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
         // call 1 starts later, MT
         doReturn(true).when(mImsConnection1).isIncoming();
         doReturn(60000L).when(mImsConnection1).getCreateTime();
@@ -2213,6 +2268,8 @@
         expectedCall1.srvccCompleted = true;
         expectedCall1.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
         expectedCall1.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
+        expectedCall1.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsageLte =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
@@ -2312,6 +2369,8 @@
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
         expectedCall.handoverInProgress = false;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
         VoiceCallRatUsage expectedRatUsageLte =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
@@ -2386,6 +2445,8 @@
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.handoverInProgress = true;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
@@ -2440,6 +2501,8 @@
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.handoverInProgress = true;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
@@ -2498,6 +2561,8 @@
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.handoverInProgress = false;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
@@ -2562,6 +2627,8 @@
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.isEmergency = true;
         expectedCall.handoverInProgress = true;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ACTIVE;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
@@ -2627,6 +2694,8 @@
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
         expectedCall.bandAtEnd = 0;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_UNKNOWN;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_IWLAN, 2000L, 8000L, 1L);
@@ -2677,6 +2746,8 @@
         expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_UNKNOWN;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_IWLAN, 2000L, 8000L, 1L);
@@ -2730,6 +2801,8 @@
         expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
         expectedCall.callDuration = VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
         expectedCall.vonrEnabled = true;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
@@ -2826,6 +2899,8 @@
         expectedCall.supportsBusinessCallComposer = true;
         // 0 is defined as UNKNOWN, adding 1 to original value.
         expectedCall.callComposerStatus = 3;
+        expectedCall.preciseCallStateOnSetup =
+                VOICE_CALL_SESSION__CALL_STATE_ON_SETUP__CALL_STATE_ALERTING;
         VoiceCallRatUsage expectedRatUsage =
                 makeRatUsageProto(
                         CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
index 7094399..21ee476 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
@@ -16,9 +16,11 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
+import static com.android.internal.telephony.SmsDispatchersController.PendingRequest;
 import static com.android.internal.telephony.satellite.DatagramController.SATELLITE_ALIGN_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -47,10 +49,10 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.os.AsyncResult;
+import android.os.Binder;
 import android.os.Looper;
 import android.os.Message;
 import android.telephony.Rlog;
-import android.telephony.SubscriptionManager;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
 import android.testing.AndroidTestingRunner;
@@ -59,6 +61,7 @@
 import com.android.internal.R;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.SmsDispatchersController;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
@@ -91,6 +94,7 @@
             SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP;
     private static final int DATAGRAM_TYPE5 =
             SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED;
+    private static final int DATAGRAM_TYPE6 = SatelliteManager.DATAGRAM_TYPE_SMS;
 
     private static final String TEST_MESSAGE = "This is a test datagram message";
     private static final long TEST_EXPIRE_TIMER_SATELLITE_ALIGN = TimeUnit.SECONDS.toMillis(1);
@@ -108,17 +112,20 @@
 
     private TestDatagramDispatcher mDatagramDispatcherUT;
 
+    @Mock private SatelliteController mMockSatelliteController;
     @Mock private DatagramController mMockDatagramController;
     @Mock private DatagramReceiver mMockDatagramReceiver;
     @Mock private SatelliteModemInterface mMockSatelliteModemInterface;
     @Mock private ControllerMetricsStats mMockControllerMetricsStats;
     @Mock private SatelliteSessionController mMockSatelliteSessionController;
     @Mock private SessionMetricsStats mMockSessionMetricsStats;
+    @Mock private SmsDispatchersController mMockSmsDispatchersController;
 
     /** Variables required to send datagram in the unit tests. */
     LinkedBlockingQueue<Integer> mResultListener;
     SatelliteDatagram mDatagram;
     InOrder mInOrder;
+    private PendingRequest mPendingSms;
 
     private static final long TIMEOUT = 500;
     private List<Integer> mIntegerConsumerResult = new ArrayList<>();
@@ -142,6 +149,8 @@
         MockitoAnnotations.initMocks(this);
         logd(TAG + " Setup!");
 
+        replaceInstance(SatelliteController.class, "sInstance", null,
+                mMockSatelliteController);
         replaceInstance(DatagramController.class, "sInstance", null,
                 mMockDatagramController);
         replaceInstance(DatagramReceiver.class, "sInstance", null,
@@ -157,6 +166,7 @@
 
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         when(mFeatureFlags.satellitePersistentLogging()).thenReturn(true);
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
         mDatagramDispatcherUT = new TestDatagramDispatcher(mContext, Looper.myLooper(),
                 mFeatureFlags,
                 mMockDatagramController);
@@ -165,6 +175,10 @@
         mDatagram = new SatelliteDatagram(TEST_MESSAGE.getBytes());
         mInOrder = inOrder(mMockDatagramController);
         when(mMockDatagramController.isPollingInIdleState()).thenReturn(true);
+        when(mMockSatelliteController.getSatellitePhone()).thenReturn(mPhone);
+        when(mPhone.getSmsDispatchersController()).thenReturn(mMockSmsDispatchersController);
+        when(mMockSatelliteController.getSatelliteCarrierId()).thenReturn(UNKNOWN_CARRIER_ID);
+        mPendingSms = createPendingRequest();
     }
 
     @After
@@ -175,6 +189,7 @@
         mResultListener = null;
         mDatagram = null;
         mInOrder = null;
+        mPendingSms = null;
         super.tearDown();
     }
 
@@ -269,13 +284,13 @@
             processAllMessages();
             verifyZeroInteractions(mMockSatelliteModemInterface);
             mInOrder.verify(mMockDatagramController)
-                    .updateSendStatus(eq(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID),
+                    .updateSendStatus(eq(mPhone.getSubId()),
                             eq(datagramType),
                             eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED),
                             eq(1),
                             eq(SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE));
             mInOrder.verify(mMockDatagramController)
-                    .updateSendStatus(eq(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID),
+                    .updateSendStatus(eq(mPhone.getSubId()),
                             eq(datagramType),
                             eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
                             eq(SATELLITE_RESULT_SUCCESS));
@@ -307,6 +322,9 @@
                     eq(SatelliteServiceUtils.isLastSosMessage(datagramType)));
             assertTrue(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
             assertEquals(0, mResultListener.size());
+            if (datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
+                assertTrue(mDatagramDispatcherUT.isEmergencyCommunicationEstablished());
+            }
 
             mDatagramDispatcherUT.onSatelliteModemStateChanged(
                     SatelliteManager.SATELLITE_MODEM_STATE_OFF);
@@ -318,6 +336,7 @@
             assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
             verify(mMockSessionMetricsStats, times(1))
                     .addCountOfFailedOutgoingDatagram(anyInt(), anyInt());
+            assertFalse(mDatagramDispatcherUT.isEmergencyCommunicationEstablished());
         }
     }
 
@@ -766,6 +785,360 @@
         mDatagramDispatcherUT.setShouldSendDatagramToModemInDemoMode(null);
     }
 
+    @Test
+    public void testSendSms_success() {
+        int datagramType = DATAGRAM_TYPE6;
+        clearInvocations(mMockSmsDispatchersController);
+        clearInvocations(mMockDatagramController);
+        doReturn(true).when(mMockDatagramController).needsWaitingForSatelliteConnected(
+                eq(datagramType));
+        when(mMockDatagramController.getDatagramWaitTimeForConnectedState(eq(false)))
+                .thenReturn(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS);
+        when(mMockDatagramController.getDatagramWaitTimeForConnectedState(eq(true)))
+                .thenReturn(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_FOR_LAST_MESSAGE_TIMEOUT_MILLIS);
+
+        mDatagramDispatcherUT.sendSms(mPendingSms);
+        processAllMessages();
+        mInOrder.verify(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramType));
+        mInOrder.verify(mMockDatagramController).updateSendStatus(eq(SUB_ID), eq(datagramType),
+                eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT),
+                eq(1),
+                eq(SATELLITE_RESULT_SUCCESS));
+        mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState(
+                eq(SatelliteServiceUtils.isLastSosMessage(datagramType)));
+        verifyZeroInteractions(mMockSmsDispatchersController);
+        assertTrue(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
+
+        doReturn(false).when(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramType));
+        mDatagramDispatcherUT.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        processAllMessages();
+        mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+        mInOrder.verify(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramType));
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(SUB_ID), eq(datagramType),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        verify(mMockSmsDispatchersController).sendCarrierRoamingNbIotNtnText(eq(mPendingSms));
+
+        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.messageId, true);
+        processAllMessages();
+
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(SUB_ID), eq(datagramType),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS),
+                        eq(0),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(SUB_ID), eq(datagramType),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        verifyNoMoreInteractions(mMockDatagramController);
+    }
+
+    @Test
+    public void testSendSms_failure() {
+        int datagramType = DATAGRAM_TYPE6;
+        clearInvocations(mMockSmsDispatchersController);
+        clearInvocations(mMockDatagramController);
+        doReturn(false).when(mMockDatagramController).needsWaitingForSatelliteConnected(
+                eq(datagramType));
+        mDatagramDispatcherUT.sendSms(mPendingSms);
+        processAllMessages();
+
+        mInOrder.verify(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramType));
+        mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(mPhone.getSubId()), eq(datagramType),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        verify(mMockSmsDispatchersController).sendCarrierRoamingNbIotNtnText(eq(mPendingSms));
+
+        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.messageId, false);
+        processAllMessages();
+
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(mPhone.getSubId()),
+                        eq(datagramType),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED),
+                        eq(0),
+                        eq(SatelliteManager.SATELLITE_RESULT_NETWORK_ERROR));
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(mPhone.getSubId()),
+                        eq(datagramType),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+                        eq(SATELLITE_RESULT_SUCCESS));
+    }
+
+    @Test
+    public void testSendSms_handleEventDatagramWaitForConnectedStateTimedOut() {
+        int datagramType = DATAGRAM_TYPE6;
+        clearInvocations(mMockSmsDispatchersController);
+        clearInvocations(mMockDatagramController);
+        doReturn(true).when(mMockDatagramController).needsWaitingForSatelliteConnected(
+                eq(datagramType));
+        when(mMockDatagramController.getDatagramWaitTimeForConnectedState(eq(false)))
+                .thenReturn(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS);
+        when(mMockDatagramController.getDatagramWaitTimeForConnectedState(eq(true)))
+                .thenReturn(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_FOR_LAST_MESSAGE_TIMEOUT_MILLIS);
+
+        mDatagramDispatcherUT.sendSms(mPendingSms);
+        processAllMessages();
+        mInOrder.verify(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramType));
+        mInOrder.verify(mMockDatagramController).updateSendStatus(eq(SUB_ID), eq(datagramType),
+                eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT),
+                eq(1),
+                eq(SATELLITE_RESULT_SUCCESS));
+        mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState(
+                eq(SatelliteServiceUtils.isLastSosMessage(datagramType)));
+        verifyZeroInteractions(mMockSmsDispatchersController);
+        assertTrue(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
+
+        moveTimeForward(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS);
+        processAllMessages();
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(mPhone.getSubId()),
+                        eq(datagramType),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED),
+                        eq(1),
+                        eq(SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE));
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(mPhone.getSubId()),
+                        eq(datagramType),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        verify(mMockSmsDispatchersController).onSendCarrierRoamingNbIotNtnTextError(
+                eq(mPendingSms), eq(SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE));
+        assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
+
+        mDatagramDispatcherUT.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        processAllMessages();
+        verifyZeroInteractions(mMockSmsDispatchersController);
+    }
+
+    @Test
+    public void testSendSms_handleModemStateOff() {
+        int datagramType = DATAGRAM_TYPE6;
+        clearInvocations(mMockSmsDispatchersController);
+        clearInvocations(mMockDatagramController);
+        doReturn(true).when(mMockDatagramController).needsWaitingForSatelliteConnected(
+                eq(datagramType));
+        when(mMockDatagramController.getDatagramWaitTimeForConnectedState(eq(false)))
+                .thenReturn(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS);
+        when(mMockDatagramController.getDatagramWaitTimeForConnectedState(eq(true)))
+                .thenReturn(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_FOR_LAST_MESSAGE_TIMEOUT_MILLIS);
+
+        mDatagramDispatcherUT.sendSms(mPendingSms);
+        processAllMessages();
+        mInOrder.verify(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramType));
+        mInOrder.verify(mMockDatagramController).updateSendStatus(eq(SUB_ID), eq(datagramType),
+                eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT),
+                eq(1),
+                eq(SATELLITE_RESULT_SUCCESS));
+        mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState(
+                eq(SatelliteServiceUtils.isLastSosMessage(datagramType)));
+        verifyZeroInteractions(mMockSmsDispatchersController);
+        assertTrue(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
+
+        mDatagramDispatcherUT.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+        processAllMessages();
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(mPhone.getSubId()),
+                        eq(datagramType),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED),
+                        eq(1),
+                        eq(SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED));
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(mPhone.getSubId()),
+                        eq(datagramType),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        verify(mMockSmsDispatchersController).onSendCarrierRoamingNbIotNtnTextError(
+                eq(mPendingSms), eq(SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED));
+        assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
+    }
+
+    @Test
+    public void testSendSmsAndDatagram_prioritizeDatagram() {
+        int datagramTypeSms = DATAGRAM_TYPE6;
+        int datagramTypeSos = DATAGRAM_TYPE1;
+        doAnswer(invocation -> {
+            Message message = (Message) invocation.getArguments()[3];
+
+            mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
+                            new AsyncResult(message.obj, null, null))
+                    .sendToTarget();
+            return null;
+        }).when(mMockSatelliteModemInterface).sendSatelliteDatagram(any(SatelliteDatagram.class),
+                anyBoolean(), anyBoolean(), any(Message.class));
+
+        clearInvocations(mMockSmsDispatchersController);
+        clearInvocations(mMockDatagramController);
+        clearInvocations(mMockSessionMetricsStats);
+        clearInvocations(mMockSatelliteModemInterface);
+        doReturn(true).when(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramTypeSms));
+        doReturn(true).when(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramTypeSos));
+        when(mMockDatagramController.getDatagramWaitTimeForConnectedState(eq(false)))
+                .thenReturn(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS);
+        when(mMockDatagramController.getDatagramWaitTimeForConnectedState(eq(true)))
+                .thenReturn(
+                        TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_FOR_LAST_MESSAGE_TIMEOUT_MILLIS);
+        mResultListener.clear();
+
+        mDatagramDispatcherUT.sendSms(mPendingSms);
+        processAllMessages();
+        mInOrder.verify(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramTypeSms));
+        mInOrder.verify(mMockDatagramController).updateSendStatus(eq(SUB_ID), eq(datagramTypeSms),
+                eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT),
+                eq(1),
+                eq(SATELLITE_RESULT_SUCCESS));
+        mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState(
+                eq(SatelliteServiceUtils.isLastSosMessage(datagramTypeSms)));
+        verifyZeroInteractions(mMockSmsDispatchersController);
+        assertTrue(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
+
+        mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, datagramTypeSos, mDatagram,
+                true, mResultListener::offer);
+        processAllMessages();
+        mInOrder.verify(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramTypeSos));
+        mInOrder.verify(mMockDatagramController).updateSendStatus(eq(SUB_ID), eq(datagramTypeSos),
+                eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT),
+                eq(2),
+                eq(SATELLITE_RESULT_SUCCESS));
+        verifyZeroInteractions(mMockSatelliteModemInterface);
+        assertTrue(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
+
+        doReturn(false).when(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramTypeSms));
+        doReturn(false).when(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramTypeSos));
+        mDatagramDispatcherUT.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        processAllMessages();
+        // First SOS message is sent, then SMS is sent
+        mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+        mInOrder.verify(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramTypeSos));
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(SUB_ID), eq(datagramTypeSos),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(2),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(SUB_ID), eq(datagramTypeSos),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS),
+                        eq(1),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        verify(mMockSessionMetricsStats, times(1))
+                .addCountOfSuccessfulOutgoingDatagram(eq(datagramTypeSos));
+        verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
+                any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+        assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
+
+        // Send pending SMS
+        mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+        mInOrder.verify(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramTypeSms));
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(SUB_ID), eq(datagramTypeSms),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        verify(mMockSmsDispatchersController).sendCarrierRoamingNbIotNtnText(eq(mPendingSms));
+
+        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.messageId, true);
+        processAllMessages();
+
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(SUB_ID), eq(datagramTypeSms),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS),
+                        eq(0),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(SUB_ID), eq(datagramTypeSms),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        verifyNoMoreInteractions(mMockDatagramController);
+    }
+
+    @Test
+    public void testSendSmsAndDatagram_sendingSmsAlreadyInProgress() {
+        int datagramTypeSms = DATAGRAM_TYPE6;
+        int datagramTypeSos = DATAGRAM_TYPE1;
+        doAnswer(invocation -> {
+            Message message = (Message) invocation.getArguments()[3];
+
+            mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
+                            new AsyncResult(message.obj, null, null))
+                    .sendToTarget();
+            return null;
+        }).when(mMockSatelliteModemInterface).sendSatelliteDatagram(any(SatelliteDatagram.class),
+                anyBoolean(), anyBoolean(), any(Message.class));
+
+        clearInvocations(mMockSmsDispatchersController);
+        clearInvocations(mMockDatagramController);
+        doReturn(false).when(mMockDatagramController).needsWaitingForSatelliteConnected(
+                eq(datagramTypeSms));
+        mDatagramDispatcherUT.sendSms(mPendingSms);
+        processAllMessages();
+
+        mInOrder.verify(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramTypeSms));
+        mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(mPhone.getSubId()), eq(datagramTypeSms),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        verify(mMockSmsDispatchersController).sendCarrierRoamingNbIotNtnText(eq(mPendingSms));
+
+        // Sending SMS is already in progress, so datagram should not be sent
+        mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, datagramTypeSos, mDatagram,
+                true, mResultListener::offer);
+        processAllMessages();
+        verifyZeroInteractions(mMockSatelliteModemInterface);
+
+        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.messageId, true);
+        processAllMessages();
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(SUB_ID), eq(datagramTypeSms),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS),
+                        eq(1),
+                        eq(SATELLITE_RESULT_SUCCESS));
+
+        // Send pending datagram
+        mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+        mInOrder.verify(mMockDatagramController)
+                .needsWaitingForSatelliteConnected(eq(datagramTypeSos));
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(SUB_ID), eq(datagramTypeSos),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(SUB_ID), eq(datagramTypeSos),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS),
+                        eq(0),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        mInOrder.verify(mMockDatagramController)
+                .updateSendStatus(eq(SUB_ID), eq(datagramTypeSos),
+                        eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+                        eq(SATELLITE_RESULT_SUCCESS));
+        verify(mMockSessionMetricsStats, times(1))
+                .addCountOfSuccessfulOutgoingDatagram(eq(datagramTypeSos));
+        verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
+                any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+        assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
+    }
+
     private boolean waitForIntegerConsumerResult(int expectedNumberOfEvents) {
         for (int i = 0; i < expectedNumberOfEvents; i++) {
             try {
@@ -819,4 +1192,18 @@
     private static void loge(String message) {
         Rlog.e(TAG, message);
     }
+
+    private static <T> ArrayList<T> asArrayList(T object) {
+        ArrayList<T> list = new ArrayList<>();
+        list.add(object);
+        return list;
+    }
+
+    private PendingRequest createPendingRequest() {
+        return new PendingRequest(
+                SmsDispatchersController.PendingRequest.TYPE_TEXT, null, "test-app",
+                Binder.getCallingUserHandle().getIdentifier(), "1111", "2222", asArrayList(null),
+                asArrayList(null), false, null, 0, asArrayList("text"), null, false, 0, false,
+                10, 100L, false);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
index 947661b..e964ced 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
+
 import static com.android.internal.telephony.satellite.DatagramController.SATELLITE_ALIGN_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -48,6 +50,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.provider.Telephony;
+import android.telephony.SubscriptionManager;
 import android.telephony.satellite.ISatelliteDatagramCallback;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
@@ -78,7 +81,7 @@
 @TestableLooper.RunWithLooper
 public class DatagramReceiverTest extends TelephonyTest {
     private static final String TAG = "DatagramReceiverTest";
-    private static final int SUB_ID = 0;
+    private static final int SUB_ID = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
     private static final String TEST_MESSAGE = "This is a test datagram message";
     private static final long TEST_EXPIRE_TIMER_SATELLITE_ALIGN = TimeUnit.SECONDS.toMillis(1);
     private static final long TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS =
@@ -144,6 +147,7 @@
         when(mMockDatagramController.isPollingInIdleState()).thenReturn(true);
         when(mMockDatagramController.needsWaitingForSatelliteConnected(
                 eq(SatelliteManager.DATAGRAM_TYPE_UNKNOWN))).thenReturn(false);
+        when(mMockSatelliteController.getSatelliteCarrierId()).thenReturn(UNKNOWN_CARRIER_ID);
         processAllMessages();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DemoSimulatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DemoSimulatorTest.java
index 319e39f..2b18468 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DemoSimulatorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DemoSimulatorTest.java
@@ -119,7 +119,7 @@
         moveToNotConnectedState();
 
         verify(mISatelliteListener).onSatelliteModemStateChanged(
-                SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+                SatelliteModemState.SATELLITE_MODEM_STATE_OUT_OF_SERVICE);
         ArgumentCaptor<NtnSignalStrength> ntnSignalStrength = ArgumentCaptor.forClass(
                 NtnSignalStrength.class);
         verify(mISatelliteListener).onNtnSignalStrengthChanged(ntnSignalStrength.capture());
@@ -156,9 +156,9 @@
         moveToConnectedState();
 
         verify(mISatelliteListener).onSatelliteModemStateChanged(
-                SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+                SatelliteModemState.SATELLITE_MODEM_STATE_OUT_OF_SERVICE);
         verify(mISatelliteListener).onSatelliteModemStateChanged(
-                SatelliteModemState.SATELLITE_MODEM_STATE_CONNECTED);
+                SatelliteModemState.SATELLITE_MODEM_STATE_IN_SERVICE);
         ArgumentCaptor<NtnSignalStrength> ntnSignalStrength = ArgumentCaptor.forClass(
                 NtnSignalStrength.class);
         verify(mISatelliteListener, times(2))
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java
index 36d32fe..a228617 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java
@@ -39,6 +39,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.UserHandle;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
 import android.telephony.satellite.PointingInfo;
 import android.telephony.satellite.SatelliteManager;
@@ -196,6 +197,11 @@
             }
         }
 
+        @Override
+        public void onSendDatagramRequested(int datagramType) {
+            logd("onSendDatagramRequested: datagramType=" + datagramType);
+        }
+
         public int getDatagramType() {
             return mDatagramType;
         }
@@ -316,7 +322,7 @@
     public void testStartPointingUI() throws Exception {
         ArgumentCaptor<Intent> startedIntentCaptor = ArgumentCaptor.forClass(Intent.class);
         mPointingAppController.startPointingUI(true, true, true);
-        verify(mContext).startActivity(startedIntentCaptor.capture());
+        verify(mContext).startActivityAsUser(startedIntentCaptor.capture(), eq(UserHandle.CURRENT));
         Intent intent = startedIntentCaptor.getValue();
         assertEquals(KEY_POINTING_UI_PACKAGE_NAME, intent.getComponent().getPackageName());
         assertEquals(KEY_POINTING_UI_CLASS_NAME, intent.getComponent().getClassName());
@@ -332,10 +338,12 @@
     @Test
     public void testRestartPointingUi() throws Exception {
         mPointingAppController.startPointingUI(true, false, true);
-        mInOrderForPointingUi.verify(mContext).startActivity(any(Intent.class));
+        mInOrderForPointingUi.verify(mContext).startActivityAsUser(any(Intent.class),
+                eq(UserHandle.CURRENT));
         testRestartPointingUi(true, false, true);
         mPointingAppController.startPointingUI(false, true, false);
-        mInOrderForPointingUi.verify(mContext).startActivity(any(Intent.class));
+        mInOrderForPointingUi.verify(mContext).startActivityAsUser(any(Intent.class),
+                eq(UserHandle.CURRENT));
         testRestartPointingUi(false, true, false);
     }
 
@@ -346,7 +354,8 @@
             .getPackagesForUid(anyInt());
         mPointingAppController.mUidImportanceListener.onUidImportance(1, IMPORTANCE_GONE);
         ArgumentCaptor<Intent> restartedIntentCaptor = ArgumentCaptor.forClass(Intent.class);
-        mInOrderForPointingUi.verify(mContext).startActivity(restartedIntentCaptor.capture());
+        mInOrderForPointingUi.verify(mContext).startActivityAsUser(restartedIntentCaptor.capture(),
+                eq(UserHandle.CURRENT));
         Intent restartIntent = restartedIntentCaptor.getValue();
         assertEquals(KEY_POINTING_UI_PACKAGE_NAME, restartIntent.getComponent().getPackageName());
         assertEquals(KEY_POINTING_UI_CLASS_NAME, restartIntent.getComponent().getClassName());
@@ -413,7 +422,6 @@
         TestSatelliteTransmissionUpdateCallback callback2 = new
                 TestSatelliteTransmissionUpdateCallback();
         int subId1 = 3;
-        int subId2 = 4;
         mPointingAppController.registerForSatelliteTransmissionUpdates(subId1, callback1);
         mInOrder.verify(mMockSatelliteModemInterface).registerForSatellitePositionInfoChanged(any(),
                 eq(1), eq(null));
@@ -424,16 +432,6 @@
                 .registerForSatellitePositionInfoChanged(any(), eq(1), eq(null));
         mInOrder.verify(mMockSatelliteModemInterface, never())
                 .registerForDatagramTransferStateChanged(any(), eq(4), eq(null));
-        mPointingAppController.registerForSatelliteTransmissionUpdates(subId2, callback1);
-        mInOrder.verify(mMockSatelliteModemInterface).registerForSatellitePositionInfoChanged(any(),
-                eq(1), eq(null));
-        mInOrder.verify(mMockSatelliteModemInterface).registerForDatagramTransferStateChanged(any(),
-                eq(4), eq(null));
-        mPointingAppController.registerForSatelliteTransmissionUpdates(subId2, callback2);
-        mInOrder.verify(mMockSatelliteModemInterface, never())
-                .registerForSatellitePositionInfoChanged(any(), eq(1), eq(null));
-        mInOrder.verify(mMockSatelliteModemInterface, never())
-                .registerForDatagramTransferStateChanged(any(), eq(4), eq(null));
         mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId1,
                 mResultListener::offer, callback1);
         processAllMessages();
@@ -446,22 +444,6 @@
                 any(Handler.class));
         mInOrder.verify(mMockSatelliteModemInterface).unregisterForDatagramTransferStateChanged(
                 any(Handler.class));
-        mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId2,
-                mResultListener::offer, callback1);
-        processAllMessages();
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_SUCCESS);
-        mResultListener.remove();
-        mInOrder.verify(mMockSatelliteModemInterface, never())
-                .unregisterForSatellitePositionInfoChanged(any(Handler.class));
-        mInOrder.verify(mMockSatelliteModemInterface, never())
-                .unregisterForDatagramTransferStateChanged(any(Handler.class));
-        mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId2,
-                mResultListener::offer, callback2);
-        processAllMessages();
-        mInOrder.verify(mMockSatelliteModemInterface).unregisterForSatellitePositionInfoChanged(
-                any(Handler.class));
-        mInOrder.verify(mMockSatelliteModemInterface).unregisterForDatagramTransferStateChanged(
-                any(Handler.class));
         mInOrder = null;
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
index d9ef95a..7f31f30 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -16,9 +16,14 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ESOS_SUPPORTED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_NIDD_APN_NAME_STRING;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL;
 import static android.telephony.NetworkRegistrationInfo.SERVICE_TYPE_DATA;
 import static android.telephony.SubscriptionManager.SATELLITE_ENTITLEMENT_STATUS;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_GOOD;
@@ -26,7 +31,10 @@
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_POOR;
 import static android.telephony.satellite.SatelliteManager.KEY_DEMO_MODE_ENABLED;
+import static android.telephony.satellite.SatelliteManager.KEY_EMERGENCY_MODE_ENABLED;
 import static android.telephony.satellite.SatelliteManager.KEY_NTN_SIGNAL_STRENGTH;
+import static android.telephony.satellite.SatelliteManager.KEY_PROVISION_SATELLITE_TOKENS;
+import static android.telephony.satellite.SatelliteManager.KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_CAPABILITIES;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_ENABLED;
@@ -41,18 +49,21 @@
 import static android.telephony.satellite.SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_DISABLE_IN_PROGRESS;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ERROR;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_ERROR;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT;
-import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_AUTHORIZED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NO_RESOURCES;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_IN_PROGRESS;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED;
@@ -64,6 +75,7 @@
 import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_MODE_ENABLED_TRUE;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -81,6 +93,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
@@ -89,9 +102,12 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.NotificationManager;
 import android.content.Context;
+import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.res.Resources;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -99,14 +115,18 @@
 import android.os.ICancellationSignal;
 import android.os.Looper;
 import android.os.Message;
+import android.os.OutcomeReceiver;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellSignalStrength;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.telephony.satellite.INtnSignalStrengthCallback;
 import android.telephony.satellite.ISatelliteCapabilitiesCallback;
 import android.telephony.satellite.ISatelliteDatagramCallback;
@@ -119,6 +139,10 @@
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.SatelliteManager.SatelliteException;
+import android.telephony.satellite.SatelliteModemEnableRequestAttributes;
+import android.telephony.satellite.SatelliteSubscriberInfo;
+import android.telephony.satellite.SatelliteSubscriberProvisionStatus;
+import android.telephony.satellite.SatelliteSubscriptionInfo;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Pair;
@@ -137,6 +161,7 @@
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
 import com.android.internal.telephony.satellite.metrics.ProvisionMetricsStats;
 import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 
 import org.junit.After;
@@ -145,15 +170,16 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.lang.reflect.Field;
 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.Optional;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Semaphore;
@@ -177,6 +203,8 @@
     private static final int[] ACTIVE_SUB_IDS = {SUB_ID};
     private static final int TEST_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMEOUT_MILLIS =
             (int) TimeUnit.SECONDS.toMillis(60);
+    private static final int TEST_WAIT_FOR_CELLULAR_MODEM_OFF_TIMEOUT_MILLIS =
+            (int) TimeUnit.SECONDS.toMillis(60);
 
     private static final String SATELLITE_PLMN = "00103";
     private List<Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener>>
@@ -187,6 +215,7 @@
     private PersistableBundle mCarrierConfigBundle;
     private ServiceState mServiceState2;
 
+    @Mock private SatelliteController mMockSatelliteController;
     @Mock private DatagramController mMockDatagramController;
     @Mock private SatelliteModemInterface mMockSatelliteModemInterface;
     @Mock private SatelliteSessionController mMockSatelliteSessionController;
@@ -205,6 +234,9 @@
     @Mock private CellSignalStrength mCellSignalStrength;
     @Mock private SatelliteConfig mMockConfig;
     @Mock private DemoSimulator mMockDemoSimulator;
+    @Mock private Resources mResources;
+    @Mock private SubscriptionManager mSubscriptionManager;
+    @Mock private SubscriptionInfo mSubscriptionInfo;
 
     private Semaphore mIIntegerConsumerSemaphore = new Semaphore(0);
     private IIntegerConsumer mIIntegerConsumer = new IIntegerConsumer.Stub() {
@@ -233,6 +265,17 @@
     private SatelliteCapabilities mEmptySatelliteCapabilities = new SatelliteCapabilities(
             new HashSet<>(), mIsPointingRequired, MAX_BYTES_PER_OUT_GOING_DATAGRAM,
             new HashMap<>());
+    final int mCarrierId = 0;
+    final String mImsi = "1234567890123";
+    final String mNiddApn = "testApn";
+    final String mMsisdn = "0987654321";
+    final String mSubscriberId = mImsi.substring(0, 6) + mMsisdn;
+    final String mIccId = "1000000000000001";
+    final String mIccId2 = "2000000000000002";
+    final String mImsi2 = "2345678901234";
+    final String mMsisdn2 = "9876543210";
+    final String mSubscriberId2 = mIccId2;
+
     private Semaphore mSatelliteCapabilitiesSemaphore = new Semaphore(0);
     private SatelliteCapabilities mQueriedSatelliteCapabilities = null;
     private int mQueriedSatelliteCapabilitiesResultCode = SATELLITE_RESULT_SUCCESS;
@@ -453,12 +496,31 @@
         }
     };
 
+    private boolean mRequestIsEmergency = false;
+    private ResultReceiver mRequestIsEmergencyReceiver = new ResultReceiver(null) {
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            logd("requestIsEmergencyReceiver: resultCode=" + resultCode);
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                if (resultData.containsKey(KEY_EMERGENCY_MODE_ENABLED)) {
+                    mRequestIsEmergency = resultData.getBoolean(
+                            KEY_EMERGENCY_MODE_ENABLED);
+                } else {
+                    loge("KEY_EMERGENCY_MODE_ENABLED does not exist.");
+
+                }
+            }
+        }
+    };
+
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
         MockitoAnnotations.initMocks(this);
         logd(TAG + " Setup!");
 
+        replaceInstance(SatelliteController.class, "sInstance", null,
+                mMockSatelliteController);
         replaceInstance(DatagramController.class, "sInstance", null,
                 mMockDatagramController);
         replaceInstance(SatelliteModemInterface.class, "sInstance", null,
@@ -480,7 +542,9 @@
                 null, mMockTelephonyConfigUpdateInstallReceiver);
         replaceInstance(DemoSimulator.class, "sInstance", null, mMockDemoSimulator);
 
-        mServiceState2 = Mockito.mock(ServiceState.class);
+        doNothing().when(mMockSatelliteController).moveSatelliteToOffStateAndCleanUpResources(
+                SATELLITE_RESULT_REQUEST_ABORTED);
+        mServiceState2 = mock(ServiceState.class);
         when(mPhone.getServiceState()).thenReturn(mServiceState);
         when(mPhone.getSubId()).thenReturn(SUB_ID);
         when(mPhone.getPhoneId()).thenReturn(0);
@@ -494,6 +558,9 @@
         mContextFixture.putIntResource(
                 R.integer.config_wait_for_satellite_enabling_response_timeout_millis,
                 TEST_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMEOUT_MILLIS);
+        mContextFixture.putIntResource(
+                R.integer.config_satellite_wait_for_cellular_modem_off_timeout_millis,
+                TEST_WAIT_FOR_CELLULAR_MODEM_OFF_TIMEOUT_MILLIS);
         doReturn(ACTIVE_SUB_IDS).when(mMockSubscriptionManagerService).getActiveSubIdList(true);
 
         mCarrierConfigBundle = mContextFixture.getCarrierConfigBundle();
@@ -521,6 +588,7 @@
                 .onSatelliteEnabledStateChanged(anyBoolean());
         doNothing().when(mMockSatelliteSessionController).onSatelliteModemStateChanged(anyInt());
         doNothing().when(mMockSatelliteSessionController).setDemoMode(anyBoolean());
+        doNothing().when(mMockSatelliteSessionController).cleanUpResource();
         doNothing().when(mMockControllerMetricsStats).onSatelliteEnabled();
         doNothing().when(mMockControllerMetricsStats).reportServiceEnablementSuccessCount();
         doNothing().when(mMockControllerMetricsStats).reportServiceEnablementFailCount();
@@ -538,15 +606,20 @@
                 .when(mMockSessionMetricsStats).setSessionDurationSec(anyInt());
         doReturn(mMockSessionMetricsStats)
                 .when(mMockSessionMetricsStats).setIsDemoMode(anyBoolean());
+        doReturn(mMockSessionMetricsStats)
+                .when(mMockSessionMetricsStats).setCarrierId(anyInt());
         doNothing().when(mMockSessionMetricsStats).reportSessionMetrics();
 
         doReturn(mMockProvisionMetricsStats).when(mMockProvisionMetricsStats)
                 .setResultCode(anyInt());
         doReturn(mMockProvisionMetricsStats).when(mMockProvisionMetricsStats)
                 .setIsProvisionRequest(anyBoolean());
+        doReturn(mMockProvisionMetricsStats).when(mMockProvisionMetricsStats)
+                .setCarrierId(anyInt());
         doNothing().when(mMockProvisionMetricsStats).reportProvisionMetrics();
         doNothing().when(mMockControllerMetricsStats).reportDeprovisionCount(anyInt());
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(false);
         doReturn(mSST).when(mPhone).getServiceStateTracker();
         doReturn(mSST).when(mPhone2).getServiceStateTracker();
         doReturn(mServiceState).when(mSST).getServiceState();
@@ -556,10 +629,6 @@
                 Context.NOTIFICATION_SERVICE);
         mSatelliteControllerUT =
                 new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
-        verify(mMockSatelliteModemInterface).registerForSatelliteProvisionStateChanged(
-                any(Handler.class),
-                eq(26) /* EVENT_SATELLITE_PROVISION_STATE_CHANGED */,
-                eq(null));
         verify(mMockSatelliteModemInterface).registerForPendingDatagrams(
                 any(Handler.class),
                 eq(27) /* EVENT_PENDING_DATAGRAMS */,
@@ -571,6 +640,9 @@
 
         doReturn(mMockConfigParser).when(mMockTelephonyConfigUpdateInstallReceiver)
                 .getConfigParser(ConfigProviderAdaptor.DOMAIN_SATELLITE);
+        doReturn(mSubscriptionInfo).when(mMockSubscriptionManagerService).getSubscriptionInfo(
+                anyInt());
+        doReturn("").when(mSubscriptionInfo).getIccId();
     }
 
     @After
@@ -581,18 +653,45 @@
     }
 
     @Test
+    public void testShouldTurnOffCarrierSatelliteForEmergencyCall() throws Exception {
+        DatagramController datagramController = mock(DatagramController.class);
+        replaceInstance(SatelliteController.class, "mDatagramController",
+                mSatelliteControllerUT, datagramController);
+
+        // Verify should turn off satellite
+        mCarrierConfigBundle.putBoolean(
+                KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL, true);
+        doReturn(false).when(datagramController).isEmergencyCommunicationEstablished();
+        invokeCarrierConfigChanged();
+        mSatelliteControllerUT.setSatellitePhone(1);
+        processAllMessages();
+
+        assertTrue(mSatelliteControllerUT.shouldTurnOffCarrierSatelliteForEmergencyCall());
+
+        // Verify should NOT turn off satellite
+        mCarrierConfigBundle.putBoolean(
+                KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL, false);
+        doReturn(true).when(datagramController).isEmergencyCommunicationEstablished();
+        invokeCarrierConfigChanged();
+        mSatelliteControllerUT.setSatellitePhone(1);
+        processAllMessages();
+
+        assertFalse(mSatelliteControllerUT.shouldTurnOffCarrierSatelliteForEmergencyCall());
+    }
+
+    @Test
     public void testRequestTimeForNextSatelliteVisibility() {
         mSatelliteVisibilityTimeSemaphore.drainPermits();
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
         assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, mQueriedSatelliteVisibilityTimeResultCode);
 
         resetSatelliteControllerUT();
-        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
@@ -604,11 +703,11 @@
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestTimeForNextSatelliteVisibility(mSatelliteNextVisibilityTime,
                 SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
-        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+        assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
                 mQueriedSatelliteVisibilityTimeResultCode);
 
         resetSatelliteControllerUT();
@@ -618,7 +717,7 @@
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestTimeForNextSatelliteVisibility(mSatelliteNextVisibilityTime,
                 SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
@@ -629,7 +728,7 @@
         provisionSatelliteService();
         setUpResponseForRequestTimeForNextSatelliteVisibility(mSatelliteNextVisibilityTime,
                 SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
@@ -640,7 +739,7 @@
         provisionSatelliteService();
         setUpNullResponseForRequestTimeForNextSatelliteVisibility(
                 SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
@@ -651,7 +750,7 @@
         provisionSatelliteService();
         setUpNullResponseForRequestTimeForNextSatelliteVisibility(
                 SATELLITE_RESULT_INVALID_MODEM_STATE);
-        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(SUB_ID,
+        mSatelliteControllerUT.requestTimeForNextSatelliteVisibility(
                 mSatelliteVisibilityTimeReceiver);
         processAllMessages();
         assertTrue(waitForRequestTimeForNextSatelliteVisibilityResult(1));
@@ -706,6 +805,87 @@
         processAllMessages();
         verify(mMockSatelliteModemInterface, times(5))
                 .requestIsSatelliteSupported(any(Message.class));
+        assertTrue(mSatelliteControllerUT.isRadioOn());
+        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
+        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());
+
+        // Radio is off during TN -> NTN image switch, SatelliteController should not set radio
+        // state to OFF
+        setRadioPower(false);
+        processAllMessages();
+        assertTrue(mSatelliteControllerUT.isRadioOn());
+        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
+        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());
+
+        // Turn on radio
+        setRadioPower(true);
+        processAllMessages();
+        assertTrue(mSatelliteControllerUT.isRadioOn());
+        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
+        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());
+
+        // APM is triggered
+        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
+        processAllMessages();
+        assertTrue(mSatelliteControllerUT.isRadioOn());
+        assertTrue(mSatelliteControllerUT.isRadioOffRequested());
+        assertTrue(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());
+
+        // SatelliteController should set the radio state to OFF
+        setRadioPower(false);
+        processAllMessages();
+        assertFalse(mSatelliteControllerUT.isRadioOn());
+        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
+        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());
+
+        // Turn on radio
+        setRadioPower(true);
+        processAllMessages();
+        assertTrue(mSatelliteControllerUT.isRadioOn());
+        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
+        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());
+
+        // APM is triggered
+        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
+        processAllMessages();
+        assertTrue(mSatelliteControllerUT.isRadioOn());
+        assertTrue(mSatelliteControllerUT.isRadioOffRequested());
+        assertTrue(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());
+
+        // Modem fails to power off radio. APM is disabled
+        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(true);
+        processAllMessages();
+        assertTrue(mSatelliteControllerUT.isRadioOn());
+        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
+        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());
+
+        // APM is triggered
+        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
+        processAllMessages();
+        assertTrue(mSatelliteControllerUT.isRadioOn());
+        assertTrue(mSatelliteControllerUT.isRadioOffRequested());
+        assertTrue(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());
+
+        // The timer WaitForCellularModemOff time out
+        moveTimeForward(TEST_WAIT_FOR_CELLULAR_MODEM_OFF_TIMEOUT_MILLIS);
+        processAllMessages();
+        assertTrue(mSatelliteControllerUT.isRadioOn());
+        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
+        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());
+
+        // APM is triggered
+        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
+        processAllMessages();
+        assertTrue(mSatelliteControllerUT.isRadioOn());
+        assertTrue(mSatelliteControllerUT.isRadioOffRequested());
+        assertTrue(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());
+
+        // Modem failed to power off the radio
+        mSatelliteControllerUT.onPowerOffCellularRadioFailed();
+        processAllMessages();
+        assertTrue(mSatelliteControllerUT.isRadioOn());
+        assertFalse(mSatelliteControllerUT.isRadioOffRequested());
+        assertFalse(mSatelliteControllerUT.isWaitForCellularModemOffTimerStarted());
     }
 
     @Test
@@ -741,12 +921,13 @@
 
     @Test
     public void testRequestSatelliteEnabled() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
         mIsSatelliteEnabledSemaphore.drainPermits();
 
         // Fail to enable satellite when SatelliteController is not fully loaded yet.
         mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mIIntegerConsumerSemaphore.drainPermits();
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
@@ -754,47 +935,61 @@
 
         // Fail to enable satellite when the device does not support satellite.
         mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
 
         // Fail to enable satellite when the device is not provisioned yet.
         mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
         resetSatelliteControllerUT();
         verify(mMockSatelliteSessionController, times(1)).onSatelliteEnabledStateChanged(eq(false));
-        verify(mMockSatelliteSessionController, times(1)).setDemoMode(eq(false));
-        verify(mMockDatagramController, times(1)).setDemoMode(eq(false));
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
                 (long) mIIntegerConsumerResults.get(0));
 
-        sendProvisionedStateChangedEvent(true, null);
+        setProvisionedState(true);
         processAllMessages();
         verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
 
-        // Successfully enable satellite
+        // Fail to enable satellite when the emergency call is in progress
         mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
         mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        doReturn(true).when(mTelecomManager).isInEmergencyCall();
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS,
+                (long) mIIntegerConsumerResults.get(0));
+        doReturn(false).when(mTelecomManager).isInEmergencyCall();
+
+        // Successfully enable satellite
+        mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
+        mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
+        mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
+        setUpResponseForRequestSatelliteEnabled(true, false, true, SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, true, mIIntegerConsumer);
         mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
+        verify(mMockSatelliteSessionController, times(1)).onEmergencyModeChanged(eq(true));
         assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
         assertTrue(mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled);
         assertEquals(
@@ -806,29 +1001,35 @@
         verify(mMockControllerMetricsStats, times(1)).reportServiceEnablementSuccessCount();
 
         // Successfully disable satellite when radio is turned off.
+        clearInvocations(mMockSatelliteSessionController);
+        clearInvocations(mMockDatagramController);
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
+        when(mMockSatelliteSessionController.isInDisablingState()).thenReturn(true);
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
         mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
         setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS);
         setRadioPower(false);
-        mSatelliteControllerUT.onCellularRadioPowerOffRequested();
+        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
         processAllMessages();
         sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_OFF, null);
         processAllMessages();
         verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
+        verify(mMockSatelliteSessionController, times(1)).onEmergencyModeChanged(eq(false));
         assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
         assertTrue(mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled);
         assertEquals(
                 SATELLITE_MODE_ENABLED_FALSE, mSatelliteControllerUT.satelliteModeSettingValue);
         verify(mMockSatelliteSessionController, times(2)).onSatelliteEnabledStateChanged(eq(false));
-        verify(mMockSatelliteSessionController, times(3)).setDemoMode(eq(false));
-        verify(mMockDatagramController, times(3)).setDemoMode(eq(false));
+        verify(mMockSatelliteSessionController, times(2)).setDemoMode(eq(false));
+        verify(mMockDatagramController, times(2)).setDemoMode(eq(false));
         verify(mMockControllerMetricsStats, times(1)).onSatelliteDisabled();
+        when(mMockSatelliteSessionController.isInDisablingState()).thenReturn(false);
 
         // Fail to enable satellite when radio is off.
         mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         // Radio is not on, can not enable satellite
@@ -840,13 +1041,13 @@
 
         // Fail to enable satellite with an error response from modem when radio is on.
         mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
         clearInvocations(mMockPointingAppController);
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
         mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
         setUpResponseForRequestSatelliteEnabled(true, false, false,
                 SATELLITE_RESULT_INVALID_MODEM_STATE);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
@@ -859,11 +1060,11 @@
 
         // Successfully enable satellite when radio is on.
         mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
         mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -871,16 +1072,16 @@
         assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
         assertTrue(mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled);
         assertEquals(SATELLITE_MODE_ENABLED_TRUE, mSatelliteControllerUT.satelliteModeSettingValue);
-        verify(mMockSatelliteSessionController, times(2)).onSatelliteEnabledStateChanged(eq(true));
-        verify(mMockSatelliteSessionController, times(4)).setDemoMode(eq(false));
-        verify(mMockDatagramController, times(4)).setDemoMode(eq(false));
+        verify(mMockSatelliteSessionController, times(1)).onSatelliteEnabledStateChanged(eq(true));
+        verify(mMockSatelliteSessionController, times(3)).setDemoMode(eq(false));
+        verify(mMockDatagramController, times(3)).setDemoMode(eq(false));
         verify(mMockControllerMetricsStats, times(2)).onSatelliteEnabled();
         verify(mMockControllerMetricsStats, times(2)).reportServiceEnablementSuccessCount();
 
         // Successfully enable satellite when it is already enabled.
         mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mIIntegerConsumerSemaphore.drainPermits();
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -888,8 +1089,8 @@
 
         // Fail to enable satellite with a different demo mode when it is already enabled.
         mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, true, false,
-                mIIntegerConsumer);
+        mIIntegerConsumerSemaphore.drainPermits();
+        mSatelliteControllerUT.requestSatelliteEnabled(true, true, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_INVALID_ARGUMENTS, (long) mIIntegerConsumerResults.get(0));
@@ -897,9 +1098,9 @@
 
         // Successfully disable satellite.
         mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
         setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(false, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -907,8 +1108,8 @@
 
         // Disable satellite when satellite is already disabled.
         mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, false,
-                mIIntegerConsumer);
+        mIIntegerConsumerSemaphore.drainPermits();
+        mSatelliteControllerUT.requestSatelliteEnabled(false, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -916,8 +1117,8 @@
 
         // Disable satellite with a different demo mode when satellite is already disabled.
         mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, true, false,
-                mIIntegerConsumer);
+        mIIntegerConsumerSemaphore.drainPermits();
+        mSatelliteControllerUT.requestSatelliteEnabled(false, true, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -925,110 +1126,229 @@
 
         // Send a second request while the first request in progress
         mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
         setUpNoResponseForRequestSatelliteEnabled(true, false, false);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertFalse(waitForIIntegerConsumerResult(1));
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_REQUEST_IN_PROGRESS, (long) mIIntegerConsumerResults.get(0));
 
         mIIntegerConsumerResults.clear();
-        resetSatelliteControllerUTToSupportedAndProvisionedState();
-        // Should receive callback for the above request when satellite modem is turned off.
+        mIIntegerConsumerSemaphore.drainPermits();
+        resetSatelliteControllerUT();
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setProvisionedState(false);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setProvisionedState(true);
+        processAllMessages();
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        // The enable request should be aborted when satellite modem move to OFF state.
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_REQUEST_ABORTED, (long) mIIntegerConsumerResults.get(0));
+
+        // Successfully enable satellite
+        mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
+        setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
 
         // Move to satellite-disabling in progress.
+        mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
         setUpNoResponseForRequestSatelliteEnabled(false, false, false);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(false, false, false, mIIntegerConsumer);
         processAllMessages();
         assertFalse(waitForIIntegerConsumerResult(1));
 
         // Disable is in progress. Thus, a new request to enable satellite will be rejected.
         mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mIIntegerConsumerSemaphore.drainPermits();
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
+        when(mMockSatelliteSessionController.isInDisablingState()).thenReturn(true);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_ERROR, (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_DISABLE_IN_PROGRESS, (long) mIIntegerConsumerResults.get(0));
 
         mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
         resetSatelliteControllerUTToOffAndProvisionedState();
-        // Should receive callback for the above request when satellite modem is turned off.
-        assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
-
-        verify(mMockSessionMetricsStats, times(15)).setInitializationResult(anyInt());
-        verify(mMockSessionMetricsStats, times(15)).setSatelliteTechnology(anyInt());
-        verify(mMockSessionMetricsStats, times(3)).setInitializationProcessingTime(anyLong());
-        verify(mMockSessionMetricsStats, times(2)).setTerminationResult(anyInt());
-        verify(mMockSessionMetricsStats, times(2)).setTerminationProcessingTime(anyLong());
-        verify(mMockSessionMetricsStats, times(2)).setSessionDurationSec(anyInt());
-        verify(mMockSessionMetricsStats, times(15)).reportSessionMetrics();
+        when(mMockSatelliteSessionController.isInDisablingState()).thenReturn(false);
 
         /**
          * Make areAllRadiosDisabled return false and move mWaitingForRadioDisabled to true, which
          * will lead to no response for requestSatelliteEnabled.
          */
         mSatelliteControllerUT.allRadiosDisabled = false;
+        mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertFalse(waitForIIntegerConsumerResult(1));
 
-        resetSatelliteControllerUTEnabledState();
         mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
         setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(false, false, false, mIIntegerConsumer);
         processAllMessages();
         // We should receive 2 callbacks for the above 2 requests.
         assertTrue(waitForIIntegerConsumerResult(2));
+        // Successful result for disable request
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
-        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(1));
+        // The enable request should be aborted after getting the successful confirmation of the
+        // disable request.
+        assertEquals(SATELLITE_RESULT_REQUEST_ABORTED, (long) mIIntegerConsumerResults.get(1));
 
         resetSatelliteControllerUTToOffAndProvisionedState();
 
         // Repeat the same test as above but with error response from modem for the second request
         mSatelliteControllerUT.allRadiosDisabled = false;
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
+        // No response for the enable request because all radios are not disabled yet
         assertFalse(waitForIIntegerConsumerResult(1));
 
-        resetSatelliteControllerUTEnabledState();
         mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
         setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_NO_RESOURCES);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(false, false, false, mIIntegerConsumer);
         processAllMessages();
-        // We should receive 2 callbacks for the above 2 requests.
-        assertTrue(waitForIIntegerConsumerResult(2));
-        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
-        assertEquals(SATELLITE_RESULT_NO_RESOURCES, (long) mIIntegerConsumerResults.get(1));
+        // We should receive result for the disable request.
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_NO_RESOURCES, (long) mIIntegerConsumerResults.get(0));
         mSatelliteControllerUT.allRadiosDisabled = true;
 
         resetSatelliteControllerUTToOnAndProvisionedState();
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false);
-        mSatelliteControllerUT.onCellularRadioPowerOffRequested();
+        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
         processAllMessages();
         // Satellite should not be powered off since the feature flag oemEnabledSatelliteFlag is
         // disabled
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
+
+        // Successfully disable satellite.
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+        mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
+        setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.requestSatelliteEnabled(false, false, false, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
+
+        // Fail to enable satellite when radio is being powered off.
+        mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
+        setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.onSetCellularRadioPowerStateRequested(false);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        // Radio is being powered off, can not enable satellite
+        assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+
+        // Modem failed to power off
+        mSatelliteControllerUT.onPowerOffCellularRadioFailed();
+
+        // Successfully enable satellite when radio is on.
+        mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
+        mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
+        mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
+        setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
+
+        // Clean up all previous resources
+        processAllFutureMessages();
+        mIIntegerConsumerSemaphore.drainPermits();
+
+        // Successfully disable satellite.
+        mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
+        setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.requestSatelliteEnabled(false, false, false, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
+
+        // Move to satellite-enabling in progress.
+        setUpNoResponseForRequestSatelliteEnabled(true, false, false);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
+        processAllMessages();
+        assertFalse(waitForIIntegerConsumerResult(1));
+
+        // Successfully disable satellite.
+        mIIntegerConsumerResults.clear();
+        mIIntegerConsumerSemaphore.drainPermits();
+        setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS);
+        mSatelliteControllerUT.requestSatelliteEnabled(false, false, false, mIIntegerConsumer);
+        processAllMessages();
+        assertTrue(waitForIIntegerConsumerResult(2));
+        // Should get success result for the disable request
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        // The enable request should be aborted
+        assertEquals(SATELLITE_RESULT_REQUEST_ABORTED, (long) mIIntegerConsumerResults.get(1));
+        // All timers waiting for enablement response should be stopped
+        assertFalse(mSatelliteControllerUT.isAnyWaitForSatelliteEnablingResponseTimerStarted());
+        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
+    }
+
+    @Test
+    public void testGetRequestIsEmergency() {
+        mIsSatelliteEnabledSemaphore.drainPermits();
+        doReturn(true).when(mFeatureFlags).carrierRoamingNbIotNtn();
+
+        // Successfully enable satellite
+        mIIntegerConsumerResults.clear();
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+
+        // Set provisioned state
+        setProvisionedState(true);
+        processAllMessages();
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+
+        mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
+        mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
+        // Set response for enabling request
+        setUpResponseForRequestSatelliteEnabled(true, false, true/*emergency*/,
+                SATELLITE_RESULT_SUCCESS);
+        // Request satellite enabling for emergency
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, true /*isEmergency*/,
+                mIIntegerConsumer);
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
+        processAllMessages();
+
+        assertTrue(waitForIIntegerConsumerResult(1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
+
+        // Verify satellite enabled for emergency
+        assertTrue(mSatelliteControllerUT.getRequestIsEmergency());
+        mSatelliteControllerUT.requestIsEmergencyModeEnabled(mRequestIsEmergencyReceiver);
+        assertTrue(mRequestIsEmergency);
     }
 
     @Test
     public void testRequestSatelliteCapabilities() {
         mSatelliteCapabilitiesSemaphore.drainPermits();
-        mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+        mSatelliteControllerUT.requestSatelliteCapabilities(mSatelliteCapabilitiesReceiver);
         processAllMessages();
         assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
@@ -1036,7 +1356,7 @@
 
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+        mSatelliteControllerUT.requestSatelliteCapabilities(mSatelliteCapabilitiesReceiver);
         processAllMessages();
         assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
         assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, mQueriedSatelliteCapabilitiesResultCode);
@@ -1046,7 +1366,7 @@
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestSatelliteCapabilities(mSatelliteCapabilities,
                 SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+        mSatelliteControllerUT.requestSatelliteCapabilities(mSatelliteCapabilitiesReceiver);
         processAllMessages();
         assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteCapabilitiesResultCode);
@@ -1059,7 +1379,7 @@
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpNullResponseForRequestSatelliteCapabilities(SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+        mSatelliteControllerUT.requestSatelliteCapabilities(mSatelliteCapabilitiesReceiver);
         processAllMessages();
         assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
@@ -1069,7 +1389,7 @@
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpNullResponseForRequestSatelliteCapabilities(SATELLITE_RESULT_INVALID_MODEM_STATE);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+        mSatelliteControllerUT.requestSatelliteCapabilities(mSatelliteCapabilitiesReceiver);
         processAllMessages();
         assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
         assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, mQueriedSatelliteCapabilitiesResultCode);
@@ -1079,7 +1399,7 @@
     public void testStartSatelliteTransmissionUpdates() {
         mIIntegerConsumerSemaphore.drainPermits();
         mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+        mSatelliteControllerUT.startSatelliteTransmissionUpdates(mIIntegerConsumer,
                 mStartTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -1089,7 +1409,7 @@
         mIIntegerConsumerResults.clear();
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+        mSatelliteControllerUT.startSatelliteTransmissionUpdates(mIIntegerConsumer,
                 mStartTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -1099,11 +1419,11 @@
         mIIntegerConsumerResults.clear();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+        mSatelliteControllerUT.startSatelliteTransmissionUpdates(mIIntegerConsumer,
                 mStartTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
+        assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
                 (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
@@ -1113,7 +1433,7 @@
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+        mSatelliteControllerUT.startSatelliteTransmissionUpdates(mIIntegerConsumer,
                 mStartTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -1124,7 +1444,7 @@
         provisionSatelliteService();
         mIIntegerConsumerResults.clear();
         setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+        mSatelliteControllerUT.startSatelliteTransmissionUpdates(mIIntegerConsumer,
                 mStartTransmissionUpdateCallback);
         verify(mMockPointingAppController).registerForSatelliteTransmissionUpdates(anyInt(),
                 eq(mStartTransmissionUpdateCallback));
@@ -1138,7 +1458,7 @@
         provisionSatelliteService();
         mIIntegerConsumerResults.clear();
         setUpResponseForStartSatelliteTransmissionUpdates(SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
-        mSatelliteControllerUT.startSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+        mSatelliteControllerUT.startSatelliteTransmissionUpdates(mIIntegerConsumer,
                 mStartTransmissionUpdateCallback);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -1151,54 +1471,11 @@
 
     @Test
     public void testStopSatelliteTransmissionUpdates() {
-        mIIntegerConsumerSemaphore.drainPermits();
-        mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
-                mStopTransmissionUpdateCallback);
-        processAllMessages();
-        assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
-                (long) mIIntegerConsumerResults.get(0));
-
-        mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
-                mStopTransmissionUpdateCallback);
-        processAllMessages();
-        assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
-
-        resetSatelliteControllerUT();
-        mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
-                mStopTransmissionUpdateCallback);
-        processAllMessages();
-        assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
-                (long) mIIntegerConsumerResults.get(0));
-
-        resetSatelliteControllerUT();
-        mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
-                mStopTransmissionUpdateCallback);
-        processAllMessages();
-        assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
-                (long) mIIntegerConsumerResults.get(0));
-
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
         provisionSatelliteService();
         setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+        mSatelliteControllerUT.stopSatelliteTransmissionUpdates(mIIntegerConsumer,
                 mStopTransmissionUpdateCallback);
         verify(mMockPointingAppController).unregisterForSatelliteTransmissionUpdates(anyInt(),
                 any(), eq(mStopTransmissionUpdateCallback));
@@ -1211,9 +1488,13 @@
         provisionSatelliteService();
         mIIntegerConsumerResults.clear();
         setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
-        mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
+        mSatelliteControllerUT.stopSatelliteTransmissionUpdates(mIIntegerConsumer,
                 mStopTransmissionUpdateCallback);
+        verify(mMockPointingAppController, times(2)).unregisterForSatelliteTransmissionUpdates(
+                anyInt(), any(), eq(mStopTransmissionUpdateCallback));
         processAllMessages();
+        verify(mMockPointingAppController, times(2)).stopSatelliteTransmissionUpdates(
+                any(Message.class));
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
                 (long) mIIntegerConsumerResults.get(0));
@@ -1223,7 +1504,7 @@
     public void testRequestIsDemoModeEnabled() {
         mIsDemoModeEnabledSemaphore.drainPermits();
         resetSatelliteControllerUT();
-        mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
+        mSatelliteControllerUT.requestIsDemoModeEnabled(mIsDemoModeEnabledReceiver);
         assertTrue(waitForRequestIsDemoModeEnabledResult(1));
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, mQueriedIsDemoModeEnabledResultCode);
         assertFalse(mQueriedIsDemoModeEnabled);
@@ -1231,7 +1512,7 @@
         resetSatelliteControllerUT();
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
+        mSatelliteControllerUT.requestIsDemoModeEnabled(mIsDemoModeEnabledReceiver);
         assertTrue(waitForRequestIsDemoModeEnabledResult(1));
         assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, mQueriedIsDemoModeEnabledResultCode);
         assertFalse(mQueriedIsDemoModeEnabled);
@@ -1239,9 +1520,9 @@
         resetSatelliteControllerUT();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
+        mSatelliteControllerUT.requestIsDemoModeEnabled(mIsDemoModeEnabledReceiver);
         assertTrue(waitForRequestIsDemoModeEnabledResult(1));
-        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, mQueriedIsDemoModeEnabledResultCode);
+        assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED, mQueriedIsDemoModeEnabledResultCode);
         assertFalse(mQueriedIsDemoModeEnabled);
 
         resetSatelliteControllerUT();
@@ -1249,7 +1530,7 @@
         setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
+        mSatelliteControllerUT.requestIsDemoModeEnabled(mIsDemoModeEnabledReceiver);
         assertTrue(waitForRequestIsDemoModeEnabledResult(1));
         assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED, mQueriedIsDemoModeEnabledResultCode);
         assertFalse(mQueriedIsDemoModeEnabled);
@@ -1257,7 +1538,7 @@
         resetSatelliteControllerUT();
         boolean isDemoModeEnabled = mSatelliteControllerUT.isDemoModeEnabled();
         provisionSatelliteService();
-        mSatelliteControllerUT.requestIsDemoModeEnabled(SUB_ID, mIsDemoModeEnabledReceiver);
+        mSatelliteControllerUT.requestIsDemoModeEnabled(mIsDemoModeEnabledReceiver);
         assertTrue(waitForRequestIsDemoModeEnabledResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedIsDemoModeEnabledResultCode);
         assertEquals(isDemoModeEnabled, mQueriedIsDemoModeEnabled);
@@ -1265,19 +1546,22 @@
 
     @Test
     public void testIsSatelliteEnabled() {
+        logd("testIsSatelliteEnabled: starting");
         setUpResponseForRequestIsSatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
         assertFalse(mSatelliteControllerUT.isSatelliteEnabled());
         mIsSatelliteEnabledSemaphore.drainPermits();
-        mSatelliteControllerUT.requestIsSatelliteEnabled(SUB_ID, mIsSatelliteEnabledReceiver);
+        mSatelliteControllerUT.requestIsSatelliteEnabled(mIsSatelliteEnabledReceiver);
         processAllMessages();
         assertTrue(waitForRequestIsSatelliteEnabledResult(1));
         assertEquals(
                 SATELLITE_RESULT_INVALID_TELEPHONY_STATE, mQueriedIsSatelliteEnabledResultCode);
 
 
+        logd("testIsSatelliteEnabled: setUpResponseForRequestIsSatelliteSupported");
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        logd("testIsSatelliteEnabled: verifySatelliteSupported");
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestIsSatelliteEnabled(SUB_ID, mIsSatelliteEnabledReceiver);
+        mSatelliteControllerUT.requestIsSatelliteEnabled(mIsSatelliteEnabledReceiver);
         processAllMessages();
         assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedIsSatelliteEnabledResultCode);
         assertEquals(mSatelliteControllerUT.isSatelliteEnabled(), mQueriedIsSatelliteEnabled);
@@ -1287,7 +1571,6 @@
     public void testOnSatelliteServiceConnected() {
         verifySatelliteSupported(false, SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         verifySatelliteEnabled(false, SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
-        verifySatelliteProvisioned(false, SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
 
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
@@ -1299,7 +1582,7 @@
 
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
     }
 
     @Test
@@ -1309,9 +1592,18 @@
             public void onSatelliteModemStateChanged(int state) {
                 logd("onSatelliteModemStateChanged: state=" + state);
             }
+
+            @Override
+            public void onEmergencyModeChanged(boolean isEmergency) {
+                logd("onEmergencyModeChanged: emergency=" + isEmergency);
+            }
+
+            @Override
+            public void onRegistrationFailure(int causeCode) {
+                logd("onRegistrationFailure: causeCode=" + causeCode);
+            }
         };
-        int errorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(
-                SUB_ID, callback);
+        int errorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(callback);
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode);
         verify(mMockSatelliteSessionController, never())
                 .registerForSatelliteModemStateChanged(callback);
@@ -1319,8 +1611,7 @@
         resetSatelliteControllerUTToSupportedAndProvisionedState();
         mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
 
-        errorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(
-                SUB_ID, callback);
+        errorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(callback);
         assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
         verify(mMockSatelliteSessionController).registerForSatelliteModemStateChanged(callback);
     }
@@ -1332,14 +1623,24 @@
             public void onSatelliteModemStateChanged(int state) {
                 logd("onSatelliteModemStateChanged: state=" + state);
             }
+
+            @Override
+            public void onEmergencyModeChanged(boolean isEmergency) {
+                logd("onEmergencyModeChanged: emergency=" + isEmergency);
+            }
+
+            @Override
+            public void onRegistrationFailure(int causeCode) {
+                logd("onRegistrationFailure: causeCode=" + causeCode);
+            }
         };
-        mSatelliteControllerUT.unregisterForModemStateChanged(SUB_ID, callback);
+        mSatelliteControllerUT.unregisterForModemStateChanged(callback);
         verify(mMockSatelliteSessionController, never())
                 .unregisterForSatelliteModemStateChanged(callback);
 
         resetSatelliteControllerUTToSupportedAndProvisionedState();
         mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
-        mSatelliteControllerUT.unregisterForModemStateChanged(SUB_ID, callback);
+        mSatelliteControllerUT.unregisterForModemStateChanged(callback);
         verify(mMockSatelliteSessionController).unregisterForSatelliteModemStateChanged(callback);
     }
 
@@ -1358,31 +1659,46 @@
                                     + "semaphore, ex=" + ex);
                         }
                     }
+
+                    @Override
+                    public void onSatelliteSubscriptionProvisionStateChanged(
+                            List<SatelliteSubscriberProvisionStatus> status) {
+                        logd("onSatelliteSubscriptionProvisionStateChanged: " + status);
+                    }
                 };
-        int errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(
-                SUB_ID, callback);
+        int errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(callback);
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode);
 
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(
-                SUB_ID, callback);
+        errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(callback);
         assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, errorCode);
 
         resetSatelliteControllerUT();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(
-                SUB_ID, callback);
+        errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(callback);
+        processAllMessages();
+        assertTrue(waitForForEvents(
+                semaphore, 1, "testRegisterForSatelliteProvisionStateChanged"));
         assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
 
-        sendProvisionedStateChangedEvent(true, null);
+        String mText = "This is test provision data.";
+        byte[] testProvisionData = mText.getBytes();
+        CancellationSignal cancellationSignal = new CancellationSignal();
+        ICancellationSignal cancelRemote = null;
+        mIIntegerConsumerResults.clear();
+        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(
+                TEST_SATELLITE_TOKEN,
+                testProvisionData, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForForEvents(
                 semaphore, 1, "testRegisterForSatelliteProvisionStateChanged"));
 
-        mSatelliteControllerUT.unregisterForSatelliteProvisionStateChanged(SUB_ID, callback);
-        sendProvisionedStateChangedEvent(true, null);
+        mSatelliteControllerUT.unregisterForSatelliteProvisionStateChanged(callback);
+        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(
+                TEST_SATELLITE_TOKEN,
+                testProvisionData, mIIntegerConsumer);
         processAllMessages();
         assertFalse(waitForForEvents(
                 semaphore, 1, "testRegisterForSatelliteProvisionStateChanged"));
@@ -1401,7 +1717,7 @@
                 };
         when(mMockDatagramController.registerForSatelliteDatagram(eq(SUB_ID), eq(callback)))
                 .thenReturn(SATELLITE_RESULT_SUCCESS);
-        int errorCode = mSatelliteControllerUT.registerForIncomingDatagram(SUB_ID, callback);
+        int errorCode = mSatelliteControllerUT.registerForIncomingDatagram(callback);
         assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
         verify(mMockDatagramController).registerForSatelliteDatagram(eq(SUB_ID), eq(callback));
     }
@@ -1419,7 +1735,7 @@
                 };
         doNothing().when(mMockDatagramController)
                 .unregisterForSatelliteDatagram(eq(SUB_ID), eq(callback));
-        mSatelliteControllerUT.unregisterForIncomingDatagram(SUB_ID, callback);
+        mSatelliteControllerUT.unregisterForIncomingDatagram(callback);
         verify(mMockDatagramController).unregisterForSatelliteDatagram(eq(SUB_ID), eq(callback));
     }
 
@@ -1439,7 +1755,7 @@
             clearInvocations(mMockDatagramController);
             clearInvocations(mMockPointingAppController);
 
-            mSatelliteControllerUT.sendDatagram(SUB_ID, datagramType, datagram, true,
+            mSatelliteControllerUT.sendDatagram(datagramType, datagram, true,
                     mIIntegerConsumer);
             processAllMessages();
             assertTrue(waitForIIntegerConsumerResult(1));
@@ -1451,10 +1767,10 @@
             mIIntegerConsumerResults.clear();
             setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
             verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-            sendProvisionedStateChangedEvent(false, null);
+            setProvisionedState(false);
             processAllMessages();
             verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-            mSatelliteControllerUT.sendDatagram(SUB_ID, datagramType, datagram, true,
+            mSatelliteControllerUT.sendDatagram(datagramType, datagram, true,
                     mIIntegerConsumer);
             processAllMessages();
             assertTrue(waitForIIntegerConsumerResult(1));
@@ -1464,10 +1780,10 @@
                     eq(datagramType), eq(datagram), eq(true), any());
 
             mIIntegerConsumerResults.clear();
-            sendProvisionedStateChangedEvent(true, null);
+            setProvisionedState(true);
             processAllMessages();
             verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-            mSatelliteControllerUT.sendDatagram(SUB_ID, datagramType, datagram, true,
+            mSatelliteControllerUT.sendDatagram(datagramType, datagram, true,
                     mIIntegerConsumer);
             processAllMessages();
             assertFalse(waitForIIntegerConsumerResult(1));
@@ -1481,7 +1797,7 @@
     @Test
     public void testPollPendingSatelliteDatagrams() {
         mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.pollPendingDatagrams(SUB_ID, mIIntegerConsumer);
+        mSatelliteControllerUT.pollPendingDatagrams(mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
@@ -1491,10 +1807,10 @@
         mIIntegerConsumerResults.clear();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        sendProvisionedStateChangedEvent(false, null);
+        setProvisionedState(false);
         processAllMessages();
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.pollPendingDatagrams(SUB_ID, mIIntegerConsumer);
+        mSatelliteControllerUT.pollPendingDatagrams(mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
@@ -1502,10 +1818,10 @@
         verify(mMockDatagramController, never()).pollPendingSatelliteDatagrams(anyInt(), any());
 
         mIIntegerConsumerResults.clear();
-        sendProvisionedStateChangedEvent(true, null);
+        setProvisionedState(true);
         processAllMessages();
         verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.pollPendingDatagrams(SUB_ID, mIIntegerConsumer);
+        mSatelliteControllerUT.pollPendingDatagrams(mIIntegerConsumer);
         processAllMessages();
         assertFalse(waitForIIntegerConsumerResult(1));
         verify(mMockDatagramController, times(1)).pollPendingSatelliteDatagrams(anyInt(), any());
@@ -1518,7 +1834,7 @@
         CancellationSignal cancellationSignal = new CancellationSignal();
         ICancellationSignal cancelRemote = null;
         mIIntegerConsumerResults.clear();
-        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(
                 TEST_SATELLITE_TOKEN,
                 testProvisionData, mIIntegerConsumer);
         processAllMessages();
@@ -1531,7 +1847,7 @@
         mIIntegerConsumerResults.clear();
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(
                 TEST_SATELLITE_TOKEN,
                 testProvisionData, mIIntegerConsumer);
         processAllMessages();
@@ -1545,9 +1861,7 @@
         setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForProvisionSatelliteService(TEST_SATELLITE_TOKEN, testProvisionData,
-                SATELLITE_RESULT_SUCCESS);
-        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(
                 TEST_SATELLITE_TOKEN,
                 testProvisionData, mIIntegerConsumer);
         processAllMessages();
@@ -1557,7 +1871,7 @@
         verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
 
         // Send provision request again after the device is successfully provisioned
-        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(
                 TEST_SATELLITE_TOKEN,
                 testProvisionData, mIIntegerConsumer);
         processAllMessages();
@@ -1565,70 +1879,19 @@
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         assertNull(cancelRemote);
 
-        // Vendor service does not support the request requestIsSatelliteProvisioned. Telephony will
-        // make decision itself
-        resetSatelliteControllerUT();
-        mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(
-                false, SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-
-        // Vendor service does not support the requests requestIsSatelliteProvisioned and
-        // provisionSatelliteService. Telephony will make decision itself
-        deprovisionSatelliteService();
-        resetSatelliteControllerUT();
-        mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(
-                false, SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForProvisionSatelliteService(TEST_SATELLITE_TOKEN, testProvisionData,
-                SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
-        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
-                TEST_SATELLITE_TOKEN,
-                testProvisionData, mIIntegerConsumer);
-        processAllMessages();
-        assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
-        assertNotNull(cancelRemote);
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-
-        resetSatelliteControllerUT();
-        mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForProvisionSatelliteService(TEST_SATELLITE_TOKEN, testProvisionData,
-                SATELLITE_RESULT_NOT_AUTHORIZED);
-        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
-                TEST_SATELLITE_TOKEN,
-                testProvisionData, mIIntegerConsumer);
-        processAllMessages();
-        assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_NOT_AUTHORIZED, (long) mIIntegerConsumerResults.get(0));
-        assertNotNull(cancelRemote);
-
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForProvisionSatelliteService(TEST_NEXT_SATELLITE_TOKEN, testProvisionData,
-                SATELLITE_RESULT_SUCCESS);
-        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(
                 TEST_NEXT_SATELLITE_TOKEN, testProvisionData, mIIntegerConsumer);
         cancellationSignal.setRemote(cancelRemote);
         cancellationSignal.cancel();
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
-        verify(mMockSatelliteModemInterface).deprovisionSatelliteService(
-                eq(TEST_NEXT_SATELLITE_TOKEN), any(Message.class));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
@@ -1636,13 +1899,10 @@
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        setUpNoResponseForProvisionSatelliteService(TEST_SATELLITE_TOKEN);
-        setUpResponseForProvisionSatelliteService(TEST_NEXT_SATELLITE_TOKEN, testProvisionData,
-                SATELLITE_RESULT_SUCCESS);
-        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(
                 TEST_SATELLITE_TOKEN,
                 testProvisionData, mIIntegerConsumer);
-        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(
                 TEST_NEXT_SATELLITE_TOKEN,
                 testProvisionData, mIIntegerConsumer);
         processAllMessages();
@@ -1657,7 +1917,7 @@
         mIIntegerConsumerResults.clear();
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+        mSatelliteControllerUT.deprovisionSatelliteService(
                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -1665,7 +1925,7 @@
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+        mSatelliteControllerUT.deprovisionSatelliteService(
                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -1676,13 +1936,11 @@
         mIIntegerConsumerResults.clear();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+        mSatelliteControllerUT.deprovisionSatelliteService(
                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
-                (long) mIIntegerConsumerResults.get(0));
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
 
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
@@ -1690,8 +1948,7 @@
         setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+        mSatelliteControllerUT.deprovisionSatelliteService(
                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -1700,21 +1957,7 @@
         resetSatelliteControllerUT();
         provisionSatelliteService();
         mIIntegerConsumerResults.clear();
-        setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
-                TEST_SATELLITE_TOKEN, mIIntegerConsumer);
-        processAllMessages();
-        assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
-        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-
-        // Vendor service does not support deprovisionSatelliteService
-        resetSatelliteControllerUT();
-        provisionSatelliteService();
-        mIIntegerConsumerResults.clear();
-        setUpResponseForDeprovisionSatelliteService(
-                TEST_SATELLITE_TOKEN, SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
-        mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+        mSatelliteControllerUT.deprovisionSatelliteService(
                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -1722,16 +1965,16 @@
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
 
         resetSatelliteControllerUT();
-        provisionSatelliteService();
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setProvisionedState(null);
         mIIntegerConsumerResults.clear();
-        setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN,
-                SATELLITE_RESULT_INVALID_MODEM_STATE);
-        mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+        mSatelliteControllerUT.deprovisionSatelliteService(
                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
-        verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
     }
 
     @Test
@@ -2209,7 +2452,7 @@
         TestSatelliteController satelliteController =
                 new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
         mSatelliteCapabilitiesSemaphore.drainPermits();
-        satelliteController.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+        satelliteController.requestSatelliteCapabilities(mSatelliteCapabilitiesReceiver);
         processAllMessages();
         assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
         assertEquals(
@@ -2223,7 +2466,7 @@
                 new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
         verifySatelliteSupported(satelliteController, true, SATELLITE_RESULT_SUCCESS);
         mSatelliteCapabilitiesSemaphore.drainPermits();
-        satelliteController.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+        satelliteController.requestSatelliteCapabilities(mSatelliteCapabilitiesReceiver);
         processAllMessages();
         assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteCapabilitiesResultCode);
@@ -2240,7 +2483,7 @@
                 new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
         verifySatelliteSupported(satelliteController, true, SATELLITE_RESULT_SUCCESS);
         mSatelliteCapabilitiesSemaphore.drainPermits();
-        satelliteController.requestSatelliteCapabilities(SUB_ID, mSatelliteCapabilitiesReceiver);
+        satelliteController.requestSatelliteCapabilities(mSatelliteCapabilitiesReceiver);
         processAllMessages();
         assertTrue(waitForRequestSatelliteCapabilitiesResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteCapabilitiesResultCode);
@@ -2269,16 +2512,23 @@
         clearInvocations(mMockDatagramController);
         sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_UNAVAILABLE, null);
         processAllMessages();
-        verify(mMockSatelliteSessionController, times(1)).onSatelliteEnabledStateChanged(eq(false));
-        verify(mMockSatelliteSessionController, times(1)).setDemoMode(eq(false));
-        verify(mMockDatagramController, times(1)).setDemoMode(eq(false));
+        verify(mMockSatelliteSessionController, times(1)).onSatelliteModemStateChanged(
+                eq(SATELLITE_MODEM_STATE_OFF));
 
         clearInvocations(mMockSatelliteSessionController);
         clearInvocations(mMockDatagramController);
-        sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_CONNECTED, null);
+        when(mMockSatelliteSessionController.isInDisablingState()).thenReturn(true);
+        sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_NOT_CONNECTED, null);
         processAllMessages();
         verify(mMockSatelliteSessionController, times(1)).onSatelliteModemStateChanged(
-                SATELLITE_MODEM_STATE_CONNECTED);
+                SATELLITE_MODEM_STATE_NOT_CONNECTED);
+
+        clearInvocations(mMockSatelliteSessionController);
+        when(mMockSatelliteSessionController.isInDisablingState()).thenReturn(false);
+        sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_NOT_CONNECTED, null);
+        processAllMessages();
+        verify(mMockSatelliteSessionController, never()).onSatelliteModemStateChanged(
+                SATELLITE_MODEM_STATE_NOT_CONNECTED);
     }
 
     @Test
@@ -2410,7 +2660,7 @@
         assertEquals(expectedLevel, signalStrength[0].getLevel());
         verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_POOR, SATELLITE_RESULT_SUCCESS);
 
-        mSatelliteControllerUT.unregisterForNtnSignalStrengthChanged(SUB_ID, callback);
+        mSatelliteControllerUT.unregisterForNtnSignalStrengthChanged(callback);
         sendNtnSignalStrengthChangedEvent(NTN_SIGNAL_STRENGTH_GREAT, null);
         processAllMessages();
         assertFalse(waitForForEvents(
@@ -2724,21 +2974,18 @@
                     }
                 };
 
-        int errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(SUB_ID,
-                callback);
+        int errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(callback);
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode);
 
         setUpResponseForRequestIsSatelliteSupported(false,
                 SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(SUB_ID,
-                callback);
+        errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(callback);
         assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, errorCode);
 
         resetSatelliteControllerUT();
         provisionSatelliteService();
-        errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(SUB_ID,
-                callback);
+        errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(callback);
         assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
         SatelliteCapabilities expectedCapabilities = mSatelliteCapabilities;
         sendSatelliteCapabilitiesChangedEvent(expectedCapabilities, null);
@@ -2754,7 +3001,7 @@
                 semaphore, 1, "testRegisterForSatelliteCapabilitiesChanged"));
         assertTrue(expectedCapabilities.equals(satelliteCapabilities[0]));
 
-        mSatelliteControllerUT.unregisterForCapabilitiesChanged(SUB_ID, callback);
+        mSatelliteControllerUT.unregisterForCapabilitiesChanged(callback);
         expectedCapabilities = mSatelliteCapabilities;
         sendSatelliteCapabilitiesChangedEvent(expectedCapabilities, null);
         processAllMessages();
@@ -2783,22 +3030,19 @@
                     }
                 };
 
-        int errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(SUB_ID,
-                callback);
+        int errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(callback);
         assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
 
         setUpResponseForRequestIsSatelliteSupported(false,
                 SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_NOT_SUPPORTED);
-        errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(SUB_ID,
-                callback);
+        errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(callback);
         assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
 
         resetSatelliteControllerUT();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_NOT_SUPPORTED);
-        errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(SUB_ID,
-                callback);
+        errorCode = mSatelliteControllerUT.registerForCapabilitiesChanged(callback);
         assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
 
         SatelliteCapabilities expectedCapabilities = mSatelliteCapabilities;
@@ -2885,8 +3129,8 @@
         List<String> barredPlmnList = new ArrayList<>();
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
                 entitlementPlmnList, barredPlmnList, mIIntegerConsumer);
-        verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(anyBoolean(),
-                anyBoolean(), anyBoolean(), any(Message.class));
+        verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(
+                any(SatelliteModemEnableRequestAttributes.class), any(Message.class));
 
         // If the entitlement plmn list and the overlay config plmn list are available and the
         // carrier plmn list and the barred plmn list are empty, verify passing to the modem.
@@ -2943,32 +3187,32 @@
         entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", ""}).toList();
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
                 entitlementPlmnList, barredPlmnList, mIIntegerConsumer);
-        verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(anyBoolean(),
-                anyBoolean(), anyBoolean(), any(Message.class));
+        verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(
+                any(SatelliteModemEnableRequestAttributes.class), any(Message.class));
 
         // If the entitlement plmn list is invalid, verify not passing to the modem.
         reset(mMockSatelliteModemInterface);
         entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", "123456789"}).toList();
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
                 entitlementPlmnList, barredPlmnList, mIIntegerConsumer);
-        verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(anyBoolean(),
-                anyBoolean(), anyBoolean(), any(Message.class));
+        verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(
+                any(SatelliteModemEnableRequestAttributes.class), any(Message.class));
 
         // If the entitlement plmn list is invalid, verify not passing to the modem.
         reset(mMockSatelliteModemInterface);
         entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", "12"}).toList();
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
                 entitlementPlmnList, barredPlmnList, mIIntegerConsumer);
-        verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(anyBoolean(),
-                anyBoolean(), anyBoolean(), any(Message.class));
+        verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(
+                any(SatelliteModemEnableRequestAttributes.class), any(Message.class));
 
         // If the entitlement plmn list is invalid, verify not passing to the modem.
         reset(mMockSatelliteModemInterface);
         entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", "1234"}).toList();
         mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false,
                 entitlementPlmnList, barredPlmnList, mIIntegerConsumer);
-        verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(anyBoolean(),
-                anyBoolean(), anyBoolean(), any(Message.class));
+        verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(
+                any(SatelliteModemEnableRequestAttributes.class), any(Message.class));
     }
 
     private void verifyPassingToModemAfterQueryCompleted(List<String> entitlementPlmnList,
@@ -3334,14 +3578,13 @@
     }
 
     @Test
-    public void testHandleEventServiceStateChanged() throws Exception {
+    public void testHandleEventServiceStateChanged() {
         when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
         // Do nothing when the satellite is not connected
         doReturn(false).when(mServiceState).isUsingNonTerrestrialNetwork();
         sendServiceStateChangedEvent();
         processAllMessages();
-        assertEquals(false,
-                mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false));
+        assertFalse(mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false));
         verify(mMockNotificationManager, never()).notifyAsUser(anyString(), anyInt(), any(), any());
 
         // Check sending a system notification when the satellite is connected
@@ -3350,8 +3593,7 @@
         processAllMessages();
         verify(mMockNotificationManager, times(1)).notifyAsUser(anyString(), anyInt(), any(),
                 any());
-        assertEquals(true,
-                mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false));
+        assertTrue(mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false));
 
         // Check don't display again after displayed already a system notification.
         sendServiceStateChangedEvent();
@@ -3366,38 +3608,44 @@
         mIIntegerConsumerResults.clear();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        sendProvisionedStateChangedEvent(true, null);
+        setProvisionedState(true);
         processAllMessages();
         verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
 
         // Successfully disable satellite
         mIIntegerConsumerResults.clear();
         setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(false, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
 
         // Time out to enable satellite
+        ArgumentCaptor<SatelliteModemEnableRequestAttributes> enableSatelliteRequest =
+                ArgumentCaptor.forClass(SatelliteModemEnableRequestAttributes.class);
         ArgumentCaptor<Message> enableSatelliteResponse = ArgumentCaptor.forClass(Message.class);
         mIIntegerConsumerResults.clear();
         setUpNoResponseForRequestSatelliteEnabled(true, false, false);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        clearInvocations(mMockSatelliteModemInterface);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertFalse(waitForIIntegerConsumerResult(1));
-        verify(mMockSatelliteModemInterface).requestSatelliteEnabled(eq(true), eq(false), eq(false),
+        verify(mMockSatelliteModemInterface).requestSatelliteEnabled(
+                enableSatelliteRequest.capture(),
                 enableSatelliteResponse.capture());
+        SatelliteModemEnableRequestAttributes request = enableSatelliteRequest.getValue();
+        assertTrue(request.isEnabled());
+        assertFalse(request.isDemoMode());
+        assertFalse(request.isEmergencyMode());
 
         clearInvocations(mMockSatelliteModemInterface);
         moveTimeForward(TEST_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMEOUT_MILLIS);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_MODEM_TIMEOUT, (long) mIIntegerConsumerResults.get(0));
-        verify(mMockSatelliteModemInterface).requestSatelliteEnabled(eq(false), eq(false),
-                eq(false), any(Message.class));
+        verify(mMockSatelliteModemInterface).requestSatelliteEnabled(
+                any(SatelliteModemEnableRequestAttributes.class), any(Message.class));
         verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
 
         // Send the response for the above request to enable satellite. SatelliteController should
@@ -3411,8 +3659,7 @@
         // Successfully enable satellite
         mIIntegerConsumerResults.clear();
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -3423,12 +3670,11 @@
         mIIntegerConsumerResults.clear();
         clearInvocations(mMockSatelliteModemInterface);
         setUpNoResponseForRequestSatelliteEnabled(false, false, false);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(false, false, false, mIIntegerConsumer);
         processAllMessages();
         assertFalse(waitForIIntegerConsumerResult(1));
-        verify(mMockSatelliteModemInterface).requestSatelliteEnabled(eq(false), eq(false),
-                eq(false),
+        verify(mMockSatelliteModemInterface).requestSatelliteEnabled(
+                any(SatelliteModemEnableRequestAttributes.class),
                 disableSatelliteResponse.capture());
 
         clearInvocations(mMockSatelliteModemInterface);
@@ -3436,9 +3682,10 @@
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_MODEM_TIMEOUT, (long) mIIntegerConsumerResults.get(0));
-        verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(anyBoolean(),
-                anyBoolean(), anyBoolean(), any(Message.class));
-        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
+        verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(
+                any(SatelliteModemEnableRequestAttributes.class), any(Message.class));
+        // Satellite should state at enabled state since satellite disable request failed
+        verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
 
         // Send the response for the above request to disable satellite. SatelliteController should
         // ignore the event
@@ -3446,7 +3693,7 @@
         AsyncResult.forMessage(response, null, null);
         response.sendToTarget();
         processAllMessages();
-        verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
     }
 
     @Test
@@ -3462,9 +3709,9 @@
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        sendProvisionedStateChangedEvent(true, null);
+        setProvisionedState(true);
         setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        sendProvisionedStateChangedEvent(true, null);
+        setProvisionedState(true);
         processAllMessages();
         verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
 
@@ -3472,8 +3719,7 @@
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
         mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -3488,8 +3734,7 @@
         // Ignore request ntn signal strength for redundant enable request
         reset(mMockSatelliteModemInterface);
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -3502,8 +3747,7 @@
         reset(mMockSatelliteModemInterface);
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(false, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -3517,8 +3761,7 @@
         doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
         mIIntegerConsumerResults.clear();
         setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(false, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -3533,8 +3776,7 @@
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
         mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false;
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -3571,8 +3813,7 @@
         resetSatelliteControllerUT();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        int errorCode = mSatelliteControllerUT.registerForSatelliteSupportedStateChanged(
-                SUB_ID, callback);
+        int errorCode = mSatelliteControllerUT.registerForSatelliteSupportedStateChanged(callback);
         assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
 
         sendSatelliteSupportedStateChangedEvent(true, null);
@@ -3606,13 +3847,12 @@
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
 
         // Successfully enable satellite
-        sendProvisionedStateChangedEvent(true, null);
+        setProvisionedState(true);
         processAllMessages();
         verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         mIIntegerConsumerResults.clear();
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -3635,7 +3875,7 @@
         // Verify satellite was disabled
         verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
 
-        mSatelliteControllerUT.unregisterForSatelliteSupportedStateChanged(SUB_ID, callback);
+        mSatelliteControllerUT.unregisterForSatelliteSupportedStateChanged(callback);
         sendSatelliteSupportedStateChangedEvent(true, null);
         processAllMessages();
         assertFalse(waitForForEvents(
@@ -3661,7 +3901,7 @@
                     }
                 };
         int errorCode = mSatelliteControllerUT.registerForSatelliteSupportedStateChanged(
-                SUB_ID, callback);
+                callback);
         assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
     }
 
@@ -3764,17 +4004,642 @@
                 mSatelliteControllerUT.getCarrierEmergencyCallWaitForConnectionTimeoutMillis());
     }
 
+    @Test
+    public void testIsCarrierRoamingNtnEligible() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(false);
+        assertFalse(mSatelliteControllerUT.isCarrierRoamingNtnEligible(null));
+
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        when(mServiceState2.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true);
+        mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 1);
+        int[] supportedServices2 = {2};
+        int[] supportedServices3 = {1, 3};
+        PersistableBundle carrierSupportedSatelliteServicesPerProvider = new PersistableBundle();
+        carrierSupportedSatelliteServicesPerProvider.putIntArray(
+                "00102", supportedServices2);
+        carrierSupportedSatelliteServicesPerProvider.putIntArray(
+                "00103", supportedServices3);
+        mCarrierConfigBundle.putPersistableBundle(CarrierConfigManager
+                        .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
+                carrierSupportedSatelliteServicesPerProvider);
+        for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+                : mCarrierConfigChangedListenerList) {
+            pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+                    /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+            );
+        }
+        mSatelliteControllerUT.setSatellitePhone(1);
+        processAllMessages();
+
+        assertTrue(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone));
+    }
+
+    @Test
+    public void testNotifyNtnEligibilityHysteresisTimedOut() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        when(mServiceState2.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true);
+        mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 1);
+        mCarrierConfigBundle.putInt(
+                KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT, 1 * 60);
+        int[] supportedServices2 = {2};
+        int[] supportedServices3 = {1, 3};
+        PersistableBundle carrierSupportedSatelliteServicesPerProvider = new PersistableBundle();
+        carrierSupportedSatelliteServicesPerProvider.putIntArray(
+                "00102", supportedServices2);
+        carrierSupportedSatelliteServicesPerProvider.putIntArray(
+                "00103", supportedServices3);
+        mCarrierConfigBundle.putPersistableBundle(CarrierConfigManager
+                        .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
+                carrierSupportedSatelliteServicesPerProvider);
+        for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+                : mCarrierConfigChangedListenerList) {
+            pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+                    /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+            );
+        }
+        mSatelliteControllerUT.setSatellitePhone(1);
+        mSatelliteControllerUT.isSatelliteAllowedCallback = null;
+        processAllMessages();
+        mSatelliteControllerUT.elapsedRealtime = 0;
+        assertTrue(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone));
+        verify(mPhone, times(0)).notifyCarrierRoamingNtnEligibleStateChanged(eq(true));
+        verify(mPhone2, times(0)).notifyCarrierRoamingNtnEligibleStateChanged(anyBoolean());
+        clearInvocations(mPhone);
+
+        // 2 minutes later and hysteresis timeout is 1 minute
+        mSatelliteControllerUT.elapsedRealtime = 2 * 60 * 1000;
+        moveTimeForward(2 * 60 * 1000);
+        processAllMessages();
+        assertNotNull(mSatelliteControllerUT.isSatelliteAllowedCallback);
+
+        mSatelliteControllerUT.isSatelliteAllowedCallback.onResult(true);
+        processAllMessages();
+        assertTrue(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone));
+        verify(mPhone, times(1)).notifyCarrierRoamingNtnEligibleStateChanged(eq(true));
+        verify(mPhone2, times(0)).notifyCarrierRoamingNtnEligibleStateChanged(anyBoolean());
+        verify(mMockNotificationManager, times(1)).notifyAsUser(anyString(), anyInt(), any(),
+                any());
+        clearInvocations(mPhone);
+
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        sendServiceStateChangedEvent();
+        processAllMessages();
+        assertFalse(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone));
+        verify(mPhone, times(1)).notifyCarrierRoamingNtnEligibleStateChanged(eq(false));
+        verify(mPhone2, times(0)).notifyCarrierRoamingNtnEligibleStateChanged(anyBoolean());
+
+        // isSatelliteAllowedCallback.onError() returns error
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        sendServiceStateChangedEvent();
+        processAllMessages();
+        mSatelliteControllerUT.elapsedRealtime = 0;
+        assertTrue(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone));
+        verify(mPhone, times(0)).notifyCarrierRoamingNtnEligibleStateChanged(eq(true));
+        verify(mPhone2, times(0)).notifyCarrierRoamingNtnEligibleStateChanged(anyBoolean());
+        clearInvocations(mPhone);
+
+        // 2 minutes later and hysteresis timeout is 1 minute
+        mSatelliteControllerUT.elapsedRealtime = 2 * 60 * 1000;
+        moveTimeForward(2 * 60 * 1000);
+        processAllMessages();
+        assertNotNull(mSatelliteControllerUT.isSatelliteAllowedCallback);
+
+        mSatelliteControllerUT.isSatelliteAllowedCallback.onError(new SatelliteException(
+                SATELLITE_RESULT_ERROR));
+        processAllMessages();
+        assertTrue(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone));
+        verify(mPhone, times(0)).notifyCarrierRoamingNtnEligibleStateChanged(eq(true));
+        verify(mPhone2, times(0)).notifyCarrierRoamingNtnEligibleStateChanged(anyBoolean());
+        verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(), anyInt(),
+                any());
+    }
+
+    @Test
+    public void testGetWwanIsInService() {
+        when(mServiceState.getNetworkRegistrationInfoListForTransportType(
+                eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+                .thenReturn(new ArrayList<>());
+        assertFalse(mSatelliteControllerUT.getWwanIsInService(mServiceState));
+
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfoListForTransportType(
+                eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+                .thenReturn(List.of(nri));
+        assertTrue(mSatelliteControllerUT.getWwanIsInService(mServiceState));
+
+        nri = new NetworkRegistrationInfo.Builder()
+                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfoListForTransportType(
+                eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+                .thenReturn(List.of(nri));
+        assertTrue(mSatelliteControllerUT.getWwanIsInService(mServiceState));
+
+        nri = new NetworkRegistrationInfo.Builder()
+                .setEmergencyOnly(true)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfoListForTransportType(
+                eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+                .thenReturn(List.of(nri));
+        assertTrue(mSatelliteControllerUT.getWwanIsInService(mServiceState));
+
+        nri = new NetworkRegistrationInfo.Builder().setRegistrationState(
+                NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfoListForTransportType(
+                eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+                .thenReturn(List.of(nri));
+        assertFalse(mSatelliteControllerUT.getWwanIsInService(mServiceState));
+    }
+
+    @Test
+    public void testRegistrationFailureCallback() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        Semaphore semaphore = new Semaphore(0);
+        final int[] resultErrorCode = new int[1];
+        ISatelliteModemStateCallback callback = new ISatelliteModemStateCallback.Stub() {
+            @Override
+            public void onSatelliteModemStateChanged(int state) {
+                logd("onSatelliteModemStateChanged: state=" + state);
+            }
+
+            @Override
+            public void onEmergencyModeChanged(boolean isEmergency) {
+                logd("onEmergencyModeChanged: emergency=" + isEmergency);
+            }
+
+            @Override
+            public void onRegistrationFailure(int causeCode) {
+                logd("onRegistrationFailure: causeCode=" + causeCode);
+                resultErrorCode[0] = causeCode;
+                semaphore.release();
+            }
+        };
+        resetSatelliteControllerUTToSupportedAndProvisionedState();
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
+
+        int RegisterErrorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(
+                callback);
+        assertEquals(SATELLITE_RESULT_SUCCESS, RegisterErrorCode);
+        verify(mMockSatelliteSessionController).registerForSatelliteModemStateChanged(callback);
+
+        int expectedErrorCode = 100;
+        mIIntegerConsumerResults.clear();
+        sendSatelliteRegistrationFailureEvent(100, null);
+        processAllMessages();
+        assertTrue(waitForForEvents(
+                semaphore, 1, "testRegistrationFailureCallback"));
+        assertEquals(expectedErrorCode, resultErrorCode[0]);
+    }
+
+    private boolean mProvisionState = false;
+    private int mProvisionSateResultCode = -1;
+    private Semaphore mProvisionSateSemaphore = new Semaphore(0);
+    private ResultReceiver mProvisionSatelliteReceiver = new ResultReceiver(null) {
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            mProvisionSateResultCode = resultCode;
+            logd("mProvisionSatelliteReceiver: resultCode=" + resultCode);
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                if (resultData.containsKey(KEY_PROVISION_SATELLITE_TOKENS)) {
+                    mProvisionState = resultData.getBoolean(KEY_PROVISION_SATELLITE_TOKENS);
+                    logd("mProvisionSatelliteReceiver: mProvisionState=" + mProvisionState);
+                } else {
+                    loge("KEY_PROVISION_SATELLITE_TOKENS does not exist.");
+                    mProvisionState = false;
+                }
+            } else {
+                mProvisionState = false;
+            }
+            try {
+                mProvisionSateSemaphore.release();
+            } catch (Exception ex) {
+                loge("mProvisionSatelliteReceiver: Got exception in releasing semaphore, ex=" + ex);
+            }
+        }
+    };
+
+    private List<SatelliteSubscriberProvisionStatus>
+            mRequestSatelliteSubscriberProvisionStatusResultList = new ArrayList<>();
+    private int mRequestSatelliteSubscriberProvisionStatusResultCode = SATELLITE_RESULT_SUCCESS;
+    private Semaphore mRequestSatelliteSubscriberProvisionStatusSemaphore = new Semaphore(0);
+    private ResultReceiver mRequestSatelliteSubscriberProvisionStatusReceiver = new ResultReceiver(
+            null) {
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            mRequestSatelliteSubscriberProvisionStatusResultCode = resultCode;
+            logd("mRequestSatelliteSubscriberProvisionStatusReceiver: resultCode=" + resultCode);
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                if (resultData.containsKey(KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN)) {
+                    mRequestSatelliteSubscriberProvisionStatusResultList =
+                            resultData.getParcelableArrayList(
+                                    KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN,
+                                    SatelliteSubscriberProvisionStatus.class);
+                } else {
+                    loge("KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN does not exist.");
+                    mRequestSatelliteSubscriberProvisionStatusResultList = new ArrayList<>();
+                }
+            } else {
+                mRequestSatelliteSubscriberProvisionStatusResultList = new ArrayList<>();
+            }
+            try {
+                mRequestSatelliteSubscriberProvisionStatusSemaphore.release();
+            } catch (Exception ex) {
+                loge("mRequestSatelliteSubscriberProvisionStatusReceiver: Got exception in "
+                        + "releasing "
+                        + "semaphore, ex=" + ex);
+            }
+        }
+    };
+
+    @Test
+    public void testRequestSatelliteSubscriberProvisionStatus() throws Exception {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+        verifyRequestSatelliteSubscriberProvisionStatus();
+    }
+
+    private void verifyRequestSatelliteSubscriberProvisionStatus() throws Exception {
+        setSatelliteSubscriberTesting();
+        List<SatelliteSubscriberInfo> list = getExpectedSatelliteSubscriberInfoList();
+        mCarrierConfigBundle.putString(KEY_SATELLITE_NIDD_APN_NAME_STRING, mNiddApn);
+        mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, true);
+        for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+                : mCarrierConfigChangedListenerList) {
+            pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+                    /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+            );
+        }
+        moveTimeForward(TimeUnit.MINUTES.toMillis(1));
+        processAllMessages();
+
+        // Verify that calling requestSatelliteSubscriberProvisionStatus returns the expected
+        // list of SatelliteSubscriberProvisionStatus.
+        mSatelliteControllerUT.requestSatelliteSubscriberProvisionStatus(
+                mRequestSatelliteSubscriberProvisionStatusReceiver);
+        moveTimeForward(TimeUnit.MINUTES.toMillis(1));
+        processAllMessages();
+        assertEquals(SATELLITE_RESULT_SUCCESS,
+                mRequestSatelliteSubscriberProvisionStatusResultCode);
+        assertEquals(list.get(0), mRequestSatelliteSubscriberProvisionStatusResultList.get(
+                0).getSatelliteSubscriberInfo());
+    }
+
+    @Test
+    public void testProvisionSatellite() throws Exception {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+        verifyRequestSatelliteSubscriberProvisionStatus();
+        List<SatelliteSubscriberInfo> inputList = getExpectedSatelliteSubscriberInfoList();
+        verifyProvisionSatellite(inputList);
+    }
+
+    private void verifyProvisionSatellite(List<SatelliteSubscriberInfo> inputList) {
+        doAnswer(invocation -> {
+            Message message = (Message) invocation.getArguments()[1];
+            AsyncResult.forMessage(message, null, new SatelliteException(SATELLITE_RESULT_SUCCESS));
+            message.sendToTarget();
+            return null;
+        }).when(mMockSatelliteModemInterface).updateSatelliteSubscription(anyString(), any());
+
+        mSatelliteControllerUT.provisionSatellite(inputList, mProvisionSatelliteReceiver);
+        processAllMessages();
+        assertEquals(SATELLITE_RESULT_SUCCESS, mProvisionSateResultCode);
+        assertTrue(mProvisionState);
+    }
+
+
+    @Test
+    public void testRegisterForSatelliteSubscriptionProvisionStateChanged() throws Exception {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+
+        Semaphore semaphore = new Semaphore(0);
+        SatelliteSubscriberProvisionStatus[] resultArray =
+                new SatelliteSubscriberProvisionStatus[2];
+        ISatelliteProvisionStateCallback callback = new ISatelliteProvisionStateCallback.Stub() {
+            @Override
+            public void onSatelliteProvisionStateChanged(boolean provisioned) {
+                logd("onSatelliteProvisionStateChanged: provisioned=" + provisioned);
+            }
+
+            @Override
+            public void onSatelliteSubscriptionProvisionStateChanged(
+                    List<SatelliteSubscriberProvisionStatus> satelliteSubscriberProvisionStatus) {
+                logd("onSatelliteSubscriptionProvisionStateChanged: "
+                        + satelliteSubscriberProvisionStatus);
+                for (int i = 0; i < satelliteSubscriberProvisionStatus.size(); i++) {
+                    resultArray[i] = satelliteSubscriberProvisionStatus.get(i);
+                }
+                try {
+                    semaphore.release();
+                } catch (Exception ex) {
+                    loge("onSatelliteSubscriptionProvisionStateChanged: Got exception in releasing "
+                            + "semaphore, ex=" + ex);
+                }
+            }
+        };
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        int errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(callback);
+        processAllMessages();
+        assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
+
+        verifyRequestSatelliteSubscriberProvisionStatus();
+
+        // Verify that onSatelliteSubscriptionProvisionStateChanged is called when requesting
+        // provisioning for the first time.
+        List<SatelliteSubscriberInfo> list = getExpectedSatelliteSubscriberInfoList();
+        List<SatelliteSubscriberInfo> inputList = new ArrayList<>();
+        inputList.add(list.get(0));
+        verifyProvisionSatellite(inputList);
+
+        verify(mMockSatelliteModemInterface, times(1)).updateSatelliteSubscription(anyString(),
+                any());
+        assertTrue(waitForForEvents(
+                semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged"));
+        assertTrue(resultArray[0].getProvisionStatus());
+        assertEquals(mSubscriberId, resultArray[0].getSatelliteSubscriberInfo().getSubscriberId());
+
+        // Request provisioning with SatelliteSubscriberInfo that has not been provisioned
+        // before, and verify that onSatelliteSubscriptionProvisionStateChanged is called.
+        inputList = new ArrayList<>();
+        inputList.add(list.get(1));
+        verifyProvisionSatellite(inputList);
+
+        verify(mMockSatelliteModemInterface, times(2)).updateSatelliteSubscription(anyString(),
+                any());
+        assertTrue(waitForForEvents(
+                semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged"));
+        assertTrue(resultArray[1].getProvisionStatus());
+        assertEquals(mSubscriberId2, resultArray[1].getSatelliteSubscriberInfo().getSubscriberId());
+
+        // Request provisioning with the same SatelliteSubscriberInfo that was previously
+        // requested, and verify that onSatelliteSubscriptionProvisionStateChanged is not called.
+        verifyProvisionSatellite(inputList);
+
+        verify(mMockSatelliteModemInterface, times(2)).updateSatelliteSubscription(anyString(),
+                any());
+        assertFalse(waitForForEvents(
+                semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged"));
+    }
+
+    private void setSatelliteSubscriberTesting() throws Exception {
+        doReturn("123").when(mContext).getAttributionTag();
+        final int carrierId = 0;
+        SubscriptionInfo subscriptionInfo = new SubscriptionInfo.Builder()
+                .setId(SUB_ID).setIccId(mIccId).setSimSlotIndex(0).setOnlyNonTerrestrialNetwork(
+                        false).setSatelliteESOSSupported(true).setCarrierId(carrierId).build();
+        SubscriptionInfo subscriptionInfo2 = new SubscriptionInfo.Builder()
+                .setId(SUB_ID1).setIccId(mIccId2).setSimSlotIndex(1).setOnlyNonTerrestrialNetwork(
+                        true).setSatelliteESOSSupported(false).setCarrierId(carrierId).build();
+        List<SubscriptionInfo> allSubInfos = new ArrayList<>();
+        allSubInfos.add(subscriptionInfo);
+        allSubInfos.add(subscriptionInfo2);
+        doReturn(allSubInfos).when(mMockSubscriptionManagerService).getAllSubInfoList(
+                anyString(), anyString());
+        SubscriptionInfoInternal subInfoInternal =
+                new SubscriptionInfoInternal.Builder().setCarrierId(0).setImsi(mImsi).setIccId(
+                        mIccId).build();
+        SubscriptionInfoInternal subInfoInternal2 =
+                new SubscriptionInfoInternal.Builder().setCarrierId(0).setImsi(mImsi2).setIccId(
+                        mIccId2).build();
+        doReturn(subscriptionInfo).when(mMockSubscriptionManagerService).getSubscriptionInfo(
+                eq(SUB_ID));
+        doReturn(subscriptionInfo2).when(mMockSubscriptionManagerService).getSubscriptionInfo(
+                eq(SUB_ID1));
+        Field field = SatelliteController.class.getDeclaredField("mInjectSubscriptionManager");
+        field.setAccessible(true);
+        field.set(mSatelliteControllerUT, mSubscriptionManager);
+        doReturn(mMsisdn).when(mSubscriptionManager).getPhoneNumber(eq(SUB_ID));
+        doReturn(mMsisdn2).when(mSubscriptionManager).getPhoneNumber(eq(SUB_ID1));
+        Field provisionedSubscriberIdField = SatelliteController.class.getDeclaredField(
+                "mProvisionedSubscriberId");
+        provisionedSubscriberIdField.setAccessible(true);
+        provisionedSubscriberIdField.set(mSatelliteControllerUT, new HashMap<>());
+        Field subscriberIdPerSubField = SatelliteController.class.getDeclaredField(
+                "mSubscriberIdPerSub");
+        subscriberIdPerSubField.setAccessible(true);
+        subscriberIdPerSubField.set(mSatelliteControllerUT, new HashMap<>());
+        Field lastConfiguredIccIdField = SatelliteController.class.getDeclaredField(
+                "mLastConfiguredIccId");
+        lastConfiguredIccIdField.setAccessible(true);
+        lastConfiguredIccIdField.set(mSatelliteControllerUT, null);
+        doReturn(subInfoInternal).when(mMockSubscriptionManagerService).getSubscriptionInfoInternal(
+                eq(SUB_ID));
+        doReturn(subInfoInternal2).when(
+                mMockSubscriptionManagerService).getSubscriptionInfoInternal(eq(SUB_ID1));
+        doReturn(mResources).when(mContext).getResources();
+        doReturn("package").when(mResources).getString(
+                eq(R.string.config_satellite_gateway_service_package));
+        doReturn("className").when(mResources).getString(
+                eq(R.string.config_satellite_carrier_roaming_esos_provisioned_class));
+    }
+
+    private List<SatelliteSubscriberInfo> getExpectedSatelliteSubscriberInfoList() {
+        List<SatelliteSubscriberInfo> list = new ArrayList<>();
+        list.add(new SatelliteSubscriberInfo.Builder().setSubscriberId(mSubscriberId).setCarrierId(
+                mCarrierId).setNiddApn(mNiddApn).setSubId(SUB_ID).setSubscriberIdType(
+                SatelliteSubscriberInfo.IMSI_MSISDN).build());
+        list.add(new SatelliteSubscriberInfo.Builder().setSubscriberId(mSubscriberId2).setCarrierId(
+                mCarrierId).setNiddApn(mNiddApn).setSubId(SUB_ID1).setSubscriberIdType(
+                SatelliteSubscriberInfo.ICCID).build());
+        return list;
+    }
+
+    @Test
+    public void testCheckForSubscriberIdChange_noChanged() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+
+        String imsi = "012345";
+        String oldMsisdn = "1234567890";
+        String newMsisdn = "1234567890";
+        List<SubscriptionInfo> allSubInfos = new ArrayList<>();
+        Optional<String> getSubscriberId;
+        SubscriptionInfoInternal subInfoInternal =
+                new SubscriptionInfoInternal.Builder().setCarrierId(0)
+                        .setImsi(imsi).build();
+
+        when(mSubscriptionInfo.getSubscriptionId()).thenReturn(SUB_ID);
+        allSubInfos.add(mSubscriptionInfo);
+        doReturn(" ").when(mContext).getOpPackageName();
+        doReturn(" ").when(mContext).getAttributionTag();
+        when(mMockSubscriptionManagerService.getAllSubInfoList(anyString(), anyString()))
+                .thenReturn(allSubInfos);
+        when(mSubscriptionInfo.isSatelliteESOSSupported()).thenReturn(true);
+        when(mMockSubscriptionManagerService.getSubscriptionInfoInternal(SUB_ID))
+                .thenReturn(subInfoInternal);
+
+        try {
+            Field field = SatelliteController.class.getDeclaredField("mInjectSubscriptionManager");
+            field.setAccessible(true);
+            field.set(mSatelliteControllerUT, mSubscriptionManager);
+        } catch (Exception e) {
+            loge("Exception InjectSubscriptionManager e: " + e);
+        }
+        when(mSubscriptionManager.getPhoneNumber(SUB_ID)).thenReturn(newMsisdn);
+        when(mSubscriptionInfo.isOnlyNonTerrestrialNetwork()).thenReturn(false);
+        mSatelliteControllerUT.subscriberIdPerSub().put(imsi + oldMsisdn, SUB_ID);
+
+        getSubscriberId = mSatelliteControllerUT.subscriberIdPerSub().entrySet().stream()
+                .filter(entry -> entry.getValue().equals(SUB_ID))
+                .map(Map.Entry::getKey).findFirst();
+        assertEquals(imsi + newMsisdn, getSubscriberId.get());
+
+        setComponentName();
+        mSatelliteControllerUT.subsInfoListPerPriority().computeIfAbsent(
+                        getKeyPriority(mSubscriptionInfo), k -> new ArrayList<>())
+                .add(mSubscriptionInfo);
+        mSatelliteControllerUT.evaluateESOSProfilesPrioritizationTest();
+        // Verify that broadcast has not been sent.
+        verify(mContext, times(0)).sendBroadcast(any(Intent.class));
+    }
+
+    @Test
+    public void testCheckForSubscriberIdChange_changed() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        List<SubscriptionInfo> allSubInfos = new ArrayList<>();
+
+        String imsi = "012345";
+        String oldMsisdn = "1234567890";
+        String newMsisdn = "4567891234";
+
+        Optional<String> getSubscriberId;
+        SubscriptionInfoInternal subInfoInternal =
+                new SubscriptionInfoInternal.Builder().setCarrierId(0).setImsi(imsi).build();
+
+        when(mSubscriptionInfo.getSubscriptionId()).thenReturn(SUB_ID);
+        allSubInfos.add(mSubscriptionInfo);
+        doReturn(" ").when(mContext).getOpPackageName();
+        doReturn(" ").when(mContext).getAttributionTag();
+        when(mMockSubscriptionManagerService.getAllSubInfoList(anyString(), anyString()))
+                .thenReturn(allSubInfos);
+
+        when(mSubscriptionInfo.isSatelliteESOSSupported()).thenReturn(true);
+        when(mMockSubscriptionManagerService.getSubscriptionInfoInternal(SUB_ID))
+                .thenReturn(subInfoInternal);
+
+        try {
+            Field field = SatelliteController.class.getDeclaredField("mInjectSubscriptionManager");
+            field.setAccessible(true);
+            field.set(mSatelliteControllerUT, mSubscriptionManager);
+        } catch (Exception e) {
+            loge("Exception InjectSubscriptionManager e: " + e);
+        }
+        when(mSubscriptionManager.getPhoneNumber(SUB_ID)).thenReturn(newMsisdn);
+        when(mSubscriptionInfo.isOnlyNonTerrestrialNetwork()).thenReturn(false);
+        mSatelliteControllerUT.subscriberIdPerSub().put(imsi + oldMsisdn, SUB_ID);
+
+        getSubscriberId = mSatelliteControllerUT.subscriberIdPerSub().entrySet().stream()
+                .filter(entry -> entry.getValue().equals(SUB_ID))
+                .map(Map.Entry::getKey).findFirst();
+        assertNotEquals(imsi + newMsisdn, getSubscriberId.get());
+
+        setComponentName();
+        mSatelliteControllerUT.subsInfoListPerPriority().computeIfAbsent(
+                        getKeyPriority(mSubscriptionInfo), k -> new ArrayList<>())
+                .add(mSubscriptionInfo);
+        mSatelliteControllerUT.evaluateESOSProfilesPrioritizationTest();
+        // Verify that broadcast has been sent.
+        verify(mContext, times(1)).sendBroadcast(any(Intent.class));
+    }
+
+    @Test
+    public void testProvisionStatusPerSubscriberIdGetFromDb() throws Exception {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+
+        setSatelliteSubscriberTesting();
+        // Check if the cache is not updated when the value read from the database is false.
+        verifyProvisionStatusPerSubscriberIdGetFromDb(false);
+
+        // Check if the cache is updated when the value read from the database is true.
+        verifyProvisionStatusPerSubscriberIdGetFromDb(true);
+    }
+
+    @Test
+    public void testProvisionStatusPerSubscriberIdStoreToDb() throws Exception {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+
+        setSatelliteSubscriberTesting();
+        // Check if the cache is not updated when the value read from the database is false.
+        verifyProvisionStatusPerSubscriberIdGetFromDb(false);
+
+        List<SatelliteSubscriberInfo> inputList = getExpectedSatelliteSubscriberInfoList();
+        verifyProvisionSatellite(inputList);
+        verify(mMockSubscriptionManagerService).setIsSatelliteProvisionedForNonIpDatagram(
+                eq(SUB_ID), eq(true));
+    }
+
+    private void verifyProvisionStatusPerSubscriberIdGetFromDb(boolean provision) {
+        doReturn(provision).when(
+                mMockSubscriptionManagerService).isSatelliteProvisionedForNonIpDatagram(anyInt());
+        mCarrierConfigBundle.putString(KEY_SATELLITE_NIDD_APN_NAME_STRING, mNiddApn);
+        mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, true);
+        for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+                : mCarrierConfigChangedListenerList) {
+            pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+                    /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+            );
+        }
+        moveTimeForward(TimeUnit.MINUTES.toMillis(1));
+        processAllMessages();
+        mSatelliteControllerUT.requestSatelliteSubscriberProvisionStatus(
+                mRequestSatelliteSubscriberProvisionStatusReceiver);
+        moveTimeForward(TimeUnit.MINUTES.toMillis(1));
+        processAllMessages();
+        assertEquals(SATELLITE_RESULT_SUCCESS,
+                mRequestSatelliteSubscriberProvisionStatusResultCode);
+        assertEquals(provision,
+                mRequestSatelliteSubscriberProvisionStatusResultList.get(0).getProvisionStatus());
+    }
+
+    private void setComponentName() {
+        when(mSatelliteControllerUT.getStringFromOverlayConfigTest(
+                R.string.config_satellite_gateway_service_package))
+                .thenReturn("com.example.package");
+        when(mSatelliteControllerUT.getStringFromOverlayConfigTest(
+                R.string.config_satellite_carrier_roaming_esos_provisioned_class))
+                .thenReturn("com.example.class");
+    }
+
+    private int getKeyPriority(SubscriptionInfo subscriptionInfo) {
+        boolean isActive = subscriptionInfo.isActive();
+        boolean isNtnOnly = subscriptionInfo.isOnlyNonTerrestrialNetwork();
+        boolean isESOSSupported = subscriptionInfo.isSatelliteESOSSupported();
+
+        int keyPriority;
+        if (isESOSSupported && isActive) {
+            keyPriority = 1;
+        } else if (isNtnOnly) {
+            keyPriority = 2;
+        } else if (isESOSSupported) {
+            keyPriority = 3;
+        } else {
+            keyPriority = -1;
+        }
+        return keyPriority;
+    }
+
     private void resetSatelliteControllerUTEnabledState() {
         logd("resetSatelliteControllerUTEnabledState");
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         doNothing().when(mMockSatelliteModemInterface)
                 .setSatelliteServicePackageName(anyString());
-        mSatelliteControllerUT.setSatelliteServicePackageName("TestSatelliteService");
+        mSatelliteControllerUT.setSatelliteServicePackageName("TestSatelliteService", null);
         processAllMessages();
 
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setProvisionedState(false);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        sendProvisionedStateChangedEvent(true, null);
+        setProvisionedState(true);
         processAllMessages();
         verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
     }
@@ -3789,7 +4654,7 @@
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
         doNothing().when(mMockSatelliteModemInterface)
                 .setSatelliteServicePackageName(anyString());
-        mSatelliteControllerUT.setSatelliteServicePackageName("TestSatelliteService");
+        mSatelliteControllerUT.setSatelliteServicePackageName("TestSatelliteService", null);
         processAllMessages();
     }
 
@@ -3797,7 +4662,7 @@
         resetSatelliteControllerUT();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        sendProvisionedStateChangedEvent(true, null);
+        setProvisionedState(true);
         processAllMessages();
         verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
     }
@@ -3806,6 +4671,8 @@
         resetSatelliteControllerUTToSupportedAndProvisionedState();
         // Clean up pending resources and move satellite controller to OFF state.
         sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_UNAVAILABLE, null);
+        mSatelliteControllerUT.moveSatelliteToOffStateAndCleanUpResources(
+                SATELLITE_RESULT_REQUEST_ABORTED);
         processAllMessages();
         verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
     }
@@ -3815,9 +4682,9 @@
         setRadioPower(true);
         processAllMessages();
 
+        mIIntegerConsumerResults.clear();
         setUpResponseForRequestSatelliteEnabled(true, false, false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, false,
-                mIIntegerConsumer);
+        mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -3878,15 +4745,7 @@
 
     private void setUpResponseForRequestIsSatelliteProvisioned(
             boolean isSatelliteProvisioned, @SatelliteManager.SatelliteResult int error) {
-        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
-                ? null : new SatelliteException(error);
-        int[] provisioned = new int[]{isSatelliteProvisioned ? 1 : 0};
-        doAnswer(invocation -> {
-            Message message = (Message) invocation.getArguments()[0];
-            AsyncResult.forMessage(message, provisioned, exception);
-            message.sendToTarget();
-            return null;
-        }).when(mMockSatelliteModemInterface).requestIsSatelliteProvisioned(any(Message.class));
+        mSatelliteControllerUT.setSatelliteProvisioned(isSatelliteProvisioned);
     }
 
     private void setUpResponseForRequestSatelliteEnabled(
@@ -3898,12 +4757,12 @@
             if (exception == null && !enabled) {
                 sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_OFF, null);
             }
-            Message message = (Message) invocation.getArguments()[3];
+            Message message = (Message) invocation.getArguments()[1];
             AsyncResult.forMessage(message, null, exception);
             message.sendToTarget();
             return null;
         }).when(mMockSatelliteModemInterface)
-                .requestSatelliteEnabled(eq(enabled), eq(demoMode), eq(emergency),
+                .requestSatelliteEnabled(any(SatelliteModemEnableRequestAttributes.class),
                         any(Message.class));
     }
 
@@ -3923,41 +4782,13 @@
     private void setUpNoResponseForRequestSatelliteEnabled(boolean enabled, boolean demoMode,
             boolean emergency) {
         doNothing().when(mMockSatelliteModemInterface)
-                .requestSatelliteEnabled(eq(enabled), eq(demoMode), eq(emergency),
+                .requestSatelliteEnabled(eq(new SatelliteModemEnableRequestAttributes(
+                                enabled, demoMode, emergency,
+                                new SatelliteSubscriptionInfo("", "")
+                        )),
                         any(Message.class));
     }
 
-    private void setUpResponseForProvisionSatelliteService(
-            String token, byte[] provisionData, @SatelliteManager.SatelliteResult int error) {
-        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
-                ? null : new SatelliteException(error);
-        doAnswer(invocation -> {
-            Message message = (Message) invocation.getArguments()[2];
-            AsyncResult.forMessage(message, null, exception);
-            message.sendToTarget();
-            return null;
-        }).when(mMockSatelliteModemInterface)
-                .provisionSatelliteService(eq(token), any(byte[].class), any(Message.class));
-    }
-
-    private void setUpNoResponseForProvisionSatelliteService(String token) {
-        doNothing().when(mMockSatelliteModemInterface)
-                .provisionSatelliteService(eq(token), any(), any(Message.class));
-    }
-
-    private void setUpResponseForDeprovisionSatelliteService(String token,
-            @SatelliteManager.SatelliteResult int error) {
-        SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
-                ? null : new SatelliteException(error);
-        doAnswer(invocation -> {
-            Message message = (Message) invocation.getArguments()[1];
-            AsyncResult.forMessage(message, null, exception);
-            message.sendToTarget();
-            return null;
-        }).when(mMockSatelliteModemInterface)
-                .deprovisionSatelliteService(eq(token), any(Message.class));
-    }
-
     private void setUpResponseForRequestSatelliteCapabilities(
             SatelliteCapabilities satelliteCapabilities,
             @SatelliteManager.SatelliteResult int error) {
@@ -4186,7 +5017,7 @@
 
     private void verifySatelliteSupported(boolean supported, int expectedErrorCode) {
         mSatelliteSupportSemaphore.drainPermits();
-        mSatelliteControllerUT.requestIsSatelliteSupported(SUB_ID, mSatelliteSupportReceiver);
+        mSatelliteControllerUT.requestIsSatelliteSupported(mSatelliteSupportReceiver);
         processAllMessages();
         assertTrue(waitForRequestIsSatelliteSupportedResult(1));
         assertEquals(expectedErrorCode, mQueriedSatelliteSupportedResultCode);
@@ -4196,7 +5027,7 @@
     private void verifySatelliteSupported(TestSatelliteController satelliteController,
             boolean supported, int expectedErrorCode) {
         mSatelliteSupportSemaphore.drainPermits();
-        satelliteController.requestIsSatelliteSupported(SUB_ID, mSatelliteSupportReceiver);
+        satelliteController.requestIsSatelliteSupported(mSatelliteSupportReceiver);
         processAllMessages();
         assertTrue(waitForRequestIsSatelliteSupportedResult(1));
         assertEquals(expectedErrorCode, mQueriedSatelliteSupportedResultCode);
@@ -4205,7 +5036,7 @@
 
     private void verifySatelliteEnabled(boolean enabled, int expectedErrorCode) {
         mIsSatelliteEnabledSemaphore.drainPermits();
-        mSatelliteControllerUT.requestIsSatelliteEnabled(SUB_ID, mIsSatelliteEnabledReceiver);
+        mSatelliteControllerUT.requestIsSatelliteEnabled(mIsSatelliteEnabledReceiver);
         processAllMessages();
         assertTrue(waitForRequestIsSatelliteEnabledResult(1));
         assertEquals(expectedErrorCode, mQueriedIsSatelliteEnabledResultCode);
@@ -4214,8 +5045,7 @@
 
     private void verifySatelliteProvisioned(boolean provisioned, int expectedErrorCode) {
         mIsSatelliteProvisionedSemaphore.drainPermits();
-        mSatelliteControllerUT.requestIsSatelliteProvisioned(
-                SUB_ID, mIsSatelliteProvisionedReceiver);
+        mSatelliteControllerUT.requestIsSatelliteProvisioned(mIsSatelliteProvisionedReceiver);
         processAllMessages();
         assertTrue(waitForRequestIsSatelliteProvisionedResult(1));
         assertEquals(expectedErrorCode, mQueriedIsSatelliteProvisionedResultCode);
@@ -4226,18 +5056,15 @@
             @NtnSignalStrength.NtnSignalStrengthLevel int signalStrengthLevel,
             int expectedErrorCode) {
         mRequestNtnSignalStrengthSemaphore.drainPermits();
-        mSatelliteControllerUT.requestNtnSignalStrength(SUB_ID, mRequestNtnSignalStrengthReceiver);
+        mSatelliteControllerUT.requestNtnSignalStrength(mRequestNtnSignalStrengthReceiver);
         processAllMessages();
         assertTrue(waitForRequestNtnSignalStrengthResult(1));
         assertEquals(expectedErrorCode, mQueriedNtnSignalStrengthResultCode);
         assertEquals(signalStrengthLevel, mQueriedNtnSignalStrengthLevel);
     }
 
-    private void sendProvisionedStateChangedEvent(boolean provisioned, Throwable exception) {
-        Message msg = mSatelliteControllerUT.obtainMessage(
-                26 /* EVENT_SATELLITE_PROVISION_STATE_CHANGED */);
-        msg.obj = new AsyncResult(null, provisioned, exception);
-        msg.sendToTarget();
+    private void setProvisionedState(@Nullable Boolean provisioned) {
+        mSatelliteControllerUT.setSatelliteProvisioned(provisioned);
     }
 
     private void sendSatelliteModemStateChangedEvent(int state, Throwable exception) {
@@ -4293,6 +5120,13 @@
         msg.sendToTarget();
     }
 
+    private void sendSatelliteRegistrationFailureEvent(int errorCode, Throwable exception) {
+        Message msg = mSatelliteControllerUT.obtainMessage(
+                54 /* EVENT_SATELLITE_REGISTRATION_FAILURE */);
+        msg.obj = new AsyncResult(null, errorCode, exception);
+        msg.sendToTarget();
+    }
+
     private void setRadioPower(boolean on) {
         mSimulatedCommands.setRadioPower(on, false, false, null);
     }
@@ -4301,14 +5135,13 @@
             INtnSignalStrengthCallback callback, int expectedError) {
         if (expectedError == SATELLITE_RESULT_SUCCESS) {
             try {
-                mSatelliteControllerUT.registerForNtnSignalStrengthChanged(subId, callback);
+                mSatelliteControllerUT.registerForNtnSignalStrengthChanged(callback);
             } catch (RemoteException ex) {
                 throw new AssertionError();
             }
         } else {
             RemoteException ex = assertThrows(RemoteException.class,
-                    () -> mSatelliteControllerUT.registerForNtnSignalStrengthChanged(subId,
-                            callback));
+                    () -> mSatelliteControllerUT.registerForNtnSignalStrengthChanged(callback));
             assertTrue("The cause is not IllegalStateException",
                     ex.getCause() instanceof IllegalStateException);
         }
@@ -4323,9 +5156,7 @@
         setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForProvisionSatelliteService(TEST_SATELLITE_TOKEN, testProvisionData,
-                SATELLITE_RESULT_SUCCESS);
-        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(SUB_ID,
+        cancelRemote = mSatelliteControllerUT.provisionSatelliteService(
                 TEST_SATELLITE_TOKEN,
                 testProvisionData, mIIntegerConsumer);
         processAllMessages();
@@ -4337,8 +5168,7 @@
 
     private void deprovisionSatelliteService() {
         mIIntegerConsumerResults.clear();
-        setUpResponseForDeprovisionSatelliteService(TEST_SATELLITE_TOKEN, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
+        mSatelliteControllerUT.deprovisionSatelliteService(
                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
@@ -4490,12 +5320,13 @@
         }
     }
 
-    private static class TestSatelliteController extends SatelliteController {
+    private class TestSatelliteController extends SatelliteController {
         public boolean setSettingsKeyForSatelliteModeCalled = false;
         public boolean allRadiosDisabled = true;
         public long elapsedRealtime = 0;
         public int satelliteModeSettingValue = SATELLITE_MODE_ENABLED_FALSE;
         public boolean setSettingsKeyToAllowDeviceRotationCalled = false;
+        public OutcomeReceiver<Boolean, SatelliteException> isSatelliteAllowedCallback = null;
 
         TestSatelliteController(
                 Context context, Looper looper, @NonNull FeatureFlags featureFlags) {
@@ -4523,6 +5354,7 @@
 
         @Override
         protected boolean areAllRadiosDisabled() {
+            logd("areAllRadiosDisabled: " + allRadiosDisabled);
             return allRadiosDisabled;
         }
 
@@ -4541,5 +5373,77 @@
         void setSatelliteSessionController(SatelliteSessionController satelliteSessionController) {
             mSatelliteSessionController = satelliteSessionController;
         }
+
+        @Override
+        protected void setSatellitePhone(int subId) {
+            logd("setSatellitePhone");
+            synchronized (mSatellitePhoneLock) {
+                mSatellitePhone = mPhone;
+            }
+        }
+
+        @Override
+        protected void requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                @NonNull OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> callback) {
+            logd("requestIsSatelliteCommunicationAllowedForCurrentLocation: callback="
+                    + callback);
+            isSatelliteAllowedCallback = callback;
+        }
+
+        @Override
+        protected boolean isSubscriptionProvisioned(int subId) {
+            synchronized (mSatellitePhoneLock) {
+                if (mSatellitePhone.getSubId() == subId) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        void setSatelliteProvisioned(@Nullable Boolean isProvisioned) {
+            synchronized (mSatelliteViaOemProvisionLock) {
+                mIsSatelliteViaOemProvisioned = isProvisioned;
+            }
+        }
+
+        public boolean isRadioOn() {
+            synchronized (mIsRadioOnLock) {
+                return mIsRadioOn;
+            }
+        }
+
+        public boolean isRadioOffRequested() {
+            synchronized (mIsRadioOnLock) {
+                return mRadioOffRequested;
+            }
+        }
+
+        public boolean isWaitForCellularModemOffTimerStarted() {
+            return hasMessages(EVENT_WAIT_FOR_CELLULAR_MODEM_OFF_TIMED_OUT);
+        }
+
+        public Map<String, Integer> subscriberIdPerSub() {
+            synchronized (mSatelliteTokenProvisionedLock) {
+                return mSubscriberIdPerSub;
+            }
+        }
+
+        public Map<Integer, List<SubscriptionInfo>> subsInfoListPerPriority() {
+            synchronized (mSatelliteTokenProvisionedLock) {
+                return mSubsInfoListPerPriority;
+            }
+        }
+
+        public void evaluateESOSProfilesPrioritizationTest() {
+            evaluateESOSProfilesPrioritization();
+        }
+
+        public String getStringFromOverlayConfigTest(int resourceId) {
+            return getStringFromOverlayConfig(resourceId);
+        }
+
+        public boolean isAnyWaitForSatelliteEnablingResponseTimerStarted() {
+            return hasMessages(EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT);
+        }
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
index f4fa48e..358ec47 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
@@ -44,6 +44,7 @@
 import android.os.Looper;
 import android.os.OutcomeReceiver;
 import android.os.RemoteException;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.telecom.Connection;
 import android.telecom.TelecomManager;
 import android.telephony.BinderCacheManager;
@@ -65,9 +66,11 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.Flags;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -92,6 +95,7 @@
     private static final int TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS = 500;
     private static final int PHONE_ID = 0;
     private static final int PHONE_ID2 = 1;
+    private static final int SUB_ID = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
     private static final String CALL_ID = "CALL_ID";
     private static final String WRONG_CALL_ID = "WRONG_CALL_ID";
     private static final String DEFAULT_SATELLITE_MESSAGING_PACKAGE = "android.com.google.default";
@@ -108,6 +112,8 @@
     private ImsManager.MmTelFeatureConnectionFactory mMmTelFeatureConnectionFactory;
     @Mock
     private FeatureFlags mFeatureFlags;
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     private TestConnection mTestConnection;
     private Uri mTestConnectionAddress = Uri.parse("tel:1234");
     private TestSOSMessageRecommender mTestSOSMessageRecommender;
@@ -127,6 +133,7 @@
                 R.integer.config_emergency_call_wait_for_connection_timeout_millis))
                 .thenReturn(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
         mTestSatelliteController = new TestSatelliteController(mContext,
                 Looper.myLooper(), mFeatureFlags);
         mTestImsManager = new TestImsManager(
@@ -542,9 +549,22 @@
         assertEquals(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS,
                 mTestSOSMessageRecommender.getTimeOutMillis());
 
-        // Both OEM and carrier support satellite. Thus, carrier's timeout duration will be used
+        // Both OEM and carrier support satellite, but device is not connected to carrier satellite
+        // within hysteresis time. Thus, OEM timer will be used.
         long carrierTimeoutMillis = 1000;
         mTestSatelliteController.isSatelliteEmergencyMessagingSupportedViaCarrier = true;
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
+        mTestSatelliteController.carrierEmergencyCallWaitForConnectionTimeoutMillis =
+                carrierTimeoutMillis;
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        processAllMessages();
+        assertEquals(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS,
+                mTestSOSMessageRecommender.getTimeOutMillis());
+
+        // Both OEM and carrier support satellite, and device is connected to carrier satellite
+        // within hysteresis time. Thus, carrier timer will be used.
+        mTestSatelliteController.isSatelliteEmergencyMessagingSupportedViaCarrier = true;
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(true);
         mTestSatelliteController.carrierEmergencyCallWaitForConnectionTimeoutMillis =
                 carrierTimeoutMillis;
         mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
@@ -552,6 +572,51 @@
         assertEquals(carrierTimeoutMillis, mTestSOSMessageRecommender.getTimeOutMillis());
     }
 
+    @Test
+    public void testGetEmergencyCallToSatelliteHandoverType_SatelliteViaCarrierAndOemAvailable() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN);
+
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(true);
+        mTestSatelliteController.mIsSatelliteViaOemProvisioned = true;
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        assertEquals(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911,
+                mTestSOSMessageRecommender.getEmergencyCallToSatelliteHandoverType());
+
+        mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN);
+    }
+
+    @Test
+    public void testGetEmergencyCallToSatelliteHandoverType_OnlySatelliteViaCarrierAvailable() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN);
+
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(true);
+        mTestSatelliteController.mIsSatelliteViaOemProvisioned = false;
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        assertEquals(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911,
+                mTestSOSMessageRecommender.getEmergencyCallToSatelliteHandoverType());
+
+        mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN);
+    }
+
+    @Test
+    public void testGetEmergencyCallToSatelliteHandoverType_OemAndCarrierNotAvailable() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN);
+
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
+        mTestSatelliteController.mIsSatelliteViaOemProvisioned = true;
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        assertEquals(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS,
+                mTestSOSMessageRecommender.getEmergencyCallToSatelliteHandoverType());
+
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
+        mTestSatelliteController.mIsSatelliteViaOemProvisioned = false;
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        assertEquals(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS,
+                mTestSOSMessageRecommender.getEmergencyCallToSatelliteHandoverType());
+
+        mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN);
+    }
+
     private void testStopTrackingCallBeforeTimeout(
             @Connection.ConnectionState int connectionState) {
         mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
@@ -685,21 +750,21 @@
 
         @Override
         @SatelliteManager.SatelliteResult public int registerForSatelliteProvisionStateChanged(
-                int subId, @NonNull ISatelliteProvisionStateCallback callback) {
+                @NonNull ISatelliteProvisionStateCallback callback) {
             mRegisterForSatelliteProvisionStateChangedCalls++;
             Set<ISatelliteProvisionStateCallback> perSubscriptionCallbacks =
-                    mProvisionStateChangedCallbacks.getOrDefault(subId, new HashSet<>());
+                    mProvisionStateChangedCallbacks.getOrDefault(SUB_ID, new HashSet<>());
             perSubscriptionCallbacks.add(callback);
-            mProvisionStateChangedCallbacks.put(subId, perSubscriptionCallbacks);
+            mProvisionStateChangedCallbacks.put(SUB_ID, perSubscriptionCallbacks);
             return SatelliteManager.SATELLITE_RESULT_SUCCESS;
         }
 
         @Override
         public void unregisterForSatelliteProvisionStateChanged(
-                int subId, @NonNull ISatelliteProvisionStateCallback callback) {
+                @NonNull ISatelliteProvisionStateCallback callback) {
             mUnregisterForSatelliteProvisionStateChangedCalls++;
             Set<ISatelliteProvisionStateCallback> perSubscriptionCallbacks =
-                    mProvisionStateChangedCallbacks.get(subId);
+                    mProvisionStateChangedCallbacks.get(SUB_ID);
             if (perSubscriptionCallbacks != null) {
                 perSubscriptionCallbacks.remove(callback);
             }
@@ -745,7 +810,7 @@
         public void sendProvisionStateChangedEvent(int subId, boolean provisioned) {
             mIsSatelliteViaOemProvisioned = provisioned;
             Set<ISatelliteProvisionStateCallback> perSubscriptionCallbacks =
-                    mProvisionStateChangedCallbacks.get(subId);
+                    mProvisionStateChangedCallbacks.get(SUB_ID);
             if (perSubscriptionCallbacks != null) {
                 for (ISatelliteProvisionStateCallback callback : perSubscriptionCallbacks) {
                     try {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteServiceUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteServiceUtilsTest.java
index 0e4adcd..7bf8ab4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteServiceUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteServiceUtilsTest.java
@@ -19,19 +19,28 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
 
 import android.os.PersistableBundle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyTest;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -41,12 +50,23 @@
 @TestableLooper.RunWithLooper
 public class SatelliteServiceUtilsTest extends TelephonyTest {
     private static final String TAG = "SatelliteServiceUtilsTest";
+    private static final int SUB_ID = 0;
+    private static final int SUB_ID1 = 1;
+    @Mock private ServiceState mServiceState2;
 
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
         MockitoAnnotations.initMocks(this);
         logd(TAG + " Setup!");
+
+        replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[]{mPhone, mPhone2});
+        when(mPhone.getServiceState()).thenReturn(mServiceState);
+        when(mPhone.getSubId()).thenReturn(SUB_ID);
+        when(mPhone.getPhoneId()).thenReturn(0);
+        when(mPhone2.getServiceState()).thenReturn(mServiceState2);
+        when(mPhone2.getSubId()).thenReturn(SUB_ID1);
+        when(mPhone2.getPhoneId()).thenReturn(1);
     }
 
     @After
@@ -119,4 +139,89 @@
         mergedList = SatelliteServiceUtils.mergeStrLists(l1, l2, l3);
         assertEquals(expectedMergedList, mergedList);
     }
+
+    @Test
+    public void testIsCellularAvailable() {
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        when(mServiceState2.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        assertFalse(SatelliteServiceUtils.isCellularAvailable());
+
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_EMERGENCY_ONLY);
+        assertTrue(SatelliteServiceUtils.isCellularAvailable());
+
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        when(mServiceState2.getState()).thenReturn(ServiceState.STATE_EMERGENCY_ONLY);
+        assertTrue(SatelliteServiceUtils.isCellularAvailable());
+
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        when(mServiceState2.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        when(mServiceState2.isEmergencyOnly()).thenReturn(true);
+        assertTrue(SatelliteServiceUtils.isCellularAvailable());
+    }
+
+    @Test
+    public void testIsSatellitePlmn() {
+        int subId = 1;
+
+        when(mSatelliteController.getSatellitePlmnsForCarrier(eq(subId)))
+                .thenReturn(new ArrayList<>());
+        assertFalse(SatelliteServiceUtils.isSatellitePlmn(subId, mServiceState));
+
+        // registered PLMN is null
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setRegisteredPlmn(null)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfoListForTransportType(
+                eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+                .thenReturn(List.of(nri));
+        assertFalse(SatelliteServiceUtils.isSatellitePlmn(subId, mServiceState));
+
+        // cell identity is null
+        when(mSatelliteController.getSatellitePlmnsForCarrier(eq(subId))).thenReturn(
+                List.of("120260"));
+        nri = new NetworkRegistrationInfo.Builder()
+                .setRegisteredPlmn("123456")
+                .setCellIdentity(null)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfoListForTransportType(
+                eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+                .thenReturn(List.of(nri));
+        assertFalse(SatelliteServiceUtils.isSatellitePlmn(subId, mServiceState));
+
+        // mcc and mnc are null
+        when(mCellIdentity.getMccString()).thenReturn(null);
+        when(mCellIdentity.getMncString()).thenReturn(null);
+        nri = new NetworkRegistrationInfo.Builder()
+                .setRegisteredPlmn("123456")
+                .setCellIdentity(mCellIdentity)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfoListForTransportType(
+                eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+                .thenReturn(List.of(nri));
+        assertFalse(SatelliteServiceUtils.isSatellitePlmn(subId, mServiceState));
+
+        // mccmnc equal to satellite PLMN
+        when(mCellIdentity.getMccString()).thenReturn("120");
+        when(mCellIdentity.getMncString()).thenReturn("260");
+        nri = new NetworkRegistrationInfo.Builder()
+                .setRegisteredPlmn("123456")
+                .setCellIdentity(mCellIdentity)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfoListForTransportType(
+                eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+                .thenReturn(List.of(nri));
+        assertTrue(SatelliteServiceUtils.isSatellitePlmn(subId, mServiceState));
+
+        // registered PLMN equal to satellite PLMN
+        when(mCellIdentity.getMccString()).thenReturn("123");
+        when(mCellIdentity.getMncString()).thenReturn("456");
+        nri = new NetworkRegistrationInfo.Builder()
+                .setRegisteredPlmn("120260")
+                .setCellIdentity(mCellIdentity)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfoListForTransportType(
+                eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+                .thenReturn(List.of(nri));
+        assertTrue(SatelliteServiceUtils.isSatellitePlmn(subId, mServiceState));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
index 78763d1..b4af458 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS;
@@ -28,8 +31,12 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -37,13 +44,17 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
+import android.os.AsyncResult;
+import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.telephony.satellite.ISatelliteModemStateCallback;
 import android.telephony.satellite.SatelliteManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.flags.FeatureFlags;
 
@@ -51,6 +62,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -58,6 +71,7 @@
 import java.util.List;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -79,7 +93,9 @@
     private static final String STATE_LISTENING = "ListeningState";
     private static final String STATE_NOT_CONNECTED = "NotConnectedState";
     private static final String STATE_CONNECTED = "ConnectedState";
-
+    private static final int SCREEN_OFF_INACTIVITY_TIMEOUT_SEC = 30;
+    private static final int P2P_SMS_INACTIVITY_TIMEOUT_SEC = 180;
+    private static final int ESOS_INACTIVITY_TIMEOUT_SEC = 600;
     private TestSatelliteModemInterface mSatelliteModemInterface;
     private TestSatelliteSessionController mTestSatelliteSessionController;
     private TestSatelliteModemStateCallback mTestSatelliteModemStateCallback;
@@ -89,6 +105,9 @@
     @Mock private DatagramDispatcher mMockDatagramDispatcher;
     @Mock private DatagramController mMockDatagramController;
 
+    @Captor ArgumentCaptor<Handler> mHandlerCaptor;
+    @Captor ArgumentCaptor<Integer> mMsgCaptor;
+
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
@@ -108,6 +127,10 @@
 
         when(mFeatureFlags.satellitePersistentLogging()).thenReturn(true);
         when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(false);
+        when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
+                anyInt())).thenReturn(false);
+        when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(false);
+        when(mMockSatelliteController.getSatellitePhone()).thenReturn(mPhone);
         mSatelliteModemInterface = new TestSatelliteModemInterface(
                 mContext, mMockSatelliteController, Looper.myLooper(), mFeatureFlags);
         mTestSatelliteSessionController = new TestSatelliteSessionController(mContext,
@@ -170,6 +193,179 @@
     }
 
     @Test
+    public void testScreenOffInactivityTimer() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged(
+                eq(mTestSatelliteSessionController.getHandler()), anyInt(), any());
+        when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(false);
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT,
+                SCREEN_OFF_INACTIVITY_TIMEOUT_SEC);
+        when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle);
+
+        // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+        assertNotNull(mTestSatelliteSessionController);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+
+        moveToIdleState();
+
+        // SatelliteSessionController should call registerForScreenStateChanged.
+        verify(mDeviceStateMonitor).registerForScreenStateChanged(mHandlerCaptor.capture(),
+                mMsgCaptor.capture(), any());
+
+        // Notify Screen off
+        sendScreenStateChanged(mHandlerCaptor.getValue(), mMsgCaptor.getValue(), false);
+        processAllMessages();
+
+        // Verify that the screen off inactivity timer is started.
+        assertTrue(mTestSatelliteSessionController.isScreenOffInActivityTimerStarted());
+
+        // Time shift to cause timeout
+        moveTimeForward(SCREEN_OFF_INACTIVITY_TIMEOUT_SEC * 1000);
+        processAllMessages();
+
+        // Verify that SatelliteController#requestSatelliteEnabled() was called.
+        verify(mMockSatelliteController).requestSatelliteEnabled(
+                eq(false), eq(false), eq(false), any(IIntegerConsumer.Stub.class));
+    }
+
+    @Test
+    public void testScreenOffInactivityTimerStop() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged(
+                eq(mTestSatelliteSessionController.getHandler()), anyInt(), any());
+        // Satellite enabling request is for an emergency.
+        when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true);
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT,
+                SCREEN_OFF_INACTIVITY_TIMEOUT_SEC);
+        when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle);
+
+        // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+        assertNotNull(mTestSatelliteSessionController);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+
+        moveToIdleState();
+
+        // SatelliteSessionController should not call registerForScreenStateChanged.
+        verify(mDeviceStateMonitor, never()).registerForScreenStateChanged(
+                eq(mTestSatelliteSessionController.getHandler()), anyInt(), any());
+
+        moveToPowerOffState();
+
+        // Satellite enabling request is not for an emergency.
+        when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(false);
+
+        moveToIdleState();
+
+        // SatelliteSessionController should call registerForScreenStateChanged.
+        verify(mDeviceStateMonitor).registerForScreenStateChanged(mHandlerCaptor.capture(),
+                mMsgCaptor.capture(), any());
+
+        // Notify Screen off
+        sendScreenStateChanged(mHandlerCaptor.getValue(), mMsgCaptor.getValue(), false);
+        processAllMessages();
+
+        // Verify that the screen off inactivity timer is started.
+        assertTrue(mTestSatelliteSessionController.isScreenOffInActivityTimerStarted());
+
+        // Notify Screen on
+        sendScreenStateChanged(mHandlerCaptor.getValue(), mMsgCaptor.getValue(), true);
+        processAllMessages();
+
+        // Verify that the screen off inactivity timer is stopped
+        assertFalse(mTestSatelliteSessionController.isScreenOffInActivityTimerStarted());
+    }
+
+    @Test
+    public void testP2pSmsInactivityTimer() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged(
+                eq(mTestSatelliteSessionController.getHandler()), anyInt(), any());
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+
+        when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(false);
+        when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
+                anyInt())).thenReturn(true);
+        when(mMockSatelliteController.isInCarrierRoamingNbIotNtn()).thenReturn(true);
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
+                P2P_SMS_INACTIVITY_TIMEOUT_SEC);
+        when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle);
+
+        // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+        assertNotNull(mTestSatelliteSessionController);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        setupDatagramTransferringState(true);
+
+        moveToNotConnectedState();
+
+        // Verify that the P2P SMS inactivity timer is started.
+        assertTrue(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted());
+
+        mTestSatelliteSessionController.setDeviceAlignedWithSatellite(true);
+
+        // Verify that the P2P SMS inactivity timer is stopped.
+        assertFalse(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted());
+
+        mTestSatelliteSessionController.setDeviceAlignedWithSatellite(false);
+
+        // Verify that the P2P SMS inactivity timer is started.
+        assertTrue(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted());
+
+        // Time shift to cause timeout
+        moveTimeForward(P2P_SMS_INACTIVITY_TIMEOUT_SEC * 1000);
+        processAllMessages();
+
+        // SatelliteSessionController should move to IDLE state.
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+    }
+
+    @Test
+    public void testEsosInactivityTimer() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged(
+                eq(mTestSatelliteSessionController.getHandler()), anyInt(), any());
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+
+        when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true);
+        when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true);
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT,
+                ESOS_INACTIVITY_TIMEOUT_SEC);
+        when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle);
+
+        // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+        assertNotNull(mTestSatelliteSessionController);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        setupDatagramTransferringState(true);
+
+        moveToNotConnectedState();
+
+        // Verify that the ESOS inactivity timer is started.
+        assertTrue(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted());
+
+        mTestSatelliteSessionController.setDeviceAlignedWithSatellite(true);
+
+        // Verify that the ESOS inactivity timer is stopped.
+        assertFalse(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted());
+
+        mTestSatelliteSessionController.setDeviceAlignedWithSatellite(false);
+
+        // Verify that the ESOS inactivity timer is started.
+        assertTrue(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted());
+
+        // Time shift to cause timeout
+        moveTimeForward(ESOS_INACTIVITY_TIMEOUT_SEC * 1000);
+        processAllMessages();
+
+        // SatelliteSessionController should move to IDLE state.
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+    }
+
+    @Test
     public void testStateTransition() {
         /**
          * Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
@@ -474,8 +670,8 @@
         processAllMessages();
 
         // SatelliteSessionController should move to CONNECTED state
-        assertSuccessfulModemStateChangedCallback(
-                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
         assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
         assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
         verify(mMockDatagramController).onSatelliteModemStateChanged(
@@ -632,8 +828,8 @@
         processAllMessages();
 
         // SatelliteSessionController should move to CONNECTED state
-        assertSuccessfulModemStateChangedCallback(
-                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
         assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
         verify(mMockDatagramController).onSatelliteModemStateChanged(
                 SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
@@ -658,8 +854,8 @@
         processAllMessages();
 
         // SatelliteSessionController should move to CONNECTED state
-        assertSuccessfulModemStateChangedCallback(
-                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
         assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
         verify(mMockDatagramController).onSatelliteModemStateChanged(
                 SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
@@ -694,8 +890,8 @@
         processAllMessages();
 
         // SatelliteSessionController should move to CONNECTED state
-        assertSuccessfulModemStateChangedCallback(
-                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
         assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
 
         // Wait for timeout
@@ -908,7 +1104,7 @@
         moveSatelliteToEnablingState();
 
         // Satellite enablement has failed
-        mTestSatelliteSessionController.onSatelliteEnablementFailed();
+        mTestSatelliteSessionController.onSatelliteEnablementFailed(true);
         processAllMessages();
 
         // Satellite should move back to POWER_OFF state
@@ -936,28 +1132,41 @@
         assertNotNull(mTestSatelliteSessionController);
         assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
 
-        // IDLE -> DISABLING
+        // IDLE -> DISABLING request failed -> NOT_CONNECTED
         moveToIdleState();
-        moveSatelliteToDisablingState();
+        moveSatelliteToDisablingRequestFailed(SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED,
+                STATE_NOT_CONNECTED);
 
-        // DISABLING -> POWER_OFF
+        // NOT_CONNECTED -> DISABLING -> POWER_OFF
+        moveSatelliteToDisablingState();
         moveToPowerOffState();
 
-        // TRANSFERRING -> DISABLING
+        // IDLE -> DISABLING -> POWER_OFF
+        moveToIdleState();
+        moveSatelliteToDisablingState();
+        moveToPowerOffState();
+
+        // TRANSFERRING -> DISABLING request failed -> CONNECTED
+        moveToIdleState();
+        moveIdleToTransferringState();
+        moveSatelliteToDisablingRequestFailed(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED, STATE_CONNECTED);
+
+        // CONNECTED -> DISABLING -> POWER_OFF
+        moveSatelliteToDisablingState();
+        moveToPowerOffState();
+
+        // TRANSFERRING -> DISABLING -> POWER_OFF
         moveToIdleState();
         moveIdleToTransferringState();
         moveSatelliteToDisablingState();
-
-        // DISABLING -> POWER_OFF
         moveToPowerOffState();
 
-        // LISTENING -> DISABLING
+        // LISTENING -> DISABLING -> POWER_OFF
         moveToIdleState();
         moveIdleToTransferringState();
         moveTransferringToListeningState();
         moveSatelliteToDisablingState();
-
-        // DISABLING -> POWER_OFF
         moveToPowerOffState();
     }
 
@@ -986,6 +1195,41 @@
         moveToPowerOffState();
     }
 
+    @Test
+    public void testEmergencyModeChanged() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(false);
+
+        // Unregister exist callback
+        mTestSatelliteSessionController.unregisterForSatelliteModemStateChanged(
+                mTestSatelliteModemStateCallback);
+
+        // Register callback
+        mTestSatelliteSessionController.registerForSatelliteModemStateChanged(
+                mTestSatelliteModemStateCallback);
+
+        // Verify initial notification
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_OFF);
+        assertSuccessfulEmergencyModeChangedCallback(
+                mTestSatelliteModemStateCallback, false);
+
+        mTestSatelliteSessionController.onEmergencyModeChanged(true);
+
+        assertSuccessfulEmergencyModeChangedCallback(
+                mTestSatelliteModemStateCallback, true);
+
+        mTestSatelliteSessionController.onEmergencyModeChanged(false);
+
+        assertSuccessfulEmergencyModeChangedCallback(
+                mTestSatelliteModemStateCallback, false);
+
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(false);
+        mTestSatelliteSessionController.onEmergencyModeChanged(false);
+
+        assertEmergencyModeChangedCallbackNotCalled(mTestSatelliteModemStateCallback);
+    }
+
     private void setupDatagramTransferringState(boolean isTransferring) {
         when(mMockDatagramController.isSendingInIdleState()).thenReturn(isTransferring);
         when(mMockDatagramController.isPollingInIdleState()).thenReturn(isTransferring);
@@ -1080,7 +1324,6 @@
         assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
                 SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
         assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
-        assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
         verify(mMockDatagramController).onSatelliteModemStateChanged(
                 SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
         clearInvocations(mMockDatagramController);
@@ -1093,8 +1336,8 @@
         processAllMessages();
 
         // SatelliteSessionController should move to CONNECTED state
-        assertSuccessfulModemStateChangedCallback(
-                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
         assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
         verify(mMockDatagramController).onSatelliteModemStateChanged(
                 SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
@@ -1112,6 +1355,18 @@
                 STATE_DISABLING_SATELLITE, mTestSatelliteSessionController.getCurrentStateName());
     }
 
+    private void moveSatelliteToDisablingRequestFailed(int state, String stateName) {
+        moveSatelliteToDisablingState();
+
+        // Satellite disabled request failed
+        mTestSatelliteSessionController.onSatelliteEnablementFailed(false);
+        processAllMessages();
+
+        // Satellite should stay in previous state as satellite disable request failed
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback, state);
+        assertEquals(stateName, mTestSatelliteSessionController.getCurrentStateName());
+    }
+
     private static class TestSatelliteModemInterface extends SatelliteModemInterface {
         private final AtomicInteger mListeningEnabledCount = new AtomicInteger(0);
         private final AtomicInteger mListeningDisabledCount = new AtomicInteger(0);
@@ -1185,12 +1440,26 @@
         boolean isEventDeferred(int event) {
             return hasDeferredMessages(event);
         }
+
+        boolean isScreenOffInActivityTimerStarted() {
+            return hasMessages(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT);
+        }
+
+        boolean isCarrierRoamingNbIotInActivityTimerStarted() {
+            return hasMessages(EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT);
+        }
+
+        protected boolean isSatelliteEnabledForNtnOnlySubscription() {
+            return true;
+        }
     }
 
     private static class TestSatelliteModemStateCallback extends ISatelliteModemStateCallback.Stub {
         private final AtomicInteger mModemState = new AtomicInteger(
                 SatelliteManager.SATELLITE_MODEM_STATE_OFF);
-        private final Semaphore mSemaphore = new Semaphore(0);
+        private final AtomicBoolean mIsEmergency = new AtomicBoolean(false);
+        private final Semaphore mSemaphoreForModemStateChanged = new Semaphore(0);
+        private final Semaphore mSemaphoreForEmergencyModeChanged = new Semaphore(0);
         private final Object mLock = new Object();
         private final List<Integer> mModemStates = new ArrayList<>();
 
@@ -1202,15 +1471,32 @@
                 mModemStates.add(state);
             }
             try {
-                mSemaphore.release();
+                mSemaphoreForModemStateChanged.release();
             } catch (Exception ex) {
                 logd("onSatelliteModemStateChanged: Got exception, ex=" + ex);
             }
         }
 
-        public boolean waitUntilResult() {
+        @Override
+        public void onEmergencyModeChanged(boolean isEmergency) {
+            logd("onEmergencyModeChanged: state=" + isEmergency);
+            mIsEmergency.set(isEmergency);
             try {
-                if (!mSemaphore.tryAcquire(EVENT_PROCESSING_TIME_MILLIS, TimeUnit.MILLISECONDS)) {
+                mSemaphoreForEmergencyModeChanged.release();
+            } catch (Exception ex) {
+                logd("onEmergencyModeChanged: Got exception, ex=" + ex);
+            }
+        }
+
+        @Override
+        public void onRegistrationFailure(int causeCode) {
+            logd("onRegistrationFailure: causeCode=" + causeCode);
+        }
+
+        public boolean waitUntilResultForModemStateChanged() {
+            try {
+                if (!mSemaphoreForModemStateChanged.tryAcquire(EVENT_PROCESSING_TIME_MILLIS,
+                        TimeUnit.MILLISECONDS)) {
                     logd("Timeout to receive onSatelliteModemStateChanged");
                     return false;
                 }
@@ -1221,6 +1507,20 @@
             }
         }
 
+        public boolean waitUntilResultForEmergencyModeChanged() {
+            try {
+                if (!mSemaphoreForEmergencyModeChanged.tryAcquire(EVENT_PROCESSING_TIME_MILLIS,
+                        TimeUnit.MILLISECONDS)) {
+                    logd("Timeout to receive onEmergencyModeChanged");
+                    return false;
+                }
+                return true;
+            } catch (Exception ex) {
+                logd("onEmergencyModeChanged: Got exception=" + ex);
+                return false;
+            }
+        }
+
         public int getModemState() {
             return mModemState.get();
         }
@@ -1243,22 +1543,48 @@
             }
         }
 
+        public boolean getEmergencyMode() {
+            return mIsEmergency.get();
+        }
+
         public void clearSemaphorePermits() {
-            mSemaphore.drainPermits();
+            mSemaphoreForModemStateChanged.drainPermits();
         }
     }
 
     private static void assertSuccessfulModemStateChangedCallback(
             TestSatelliteModemStateCallback callback,
             @SatelliteManager.SatelliteModemState int expectedModemState) {
-        boolean successful = callback.waitUntilResult();
+        boolean successful = callback.waitUntilResultForModemStateChanged();
         assertTrue(successful);
         assertEquals(expectedModemState, callback.getModemState());
     }
 
     private static void assertModemStateChangedCallbackNotCalled(
             TestSatelliteModemStateCallback callback) {
-        boolean successful = callback.waitUntilResult();
+        boolean successful = callback.waitUntilResultForModemStateChanged();
         assertFalse(successful);
     }
+
+    private static void assertSuccessfulEmergencyModeChangedCallback(
+            TestSatelliteModemStateCallback callback,
+            boolean isEmergency) {
+        boolean successful = callback.waitUntilResultForEmergencyModeChanged();
+        assertTrue(successful);
+        assertEquals(isEmergency, callback.getEmergencyMode());
+    }
+
+    private static void assertEmergencyModeChangedCallbackNotCalled(
+            TestSatelliteModemStateCallback callback) {
+        boolean successful = callback.waitUntilResultForEmergencyModeChanged();
+        assertFalse(successful);
+    }
+
+    private void sendScreenStateChanged(Handler h, int what, boolean screenOn) {
+        Message msg = Message.obtain();
+
+        msg.what = what;
+        msg.obj = new AsyncResult(null, screenOn, null);
+        h.sendMessage(msg);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
index 62b9def..4b1b4a5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
@@ -122,8 +122,8 @@
     static final int FAKE_USER_ID2 = 11;
     static final int FAKE_SATELLITE_ATTACH_FOR_CARRIER_ENABLED = 1;
     static final int FAKE_SATELLITE_ATTACH_FOR_CARRIER_DISABLED = 0;
-    static final int FAKE_SATELLITE_IS_NTN_ENABLED = 1;
-    static final int FAKE_SATELLITE_IS_NTN_DISABLED = 0;
+    static final int FAKE_SATELLITE_IS_ONLY_NTN_ENABLED = 1;
+    static final int FAKE_SATELLITE_IS_ONLY_NTN_DISABLED = 0;
     static final int FAKE_SERVICE_CAPABILITIES_1 =
             SubscriptionManager.SERVICE_CAPABILITY_DATA_BITMASK;
     static final int FAKE_SERVICE_CAPABILITIES_2 =
@@ -132,6 +132,8 @@
     static final int FAKE_SATELLITE_ENTITLEMENT_STATUS_DISABLED = 0;
     static final String FAKE_SATELLITE_ENTITLEMENT_PLMNS1 = "123123,12310";
     static final String FAKE_SATELLITE_ENTITLEMENT_PLMNS2 = "";
+    static final int FAKE_SATELLITE_ESOS_SUPPORTED_ENABLED = 1;
+    static final int FAKE_SATELLITE_ESOS_SUPPORTED_DISABLED = 0;
 
     static final String FAKE_MAC_ADDRESS1 = "DC:E5:5B:38:7D:40";
     static final String FAKE_MAC_ADDRESS2 = "DC:B5:4F:47:F3:4C";
@@ -141,6 +143,9 @@
     static final int FAKE_TRANSFER_STATUS_TRANSFERRED_OUT = 1;
     static final int FAKE_TRANSFER_STATUS_CONVERTED = 2;
 
+    static final int FAKE_SATELLITE_PROVISIONED = 1;
+    static final int FAKE_SATELLITE_NOT_PROVISIONED = 0;
+
     static final SubscriptionInfoInternal FAKE_SUBSCRIPTION_INFO1 =
             new SubscriptionInfoInternal.Builder()
                     .setId(1)
@@ -208,12 +213,14 @@
                     .setSatelliteEnabled(0)
                     .setSatelliteAttachEnabledForCarrier(
                             FAKE_SATELLITE_ATTACH_FOR_CARRIER_DISABLED)
-                    .setOnlyNonTerrestrialNetwork(FAKE_SATELLITE_IS_NTN_DISABLED)
+                    .setOnlyNonTerrestrialNetwork(FAKE_SATELLITE_IS_ONLY_NTN_DISABLED)
                     .setGroupDisabled(false)
                     .setServiceCapabilities(FAKE_SERVICE_CAPABILITIES_1)
                     .setTransferStatus(FAKE_TRANSFER_STATUS_TRANSFERRED_OUT)
                     .setSatelliteEntitlementStatus(FAKE_SATELLITE_ENTITLEMENT_STATUS_DISABLED)
                     .setSatelliteEntitlementPlmns(FAKE_SATELLITE_ENTITLEMENT_PLMNS2)
+                    .setSatelliteESOSSupported(FAKE_SATELLITE_ESOS_SUPPORTED_DISABLED)
+                    .setIsSatelliteProvisionedForNonIpDatagram(FAKE_SATELLITE_NOT_PROVISIONED)
                     .build();
 
     static final SubscriptionInfoInternal FAKE_SUBSCRIPTION_INFO2 =
@@ -283,12 +290,14 @@
                     .setSatelliteEnabled(1)
                     .setSatelliteAttachEnabledForCarrier(
                             FAKE_SATELLITE_ATTACH_FOR_CARRIER_ENABLED)
-                    .setOnlyNonTerrestrialNetwork(FAKE_SATELLITE_IS_NTN_ENABLED)
+                    .setOnlyNonTerrestrialNetwork(FAKE_SATELLITE_IS_ONLY_NTN_ENABLED)
                     .setGroupDisabled(false)
                     .setServiceCapabilities(FAKE_SERVICE_CAPABILITIES_2)
                     .setTransferStatus(FAKE_TRANSFER_STATUS_CONVERTED)
                     .setSatelliteEntitlementStatus(FAKE_SATELLITE_ENTITLEMENT_STATUS_ENABLED)
                     .setSatelliteEntitlementPlmns(FAKE_SATELLITE_ENTITLEMENT_PLMNS1)
+                    .setSatelliteESOSSupported(FAKE_SATELLITE_ESOS_SUPPORTED_ENABLED)
+                    .setIsSatelliteProvisionedForNonIpDatagram(FAKE_SATELLITE_PROVISIONED)
                     .build();
 
     private SubscriptionDatabaseManager mDatabaseManagerUT;
@@ -450,6 +459,7 @@
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         when(mFeatureFlags.dataOnlyCellularService()).thenReturn(true);
         when(mFeatureFlags.supportPsimToEsimConversion()).thenReturn(true);
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
         mDatabaseManagerUT = new SubscriptionDatabaseManager(mContext, Looper.myLooper(),
                 mFeatureFlags, mSubscriptionDatabaseManagerCallback);
         logd("SubscriptionDatabaseManagerTest -Setup!");
@@ -2010,28 +2020,28 @@
         // exception is expected if there is nothing in the database.
         assertThrows(IllegalArgumentException.class,
                 () -> mDatabaseManagerUT.setNtn(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
-                        FAKE_SATELLITE_IS_NTN_ENABLED));
+                        FAKE_SATELLITE_IS_ONLY_NTN_ENABLED));
 
         SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
         mDatabaseManagerUT.setNtn(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
-                FAKE_SATELLITE_IS_NTN_ENABLED);
+                FAKE_SATELLITE_IS_ONLY_NTN_ENABLED);
         processAllMessages();
 
         subInfo = new SubscriptionInfoInternal.Builder(subInfo)
-                .setOnlyNonTerrestrialNetwork(FAKE_SATELLITE_IS_NTN_ENABLED)
+                .setOnlyNonTerrestrialNetwork(FAKE_SATELLITE_IS_ONLY_NTN_ENABLED)
                 .build();
         verifySubscription(subInfo);
         verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
 
         assertThat(mDatabaseManagerUT.getSubscriptionProperty(
                 FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
-                SimInfo.COLUMN_IS_NTN)).isEqualTo(FAKE_SATELLITE_IS_NTN_ENABLED);
+                SimInfo.COLUMN_IS_ONLY_NTN)).isEqualTo(FAKE_SATELLITE_IS_ONLY_NTN_ENABLED);
 
         mDatabaseManagerUT.setSubscriptionProperty(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
-                SimInfo.COLUMN_IS_NTN, FAKE_SATELLITE_IS_NTN_DISABLED);
+                SimInfo.COLUMN_IS_ONLY_NTN, FAKE_SATELLITE_IS_ONLY_NTN_DISABLED);
         assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(
                         FAKE_SUBSCRIPTION_INFO1.getSubscriptionId()).getOnlyNonTerrestrialNetwork())
-                .isEqualTo(FAKE_SATELLITE_IS_NTN_DISABLED);
+                .isEqualTo(FAKE_SATELLITE_IS_ONLY_NTN_DISABLED);
     }
 
     @Test
@@ -2074,13 +2084,13 @@
         SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
         mDatabaseManagerUT.setSatelliteAttachEnabledForCarrier(
                 FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
-                FAKE_SATELLITE_IS_NTN_DISABLED);
+                FAKE_SATELLITE_IS_ONLY_NTN_DISABLED);
         processAllMessages();
 
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false);
         reset(mSubscriptionDatabaseManagerCallback);
         subInfo = new SubscriptionInfoInternal.Builder(subInfo)
-                .setOnlyNonTerrestrialNetwork(FAKE_SATELLITE_IS_NTN_ENABLED)
+                .setOnlyNonTerrestrialNetwork(FAKE_SATELLITE_IS_ONLY_NTN_ENABLED)
                 .build();
 
         int subId = subInfo.getSubscriptionId();
@@ -2100,13 +2110,13 @@
 
         assertThat(mDatabaseManagerUT.getSubscriptionProperty(
                 FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
-                SimInfo.COLUMN_IS_NTN)).isNotEqualTo(FAKE_SATELLITE_IS_NTN_ENABLED);
+                SimInfo.COLUMN_IS_ONLY_NTN)).isNotEqualTo(FAKE_SATELLITE_IS_ONLY_NTN_ENABLED);
 
         mDatabaseManagerUT.setSubscriptionProperty(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
-                SimInfo.COLUMN_IS_NTN, FAKE_SATELLITE_IS_NTN_ENABLED);
+                SimInfo.COLUMN_IS_ONLY_NTN, FAKE_SATELLITE_IS_ONLY_NTN_ENABLED);
         assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(
                 FAKE_SUBSCRIPTION_INFO1.getSubscriptionId()).getOnlyNonTerrestrialNetwork())
-                .isNotEqualTo(FAKE_SATELLITE_IS_NTN_ENABLED);
+                .isNotEqualTo(FAKE_SATELLITE_IS_ONLY_NTN_ENABLED);
     }
 
     @Test
@@ -2357,4 +2367,69 @@
                 .getSatelliteEntitlementPlmns())
                 .isEqualTo(FAKE_SATELLITE_ENTITLEMENT_PLMNS2);
     }
+
+    @Test
+    public void testUpdateSatelliteESOSSupported() throws Exception {
+        // exception is expected if there is nothing in the database.
+        assertThrows(IllegalArgumentException.class,
+                () -> mDatabaseManagerUT.setSatelliteESOSSupported(
+                        FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+                        FAKE_SATELLITE_ESOS_SUPPORTED_ENABLED));
+
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setSatelliteESOSSupported(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+                FAKE_SATELLITE_ESOS_SUPPORTED_ENABLED);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setSatelliteESOSSupported(FAKE_SATELLITE_ESOS_SUPPORTED_ENABLED)
+                .build();
+        verifySubscription(subInfo);
+        verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+        assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+                FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+                SimInfo.COLUMN_SATELLITE_ESOS_SUPPORTED)).isEqualTo(
+                FAKE_SATELLITE_ESOS_SUPPORTED_ENABLED);
+
+        mDatabaseManagerUT.setSubscriptionProperty(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+                SimInfo.COLUMN_SATELLITE_ESOS_SUPPORTED, FAKE_SATELLITE_ESOS_SUPPORTED_DISABLED);
+        assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(
+                        FAKE_SUBSCRIPTION_INFO1.getSubscriptionId())
+                .getSatelliteESOSSupported()).isEqualTo(FAKE_SATELLITE_ESOS_SUPPORTED_DISABLED);
+    }
+
+    @Test
+    public void testUpdateSatelliteProvisionedStatus() throws Exception {
+        // exception is expected if there is nothing in the database.
+        assertThrows(IllegalArgumentException.class,
+                () -> mDatabaseManagerUT.setIsSatelliteProvisionedForNonIpDatagram(
+                        FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+                        FAKE_SATELLITE_PROVISIONED));
+
+        SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+        mDatabaseManagerUT.setIsSatelliteProvisionedForNonIpDatagram(
+                FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+                FAKE_SATELLITE_PROVISIONED);
+        processAllMessages();
+
+        subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+                .setIsSatelliteProvisionedForNonIpDatagram(FAKE_SATELLITE_PROVISIONED)
+                .build();
+        verifySubscription(subInfo);
+        verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+        assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+                FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+                SimInfo.COLUMN_IS_SATELLITE_PROVISIONED_FOR_NON_IP_DATAGRAM))
+                .isEqualTo(FAKE_SATELLITE_PROVISIONED);
+
+        mDatabaseManagerUT.setSubscriptionProperty(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+                SimInfo.COLUMN_IS_SATELLITE_PROVISIONED_FOR_NON_IP_DATAGRAM,
+                FAKE_SATELLITE_NOT_PROVISIONED);
+        assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(
+                        FAKE_SUBSCRIPTION_INFO1.getSubscriptionId())
+                .getIsSatelliteProvisionedForNonIpDatagram())
+                .isEqualTo(FAKE_SATELLITE_NOT_PROVISIONED);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
index de43b85..925cf71 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
@@ -110,7 +110,7 @@
                             SubscriptionDatabaseManagerTest
                                     .FAKE_SATELLITE_ATTACH_FOR_CARRIER_ENABLED)
                     .setOnlyNonTerrestrialNetwork(
-                            SubscriptionDatabaseManagerTest.FAKE_SATELLITE_IS_NTN_ENABLED)
+                            SubscriptionDatabaseManagerTest.FAKE_SATELLITE_IS_ONLY_NTN_ENABLED)
                     .setGroupDisabled(false)
                     .setOnlyNonTerrestrialNetwork(1)
                     .setServiceCapabilities(
@@ -122,6 +122,10 @@
                     .setSatelliteEntitlementPlmns(
                             SubscriptionDatabaseManagerTest
                                     .FAKE_SATELLITE_ENTITLEMENT_PLMNS1)
+                    .setSatelliteESOSSupported(
+                            SubscriptionDatabaseManagerTest.FAKE_SATELLITE_ESOS_SUPPORTED_ENABLED)
+                    .setIsSatelliteProvisionedForNonIpDatagram(
+                            SubscriptionDatabaseManagerTest.FAKE_SATELLITE_PROVISIONED)
                     .build();
 
     private final SubscriptionInfoInternal mSubInfoNull =
@@ -240,7 +244,7 @@
                 .isEqualTo(SubscriptionDatabaseManagerTest
                         .FAKE_SATELLITE_ATTACH_FOR_CARRIER_ENABLED);
         assertThat(mSubInfo.getOnlyNonTerrestrialNetwork()).isEqualTo(
-                SubscriptionDatabaseManagerTest.FAKE_SATELLITE_IS_NTN_ENABLED);
+                SubscriptionDatabaseManagerTest.FAKE_SATELLITE_IS_ONLY_NTN_ENABLED);
         assertThat(mSubInfo.isGroupDisabled()).isFalse();
         assertThat(mSubInfo.getOnlyNonTerrestrialNetwork()).isEqualTo(1);
         assertThat(mSubInfo.getServiceCapabilities()).isEqualTo(
@@ -252,6 +256,10 @@
         assertThat(mSubInfo.getSatelliteEntitlementPlmns())
                 .isEqualTo(SubscriptionDatabaseManagerTest
                         .FAKE_SATELLITE_ENTITLEMENT_PLMNS1);
+        assertThat(mSubInfo.getSatelliteESOSSupported())
+                .isEqualTo(SubscriptionDatabaseManagerTest.FAKE_SATELLITE_ESOS_SUPPORTED_ENABLED);
+        assertThat(mSubInfo.getIsSatelliteProvisionedForNonIpDatagram())
+                .isEqualTo(SubscriptionDatabaseManagerTest.FAKE_SATELLITE_PROVISIONED);
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
index eb06ff1..65790f8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
@@ -44,7 +44,7 @@
 import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_RCS_CONFIG1;
 import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_RCS_CONFIG2;
 import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_SATELLITE_ENTITLEMENT_PLMNS1;
-import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_SATELLITE_IS_NTN_DISABLED;
+import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_SATELLITE_IS_ONLY_NTN_DISABLED;
 import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_SUBSCRIPTION_INFO1;
 import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_SUBSCRIPTION_INFO2;
 import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_UUID1;
@@ -109,8 +109,6 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.euicc.EuiccController;
-import com.android.internal.telephony.flags.FeatureFlags;
-import com.android.internal.telephony.subscription.SubscriptionDatabaseManager.SubscriptionDatabaseManagerCallback;
 import com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.SubscriptionProvider;
 import com.android.internal.telephony.subscription.SubscriptionManagerService.BinderWrapper;
 import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback;
@@ -164,7 +162,6 @@
     // mocked
     private SubscriptionManagerServiceCallback mMockedSubscriptionManagerServiceCallback;
     private EuiccController mEuiccController;
-    private FeatureFlags mFlags;
     private BinderWrapper mBinder;
     private Set<Integer> mActiveSubs = new ArraySet<>();
 
@@ -215,9 +212,13 @@
         ((MockContentResolver) mContext.getContentResolver()).addProvider(
                 Telephony.Carriers.CONTENT_URI.getAuthority(), mSubscriptionProvider);
 
-        mFlags = Mockito.mock(FeatureFlags.class);
+        doReturn(true).when(mFeatureFlags).saferGetPhoneNumber();
+        doReturn(true).when(mFeatureFlags).uiccPhoneNumberFix();
+        doReturn(true).when(mFeatureFlags).ddsCallback();
+        doReturn(true).when(mFeatureFlags).oemEnabledSatelliteFlag();
+
         mSubscriptionManagerServiceUT = new SubscriptionManagerService(mContext, Looper.myLooper(),
-                mFlags);
+                mFeatureFlags);
 
         monitorTestableLooper(new TestableLooper(getBackgroundHandler().getLooper()));
         monitorTestableLooper(new TestableLooper(getSubscriptionDatabaseManager().getLooper()));
@@ -240,8 +241,6 @@
         doReturn(true).when(mUserManager)
                 .isManagedProfile(eq(FAKE_MANAGED_PROFILE_USER_HANDLE.getIdentifier()));
 
-        // Due to affect exist implementation, bypass feature flag.
-        doReturn(false).when(mFlags).enforceTelephonyFeatureMappingForPublicApis();
         doReturn(true).when(mPackageManager).hasSystemFeature(
                 eq(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION));
         logd("SubscriptionManagerServiceTest -Setup!");
@@ -275,12 +274,6 @@
         return (SubscriptionDatabaseManager) field.get(mSubscriptionManagerServiceUT);
     }
 
-    private SubscriptionDatabaseManagerCallback getSubscriptionDatabaseCallback() throws Exception {
-        Field field = SubscriptionDatabaseManager.class.getDeclaredField("mCallback");
-        field.setAccessible(true);
-        return (SubscriptionDatabaseManagerCallback) field.get(getSubscriptionDatabaseManager());
-    }
-
     /**
      * Insert the subscription info to the database. This is an instant insertion method. For real
      * insertion sequence please use {@link #testInsertNewSim()}.
@@ -457,7 +450,7 @@
     @Test
     @DisableCompatChanges({TelephonyManager.ENABLE_FEATURE_MAPPING})
     public void testSetPhoneNumber() {
-        doReturn(false).when(mFlags).enforceTelephonyFeatureMapping();
+        doReturn(false).when(mFeatureFlags).enforceTelephonyFeatureMapping();
         doReturn(true).when(mPackageManager).hasSystemFeature(
                 eq(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION));
 
@@ -517,7 +510,7 @@
                 mSubscriptionManagerServiceUT, vendorApiLevel);
 
         // Enabled FeatureFlags and ENABLE_FEATURE_MAPPING, telephony features are defined
-        doReturn(true).when(mFlags).enforceTelephonyFeatureMappingForPublicApis();
+        doReturn(true).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
         doReturn(true).when(mPackageManager).hasSystemFeature(
                 eq(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION));
         try {
@@ -786,6 +779,8 @@
 
         assertThat(b.containsKey(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX)).isTrue();
         assertThat(b.getInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX)).isEqualTo(1);
+
+        verify(mMockedSubscriptionManagerServiceCallback).onDefaultDataSubscriptionChanged(eq(1));
     }
 
     @Test
@@ -1281,8 +1276,8 @@
     @EnableCompatChanges({SubscriptionManagerService.REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID,
             SubscriptionManagerService.FILTER_ACCESSIBLE_SUBS_BY_USER})
     public void testIsSubscriptionAssociatedWithUserMultiSubs() {
-        doReturn(true).when(mFlags).workProfileApiSplit();
-        doReturn(true).when(mFlags).enforceSubscriptionUserFilter();
+        doReturn(true).when(mFeatureFlags).workProfileApiSplit();
+        doReturn(true).when(mFeatureFlags).enforceSubscriptionUserFilter();
         mContextFixture.addCallingOrSelfPermission(
                 Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
         insertSubscription(FAKE_SUBSCRIPTION_INFO1);
@@ -1344,8 +1339,8 @@
     public void testSubscriptionAssociationWorkProfileCallerVisibility() {
         // Split mode is defined as when a profile owns a dedicated sub, it loses the visibility to
         // the unassociated sub.
-        doReturn(true).when(mFlags).enforceSubscriptionUserFilter();
-        doReturn(true).when(mFlags).workProfileApiSplit();
+        doReturn(true).when(mFeatureFlags).enforceSubscriptionUserFilter();
+        doReturn(true).when(mFeatureFlags).workProfileApiSplit();
         mContextFixture.addCallingOrSelfPermission(
                 Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
         // Sub 1 is associated with work profile; Sub 2 is unassociated.
@@ -1480,8 +1475,8 @@
     public void testSubscriptionAssociationPersonalCallerVisibility() {
         // Split mode is defined as when a profile owns a dedicated sub, it loses the visibility to
         // the unassociated sub.
-        doReturn(true).when(mFlags).enforceSubscriptionUserFilter();
-        doReturn(true).when(mFlags).workProfileApiSplit();
+        doReturn(true).when(mFeatureFlags).enforceSubscriptionUserFilter();
+        doReturn(true).when(mFeatureFlags).workProfileApiSplit();
         mContextFixture.addCallingOrSelfPermission(
                 Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
         // Sub 1 is unassociated; Sub 2 is associated with work profile.
@@ -1832,7 +1827,7 @@
         multiNumberSubInfo =
                 new SubscriptionInfoInternal.Builder(multiNumberSubInfo)
                         .setNumberFromCarrier("")
-                        .setNumber(phoneNumberFromUicc)
+                        .setNumber("")
                         .setNumberFromIms(phoneNumberFromIms)
                         .build();
         subId = insertSubscription(multiNumberSubInfo);
@@ -2524,6 +2519,23 @@
     }
 
     @Test
+    public void testGetPhoneNumberFromUicc() {
+        mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+        testSetPhoneNumber();
+        // Number from line1Number should be FAKE_PHONE_NUMBER1 instead of FAKE_PHONE_NUMBER2
+        assertThat(mSubscriptionManagerServiceUT.getPhoneNumber(1,
+                SubscriptionManager.PHONE_NUMBER_SOURCE_UICC, CALLING_PACKAGE, CALLING_FEATURE))
+                .isEqualTo(FAKE_PHONE_NUMBER1);
+
+        doReturn("").when(mPhone).getLine1Number();
+
+        // If getLine1Number is empty, then the number should be from the sub info.
+        assertThat(mSubscriptionManagerServiceUT.getPhoneNumber(1,
+                SubscriptionManager.PHONE_NUMBER_SOURCE_UICC, CALLING_PACKAGE, CALLING_FEATURE))
+                .isEqualTo(FAKE_PHONE_NUMBER2);
+    }
+
+    @Test
     public void testGetPhoneNumberFromInactiveSubscription() {
         mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
         testInactiveSimRemoval();
@@ -2543,8 +2555,6 @@
 
     @Test
     public void testGetPhoneNumberFromDefaultSubscription() {
-        doReturn(true).when(mFlags).saferGetPhoneNumber();
-
         mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
         mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
         int subId = insertSubscription(FAKE_SUBSCRIPTION_INFO1);
@@ -3192,7 +3202,6 @@
         mContextFixture.putResource(R.string.config_satellite_sim_spn_identifier,
                 FAKE_CARRIER_NAME1);
         System.setProperty("persist.radio.allow_mock_modem", "true");
-        doReturn(true).when(mFlags).oemEnabledSatelliteFlag();
 
         EuiccProfileInfo profileInfo1 = new EuiccProfileInfo.Builder(FAKE_ICCID1)
                 .setIccid(FAKE_ICCID1)
@@ -3222,14 +3231,12 @@
         mContextFixture.putResource(R.string.config_satellite_sim_spn_identifier,
                 FAKE_CARRIER_NAME1);
         System.setProperty("persist.radio.allow_mock_modem", "false");
-        doReturn(false).when(mFlags).oemEnabledSatelliteFlag();
     }
 
     @Test
     public void testIsSatelliteSpnWithEmptySpn() {
         mContextFixture.putResource(R.string.config_satellite_sim_spn_identifier, ""); // Empty
         System.setProperty("persist.radio.allow_mock_modem", "true");
-        doReturn(true).when(mFlags).oemEnabledSatelliteFlag();
 
         EuiccProfileInfo profileInfo1 = new EuiccProfileInfo.Builder(FAKE_ICCID1)
                 .setIccid(FAKE_ICCID1)
@@ -3255,7 +3262,7 @@
         SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
                 .getSubscriptionInfoInternal(1);
         assertThat(subInfo.getOnlyNonTerrestrialNetwork())
-                .isEqualTo(FAKE_SATELLITE_IS_NTN_DISABLED);
+                .isEqualTo(FAKE_SATELLITE_IS_ONLY_NTN_DISABLED);
 
         mContextFixture.putResource(R.string.config_satellite_sim_spn_identifier,
                 FAKE_CARRIER_NAME1);
@@ -3282,10 +3289,9 @@
         subInfo = mSubscriptionManagerServiceUT
                 .getSubscriptionInfoInternal(2);
         assertThat(subInfo.getOnlyNonTerrestrialNetwork())
-                .isEqualTo(FAKE_SATELLITE_IS_NTN_DISABLED);
+                .isEqualTo(FAKE_SATELLITE_IS_ONLY_NTN_DISABLED);
 
         System.setProperty("persist.radio.allow_mock_modem", "false");
-        doReturn(false).when(mFlags).oemEnabledSatelliteFlag();
     }
 
     @Test
@@ -3293,7 +3299,6 @@
         mContextFixture.putResource(R.string.config_satellite_sim_spn_identifier,
                 FAKE_CARRIER_NAME1);
         System.setProperty("persist.radio.allow_mock_modem", "true");
-        doReturn(true).when(mFlags).oemEnabledSatelliteFlag();
 
         EuiccProfileInfo profileInfo1 = new EuiccProfileInfo.Builder(FAKE_ICCID1)
                 .setIccid(FAKE_ICCID1)
@@ -3322,7 +3327,6 @@
         mContextFixture.putResource(R.string.config_satellite_sim_spn_identifier,
                 FAKE_CARRIER_NAME1);
         System.setProperty("persist.radio.allow_mock_modem", "false");
-        doReturn(false).when(mFlags).oemEnabledSatelliteFlag();
     }
 
     @Test
@@ -3330,7 +3334,6 @@
         mContextFixture.putResource(R.string.config_satellite_sim_spn_identifier,
                 FAKE_CARRIER_NAME1);
         System.setProperty("persist.radio.allow_mock_modem", "true");
-        doReturn(true).when(mFlags).oemEnabledSatelliteFlag();
 
         EuiccProfileInfo profileInfo1 = new EuiccProfileInfo.Builder(FAKE_ICCID1)
                 .setIccid(FAKE_ICCID1)
@@ -3360,7 +3363,6 @@
         mContextFixture.putResource(R.string.config_satellite_sim_spn_identifier,
                 FAKE_CARRIER_NAME1);
         System.setProperty("persist.radio.allow_mock_modem", "false");
-        doReturn(false).when(mFlags).oemEnabledSatelliteFlag();
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
index 6f4666c..f33b002 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
@@ -66,7 +66,7 @@
     private class IsimUiccRecordsUT extends IsimUiccRecords {
         IsimUiccRecordsUT(UiccCardApplication app, Context c,
                 CommandsInterface ci, IccFileHandler mFhMock) {
-            super(app, c, ci);
+            super(app, c, ci, mFeatureFlags);
             mFh = mFhMock;
         }
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
index d2490ef..10c51c3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
@@ -74,7 +74,7 @@
         mUiccCardAppStatus.pin2 = IccCardStatus.PinState.PINSTATE_ENABLED_VERIFIED;
 
         mUiccCardApplication = new UiccCardApplication(mUiccProfile, mUiccCardAppStatus,
-            mContext, mSimulatedCommands);
+            mContext, mSimulatedCommands, mFeatureFlags);
         mHandler = new Handler() {
             @Override
             public void handleMessage(Message msg) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
index 9265a62..58a8153 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
@@ -123,7 +123,7 @@
         // (before 1.2) of hal
         mIccCardStatus.mSlotPortMapping = new IccSlotPortMapping();
         mIccCardStatus.mSlotPortMapping.mPhysicalSlotIndex = 0;
-        mUiccControllerUT = UiccController.make(mContext);
+        mUiccControllerUT = UiccController.make(mContext, mFeatureFlags);
         // reset sLastSlotStatus so that onGetSlotStatusDone always sees a change in the slot status
         mUiccControllerUT.sLastSlotStatus = null;
         processAllMessages();
@@ -145,7 +145,7 @@
                 com.android.internal.R.array.non_removable_euicc_slots,
                 nonRemovableEuiccSlots);
         replaceInstance(UiccController.class, "mInstance", null, null);
-        mUiccControllerUT = UiccController.make(mContext);
+        mUiccControllerUT = UiccController.make(mContext, mFeatureFlags);
         processAllMessages();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java
index 5c1993f..a2b42af 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java
@@ -29,7 +29,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.os.Binder;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -38,11 +37,9 @@
 
 import com.android.internal.telephony.IccLogicalChannelRequest;
 import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.flags.Flags;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -61,13 +58,9 @@
 
     private int mPhoneId = 0;
 
-    @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
-        mSetFlagsRule.enableFlags(Flags.FLAG_CLEANUP_OPEN_LOGICAL_CHANNEL_RECORD_ON_DISPOSE);
         mUiccCard = mock(UiccCard.class);
         mIccCardStatus = mock(IccCardStatus.class);
         /* initially there are no application available */
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
index 8209dfa..f88bc1e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
@@ -32,6 +32,7 @@
 import android.content.res.Resources;
 import android.os.Looper;
 import android.os.Message;
+import android.os.UserHandle;
 import android.telephony.TelephonyManager;
 
 import androidx.test.filters.SmallTest;
@@ -89,7 +90,7 @@
         }
 
         UiccStateChangedLauncher uiccLauncher =
-                new UiccStateChangedLauncher(mContext, UiccController.getInstance());
+                new UiccStateChangedLauncher(mContext, UiccController.getInstance(), mFeatureFlags);
         ArgumentCaptor<Integer> integerArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(UiccController.getInstance(), times(1)).registerForIccChanged(eq(uiccLauncher),
                 integerArgumentCaptor.capture(),
@@ -108,7 +109,8 @@
 
         // Amount of sent broadcasts to the device provisioning package.
         int broadcast_count = 1;
-        verify(mContext, times(broadcast_count)).sendBroadcast(intentArgumentCaptor.capture());
+        verify(mContext, times(broadcast_count)).sendBroadcastAsUser(intentArgumentCaptor.capture(),
+                eq(UserHandle.ALL));
         assertEquals(PROVISIONING_PACKAGE_NAME, intentArgumentCaptor.getValue().getPackage());
         assertEquals(TelephonyIntents.ACTION_SIM_STATE_CHANGED,
                 intentArgumentCaptor.getValue().getAction());
@@ -119,14 +121,16 @@
         uiccLauncher.handleMessage(msg);
 
         broadcast_count++;
-        verify(mContext, times(broadcast_count)).sendBroadcast(intentArgumentCaptor.capture());
+        verify(mContext, times(broadcast_count)).sendBroadcastAsUser(intentArgumentCaptor.capture(),
+                eq(UserHandle.ALL));
         assertEquals(PROVISIONING_PACKAGE_NAME, intentArgumentCaptor.getValue().getPackage());
         assertEquals(TelephonyIntents.ACTION_SIM_STATE_CHANGED,
                 intentArgumentCaptor.getValue().getAction());
 
         // Nothing's changed. Broadcast should not be sent.
         uiccLauncher.handleMessage(msg);
-        verify(mContext, times(broadcast_count)).sendBroadcast(any(Intent.class));
+        verify(mContext, times(broadcast_count)).sendBroadcastAsUser(any(Intent.class),
+                eq(UserHandle.ALL));
 
         // Card state's changed from restricted. Broadcast should be sent.
         card.update(mContext, mSimulatedCommands,
@@ -134,7 +138,8 @@
         uiccLauncher.handleMessage(msg);
 
         broadcast_count++;
-        verify(mContext, times(broadcast_count)).sendBroadcast(intentArgumentCaptor.capture());
+        verify(mContext, times(broadcast_count)).sendBroadcastAsUser(any(Intent.class),
+                eq(UserHandle.ALL));
         assertEquals(PROVISIONING_PACKAGE_NAME, intentArgumentCaptor.getValue().getPackage());
         assertEquals(TelephonyIntents.ACTION_SIM_STATE_CHANGED,
                 intentArgumentCaptor.getValue().getAction());
@@ -151,7 +156,7 @@
         }
 
         UiccStateChangedLauncher uiccLauncher =
-                new UiccStateChangedLauncher(mContext, UiccController.getInstance());
+                new UiccStateChangedLauncher(mContext, UiccController.getInstance(), mFeatureFlags);
         verify(UiccController.getInstance(), never()).registerForIccChanged(eq(uiccLauncher),
                 anyInt(), anyObject());
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
index c38be60..bcb5c4c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
@@ -197,7 +197,7 @@
         int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi,
                 "E00582030200009000");
         LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, responses);
-        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
         return channel;
     }
 }
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java
index d140ca8..2fef021 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java
@@ -1202,7 +1202,7 @@
         int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi,
                 "E00582030200009000");
         LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, responses);
-        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
         return channel;
     }
 
@@ -1210,7 +1210,7 @@
         int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi,
                 "E00582030201009000");
         LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, responses);
-        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
         return channel;
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
index cf3f900..172211c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
@@ -16,37 +16,52 @@
 
 package com.android.internal.telephony.uicc.euicc.apdu;
 
+import static com.android.internal.telephony.CommandException.Error.RADIO_NOT_AVAILABLE;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.os.Handler;
 import android.os.Looper;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.preference.PreferenceManager;
+import android.telephony.IccOpenLogicalChannelResponse;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.InstrumentationRegistry;
+
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.euicc.EuiccSession;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.uicc.IccIoResult;
 import com.android.internal.telephony.uicc.IccUtils;
-import androidx.test.InstrumentationRegistry;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class ApduSenderTest {
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     private static class ResponseCaptor extends ApduSenderResultCallback {
         public byte[] response;
@@ -76,6 +91,13 @@
         }
     }
 
+    private static final int PHONE_ID = 0;
+    private static final String SESSION_ID = "TEST";
+    // keep in sync with ApduSender.mChannelKey
+    private static final String SHARED_PREFS_KEY_CHANNEL_ID = "esim-channel_0";
+    // keep in sync with ApduSender.mChannelResponseKey
+    private static final String SHARED_PREFS_KEY_CHANNEL_RESPONSE = "esim-res-id_0";
+
     // Mocked classes
     private CommandsInterface mMockCi;
 
@@ -83,20 +105,20 @@
     private Handler mHandler;
     private ResponseCaptor mResponseCaptor;
     private byte[] mSelectResponse;
-    private static final String AID = "B2C3D4";
     private ApduSender mSender;
 
     @Before
     public void setUp() {
-        mMockCi = mock(CommandsInterface.class);
-        mHandler = new Handler(Looper.myLooper());
+        mSetFlagsRule.enableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER);
 
+        mMockCi = mock(CommandsInterface.class);
+        mLooper = TestableLooper.get(this);
+        mHandler = new Handler(mLooper.getLooper());
         mResponseCaptor = new ResponseCaptor();
         mSelectResponse = null;
 
-        mSender = new ApduSender(InstrumentationRegistry.getContext(), 0 /* phoneId= */,
-                            mMockCi, AID, false /* supportExtendedApdu */);
-        mLooper = TestableLooper.get(this);
+        mSender = new ApduSender(InstrumentationRegistry.getContext(), PHONE_ID,
+                            mMockCi, ApduSender.ISD_R_AID, false /* supportExtendedApdu */);
     }
 
     @After
@@ -107,12 +129,25 @@
         mResponseCaptor = null;
         mSelectResponse = null;
         mSender = null;
+
+        EuiccSession.get().endSession(SESSION_ID);
+        clearSharedPreferences();
+    }
+
+    @Test
+    public void testWrongAid_throwsIllegalArgumentException() {
+        String wrongAid = "-1";
+
+        assertThrows(IllegalArgumentException.class, () -> {
+            new ApduSender(InstrumentationRegistry.getContext(), 0 /* phoneId= */,
+                            mMockCi, wrongAid, false /* supportExtendedApdu */);
+        });
     }
 
     @Test
     public void testSendEmptyCommands() throws InterruptedException {
         int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "A1A1A19000");
-        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
 
         mSender.send((selectResponse, requestBuilder) -> mSelectResponse = selectResponse,
                 mResponseCaptor, mHandler);
@@ -121,7 +156,7 @@
         assertEquals("A1A1A19000", IccUtils.bytesToHexString(mSelectResponse));
         assertNull(mResponseCaptor.response);
         assertNull(mResponseCaptor.exception);
-        verify(mMockCi).iccOpenLogicalChannel(eq(AID), anyInt(), any());
+        verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
         verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
     }
 
@@ -137,22 +172,25 @@
         assertNull("Request provider should not be called when failed to open channel.",
                 mSelectResponse);
         assertTrue(mResponseCaptor.exception instanceof ApduException);
-        verify(mMockCi).iccOpenLogicalChannel(eq(AID), anyInt(), any());
+        verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
     }
 
     @Test
     public void testSend() throws InterruptedException {
         int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
         LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A1A1A19000");
-        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
 
         mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
                 10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
         mLooper.processAllMessages();
 
         assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response));
-        verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
-                eq(3), eq(0), eq("a"), anyBoolean(), any());
+        InOrder inOrder = inOrder(mMockCi);
+        inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
+                eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
+        inOrder.verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
     }
 
     @Test
@@ -160,7 +198,7 @@
         int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
         LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A19000", "A29000",
                 "A39000", "A49000");
-        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
 
         mSender.send((selectResponse, requestBuilder) -> {
             requestBuilder.addApdu(10, 1, 2, 3, 0, "a");
@@ -171,14 +209,17 @@
         mLooper.processAllMessages();
 
         assertEquals("A4", IccUtils.bytesToHexString(mResponseCaptor.response));
-        verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
-                eq(3), eq(0), eq("a"), anyBoolean(), any());
-        verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
-                eq(3), eq(1), eq("ab"), anyBoolean(), any());
-        verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
-                eq(3), eq(0), eq(""), anyBoolean(), any());
-        verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91),
-                eq(0), eq(2), eq("abcd"), anyBoolean(), any());
+        InOrder inOrder = inOrder(mMockCi);
+        inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
+                eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
+                eq(1), eq(2), eq(3), eq(1), eq("ab"), anyBoolean(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
+                eq(1), eq(2),  eq(3), eq(0), eq(""), anyBoolean(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81),
+                eq(0xE2), eq(0x91), eq(0), eq(2), eq("abcd"), anyBoolean(), any());
+        inOrder.verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
     }
 
     @Test
@@ -186,7 +227,7 @@
         int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
         LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A19000", "A29000",
                 "A39000", "A49000");
-        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
         mResponseCaptor.stopApduIndex = 2;
 
         mSender.send((selectResponse, requestBuilder) -> {
@@ -211,7 +252,7 @@
         int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
         LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A1A1A16104",
                 "B2B2B2B26102", "C3C39000");
-        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
 
         mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
                 10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
@@ -231,7 +272,7 @@
         int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
         LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A19000", "9000", "9000",
                 "B22B6103", "B2222B9000", "C39000");
-        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
 
         // Each segment has 0xFF (the limit of a single command) bytes.
         String s1 = new String(new char[0xFF]).replace("\0", "AA");
@@ -262,7 +303,7 @@
     public void testSendStoreDataLongDataMod0() throws InterruptedException {
         int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
         LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "9000", "B2222B9000");
-        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
 
         // Each segment has 0xFF (the limit of a single command) bytes.
         String s1 = new String(new char[0xFF]).replace("\0", "AA");
@@ -284,7 +325,7 @@
     public void testSendStoreDataLen0() throws InterruptedException {
         int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
         LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "B2222B9000");
-        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
 
         mSender.send((selectResponse, requestBuilder) -> {
             requestBuilder.addStoreData("");
@@ -301,7 +342,7 @@
         int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
         LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A19000", "9000",
                 "B22B6103", "6985");
-        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
 
         // Each segment has 0xFF (the limit of a single command) bytes.
         String s1 = new String(new char[0xFF]).replace("\0", "AA");
@@ -328,7 +369,7 @@
     @Test
     public void testChannelAlreadyOpened() throws InterruptedException {
         int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
-        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
 
         ResponseCaptor outerResponseCaptor = new ResponseCaptor();
         mSender.send(
@@ -341,6 +382,157 @@
 
         assertNull("Should not open channel when another one is already opened.", mSelectResponse);
         assertTrue(mResponseCaptor.exception instanceof ApduException);
-        verify(mMockCi, times(1)).iccOpenLogicalChannel(eq(AID), anyInt(), any());
+        verify(mMockCi, times(1)).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+    }
+
+    @Test
+    public void testConstructor_closeOpenChannelInSharedPreference() throws InterruptedException {
+        // Open a channel and not close it, by making CI.iccTransmitApduLogicalChannel throw.
+        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+        doThrow(new RuntimeException()).when(mMockCi).iccTransmitApduLogicalChannel(
+                eq(channel), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), any(),
+                anyBoolean(), any());
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+        // Stub close channel
+        reset(mMockCi);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
+
+        // Call constructor
+        mSender = new ApduSender(InstrumentationRegistry.getContext(), PHONE_ID,
+                            mMockCi, ApduSender.ISD_R_AID, false /* supportExtendedApdu */);
+        mLooper.processAllMessages();
+
+        // The constructor should have closed channel
+        verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
+        assertEquals(-1, getChannelIdFromSharedPreferences());
+    }
+
+    @Test
+    public void testSend_OpenChannelFailedNoSuchElement_useChannelInSharedPreference() {
+        // Open a channel but not close, by making CI.iccTransmitApduLogicalChannel throw.
+        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+        doThrow(new RuntimeException()).when(mMockCi).iccTransmitApduLogicalChannel(
+                eq(channel), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), any(),
+                anyBoolean(), any());
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+        reset(mMockCi);
+        // Constructor fails to close channel
+        LogicalChannelMocker.mockCloseLogicalChannel(
+                mMockCi, channel, new CommandException(RADIO_NOT_AVAILABLE));
+        mSender = new ApduSender(InstrumentationRegistry.getContext(), PHONE_ID,
+                            mMockCi, ApduSender.ISD_R_AID, false /* supportExtendedApdu */);
+        mLooper.processAllMessages();
+        reset(mMockCi);
+        // Stub open channel failure NO_SUCH_ELEMENT
+        LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi,
+                new CommandException(CommandException.Error.NO_SUCH_ELEMENT));
+        LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A1A1A19000");
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
+
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+
+        // open channel would fail, and send/close would succeed because of
+        // previous open response saved in sharedPref
+        InOrder inOrder = inOrder(mMockCi);
+        inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel),
+                 eq(channel | 10), eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
+        inOrder.verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testSend_euiccSession_shouldNotCloseChannel()
+            throws InterruptedException {
+        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+        LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A1A1A19000");
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
+        EuiccSession.get().startSession(SESSION_ID);
+
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+
+        assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response));
+        InOrder inOrder = inOrder(mMockCi);
+        inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
+                eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
+        // No iccCloseLogicalChannel
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testSendTwice_euiccSession_shouldOpenChannelOnceNotCloseChannel()
+            throws InterruptedException {
+        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+        LogicalChannelMocker.mockSendToLogicalChannel(
+                    mMockCi, channel, "A1A1A19000", "A1A1A19000");
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
+        EuiccSession.get().startSession(SESSION_ID);
+
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+
+        assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response));
+        InOrder inOrder = inOrder(mMockCi);
+        // iccOpenLogicalChannel once
+        inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+        // iccTransmitApduLogicalChannel twice
+        inOrder.verify(mMockCi, times(2)).iccTransmitApduLogicalChannel(eq(channel),
+                 eq(channel | 10), eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
+        // No iccCloseLogicalChannel
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testSendTwice_thenEndSession() throws InterruptedException {
+        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+        LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel,
+                "A1A1A19000", "A1A1A19000");
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
+        EuiccSession.get().startSession(SESSION_ID);
+
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+        EuiccSession.get().endSession(SESSION_ID);
+        mLooper.processAllMessages();
+
+        assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response));
+        InOrder inOrder = inOrder(mMockCi);
+        // iccOpenLogicalChannel once
+        inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+        // iccTransmitApduLogicalChannel twice
+        inOrder.verify(mMockCi, times(2)).iccTransmitApduLogicalChannel(eq(channel),
+                 eq(channel | 10), eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
+        // iccCloseLogicalChannel once
+        inOrder.verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
+    }
+
+    private int getChannelIdFromSharedPreferences() {
+        return PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getContext())
+                .getInt(SHARED_PREFS_KEY_CHANNEL_ID, -1);
+    }
+
+    private void clearSharedPreferences() {
+        PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getContext())
+                .edit()
+                .remove(SHARED_PREFS_KEY_CHANNEL_ID)
+                .remove(SHARED_PREFS_KEY_CHANNEL_RESPONSE)
+                .apply();
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/LogicalChannelMocker.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/LogicalChannelMocker.java
index 27f743f..e8aeed1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/LogicalChannelMocker.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/LogicalChannelMocker.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.uicc.euicc.apdu;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -30,7 +31,6 @@
 import com.android.internal.telephony.uicc.IccIoResult;
 import com.android.internal.telephony.uicc.IccUtils;
 
-import org.mockito.ArgumentCaptor;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
@@ -48,13 +48,12 @@
         int[] responseInts = isException ? null : getSelectResponse(responseObject.toString());
         Throwable exception = isException ? (Throwable) responseObject : null;
 
-        ArgumentCaptor<Message> response = ArgumentCaptor.forClass(Message.class);
         doAnswer((Answer<Void>) invocation -> {
-            Message msg = response.getValue();
+            Message msg = invocation.getArgument(2);
             AsyncResult.forMessage(msg, responseInts, exception);
             msg.sendToTarget();
             return null;
-        }).when(mockCi).iccOpenLogicalChannel(anyString(), anyInt(), response.capture());
+        }).when(mockCi).iccOpenLogicalChannel(anyString(), anyInt(), any());
         return LOGICAL_CHANNEL;
     }
 
@@ -64,22 +63,20 @@
      */
     public static void mockSendToLogicalChannel(CommandsInterface mockCi, int channel,
             Object... responseObjects) {
-        ArgumentCaptor<Message> response = ArgumentCaptor.forClass(Message.class);
-
         doAnswer(new Answer() {
             private int mIndex = 0;
 
             @Override
             public Object answer(InvocationOnMock invocation) throws Throwable {
-                Object responseObject = responseObjects[mIndex++];
-                mockIccTransmitApduLogicalChannelResponse(response, responseObject);
+                Object response = responseObjects[mIndex++];
+                mockIccTransmitApduLogicalChannelResponse(invocation.getArgument(8), response);
                 return null;
             }
         }).when(mockCi).iccTransmitApduLogicalChannel(eq(channel), anyInt(), anyInt(), anyInt(),
-                anyInt(), anyInt(), anyString(), anyBoolean(), response.capture());
+                anyInt(), anyInt(), anyString(), anyBoolean(), any());
     }
 
-    private static void mockIccTransmitApduLogicalChannelResponse(ArgumentCaptor<Message> response,
+    private static void mockIccTransmitApduLogicalChannelResponse(Message msg,
             Object responseObject) throws Throwable {
 
         boolean isException = responseObject instanceof Throwable;
@@ -95,20 +92,22 @@
         IccIoResult result = isException ? null : new IccIoResult(sw1, sw2, hex);
         Throwable exception = isException ? (Throwable) responseObject : null;
 
-        Message msg = response.getValue();
         AsyncResult.forMessage(msg, result, exception);
         msg.sendToTarget();
     }
 
-    public static void mockCloseLogicalChannel(CommandsInterface mockCi, int channel) {
-        ArgumentCaptor<Message> response = ArgumentCaptor.forClass(Message.class);
+    /**
+     * @param error can be {@code null} for a success response or an exception for a failure
+     */
+    public static void mockCloseLogicalChannel(
+            CommandsInterface mockCi, int channel, @Nullable Throwable error) {
         doAnswer((Answer<Void>) invocation -> {
-            Message msg = response.getValue();
-            AsyncResult.forMessage(msg);
+            Message msg = invocation.getArgument(2);
+            AsyncResult.forMessage(msg, null, error);
             msg.sendToTarget();
             return null;
         }).when(mockCi).iccCloseLogicalChannel(eq(channel),
-                eq(true /*isEs10*/), response.capture());
+                eq(true /*isEs10*/), any());
     }
 
     private static int[] getSelectResponse(String responseHex) {